Skip to content

Commit f54e614

Browse files
authored
PEP 794: Introduce Import-Namespace (#4495)
1 parent b13314f commit f54e614

File tree

1 file changed

+109
-53
lines changed

1 file changed

+109
-53
lines changed

peps/pep-0794.rst

Lines changed: 109 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ Abstract
1414
========
1515

1616
This PEP proposes extending the core metadata specification for Python
17-
packaging to include a new, repeatable field named ``Import-Name`` to record
18-
the import names that a project owns/provides once installed. A new key named
19-
``import-names`` will be added to the ``[project]`` table in
20-
``pyproject.toml`` for providing the values for the new core metadata field.
21-
This also leads to the introduction of core metadata version 2.5.
17+
packaging to include two new, repeatable fields named ``Import-Name`` and
18+
``Import-Namespace`` to record the import names that a project provides once
19+
installed. New keys named ``import-names`` and ``import-namespaces`` will be
20+
added to the ``[project]`` table in ``pyproject.toml`` for providing the values
21+
for the new core metadata field. This also leads to the introduction of core
22+
metadata version 2.5.
2223

2324

2425
Motivation
@@ -43,6 +44,13 @@ name(s) and would have their memory jogged when seeing a list of import names
4344
a package provides. Finally, tools would be able to notify users what import
4445
names will become available once they install a project.
4546

47+
There is also no easy way to know whether installing two projects will conflict
48+
with one another based on the import names they provide. For instance, if two
49+
different projects have a ``_utils`` module, installing both project will lead
50+
to a clash as one project's ``_utils`` module would take precedence over the
51+
other project's version. This issue has been
52+
`seen in the wild <https://github.com/astral-sh/uv/pull/13437>`__.
53+
4654
It may also help with spam detection. If a project specifies the same import
4755
names as a very popular project it can act as a signal to take a closer look
4856
at the validity of the less popular project. A project found to be lying
@@ -54,29 +62,31 @@ Rationale
5462

5563
This PEP proposes extending the packaging :ref:`packaging:core-metadata` so
5664
that project owners can specify the highest-level import names that a project
57-
provides and owns if installed on some platform.
65+
provides if installed on some platform.
5866

5967
Putting this metadata in the core metadata means the data is (potentially)
60-
served independently of any sdist or wheel by an index server. That negates
61-
needing to come up with another way to expose the metadata to tools to avoid
68+
served by an index server, independent of any sdist or wheel. That negates
69+
needing to come up with a way to expose the metadata to tools to avoid
6270
having to download an entire e.g. wheel.
6371

6472
Having this metadata be the same across all release artifacts would allow for
6573
projects to only have to check a single file's core metadata to get all
6674
possible import names instead of checking all the released files. This also
6775
means one does not need to worry if a file is missing when reading the core
6876
metadata or one can work solely from an sdist if the metadata is provided. As
69-
well, it simplifies having a ``project.import-names`` key in
70-
``pyproject.toml`` by having it be consistent for the entire project version
71-
and not unique per released file for the same version.
77+
well, it simplifies having ``project.import-names`` and ``project.import-namespaces``
78+
keys in ``pyproject.toml`` by having it be consistent for the entire project
79+
version and not unique per released file for the same version.
7280

7381
This PEP is not overly strict on what to (not) list in the proposed metadata on
7482
purpose. Having build back-ends verify that a project is accurately following
7583
a specification that is somehow strict about what can be listed would be near
7684
impossible to get right due to how flexible Python's import system is. As such,
7785
this PEP only requires that valid import names be used and that projects don't
7886
lie (and it is acknowledged the latter requirements cannot be validated
79-
programmatically).
87+
programmatically). Project do, though, need to account for all levels of the
88+
names they list (e.g. you can't list ``a.b.c`` and not account for ``a`` and
89+
``a.b``).
8090

8191
Various other attempts have been made to solve this, but they all have to
8292
make various trade-offs. For instance, one could download every wheel for
@@ -101,55 +111,92 @@ Specification
101111
Because this PEP introduces a new field to the core metadata, it bumps the
102112
latest core metadata version to 2.5.
103113

104-
The ``Import-Name`` field is a "multiple uses" field. Each entry of
105-
``Import-Name`` MUST be a valid import name. The names specified in
106-
``Import-Name`` MUST be importable when the project is installed on *some*
107-
platform for the same version of the project (e.g. the metadata MUST be
108-
consistent across all sdists and wheels for a project release). This does
109-
imply that the information isn't specific to the distribution artifact it is
110-
found in, but for the release version the distribution artifact belongs to.
111-
112-
Projects are not required to list every single import name that is provided.
113-
Instead, projects SHOULD list the highest-level/shortest import name that the
114-
project would "own" when installed (this includes "private" names). For
115-
example, if you install a project that has a single package named ``myproj``
116-
which itself has multiple submodules, the expectation is only ``myproj``
117-
would be listed in ``Import-Name`` and not every submodule. If a project is
118-
part of a namespace package named ``ns`` and it provides a subpackage called
119-
``ns.myproj`` (i.e. ``ns.myproj.__init__`` exists), then ``ns.myproj`` should
120-
be listed in ``Import-Name``, but NOT ``ns`` alone as that is not "owned" by
121-
the project upon installation (i.e. other projects can be installed which
122-
also contribute to ``ns``).
123-
124-
If a project chooses not to provide any ``Import-Name`` entries, tools MAY
125-
assume the import name matches the project name (including de-normalization of
126-
the project name, e.g. ``my-proj`` as ``my_proj``).
114+
The ``Import-Name`` and ``Import-Namespace`` fields are "multiple uses" fields.
115+
Each entry of both fields MUST be a valid import name. The names specified MUST
116+
be importable when the project is installed on *some* platform for the same
117+
version of the project (e.g. the metadata MUST be consistent across all sdists
118+
and wheels for a project release). This does imply that the information isn't
119+
specific to the distribution artifact it is found in, but for the release
120+
version the distribution artifact belongs to.
121+
122+
``Import-Name`` lists import names which a project, when installed, would
123+
*exclusively* provide (i.e. if two projects were installed with the same import
124+
names listed in ``Import-Name``, then one of the projects would shadow the
125+
name for the other). ``Import-Namespace`` lists import names that, when
126+
installed, would be provided by the project, but not exclusively (i.e.
127+
projects all listing the same import name in ``Import-Namespace`` being
128+
installed together would not shadow those shared names).
127129

128130
The :ref:`declaring-project-metadata` will gain an ``import-names`` key. It
129131
will be an array of strings that stores what will be written out to
130132
``Import-Name``. Build back-ends MAY support dynamically calculating the
131133
value on the user's behalf if desired, if the user declares the key in
132-
``project.dynamic``.
134+
``project.dynamic``. The same applies to ``import-namespaces`` for
135+
``Import-Namespace``.
136+
137+
Projects SHOULD list all the shortest import names that are exclusively provided
138+
by a project which would cover all import name scenarios. If any of the shortest
139+
names are dotted names, all intervening names from that name to the top-level
140+
name should also be listed appropriately in ``Import-Namespace``.
141+
For instance, a project which is a single package named ``spam`` with multiple
142+
submodules would only list ``project.import-names = ["spam"]``. A project that
143+
provides ``spam.bacon.eggs`` which is exclusively from the project while the
144+
intervening names are namespaces would have
145+
``project-names = ["spam.bacon.eggs"]`` and
146+
``project-namespaces = ["spam", "spam.bacon"]``. Listing all names acts as a
147+
check that the intent of the import names is as expected.
148+
149+
Tools SHOULD raise an error when two projects that are to be installed list
150+
names that overlap in each others' ``Import-Name`` entries. This is to avoid
151+
projects unexpectedly shadowing another project's code. The same applies to when
152+
a project has an entry in ``Import-Name`` that overlaps with another project's
153+
``Import-Namespace`` entries.
154+
155+
Tools SHOULD raise an error when an entry in ``Import-Name`` is higher than
156+
``Import-Namespace`` in the same project, e.g.
157+
``project.import-names = ["spam"]`` and
158+
``project.import-namespaces = ["spam.bacon"]``. This is because if a project
159+
exclusively owns a higher import name then that would mean it is impossible for
160+
another project to install with the same import name found in ``Import-Name``
161+
in order to contribute to the namespace listed in ``Import-Namespace``.
162+
163+
Projects MAY leave ``Import-Name`` and ``Import-Namespace`` empty. In that
164+
instance, tools SHOULD assume that the normalized project name when converted to
165+
an import name would be an entry in ``Import-Name``
166+
(i.e. ``-`` substituted for ``-`` in the normalized project name).
133167

134168

135169
Examples
136170
--------
137171

138-
`In scikit-learn 1.7.0
139-
<https://pypi-browser.org/package/scikit-learn/scikit_learn-1.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl>`__ ,
140-
an entry for the ``sklearn`` package would be used.
172+
For `scikit-learn 1.7.0
173+
<https://pypi-browser.org/package/scikit-learn/scikit_learn-1.7.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl>`__:
174+
175+
.. code-block:: TOML
176+
177+
[project]
178+
import-names = ["sklearn"]
141179
142-
`In pytest 8.3.5
180+
For `pytest 8.3.5
143181
<https://pypi-browser.org/package/pytest/pytest-8.3.5-py3-none-any.whl>`__
144182
there would be 3 expected entries:
145183

146-
1. ``_pytest``
147-
2. ``py``
148-
3. ``pytest``
184+
.. code-block:: TOML
149185
150-
In `azure-mgmt-search 9.1.0
186+
[project]
187+
import-names = ["_pytest", "py", "pytest"]
188+
189+
190+
For `azure-mgmt-search 9.1.0
151191
<https://pypi-browser.org/package/azure-mgmt-search/azure_mgmt_search-9.1.0-py3-none-any.whl>`__,
152-
there should be a single entry for ``azure.mgmt.search``.
192+
there should be two namespace entries and one name entry for
193+
``azure.mgmt.search``:
194+
195+
.. code-block:: TOML
196+
197+
[project]
198+
import-names = ["azure.mgmt.search"]
199+
import-namespaces = ["azure", "azure.mgmt"]
153200
154201
155202
Backwards Compatibility
@@ -170,13 +217,13 @@ malicious in some way.
170217
How to Teach This
171218
=================
172219

173-
Project owners should be taught that they can now record what namespaces
174-
their project provides. They should be told that if their project has a
175-
non-obvious namespace from the file structure of the project that they should
176-
specify the appropriate information. They should have it explained to them
177-
that they should use the shortest name possible that appropriately explains
178-
what the project provides (i.e. what the specification requires to be
179-
recorded).
220+
Project owners should be taught that they can now record what names their
221+
projects provide for importing. If their project name matches the module or
222+
package name their project provides they don't have to do anything. If there is
223+
a difference, though, they should record all the import names their project
224+
provides, using the shortest names possible. If any of the names are implicit
225+
namespaces, those go into ``project.import-namespaces`` in ``pyproject.toml``,
226+
otherwise the name goes into ``project.import-names``.
180227

181228
Users of projects don't necessarily need to know about this new metadata.
182229
While they may be exposed to it via tooling, the details of where that data
@@ -196,6 +243,16 @@ https://github.com/brettcannon/packaging/tree/pep-794 is a branch to update
196243
Rejected Ideas
197244
==============
198245

246+
Infer the value for ``Import-Namespace``
247+
----------------------------------------
248+
249+
A previous version of this PEP inferred what would have been the values for
250+
``Import-Namespace`` based on dotted names in ``Import-Name``. It was decided
251+
that it would better to be explicit not only to avoid mistakes by accidentally
252+
listing something that would be interpreted as an implicit namespace, but it
253+
also made the data more self-documenting.
254+
255+
199256
Re-purpose the ``Provides`` field
200257
----------------------------------
201258

@@ -266,4 +323,3 @@ Copyright
266323

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

0 commit comments

Comments
 (0)