Python 3.7 enters beta with changes to everyday interpreter assumptions
Python 3.7's first beta brings data classes and changes to annotation evaluation, text handling and bytecode caches. The release makes several implicit interpreter policies configurable.
Python programs inherit decisions from their environment before application code does much work. A locale influences text decoding. File timestamps determine whether cached bytecode looks current. An annotation can evaluate an expression while a module imports. These behaviours are easy to overlook when the environment happens to match the programmer's expectations. Several changes entering Python 3.7 address the cases where it does not.
The first Python 3.7 beta is available today, January 31. The announcement describes the release as entering its feature-complete phase, with changes still possible during beta testing. This is a preview for compatibility work, and the release team explicitly advises against production use. That distinction matters because a beta establishes something concrete for library authors to examine without promising that every implementation decision is permanent.
I appreciate Python most when a library fits its object protocols and makes ordinary code easier to read. My reservations concern the surrounding machinery, especially installation and deployment. This beta deserves attention on both grounds. Data classes reduce repetitive object definitions, while interpreter changes expose policies that otherwise depend on the machine running the program.
Data classes reuse ordinary class definitions
The what's new document in the beta source tree describes the new dataclasses module. Its decorator examines variable annotations and adds methods such as __init__, __repr__ and __eq__. The class definition supplies the fields once, allowing the generated methods to agree about those fields.
from dataclasses import dataclass
@dataclass
class Position:
x: float
y: float
z: float = 0.0
position = Position(1.5, 2.5)
print(position)
The generated representation includes the class name and field values. The default for z also becomes part of construction, so the two supplied arguments leave that coordinate at 0.0. An application no longer needs to repeat the same field names in a constructor and a representation method merely to obtain this behaviour.
This is useful because repetitive methods create several places to update when a record changes. Adding an attribute to a handwritten constructor does not automatically add it to equality or diagnostic output. Generation makes the field declaration the common input to those operations. That is a change in how a class author maintains consistency, even though the call site still constructs and accesses an ordinary object.
The beta documentation compares data classes with typing.NamedTuple and specifically notes support for mutable instances. That difference matters when the model represents something whose attributes change during processing. A concise declaration need not require the application to adopt tuple behaviour. It also gives tools a field declaration written in the language's annotation syntax instead of a separate list of attribute names.
My preference here is for the small amount of mechanism visible in the class definition. A decorator generating familiar special methods suits Python's existing object model. The important review question becomes whether those generated operations express the intended model, especially whether field-based equality is appropriate for that class.
Annotations can stop executing during definition
The same beta documentation explains two problems with immediate annotation evaluation. A referenced name may not exist yet, and evaluating annotation expressions adds work during startup. Python 3.7 introduces an opt-in change through a future import.
from __future__ import annotations
class Node:
def connect(self, other: Node) -> None:
pass
The compiler stores annotations in string form derived from their syntax. The reference to Node therefore does not need to resolve while the class body defines connect. Code that actually needs resolved annotations can use typing.get_type_hints(), as the beta documentation describes.
This changes the moment at which names must be available. It is particularly relevant to libraries that inspect annotations while building their own runtime objects. Reading an annotation and resolving it are now separate operations in modules that opt in. Consumers cannot assume that the value they retrieve is already the evaluated Python object.
The per-module switch provides a compatibility boundary. Existing modules retain their existing evaluation rules unless they request the new behaviour. Library authors can examine their annotation consumers while application authors adopt the syntax where forward references make definitions awkward. The practical issue is the agreement between the module producing annotations and the code interpreting them.
Text handling has an interpreter policy
Python 3.7 also addresses the legacy C locale's ASCII assumption. The beta's account of PEP 538 explains that the command-line interpreter can coerce that locale to an available UTF-8 locale. Setting LC_CTYPE affects both Python and locale-aware C extensions, including the example of readline given in the documentation.
That reach into extension code explains why the change involves the process locale. A Python-only decoding decision would leave native libraries making their own decisions from the old environment. Locale coercion attempts to give participating components a consistent text encoding assumption.
PEP 540 introduces a separate UTF-8 mode, controlled through -X utf8 or PYTHONUTF8. The beta documentation says this mode ignores the locale for the affected Python text behaviour. These mechanisms address related problems through different controls, so a deployment investigation needs to establish which one is active. Treating every UTF-8 result as evidence of the same configuration would obscure the role of native code.
These changes also alter error handling for standard streams in the situations the documentation describes. The details matter for command-line programs that receive bytes outside their expected encoding. A program's behaviour depends on both the selected encoding and the error handler, so a successful startup alone does not explain how its input will be interpreted.
Cached bytecode can depend on source content
The beta documentation describes the old bytecode-cache check as a comparison of source timestamp and size against metadata in the cache header. Coarse timestamps can miss an update. Timestamps also interfere with reproducible builds because identical source content need not have identical filesystem metadata.
Hash-based cache files let source content determine validity. Checked variants validate against source at runtime. Unchecked variants leave freshness to an external system responsible for keeping the files consistent. Python still defaults to timestamp-based invalidation, and hash-based files can be generated through py_compile or compileall.
That division of responsibility is useful for packaging systems. A build process that controls source and bytecode together can make a different choice from an interpreter loading files edited in place. Python 3.7's beta exposes that distinction in the cache format, giving package builders a way to represent their actual ownership of cache freshness.