-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
[BUG] PEP 420 namespace packages via package_dir
don't work with editable installs
#4943
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Hi @mgorny , I believe that with a complex The problem is that meta path finders are tricky, and the import machinery has some quirks when it comes to namespaces. I believe this is described in the docs as part of the limitations: https://setuptools.pypa.io/en/latest/userguide/development_mode.html#limitations. So the TL;DR is that I played around with this use case before, but I did not manage to find a full blown solution given the state of the ecosystem. Things could be a bit different different if wheels allowed symlinks. Let me try to run the reproducer locally to see if I find something different than what I had investigated before |
Due to a setuptools bug, the combination of `package_dir`, namespace packages and editable installs don't work. Add an empty `__init__.py` to workaround that, much like the nvidia backend has. See pypa/setuptools#4943
So I run the following: > docker run --rm -it python:3.13-bookworm /bin/bash
python3 -m venv /tmp/venv
mkdir /tmp/pkg
cd /tmp/pkg
mkdir -p python/test third_party/{a,b}
touch python/test/__init__.py
touch third_party/a/__init__.py
touch third_party/a/foo.py
touch third_party/b/foo.py
cat > setup.py <<EOF
from setuptools import setup
setup(name="test",
packages=["test", "test.a", "test.b"],
package_dir={"": "python",
"test.a": "third_party/a",
"test.b": "third_party/b"})
EOF
cat << EOF > pyproject.toml
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
EOF
/tmp/venv/bin/python -m pip install -e . As you mentioned, if I run So I opened the REPL to investigate the following: >>> import sys
>>> editable = sys.meta_path[-1]
>>> editable.find_spec('test.b.foo')
ModuleSpec(name='test.b.foo', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7fea76ead010>, origin='/tmp/pkg/third_party/b/foo.py') So we can see here that, if it would come to the meta path finder itself, it would have found the module, but I suspect that the import machinery is not reaching that stage and it halts before reaching the meta path finder. |
There is an open question, discussion in python/cpython#92054 related to this. I believe we would need some input from the cpython team about how to proceed. But if any member of the community finds a way to support this use case that does not depend on inputs from the cpython team and would like to contribute, it would be great. |
Due to a setuptools bug, the combination of `package_dir`, namespace packages and editable installs don't work. Add an empty `__init__.py` to workaround that, much like the nvidia backend has. See pypa/setuptools#4943
I encountered a related problem when working out the editable strategy for coherent.build, which has a similar problem that the layout of the project doesn't match the runtime expectation. In the implementation, I explored using the editables package, but found it inadequate. Ultimately, however, I ended up using a proxy module to make these packages appear virtually in the requisite namespace. To illustrate how this works with the coherent.build project itself (which builds itself):
While the indirection is a little disconcerting, the implementation is tight (just 12 lines of code plus a template) and flexible (works for namespace and simple packages alike) and has proven reliable (after working out encoding flaws) and compatible across editable and non-editable installs (across namespaces in the same installation). I imagine Setuptools could do something similar, taking the |
Applying that concept to the above example, I realize the problem is more complicated, because:
Although Python does support PEP 420 namespace packages as subpackages of simple packages, I'm not sure that's a well-supported scenario, and that's what's happening here. |
Right, but the reason it never comes to that meta path finder is because the import machinery sees |
Interesting.
That's true, but it fails to find >>> editable.find_spec('test.b') is None
True |
Here's where the editable finder requires that the package be a simple package: setuptools/setuptools/command/editable_wheel.py Lines 834 to 838 in 56962ec
|
It looks like adding support for nested namespace packages may work around the issue (#4954). I created this Dockerfile: FROM jaraco/multipy-tox
# disable pip-run
ENV PYTHONPATH=
WORKDIR /tmp/pkg
RUN py -m venv .venv
# get rid of the test module for clarity
RUN rm -r /usr/lib/python3.13/test
RUN mkdir -p python/test third_party/a third_party/b
RUN touch python/test/__init__.py \
third_party/a/__init__.py \
third_party/a/foo.py \
third_party/b/foo.py
RUN echo 'from setuptools import setup\n\
setup(\n\
name="test",\n\
packages=["test", "test.a", "test.b"],\n\
package_dir={\n\
"": "python",\n\
"test.a": "third_party/a",\n\
"test.b": "third_party/b"\n\
}\n\
)' > setup.py
RUN echo '[build-system]\n\
requires = ["setuptools>=42"]\n\
build-backend = "setuptools.build_meta"' > pyproject.toml
RUN py -m pip install -q -U pip setuptools@git+https://github.com/pypa/setuptools@feature/4943-namespace-child-simple
RUN py -m pip install --no-build-isolation -e . When running it, it emits:
|
@abravalheri Would you be willing to examine that draft PR and decide if and how you'd like to employ that change? Thanks. |
setuptools version
78.1.0
Python version
3.13.3
OS
Gentoo Linux amd64
Additional environment information
No response
Description
I'm trying to port Triton's setup.py to stop using symlinks to cross-link Python packages and use package_dir instead. Unfortunately, it seems that if a package referenced through
package_dir
does not have a__init__.py
(i.e. is effectively a PEP 420 namespace package), it cannot be imported from an editable install.Expected behavior
All packages being importable from an editable install.
How to Reproduce
Output
Note that wheel has a working file structure, by comparison:
The text was updated successfully, but these errors were encountered: