Skip to content

Better isolation from current working directory #453

@warsaw

Description

@warsaw

I hate to be prescriptive in the subject because maybe there's a better way to handle this, but I found adding changedir={envtmpdir} to be an easy solution to the problem. Feel free to re-title this bug as needed.

This may be related to #430 but let me explain what's happening. I've been looking at build failures for the lazr.config package, which also depends on lazr.delegates. Both packages are compatible with Python 2 and Python 3 and the upstream repo is a single bilingual source. Under Python 3, lazr is a proper namespace package, but of course, not under Python 2. The source tree has a file lazr/__init__.py which contains:

# This is a namespace package.
try:
    import pkg_resources
    pkg_resources.declare_namespace(__name__)
except ImportError:
    import pkgutil
    __path__ = pkgutil.extend_path(__path__, __name__)

which I believe is still current best practice. Now, if you pip install lazr.config into a venv, your venv Python can import everything just fine. In fact, if you tox -e py35 --notest -r and then activate the .tox/py35 venv, both packages import just fine. This is true for Python 2 also.

However, when you run tox -e py35 the tests fail because lazr.delegates cannot be imported. This confused me a lot because outside of tox, i.e. using the venvs as described above, everything works fine.

What I think is happening is that because tox by defaults runs out of the {toxinidir} it ends up importing lazr.config from the source tree, which at the top level looks roughly like:

lazr/
lazr/__init__.py
tox.ini

I.e. the package directory lives at the top level (but note that the bug does not go away if you put that inside say a src/ directory).

So now, because the source tree has a lazr/__init__.py file, in Python 3 it ends up not being a namespace package, and thus when it tries to import lazr.delegates from the tox venv, it can't find it. Liberally sprinkling pdb's around shows that indeed tox ends up failing with an ImportError on lazr.delegates, and that sys.modules['lazr'] is not a namespace package. Looking at sys.path at the point of failure, you can see that the cwd appears earlier than say .tox/py35/lib/python3.5/site-packages so the import of lazr.config finds the source tree one instead of the venv one.

Adding

[testenv]
changedir = {envtmpdir}

solves the problem, because when tox runs it will not try to import lazr.config from the source tree, but instead from the venv and of course there, lazr is properly a namespace package.

This kind of explains some failures I've seen in other bilingual packages, but for which I've solved in worse ways (e.g. making them Python 3 only and removing the under-Python-2-needed-namespace-__init__.py-file). By eliminating everything else, I'm interpreting this as a tox bug.

So then the question is, how should tox support bilingual source trees of namespace packages? If the source tree has a concrete <namespace>/__init__.py file to support the Python 2 case, then importing from cwd first will always fail under Python 3. So maybe changedir={envtmpdir} is a better default?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions