@@ -14,11 +14,12 @@ Abstract
14
14
========
15
15
16
16
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.
22
23
23
24
24
25
Motivation
@@ -43,6 +44,13 @@ name(s) and would have their memory jogged when seeing a list of import names
43
44
a package provides. Finally, tools would be able to notify users what import
44
45
names will become available once they install a project.
45
46
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
+
46
54
It may also help with spam detection. If a project specifies the same import
47
55
names as a very popular project it can act as a signal to take a closer look
48
56
at the validity of the less popular project. A project found to be lying
@@ -54,29 +62,31 @@ Rationale
54
62
55
63
This PEP proposes extending the packaging :ref: `packaging:core-metadata ` so
56
64
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.
58
66
59
67
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
62
70
having to download an entire e.g. wheel.
63
71
64
72
Having this metadata be the same across all release artifacts would allow for
65
73
projects to only have to check a single file's core metadata to get all
66
74
possible import names instead of checking all the released files. This also
67
75
means one does not need to worry if a file is missing when reading the core
68
76
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.
72
80
73
81
This PEP is not overly strict on what to (not) list in the proposed metadata on
74
82
purpose. Having build back-ends verify that a project is accurately following
75
83
a specification that is somehow strict about what can be listed would be near
76
84
impossible to get right due to how flexible Python's import system is. As such,
77
85
this PEP only requires that valid import names be used and that projects don't
78
86
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 ``).
80
90
81
91
Various other attempts have been made to solve this, but they all have to
82
92
make various trade-offs. For instance, one could download every wheel for
@@ -101,55 +111,92 @@ Specification
101
111
Because this PEP introduces a new field to the core metadata, it bumps the
102
112
latest core metadata version to 2.5.
103
113
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).
127
129
128
130
The :ref: `declaring-project-metadata ` will gain an ``import-names `` key. It
129
131
will be an array of strings that stores what will be written out to
130
132
``Import-Name ``. Build back-ends MAY support dynamically calculating the
131
133
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).
133
167
134
168
135
169
Examples
136
170
--------
137
171
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"]
141
179
142
- ` In pytest 8.3.5
180
+ For ` pytest 8.3.5
143
181
<https://pypi-browser.org/package/pytest/pytest-8.3.5-py3-none-any.whl> `__
144
182
there would be 3 expected entries:
145
183
146
- 1. ``_pytest ``
147
- 2. ``py ``
148
- 3. ``pytest ``
184
+ .. code-block :: TOML
149
185
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
151
191
<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"]
153
200
154
201
155
202
Backwards Compatibility
@@ -170,13 +217,13 @@ malicious in some way.
170
217
How to Teach This
171
218
=================
172
219
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 `` .
180
227
181
228
Users of projects don't necessarily need to know about this new metadata.
182
229
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
196
243
Rejected Ideas
197
244
==============
198
245
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
+
199
256
Re-purpose the ``Provides `` field
200
257
----------------------------------
201
258
@@ -266,4 +323,3 @@ Copyright
266
323
267
324
This document is placed in the public domain or under the
268
325
CC0-1.0-Universal license, whichever is more permissive.
269
- CC0-1.0-Universal license, whichever is more permissive.
0 commit comments