python-peps/peps/pep-0729.rst

426 lines
22 KiB
ReStructuredText
Raw Normal View History

PEP: 729
Title: Typing governance process
Author: Jelle Zijlstra <jelle.zijlstra@gmail.com>, Shantanu Jain <hauntsaninja at gmail.com>
2023-10-04 14:36:33 -04:00
Discussions-To: https://discuss.python.org/t/pep-729-typing-governance-process/35362
Status: Draft
Type: Process
Topic: Typing
Created: 19-Sep-2023
2023-10-04 14:36:33 -04:00
Post-History: `04-Oct-2023 <https://discuss.python.org/t/pep-729-typing-governance-process/35362>`__,
`20-Sep-2023 <https://discuss.python.org/t/proposed-new-typing-governance-process/34244>`__
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 :mod:`dataclasses` module, runtime type
checking and validation by packages such as `Pydantic <https://docs.pydantic.dev/latest/>`__,
and static compilation by tools such as `mypyc <https://mypyc.readthedocs.io/en/latest/>`__.
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 :mod:`typing` module by :pep:`484`, :pep:`3156`
introduced the :mod:`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, :mod:`asyncio` and :mod:`typing` are different in an essential aspect:
a user who uses :mod:`asyncio` interacts only with the standard library itself,
while a user of :mod:`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 <https://packaging.python.org/en/latest/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 :data:`typing.NoReturn` cannot be used in
argument annotations. Nevertheless, type checkers have long accepted such
usage.
* A `2023 discussion <https://discuss.python.org/t/pep-561-clarification-regarding-n/32875>`_
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 :mod:`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 <https://github.com/python/typing/discussions/946>`_ `with methods <https://github.com/microsoft/pyright/issues/3954#issuecomment-1250098464>`_
* The concept of `recursive aliases <https://github.com/python/typing/issues/182>`_
* Semantics of `variable initialization <https://mail.python.org/archives/list/typing-sig@python.org/thread/GYVM5KEE6URE6PAH7UTK6324M7GWSFQS/#SY3KPJCAW4UTOOCH3XRJYROSGDEGOTWI>`_
* `Reachability semantics <https://github.com/python/mypy/issues/15158#issuecomment-1677915108>`__
of annotations on ``__exit__``
* `Symbol visibility <https://mail.python.org/archives/list/typing-sig@python.org/thread/YLJPWECBNPD2K4TRIBRIPISNUZJCRREY/#OX4GLBQOOCMRE5YPZEY3R3XNV6DD7XLW>`_
* Use of `NoReturn for exhaustiveness checking <https://github.com/python/mypy/issues/5818>`_
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.
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 :mod:`typing` module does follow
Python's standard compatibility policy in :pep:`387`.
Operations and process
----------------------
The council would have three 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 Steering Council would appoint the initial Typing Council. There is no term
limit for council members. Council members may resign their position at any time.
There is an expectation that at least one person will voluntarily resign from the
Typing Council each year.
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 <https://discuss.python.org/c/typing/32>`__, 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 :data:`typing.Never` as an alias for
:data:`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 <https://discuss.python.org/t/pep-561-clarification-regarding-n/32875/27>`_,
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 :mod:`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
:func:`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
:func:`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 <https://github.com/hauntsaninja/type_checker_consistency>`_.
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 <https://github.com/JelleZijlstra/typing-spec>`_.
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 end users of typing, who typically do not
need to worry about compatibility across type checkers. Such users
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" <https://docs.google.com/document/d/1mVCU-nVPT_zVfqivVdMY1aOOZqJ9lsgOLweO1U3uwUM/edit>`_
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 <https://typing.readthedocs.io/en/latest/>`_ 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 :class:`typing.Protocol`, :class:`typing.Literal`, and :class:`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.
Copyright
=========
This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.