|
| 1 | +PEP: 794 |
| 2 | +Title: Import Name Metadata |
| 3 | +Author: Brett Cannon <brett@python.org> |
| 4 | +Discussions-To: Pending |
| 5 | +Status: Draft |
| 6 | +Type: Standards Track |
| 7 | +Topic: Packaging |
| 8 | +Created: 05-Jun-2025 |
| 9 | +Post-History: `02-May-2025 <https://discuss.python.org/t/90506>`__ |
| 10 | + |
| 11 | + |
| 12 | +Abstract |
| 13 | +======== |
| 14 | + |
| 15 | +This PEP proposes extending the core metadata specification for Python |
| 16 | +packaging to include a new, repeatable field named ``Import-Name`` to record |
| 17 | +the import names that a project owns once installed. A new key named |
| 18 | +``import-names`` will be added to the ``[project]`` table in |
| 19 | +``pyproject.toml``. This also leads to the introduction of core metadata |
| 20 | +version 2.5. |
| 21 | + |
| 22 | + |
| 23 | +Motivation |
| 24 | +========== |
| 25 | + |
| 26 | +In Python packaging there is no requirement that a project name match the |
| 27 | +name(s) that you can import for that project. As such, there is no clean, |
| 28 | +easy, accurate way to go from import name to project name and vice-versa. |
| 29 | +This can make it difficult for tools that try to help people in discovering |
| 30 | +the right project to install when they know the import name or knowing what |
| 31 | +import names a project will provide once installed. |
| 32 | + |
| 33 | +As an example, a code editor may detect a user has an unsatisfied import in a |
| 34 | +selected virtual environment. But with no way to reliably gather the import |
| 35 | +names that various projects provide, the code editor cannot accurately |
| 36 | +provide a user with a list of potential projects to install to satisfy that |
| 37 | +import requirement (e.g. it is not obvious that ``import PIL`` very likely |
| 38 | +implies the user wants the `Pillow project |
| 39 | +<https://pypi.org/project/pillow/>`__ installed). This also applies to when a |
| 40 | +user vaguely remembers the project name but does not remember the import |
| 41 | +name(s) and would have their memory jogged when seeing a list of import names |
| 42 | +a package provides. Finally, tools would be able to notify users what import |
| 43 | +names will become available once they install a project. |
| 44 | + |
| 45 | +It may also help with spam detection. If a project specifies the same import |
| 46 | +names as a very popular project it can act as a signal to take a closer look |
| 47 | +at the validity of the less popular project. A project found to be lying |
| 48 | +about what import names it provides would be another signal. |
| 49 | + |
| 50 | + |
| 51 | +Rationale |
| 52 | +========= |
| 53 | + |
| 54 | +This PEP proposes extending the packaging :ref:`packaging:core-metadata` so |
| 55 | +that project owners can specify the highest-level import names that a project |
| 56 | +provides and owns if installed. |
| 57 | + |
| 58 | +By keeping the information to the import names a project would own (i.e. not |
| 59 | +implicit namespace packages but modules, regular packages, submodules, and |
| 60 | +subpackages in an explicit namespace package), it makes it clear which |
| 61 | +project maps directly to what import name once the project is installed. |
| 62 | + |
| 63 | +By keeping it to the highest-level name that's owned, it keeps the data small |
| 64 | +and allows for inferring implicit namespace packages that a project |
| 65 | +contributes to. This will hopefully encourage use when appropriate by not |
| 66 | +being a burden to provide appropriate information. |
| 67 | + |
| 68 | +Putting this metadata in the core metadata means the data is (potentially) |
| 69 | +served independently of any sdist or wheel by an index server. That negates |
| 70 | +needing to come up with another way to expose the metadata to tools to avoid |
| 71 | +having to download an entire e.g. wheel. |
| 72 | + |
| 73 | +Various other attempts have been made to solve this, but they all have to |
| 74 | +make various trade-offs. For instance, one could download every wheel for |
| 75 | +every project release and look at what files are provided via the |
| 76 | +:ref:`packaging:binary-distribution-format`, but that's a lot of CPU and |
| 77 | +bandwidth for something that is static information (although tricks can be |
| 78 | +used to lessen the data requests such as using HTTP range requests to only |
| 79 | +read the table of contents of the zip file). This sort of calculation is also |
| 80 | +currently repeated by everyone independently instead of having the metadata |
| 81 | +hosted by a central index server like PyPI. It also doesn't work for sdists as |
| 82 | +the structure of the wheel isn't known yet, and so inferring the structure of |
| 83 | +the code installed isn't known yet. As well, these solutions are not |
| 84 | +necessarily accurate as it is based on inference instead of being explicitly |
| 85 | +provided by the project owners. |
| 86 | + |
| 87 | + |
| 88 | +Specification |
| 89 | +============= |
| 90 | + |
| 91 | +Because this PEP introduces a new field to the core metadata, it bumps the |
| 92 | +latest core metadata version to 2.5. |
| 93 | + |
| 94 | +The ``Import-Name`` field is a "multiple uses" field. Each entry of |
| 95 | +``Import-Name`` represents an importable name that the project provides. The |
| 96 | +names provided MUST be importable via *some* artifact the project provides |
| 97 | +for that version, i.e. the metadata MUST be consistent across all sdists and |
| 98 | +wheels for a project release to avoid having to read every file to find |
| 99 | +variances. It also avoids having to declare this field as dynamic in an |
| 100 | +sdist due to the import names varying across wheels. This does imply that the |
| 101 | +information isn't specific to the distribution artifact it is found in, but |
| 102 | +for the release version the distribution artifact belongs to. |
| 103 | + |
| 104 | +The names provided MUST be one of the following: |
| 105 | + |
| 106 | +- Highest-level, regular packages |
| 107 | +- Top-level modules |
| 108 | +- The submodules and regular subpackages within implicit namespace packages |
| 109 | + |
| 110 | +provided by the project. This makes the vast majority of projects only |
| 111 | +needing a single ``Import-Name`` entry which represents the top-level, |
| 112 | +regular package the project provides. But it also allows for implicit |
| 113 | +namespace packages to be able to differentiate among themselves (e.g., it |
| 114 | +avoids having all projects contributing to the ``azure`` namespace via an |
| 115 | +implicit namespace package all having ``azure`` as their entry for |
| 116 | +``Import-Name``, but instead a more accurate entry like |
| 117 | +``azure.mgmt.search``) |
| 118 | + |
| 119 | +If a project chooses not to provide any ``Import-Name`` entries, tools MAY |
| 120 | +assume the import name matches the project name. |
| 121 | + |
| 122 | +Project owners MUST specify accurate information when provided and SHOULD be |
| 123 | +exhaustive in what they provide. Project owners SHOULD NOT filter out names |
| 124 | +that they consider private. This is because even "private" names can be |
| 125 | +imported by anyone and can "take up space" in the namespace of the |
| 126 | +environment. Tools consuming the metadata SHOULD consider the information |
| 127 | +provided in ``Import-Name`` as accurate, but not exhaustive. |
| 128 | + |
| 129 | +The :ref:`declaring-project-metadata` will gain an ``import-names`` key. It |
| 130 | +will be an array of strings that stores what will be written out to |
| 131 | +``Import-Name``. Build back-ends MAY support dynamically calculating the |
| 132 | +value on the user's behalf if desired, if the user declares the key to be |
| 133 | +dynamic. |
| 134 | + |
| 135 | + |
| 136 | +Examples |
| 137 | +-------- |
| 138 | + |
| 139 | +`In httpx 0.28.1 |
| 140 | +<https://pypi-browser.org/package/httpx/httpx-0.28.1-py3-none-any.whl>`__ |
| 141 | +there would be only a single entry for the ``httpx`` package as it's a |
| 142 | +regular package and there are no other regular packages or modules at the top |
| 143 | +of the project. |
| 144 | + |
| 145 | +`In pytest 8.3.5 |
| 146 | +<https://pypi-browser.org/package/pytest/pytest-8.3.5-py3-none-any.whl>`__ |
| 147 | +there would be 3 entries: |
| 148 | + |
| 149 | +1. ``_pytest`` (a top-level, regular package) |
| 150 | +2. ``py`` (a top-level module) |
| 151 | +3. ``pytest`` (a top-level, regular package) |
| 152 | + |
| 153 | +In `azure-mgmt-search 9.1.0 |
| 154 | +<https://pypi-browser.org/package/azure-mgmt-search/azure_mgmt_search-9.1.0-py3-none-any.whl>`__, |
| 155 | +there would be a single entry for ``azure.mgmt.search`` as ``azure`` and |
| 156 | +``azure.mgmt`` are implicit namespace packages. |
| 157 | + |
| 158 | + |
| 159 | +Backwards Compatibility |
| 160 | +======================= |
| 161 | + |
| 162 | +As this is a new field for the core metadata and a new core metadata version, |
| 163 | +there should be no backwards compatibility concerns. |
| 164 | + |
| 165 | + |
| 166 | +Security Implications |
| 167 | +===================== |
| 168 | + |
| 169 | +Tools should treat the metadata as potentially inaccurate. As such, any |
| 170 | +decisions made based on the provided metadata should be assumed to be |
| 171 | +malicious in some way. |
| 172 | + |
| 173 | + |
| 174 | +How to Teach This |
| 175 | +================= |
| 176 | + |
| 177 | +Project owners should be taught that they can now record what namespaces |
| 178 | +their project provides. They should be told that if their project has a |
| 179 | +non-obvious namespace from the file structure of the project that they should |
| 180 | +specify the appropriate information. They should have it explained to them |
| 181 | +that they should use the shortest name possible that appropriately explains |
| 182 | +what the project provides (i.e. what the specification requires to be |
| 183 | +recorded). |
| 184 | + |
| 185 | +Users of projects don't necessarily need to know about this new metadata. |
| 186 | +While they may be exposed to it via tooling, the details of where that data |
| 187 | +came from isn't critical. It's possible they may come across it if an index |
| 188 | +server exposed it (e.g., listed the values from ``Import-Name`` and marked |
| 189 | +whether the file structure backed up the claims the metadata makes), but that |
| 190 | +still wouldn't require users to know the technical details of this PEP. |
| 191 | + |
| 192 | + |
| 193 | +Reference Implementation |
| 194 | +======================== |
| 195 | + |
| 196 | +https://github.com/brettcannon/packaging/tree/pep-794 is a branch to update |
| 197 | +'packaging' to support this PEP. |
| 198 | + |
| 199 | + |
| 200 | +Rejected Ideas |
| 201 | +============== |
| 202 | + |
| 203 | +Re-purpose the ``Provides`` field |
| 204 | +---------------------------------- |
| 205 | + |
| 206 | +Introduced in metadata version 1.1 and deprecated in 1.2, the ``Provides`` |
| 207 | +field was meant to provide similar information, except for **all** names |
| 208 | +provided by a project instead of the distinguishing namespaces as this PEP |
| 209 | +proposes. Based on that difference and the fact that ``Provides`` is |
| 210 | +deprecated and thus could be ignored by preexisting code, the decision was |
| 211 | +made to go with a new field. |
| 212 | + |
| 213 | + |
| 214 | +Name the field ``Namespace`` |
| 215 | +---------------------------- |
| 216 | + |
| 217 | +While the term "namespace" name is technically accurate from an import |
| 218 | +perspective, it could be confused with implicit namespace packages. |
| 219 | + |
| 220 | + |
| 221 | +Serving the ``RECORD`` file |
| 222 | +--------------------------- |
| 223 | + |
| 224 | +During `discussions about a pre-PEP version |
| 225 | +<https://discuss.python.org/t/90506/>`__ of this |
| 226 | +PEP, it was suggested that the ``RECORD`` file from wheels be served from |
| 227 | +index servers instead of this new metadata. That would have the benefit of |
| 228 | +being implementable immediately. But in order to provide the equivalent |
| 229 | +information there would be necessary inference based on the file structure of |
| 230 | +what would be installed by the wheel. That could lead to inaccurate |
| 231 | +information. It also doesn't support sdists. |
| 232 | + |
| 233 | +In the end a `poll |
| 234 | +<https://discuss.python.org/t/90506/46>`__ was |
| 235 | +held and the approach this PEP takes won out. |
| 236 | + |
| 237 | + |
| 238 | +Open Issues |
| 239 | +=========== |
| 240 | + |
| 241 | +N/A |
| 242 | + |
| 243 | + |
| 244 | +Acknowledgments |
| 245 | +=============== |
| 246 | + |
| 247 | +Thanks to HeeJae Chang for ~~complaining about~~ bringing up regularly the |
| 248 | +usefulness that this metadata would provide. Thanks to Josh Cannon (no |
| 249 | +relation) for reviewing drafts of this PEP and providing feedback. Also, |
| 250 | +thanks to everyone who participated in a `previous discussion |
| 251 | +<https://discuss.python.org/t/29494>`__ |
| 252 | +on this topic. |
| 253 | + |
| 254 | + |
| 255 | +Copyright |
| 256 | +========= |
| 257 | + |
| 258 | +This document is placed in the public domain or under the |
| 259 | +CC0-1.0-Universal license, whichever is more permissive. |
0 commit comments