PEP 561 v2 - Packaging Static Type Information

Hello, V2 of my PEP on packaging type information is available at https://www.python.org/dev/peps/pep-0561/. It is also replicated below. I look forward to any suggestions or comments that people may have! Thanks Ethan ----------------------------------------------------------------------- PEP: 561 Title: Distributing and Packaging Type Information Author: Ethan Smith <ethan@ethanhs.me> Status: Draft Type: Standards Track Content-Type: text/x-rst Created: 09-Sep-2017 Python-Version: 3.7 Post-History: Abstract ======== PEP 484 introduced type hints to Python, with goals of making typing gradual and easy to adopt. Currently, typing information must be distributed manually. This PEP provides a standardized means to package and distribute type information and an ordering for type checkers to resolve modules and collect this information for type checking using existing packaging architecture. Rationale ========= Currently, package authors wish to distribute code that has inline type information. However, there is no standard method to distribute packages with inline type annotations or syntax that can simultaneously be used at runtime and in type checking. Additionally, if one wished to ship typing information privately the only method would be via setting ``MYPYPATH`` or the equivalent to manually point to stubs. If the package can be released publicly, it can be added to typeshed [1]_. However, this does not scale and becomes a burden on the maintainers of typeshed. Additionally, it ties bugfixes to releases of the tool using typeshed. PEP 484 has a brief section on distributing typing information. In this section [2]_ the PEP recommends using ``shared/typehints/pythonX.Y/`` for shipping stub files. However, manually adding a path to stub files for each third party library does not scale. The simplest approach people have taken is to add ``site-packages`` to their ``MYPYPATH``, but this causes type checkers to fail on packages that are highly dynamic (e.g. sqlalchemy and Django). Specification ============= There are several motivations and methods of supporting typing in a package. This PEP recognizes three (3) types of packages that may be created: 1. The package maintainer would like to add type information inline. 2. The package maintainer would like to add type information via stubs. 3. A third party would like to share stub files for a package, but the maintainer does not want to include them in the source of the package. This PEP aims to support these scenarios and make them simple to add to packaging and deployment. The two major parts of this specification are the packaging specifications and the resolution order for resolving module type information. This spec is meant to replace the ``shared/typehints/pythonX.Y/`` spec of PEP 484 [2]_. New third party stub libraries are encouraged to distribute stubs via the third party packaging proposed in this PEP in place of being added to typeshed. Typeshed will remain in use, but if maintainers are found, third party stubs in typeshed are encouraged to be split into their own package. Packaging Type Information -------------------------- Packages must opt into supporting typing. This will be done though a distutils extension [3]_, providing a ``typed`` keyword argument to the distutils ``setup()`` command. The argument value will depend on the kind of type information the package provides. The new keyword will be added to Python 3.7 and will also be accessible in the ``typing_extensions`` package though a distutils extension. This enables a package maintainer to write :: setup( ... setup_requires=["typing_extensions"], typed="inline", ... ) Inline Typed Packages ''''''''''''''''''''' Packages that have inline type annotations simply have to pass the value ``"inline"`` to the ``typed`` argument in ``setup()``. Stub Only Packages '''''''''''''''''' For package maintainers wishing to ship stub files containing all of their type information, it is prefered that the ``*.pyi`` stubs are alongside the corresponding ``*.py`` files. However, the stubs may be put in a sub-folder of the Python sources, with the same name the ``*.py`` files are in. For example, the ``flyingcircus`` package would have its stubs in the folder ``flyingcircus/flyingcircus/``. This path is chosen so that if stubs are not found in ``flyingcircus/`` the type checker may treat the subdirectory as a normal package. The normal resolution order of checking ``*.pyi`` before ``*.py`` will be maintained. The value of the ``typed`` argument to ``setup()`` is ``"stubs"`` for this type of distribution. The author of the package is suggested to use ``package_data`` to assure the stub files are installed alongside the runtime Python code. Third Party Stub Packages ''''''''''''''''''''''''' Third parties seeking to distribute stub files are encouraged to contact the maintainer of the package about distribution alongside the package. If the maintainer does not wish to maintain or package stub files or type information inline, then a "third party stub package" should be created. The structure is similar, but slightly different from that of stub only packages. If the stubs are for the library ``flyingcircus`` then the package should be named ``flyingcircus-stubs`` and the stub files should be put in a sub-directory named ``flyingcircus``. This allows the stubs to be checked as if they were in a regular package. These packages should also pass ``"stubs"`` as the value of ``typed`` argument in ``setup()``. These packages are suggested to use ``package_data`` to package stub files. In addition, the package should indicate which version(s) of the runtime package are supported via the ``install_requires`` argument to ``setup()``. Type Checker Module Resolution Order ------------------------------------ The following is the order that type checkers supporting this PEP should resolve modules containing type information: 1. User code - the files the type checker is running on. 2. Stubs or Python source manually put in the beginning of the path. Type checkers should provide this to allow the user complete control of which stubs to use, and patch broken stubs/inline types from packages. 3. Third party stub packages - these packages can supersede the installed untyped packages. They can be found at ``pkg-stubs`` for package ``pkg``, however it is encouraged to check their metadata to confirm that they opt into type checking via the ``typed`` keyword. 4. Inline packages - finally, if there is nothing overriding the installed package, and it opts into type checking. 5. Typeshed (if used) - Provides the stdlib types and several third party libraries When resolving step (3) type checkers should assure the version of the stubs is compatible with the installed runtime package through the method described above. Type checkers that check a different Python version than the version they run on must find the type information in the ``site-packages``/``dist-packages`` of that Python version. This can be queried e.g. ``pythonX.Y -c 'import sys; print(sys.exec_prefix)'``. It is also recommended that the type checker allow for the user to point to a particular Python binary, in case it is not in the path. To check if a package has opted into type checking, type checkers are recommended to use the ``pkg_resources`` module to query the package metadata. If the ``typed`` package metadata has ``None`` as its value, the package has not opted into type checking, and the type checker should skip that package. References ========== .. [1] Typeshed (https://github.com/python/typeshed) .. [2] PEP 484, Storing and Distributing Stub Files (https://www.python.org/dev/peps/pep-0484/#storing-and-distributing-stub-file...) .. [3] Distutils Extensions, Adding setup() arguments (http://setuptools.readthedocs.io/en/latest/setuptools.html#adding-setup-argu...) Copyright ========= This document has been placed in the public domain. .. Local Variables: mode: indented-text indent-tabs-mode: nil sentence-end-double-space: t fill-column: 70 coding: utf-8 End:

On 13 September 2017 at 09:43, Ethan Smith <ethan@ethanhs.me> wrote:
There are a lot of packaging tools in use other than distutils, so I don't think the distutils update proposal belongs in the PEP. Rather, the PEP should focus on defining how type analysers should search for typing information, and then updating packaging tools to help with that can be treated as separate RFEs for each of the publishing tools that people use (perhaps with a related task-oriented guide on packaging.python.org)
I'm not clear on how this actually differs from the existing search protocol in PEP 484, since step 3 is exactly what the 'shared/typehints/pythonX.Y' directory is intended to cover. Is it just a matter allowing the use of "<name>-stubs" as the typehint installation directory, since installing under a different package name is easier to manage using existing publishing tools than installing to a different target directory? Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Tue, Sep 12, 2017 at 8:30 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
I think this makes a lot of sense. Would a description of the package metadata being queried suffice to be generic enough? And a guide on packaging.python.org makes a lot of sense, thank you for the suggestion!
Perhaps I could be clearer in the PEP text on this. The idea is that people can ship normal sdists (or what have you) and install those to the package installation directory. Then the type checkers would pick up `pkg-stub` when looking for `pkg` type information via the package API. This allows a third party to ship just *.pyi files in a package and install it as if it were the runtime package, but still be picked up by type checkers. This is different than using 'shared/typehints/pythonX.Y' because that directory cannot be queried by package resource APIs, and since no type checker implements PEP 484's method, I thought it would be better to have everything be unified under the same system of installing packages. So I suppose that is a rather long, yes. :)

On 13 September 2017 at 14:33, Ethan Smith <ethan@ethanhs.me> wrote:
It would - a spec to say "Typecheckers should look for <x> to learn <y>" and "Publishers should provide <x> to tell typecheckers <y>". PEP 376 is the current definition of the installed package metadata, so if you describe this idea in terms of *.dist-info/METADATA entries, then folks will be able to translate that to the wheel archive format and the various legacy install db formats.
OK, it wasn't clear to me that none of the current typecheckers actually implement looking for extra stubs in 'shared/typehints/pythonX.Y' . In that case, it makes a lot of sense to me to try to lower barriers to adoption by switching to a scheme that's more consistent with the way Python packaging and installation tools already work, and a simple suffix-based shadow tree approach makes a lot of sense to me from the packaging perspective (I'll leave it to the folks actually working on mypy et al to say how the feel about this more decentralised approach to managing 3rd party stubs). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On 13 September 2017 at 09:43, Ethan Smith <ethan@ethanhs.me> wrote:
There are a lot of packaging tools in use other than distutils, so I don't think the distutils update proposal belongs in the PEP. Rather, the PEP should focus on defining how type analysers should search for typing information, and then updating packaging tools to help with that can be treated as separate RFEs for each of the publishing tools that people use (perhaps with a related task-oriented guide on packaging.python.org)
I'm not clear on how this actually differs from the existing search protocol in PEP 484, since step 3 is exactly what the 'shared/typehints/pythonX.Y' directory is intended to cover. Is it just a matter allowing the use of "<name>-stubs" as the typehint installation directory, since installing under a different package name is easier to manage using existing publishing tools than installing to a different target directory? Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Tue, Sep 12, 2017 at 8:30 PM, Nick Coghlan <ncoghlan@gmail.com> wrote:
I think this makes a lot of sense. Would a description of the package metadata being queried suffice to be generic enough? And a guide on packaging.python.org makes a lot of sense, thank you for the suggestion!
Perhaps I could be clearer in the PEP text on this. The idea is that people can ship normal sdists (or what have you) and install those to the package installation directory. Then the type checkers would pick up `pkg-stub` when looking for `pkg` type information via the package API. This allows a third party to ship just *.pyi files in a package and install it as if it were the runtime package, but still be picked up by type checkers. This is different than using 'shared/typehints/pythonX.Y' because that directory cannot be queried by package resource APIs, and since no type checker implements PEP 484's method, I thought it would be better to have everything be unified under the same system of installing packages. So I suppose that is a rather long, yes. :)

On 13 September 2017 at 14:33, Ethan Smith <ethan@ethanhs.me> wrote:
It would - a spec to say "Typecheckers should look for <x> to learn <y>" and "Publishers should provide <x> to tell typecheckers <y>". PEP 376 is the current definition of the installed package metadata, so if you describe this idea in terms of *.dist-info/METADATA entries, then folks will be able to translate that to the wheel archive format and the various legacy install db formats.
OK, it wasn't clear to me that none of the current typecheckers actually implement looking for extra stubs in 'shared/typehints/pythonX.Y' . In that case, it makes a lot of sense to me to try to lower barriers to adoption by switching to a scheme that's more consistent with the way Python packaging and installation tools already work, and a simple suffix-based shadow tree approach makes a lot of sense to me from the packaging perspective (I'll leave it to the folks actually working on mypy et al to say how the feel about this more decentralised approach to managing 3rd party stubs). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
participants (2)
-
Ethan Smith
-
Nick Coghlan