PEP 729 – Typing governance process
- Jelle Zijlstra <jelle.zijlstra at gmail.com>, Shantanu Jain <hauntsaninja at gmail.com>
- Discourse thread
- Governance, Typing
- 04-Oct-2023, 20-Sep-2023
- Discourse message
Table of Contents
- Rejected ideas
This PEP proposes a new way to govern the Python type system: a council that is responsible for maintaining and developing the Python type system. The council will maintain a specification and conformance test suite and will initially be appointed by the Python Steering Council.
The Python type system was created by PEP 484, almost ten years ago. The type
system is now widely used, and typing has become an important tool for writing
good, maintainable Python code. Many changes have been made to the type system to cover
more use cases and improve usability. Several type checkers have been created, each
with their own strengths. The type annotation syntax has driven several major innovations
in the Python ecosystem, such as the popular
dataclasses module, runtime type
checking and validation by packages such as Pydantic,
and static compilation by tools such as mypyc.
However, as the type system has grown, several interrelated problems with the current way to manage the type system have become apparent.
PEPs are the only specification
The Python type system was initially created by a PEP (PEP 484), and changes to the type system are still made by PEPs. The specification for the Python type system, to the extent there is one, consists of this series of PEPs. But Standards Track PEPs aren’t meant to be living documents or specifications; they are change proposals.
An example may illustrate the problem here. Around the same time
as the introduction of the
typing module by PEP 484, PEP 3156
asyncio module, another major new feature that has
been instrumental to the success of Python 3. Both modules
have evolved greatly since their initial creation and inspired changes to the
typing are different in an essential aspect:
a user who uses
asyncio interacts only with the standard library itself,
while a user of
typing has to also think about an external tool, the type
checker. The Python language reference covers the symbols in the typing module, but does
not (and should not) go into detail on how the full type system should be
interpreted by type checkers. That material currently exists only in the PEPs.
This problem is shared by the packaging ecosystem, which attempts to solve it by maintaining a separate set of PyPA specifications.
It’s hard to evolve the specification
Because the PEPs are the only specification we have, anything that could be seen as a change to the specification would theoretically require a new PEP. But that is often too heavy a process for a small change. Sometimes changes are made directly to old PEPs instead, but that goes against the idea that accepted and implemented PEPs become historical documents that should no longer be changed.
Some concrete examples include:
- PEP 484 explicitly says that
typing.NoReturncannot be used in argument annotations. Nevertheless, type checkers have long accepted such usage.
- A 2023 discussion noted that PEP 561’s description of partial stubs is unclear, and major type checkers did not implement it exactly as specified.
- The widely used third-party
typing_extensionspackage provides backports of new type system features. Type checkers are expected to treat symbols in this module the same as symbols in
typing, but this is not explicitly specified in any of the PEPs.
The type system is underspecified
While the PEPs provide a specification, they are often not sufficiently precise (sometimes intentionally so). This is especially true as the combinatorial complexity of the type system has grown.
It ends up falling to individual type checkers to decide how to navigate underspecified areas. In cases where type checkers informally coordinate, this results in de facto standards that aren’t clearly recorded anywhere, making the type system less accessible to newcomers. For example:
The Steering Council is not well-placed to solve the above problems
The SC has the entire language in its remit, and is not well-placed to make decisions that are purely about the type system – if only because they don’t have the time to deal with type system arcana alongside their other responsibilities. This is similar in spirit to the reasons why the Steering Council sometimes uses PEP delegation.
This PEP was endorsed by maintainers of all major type checkers, including Rebecca Chen (pytype), Eric Traut (Pyright), and privately by maintainers of mypy and Pyre.
We propose the creation of a new group, the Typing Council. This group will be responsible for developing and maintaining the Python type system, and for solving the above problems.
The “operations and process” section describes how this group would operate and be governed.
The more exciting “projects” section describes solutions to the above problems that the Typing Council could shepherd.
The Typing Council’s mandate is to ensure that the Python type system is:
- Useful: The type system should serve common use cases. As identified by PEP 484, the primary use case is static analysis, but there are others, such as runtime type checking, static compilation, IDE support, and documentation. The Typing Council should consider all of these use cases when making decisions, and be open to supporting additional use cases as they come up.
- Usable: The type system should be easy to use for Python developers. It should be ergonomic to write well-typed Python code that is accepted by type checkers. There should be good documentation for the type system.
- Stable: As the type system matures, users should be able to rely on their
typed code continuing to work and be able to trust their mental model for the
type system. Changes should be made with care and in a way
that minimizes disruption. Nevertheless, the type system should be able to
evolve, and it does not make sense to use the same compatibility guidelines
for type checker behavior as for Python itself. Of course, the existence
and runtime behavior of objects in the
typingmodule does follow Python’s standard compatibility policy in PEP 387.
Operations and process
The council would have three to five members, comprised of prominent community members, such as Python core developers and maintainers of major type checkers. The members should include people affiliated with a variety of projects related to type checking, which may include type checkers, CPython, typeshed, or other projects.
The initial members of the council are:
- Eric Traut (Pyright; author of PEP 647, PEP 681, and PEP 695)
- Guido van Rossum (core developer; author of PEP 484 and PEP 526)
- Jelle Zijlstra (core developer; typeshed; pyanalyze; author of PEP 688 and PEP 702)
- Rebecca Chen (pytype)
- Shantanu Jain (core developer; typeshed; mypy)
There is no term limit for council members. Council members may resign their position at any time. There is an expectation that each member serves at most five consecutive years before resigning.
If there is a vacancy and there are three or more remaining members, it is up to the Council to decide whether to appoint a new member. To determine replacements, nominations will be collected from the typing community. Self-nominations are allowed. The existing Typing Council will then decide the replacement member(s) from the nominees. The expectation is that this would be done by fiat, but the Typing Council can choose a replacement by any means they see fit, including a vote.
The Typing Council remains accountable to the Steering Council. At any point, for any reason, the Steering Council could (publicly or privately) make a specific change or request a non-specific change to the composition of the Typing Council.
We acknowledge that this is a not particularly democratic structure and puts a lot of faith in the Typing Council. However, the Python community has a long history of success with not particularly democratic structures! We believe self-governance, cycling of membership, and accountability to the Steering Council will be sufficient to ensure that the Typing Council is meeting the needs of the community.
The council would operate primarily through reviews of GitHub PRs. Regular meetings are likely not necessary, but the council may set up video calls, a private chat, or whatever other mechanism they decide upon internally.
The council should aim for transparency, posting all decisions publicly on discuss.python.org, with a rationale if possible. Before making a decision, the council should give all interested community members a chance to weigh in. There should be at least a week between the start of a discussion and the council’s decision.
Members of the council will be eligible to sponsor PEPs. If this PEP is accepted, PEP 1 should be amended to note this fact.
Relationship with the Steering Council
Just like today, the Python Steering Council would remain responsible for the overall direction of the Python language and would continue to decide on typing-related PEPs. The Typing Council would provide written opinions and recommendations to the Steering Council on typing-related PEPs.
However, smaller changes to the type system could be made by the Typing Council directly. The Steering Council could also choose to delegate decisions on some PEPs to the Typing Council (exactly as any other PEP delegation).
Some examples of how past and recent issues could have been handled under this model:
- A PEP like PEP 695 (type parameter syntax), which changes the language syntax, would need to be decided upon by the Steering Council; the Typing Council would merely provide opinion or endorsement. Similarly, PEPs like PEP 702 (deprecations) would be decided upon by the Steering Council, because it concerns runtime behaviour beyond pure typing. Other examples that would need to be decided by the SC include PEP 718 (subscriptable functions) and PEP 727 (documentation metadata).
- A PEP like PEP 698 (
@override), which affects only users of type checkers and does not change the overall language, would also by default be decided upon by the Steering Council. However, such PEPs could be delegated to the Typing Council for a decision (like any other PEP delegation). Other examples of PEPs that could potentially be delegated include PEP 647 (type guards), PEP 655 (individual required
TypedDictitems), PEP 673 (
Self), and PEP 675 (
- Adding a smaller feature, such as
typing.Neveras an alias for
typing.NoReturn, would be done by means of a PR to the spec and conformance test suite. The Typing Council would then decide whether or not to merge the PR. They may ask for the feature to be specified and discussed in a PEP if they feel that is warranted.
- If there is confusion about the interpretation of some part of the spec, like happened recently with partial stubs in PEP 561, somebody would make a PR to the typing specification to clarify the spec, and then the Typing Council would decide on the spec change.
typing module will continue to be maintained by the
CPython core developer team. However, any changes to the runtime module that
affect type checker behavior should be made in conjunction with a change
to the specification (see below) and should be approved by the Typing Council.
For example, in Python 3.11 the core developers added the new function
typing.assert_type(). If the Typing Council had been in place, this
change would require a matching change to the specification and approval
by the Typing Council. On the other hand, Python 3.11 also added the
typing.get_overloads() introspection helper. As this function does not
affect type checker behavior, it would not require approval by the Typing
Council. However, as support for runtime type checkers is within the remit
of the Council, they should monitor such changes and provide feedback when
Relationship with type checkers
The Typing Council has no direct authority over type checkers; it cannot
force them to implement particular features or make behavior changes. Type
checkers are incentivized to follow the specification set out by the Council
because it allows them to take advantage of shared resources, such as
libraries that expose typing information that follows the specification,
the stub files in typeshed, the
typing standard library module, and
user documentation that covers the standard type system.
Type checkers are free to extend the type system or deviate from the
specification, but they should document such differences clearly.
The fact that type checkers need to implement any decisions made by the Typing Council acts as a useful brake on the Council, ensuring that its decisions are conservative and well-considered. Individual type checkers remain free to innovate as they see fit, and successful innovations can be incorporated into the standard type system.
Here are some efforts a Typing Council would be responsible for.
Conformance test suite
A conformance test suite would provide machine checkable documentation for how type checkers should check Python code, accompanied by the results of major type checker implementations on the test suite. A rough sketch for what this could look like was created by Shantanu.
This would contain prescriptive tests from behavior prescribed by previous PEPs and descriptive tests that let us document behavior of existing implementations in areas that are not prescribed by any standard. These descriptions would be useful to inform efforts below and to identify areas of focus for standardization.
Specification for the type system
A specification would initially be created by stitching together the specification sections from the existing PEPs, and then gradually improved to clarify points of confusion and cover more areas. A draft of such a stitched-together spec was created by Jelle.
The specification has a few audiences:
- For type checkers, it provides a description of how an idealized type checker should behave. Individual type checkers have different goals and technical constraints and they are free to deviate from the spec if they do not have the resources to fully implement it or if they believe a different behavior better serves their users. However, they should document such deviations from the spec.
- For projects such as typeshed, or libraries that want to be compatible with multiple type checkers, it provides a set of rules that they can follow to make their code understood by type checkers.
- For people who want to propose changes to the type system, it provides a foundation for any new proposals.
Notably, the specification is not aimed at application developers who use typing. Such users typically do not need to worry about compatibility across type checkers. They are better served by a more informal user-facing reference, which is discussed in the next section.
There are different opinions within the community about how formal such a specification should be. While this document recommends an incremental approach that builds off existing specification, it does not aim to prescribe a final state. The Typing Council would provide a mechanism to allow the specification to evolve to meet the level of formality that the community desires, for instance, by incorporating parts of Kevin Millikin’s document on “Python Static Types” as a means to achieve a better formalisation of the spec.
Proposed changes to the specification, including PEPs, should generally be accompanied by the following:
- Buy-in from type checker maintainers to confirm that the change can be implemented and maintained within their type checkers.
- For changes to existing features, a survey of the behavior of existing type checkers. If existing type checkers behave roughly similarly, that is evidence that their shared behavior should be made part of the specification.
- Changes to the conformance test suite that demonstrate the specified behavior.
User-facing reference for the type system
Documentation is important for the success of the Python type system, so the Typing Council should ensure that there is good documentation for the type system.
As mentioned previously, PEPs are point in time change proposals aimed at multiple audiences that are hard to clarify. This makes them ill-suited as user documentation. The specification discussed in the previous section would be a living document, but it would likely be too technical to serve as documentation for normal usage.
Therefore, a separate user-facing reference for the type system would be useful. Such an effort could expand the documentation on typing.readthedocs.io and reuse material from the documentation sections of individual type checkers and the CPython documentation.
This PEP serves as a charter for the Typing Council. Changes to its operation can be made either through a new PEP or through a change to this PEP. In either case, the change would be decided upon by the Steering Council after discussion in the community.
Writing the specification from scratch
This PEP proposes creating the typing specification by starting from the existing PEPs, then clarifying and improving the specification as necessary. Some members of the community prefer to start from scratch, writing a new, more formal specification covering the entire type system. This could provide a more solid basis for the specification.
However, this would be a much larger undertaking. The existing formalization
effort by Kevin Millikin is a good start, but so far covers only a subset of
PEP 484. Covering the rest of the type system would likely require several
times more effort when we consider that major type system features such
were introduced only after PEP 484. It is not clear that there is even energy
in the community for such a huge undertaking. Even if someone steps up to
do all the work of putting together a specification, lots of effort would be
required from community members and type checker maintainers to consider
whether the specification accurately reflects current behavior, and if not,
whether the specification or the type checkers should change.
Starting with the existing PEPs creates a lower-quality specification, but it means that the Typing Council can immediately start making a difference anywhere in the type system by improving and clarifying the specification. A formalization effort can still proceed by gradually replacing sections of the specification.
Alternate governance mechanisms
An earlier draft of this PEP suggested that the Steering Council appoint members of the Typing Council each year. The current Steering Council suggested that it would be better to have the Typing Council self-organise and avoid the need for the Steering Council to continuously supervise the Typing Council.
Alternate governance mechanisms are possible, including more democratic ones, but these typically raise several thorny questions, require much heavier amounts of process and are potentially more divisive. For example, see the PEP 8000 series, or recent discussions about alternative governance in other Python subcommunities. Ultimately, the Typing Council exists under the authority of the Steering Council, and so can rely on it to bootstrap governance and serve as an accountability mechanism.
We are hopeful substantial progress will be made on projects that improve the type system regardless of whether this PEP is accepted. We anticipate projects like specification or the potential for PEP delegation would benefit more from a Typing Council, and projects like end user documentation would benefit less. Certainly the bottleneck is likely to be contributor effort, not governance.
However, currently the tools available to the community to resolve potential contention are either establishment of approximate consensus or the exercise of power by individual projects or contributors. While very valuable, the former is a slow process that can often end in inaction. The latter can result in a less consistent ecosystem. Finally, easily legible governance structures make the community more accessible and equitable.
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.
Last modified: 2023-11-24 23:06:11 GMT