pytest-dev / pytest

The pytest framework makes it easy to write small tests, yet scales to support complex functional testing
https://pytest.org
MIT License
11.65k stars 2.59k forks source link

Doctests: CWD vs PATH import ambiguity #727

Open pytestbot opened 9 years ago

pytestbot commented 9 years ago

Originally reported by: Andrew Pashkin (BitBucket: andrew_pashkin, GitHub: andrew_pashkin)


Currently, when invoked with --doctest-modules option, Pytest imports modules from current working directory, which in some cases might break testing if it require to import package installed by setup.py. For example package can contain C-extensions - they can not be imported from project repository root, they need to be prepared for that by distutils.

I see two strategies of resolving this issue:

  1. Make Pytest prefer installed packages to packages from CWD.
  2. Make possible to explicitly set module(s) that py.test must import, so instead of py.test --doctest-modules ./mypkg invokation will look like py.test --doctest-modules mypkg.* or py.test --doctest-modules mypkg.foo.bar. I like this way more, because it completely unambugous - user will immediately understand, that Pytest will do from mypkg.foo import bar with respect to PATH. And PATH may contain either site-packages or current working directory.

pytestbot commented 9 years ago

Original comment by holger krekel (BitBucket: hpk42, GitHub: hpk42):


the current default trunk of pytest should already implement solution 1.

You should be able to install it via:

#!python

pip install --pre -i https://devpi.net/hpk/dev -U pytest 

Could you confirm?

pytestbot commented 9 years ago

Original comment by Andrew Pashkin (BitBucket: andrew_pashkin, GitHub: andrew_pashkin):


@hpk42 Nope, I get the same errors as with 2.7.0

pytestbot commented 9 years ago

Original comment by Andrew Pashkin (BitBucket: andrew_pashkin, GitHub: andrew_pashkin):


@hpk42 Do you talk about 67658403?

pytestbot commented 9 years ago

Original comment by holger krekel (BitBucket: hpk42, GitHub: hpk42):


Yes i meant that change but hadn't uploaded yet the new pytest. If you try again, you could get 2.8.0.dev2 and then it should work. Thanks for testing.

pytestbot commented 9 years ago

Original comment by Andrew Pashkin (BitBucket: andrew_pashkin, GitHub: andrew_pashkin):


Still no luck.

That is my project: https://github.com/AndrewPashkin/funclib

Prepare development virtualenv with:

tox -e env -- pip install --pre -i https://devpi.net/hpk/dev -U pytest 

Then it will fail:

tox -e env -- py.test --pep8 --doctest-modules funclib/ tests/

But it will work:

tox -e env -- py.test --pep8 --doctest-modules tests/

And it will work too:

tox -e env -- py.test --pep8 funclib/ tests/
pytestbot commented 9 years ago

Original comment by Andrew Pashkin (BitBucket: andrew_pashkin, GitHub: andrew_pashkin):


What can you say about second strategy?

pytestbot commented 9 years ago

Original comment by Andrew Pashkin (BitBucket: andrew_pashkin, GitHub: andrew_pashkin):


I was able to run doctests by adding ensuresyspath="append" here: https://bitbucket.org/pytest-dev/pytest/src/05b2ce3c000f0cb654e0c25009254b44658b0100/_pytest/doctest.py?at=default#cl-138

and removing check if actual imported module is imported from the same file that was provided for importing: https://bitbucket.org/pytest-dev/py/src/b86f6365033a53c8f57e7b17e28d3f414a5a1036/py/_path/local.py?at=default#cl-661

I think such check does not makes sense, becuase the pyimport method gives option to rely on PYTHONPATH in importing and its implies, that modules can be imported not only from CWD, but also from some location in PYTHONPATH. But I'm not sure about that, want to discuss.

pytestbot commented 9 years ago

Original comment by holger krekel (BitBucket: hpk42, GitHub: hpk42):


Good catch but not sure why you needed to remove the check.

The check is there because pytest works form the filesystem, so if it finds "x.py" it tries to import it. If the imported actually comes from somewhere else we consider it a mismatch with what the developer (working on x.py) probably expects.

We introduced ensuresyspath='append' to avoid the side effect of influencing the import of the package-under-test just for importing a test module for that package.

Why do you need to remove the check in your case?

pytestbot commented 9 years ago

Original comment by Andrew Pashkin (BitBucket: andrew_pashkin, GitHub: andrew_pashkin):


@hpk42

Why do you need to remove the check in your case?

Actually, I get unhandled error:

../../py/py/py/_path/local.py:662: in pyimport
    issame = self.samefile(modfile)
../../py/py/py/_path/local.py:194: in samefile
    os.path.samefile, self.strpath, other)
../../py/py/py/_error.py:84: in checked_call
    raise cls("%s%r" % (func.__name__, args))
E   ENOTDIR: [Not a directory]: samefile('/home/andrew/projects/funclib/funclib/funclib/functions.py', '/home/andrew/projects/funclib/funclib/.tox/env/local/lib/python2.7/site-packages/funclib-0.0.1-py2.7-linux-x86_64.egg/funclib/functions.py')

And what is more interesting:

$ ls -l /home/andrew/projects/funclib/funclib/.tox/env/local/lib/python2.7/site-packages/ | grep funclib
drwxrwxr-x  2 andrew andrew  4096 апр.  21 22:23 funclib
drwxrwxr-x  2 andrew andrew  4096 апр.  21 17:54 funclib-0.0.1-py2.7.egg-info
-rw-rw-r--  1 andrew andrew 62283 апр.  23 15:46 funclib-0.0.1-py2.7-linux-x86_64.egg

funclib-0.0.1-py2.7-linux-x86_64.egg is not a directory! But when python imports module, it appears like its file live in that "directory".

Seems like crazy corner case of Python packaging system.

I think the better way to handle it is to delegate to packaging system handling of its crazy corner cases.

Good catch but not sure why you needed to remove the check.

The check is there because pytest works form the filesystem, so if it finds "x.py" it tries to import it. If the imported > actually comes from somewhere else we consider it a mismatch with what the developer (working on x.py) probably expects.>

We introduced ensuresyspath='append' to avoid the side effect of influencing the import of the package-under-test just for > importing a test module for that package.

I don't get last phrase, can you give an example?

As I understand - if PYTHONPATH is /bin:/home/me/virtualenv/bin - /bin has more priority. So if we append to PYTHONPATH, we can be sure, that Python will import from appended path only if no such module in site-packages. So by appending we give to installed packages higher priority than for packages in current directory, am I right?

graingert commented 8 years ago

@hpk42 :I have a minimal failing example here:

https://github.com/graingert/tox_doctest_fail

GLOB sdist-make: /home/graingert/projects/tox_doctest_fail/setup.py
py27 inst-nodeps: /home/graingert/projects/tox_doctest_fail/.tox/dist/tox-doctest-fail-0.0.0.zip
py27 installed: py==1.4.30,pytest==2.7.2,tox-doctest-fail==0.0.0,wheel==0.24.0
py27 runtests: PYTHONHASHSEED='1701257809'
py27 runtests: commands[0] | py.test
====================================================== test session starts ======================================================
platform linux2 -- Python 2.7.9 -- py-1.4.30 -- pytest-2.7.2
rootdir: /home/graingert/projects/tox_doctest_fail, inifile: tox.ini
collected 0 items / 1 errors 

============================================================ ERRORS =============================================================
___________________________________________ ERROR collecting tox_doctest_fail/fail.py ___________________________________________
.tox/py27/local/lib/python2.7/site-packages/py/_path/local.py:668: in pyimport
    raise self.ImportMismatchError(modname, modfile, self)
E   ImportMismatchError: ('tox_doctest_fail.fail', '/home/graingert/projects/tox_doctest_fail/.tox/py27/local/lib/python2.7/site-packages/tox_doctest_fail/fail.py', local('/home/graingert/projects/tox_doctest_fail/tox_doctest_fail/fail.py'))
==================================================== 1 error in 0.04 seconds ====================================================
ERROR: InvocationError: '/home/graingert/projects/tox_doctest_fail/.tox/py27/bin/py.test'
____________________________________________________________ summary ____________________________________________________________
ERROR:   py27: commands failed