2

I'm curious about the following situation. Let's say I have two projects named project_alpha and project_bravo, both defining a top-level namespace package mymeta. The layout:

project_alpha/
   -> mymeta/
        -> __init__.py
        -> project_alpha/
             -> __init__.py
             -> version.py
   -> setup.py

project_bravo/
   -> mymeta/
        -> __init__.py
        -> project_bravo/
             -> __init__.py
             -> version.py
   -> setup.py

Both mymeta/__init__.pys contain only the line __import__('pkg_resources').declare_namespace(__name__) (according to namespace section in setuptools docs). Contents of both version.pys:

__version_info__ = (0, 9, 9, 'dev0')
__version__ = '.'.join((str(entry) for entry in __version_info__)) 

The setup.py script for project_alpha is pretty simple. The namespace package mymeta is declared, and the version is taken from the version module:

# project_alpha
from setuptools import find_packages, setup
from mymeta.project_alpha.version import __version__

setup(
    name='mymeta.project-alpha',
    version=__version__,
    namespace_packages=['mymeta'],
    packages=find_packages(),
)

The setup.py script for project_bravo has the same structure, but with a twist: project_bravo depends on project_alpha when building:

from setuptools import find_packages, setup
from mymeta.project_bravo.version import __version__

setup(
    name='mymeta.project-bravo',
    version=__version__,
    namespace_packages=['mymeta'],
    setup_requires=['mymeta.project-alpha'],
    packages=find_packages(),
)

When building project_bravo, I get the following error:

~/project_bravo $ python setup.py sdist 
Traceback (most recent call last):
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 156, in save_modules
    yield saved
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 197, in setup_context
    yield
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 246, in run_setup
    DirectorySandbox(setup_dir).run(runner)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 276, in run
    return func()
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 245, in runner
    _execfile(setup_script, ns)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 47, in _execfile
    exec(code, globals, locals)
  File "/tmp/easy_install-ahmxos98/mymeta.project-alpha-0.9.9.dev0/setup.py", line 6, in <module>
    try:
ImportError: No module named 'mymeta.project_alpha'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "setup.py", line 22, in <module>
    packages=find_packages(),
  File "/usr/lib64/python3.5/distutils/core.py", line 108, in setup
    _setup_distribution = dist = klass(attrs)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/dist.py", line 315, in __init__
    self.fetch_build_eggs(attrs['setup_requires'])
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/dist.py", line 361, in fetch_build_eggs
    replace_conflicting=True,
  File "/tmp/tstenv/lib/python3.5/site-packages/pkg_resources/__init__.py", line 853, in resolve
    dist = best[req.key] = env.best_match(req, ws, installer)
  File "/tmp/tstenv/lib/python3.5/site-packages/pkg_resources/__init__.py", line 1125, in best_match
    return self.obtain(req, installer)
  File "/tmp/tstenv/lib/python3.5/site-packages/pkg_resources/__init__.py", line 1137, in obtain
    return installer(requirement)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/dist.py", line 429, in fetch_build_egg
    return cmd.easy_install(req)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 665, in easy_install
    return self.install_item(spec, dist.location, tmpdir, deps)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 695, in install_item
    dists = self.install_eggs(spec, download, tmpdir)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 876, in install_eggs
    return self.build_and_install(setup_script, setup_base)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 1115, in build_and_install
    self.run_setup(setup_script, setup_base, args)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 1101, in run_setup
    run_setup(setup_script, args)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 249, in run_setup
    raise
  File "/usr/lib64/python3.5/contextlib.py", line 77, in __exit__
    self.gen.throw(type, value, traceback)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 197, in setup_context
    yield
  File "/usr/lib64/python3.5/contextlib.py", line 77, in __exit__
    self.gen.throw(type, value, traceback)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 168, in save_modules
    saved_exc.resume()
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 143, in resume
    six.reraise(type, exc, self._tb)
  File "/tmp/tstenv/lib/python3.5/site-packages/pkg_resources/_vendor/six.py", line 685, in reraise
    raise value.with_traceback(tb)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 156, in save_modules
    yield saved
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 197, in setup_context
    yield
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 246, in run_setup
    DirectorySandbox(setup_dir).run(runner)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 276, in run
    return func()
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 245, in runner
    _execfile(setup_script, ns)
  File "/tmp/tstenv/lib/python3.5/site-packages/setuptools/sandbox.py", line 47, in _execfile
    exec(code, globals, locals)
  File "/tmp/easy_install-ahmxos98/mymeta.project-alpha-0.9.9.dev0/setup.py", line 6, in <module>
    try:
ImportError: No module named 'mymeta.project_alpha'

Unfortunately, I don't understand the error here. It has something to do with the imports order, right? If I comment out the import of mymeta.project_bravo.version in project_bravo/setup.py and replace the version with some hard-coded string, suddenly the build succeeds...


Edit: I introduced a workaround for this issue. Instead of trying to import the version directly, I exec the version module to avoid the import problems. Of course, this is not a proper solution, thus not posting this as answer, but still here it is:

__version__ = None # if the exec fails, leave the version unset, the resulting build version will be 0.0.0
version_script_path = os.path.relpath(os.path.join(os.path.dirname(__file__), 'mymeta', 'project_alpha', 'version.py'))
with open(version_script_path) as version_script:
    exec(version_script.read())

After the version script is read and executed, the version is initialized, thus no need to import anything from mymeta package.

3
  • I tried to reproduce the error but I couldn't. On my system everything works fine. Maybe try with a fresh virtualenv? How do you provide mymeta.project_alpha before you try to build mymeta.project_bravo? Do you do setup.py install in mymeta.project_alpha? Commented Sep 30, 2016 at 12:29
  • Ah, I forgot to mention that. I issue python setup.py sdist upload to upload project_alpha to a private PyPI repo. Commented Sep 30, 2016 at 12:35
  • Hmm, issuing python setup.py install works for me either, but this explicitly installs project_alpha to the venv so the package is already present when building project_bravo. The issue appears only when setuptools tries to download project_alphas egg into project_bravo/.eggs dir Commented Sep 30, 2016 at 12:40

2 Answers 2

2

Almost one year later, I once again faced this issue and the solution for python>=3.3 is to use implicit namespace packages as specified in PEP 420. The project structure is barely changed, just both the __init__.pys for pkgutil-style namespace package mymeta are gone:

project_alpha/
   -> mymeta/
        -> project_alpha/
             -> __init__.py
             -> version.py
   -> setup.py

project_bravo/
   -> mymeta/
        -> project_bravo/
             -> __init__.py
             -> version.py
   -> setup.py

To make setuptools happy, both setup scripts need to be adjusted as well:

...
setup(
    ...
    packages=['mymeta.' + pkg for pkg in find_packages('mymeta')],
)

Now the imports will be resolved correctly when building project-bravo.

Sign up to request clarification or add additional context in comments.

Comments

1

As mentioned in the comments: The command

$ python setup.py sdist

for mymeta.project_bravo downloads an egg of mymeta.project_alpha from a private pypi repo.

Namespace packages are very delicate concerning their imports.

e.g. I found out that when I install ns.a and ns.b regularly in my environment and supply the import path of ns.d in a .pth file in the site-packages directory, it will not work, no matter what. However when I symlink to ns.d in site-packeges/ns directory it will work.

If I install none of the components and supply all paths in a .pth in the site-packages directory, everything works fine.

If I install all of the components regularly also everything works fine.

Just when I mix concepts it will not work.

Therefore I suspect that this might also be the issue here: Different strategies for the import paths.

You might want to try to modify your __init__.py file to:

from pkgutil import extend_path

__path__ = extend_path(__path__, __name__)
__import__('pkg_resources').declare_namespace(__name__)

I regret my decision to do namespace packages. I would never do it again.

2 Comments

unfortunately, this doesn't help. Nevertheless, thank you for your efforts as the answer explains the origin of the error, which gives me a direction for further digging!
Hi, if you are still interested in a solution, I found one for at least python 3.3 and newer - the implicit namespace packages. See my answer for details.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.