PEP: 729 Title: Typing governance process Author: Jelle Zijlstra
<jelle.zijlstra@gmail.com>, Shantanu Jain <hauntsaninja at gmail.com>
Discussions-To:
https://discuss.python.org/t/pep-729-typing-governance-process/35362
Status: Active Type: Process Topic: Governance, Typing Created:
19-Sep-2023 Post-History: 04-Oct-2023, 20-Sep-2023 Resolution:
20-Nov-2023

Abstract

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.

Motivation

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 introduced the
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 core language.

However, asyncio and 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.NoReturn cannot 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_extensions package 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:

-   How @overload matching works
-   How ParamSpec should work with methods
-   The concept of recursive aliases
-   Semantics of variable initialization
-   Reachability semantics of annotations on __exit__
-   Symbol visibility
-   Use of NoReturn for exhaustiveness checking

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.

Endorsements

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.

Specification

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.

Mandate

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 typing module 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)

Current membership of the council is recorded in the
python/typing-council repository.

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 TypedDict items), PEP 673 (Self), and PEP 675 (Literal).
-   Adding a smaller feature, such as typing.Never as 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.

The runtime 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 appropriate.

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.

Projects

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.

Amendments

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.

Rejected ideas

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 as typing.Protocol, typing.Literal, and
typing.TypedDict 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.

Do nothing

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.

Contact

To ask the Typing Council for a decision, community members may open an
issue in the python/typing-council repository.

Copyright

This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.