Skip to content

Commit

Permalink
Fix issue 2985.
Browse files Browse the repository at this point in the history
  • Loading branch information
cryvate committed Dec 7, 2017
1 parent a9dd37f commit 7ccc72d
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 17 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Grig Gheorghiu
Grigorii Eremeev (budulianin)
Guido Wesdorp
Harald Armin Massa
Henk-Jaap Wagenaar
Hugo van Kemenade
Hui Wang (coldnight)
Ian Bicking
Expand Down
75 changes: 58 additions & 17 deletions _pytest/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,23 +728,64 @@ def _tryconvertpyarg(self, x):
"""Convert a dotted module name to path.
"""
import pkgutil
try:
loader = pkgutil.find_loader(x)
except ImportError:
return x
if loader is None:
return x
# This method is sometimes invoked when AssertionRewritingHook, which
# does not define a get_filename method, is already in place:
try:
path = loader.get_filename(x)
except AttributeError:
# Retrieve path from AssertionRewritingHook:
path = loader.modules[x][0].co_filename
if loader.is_package(x):
path = os.path.dirname(path)
return path
if six.PY2:
# this is deprecated in python 3.4 and above
import pkgutil

def find_module_patched(self, fullname, path=None):
subname = fullname.split(".")[-1]
if subname != fullname and self.path is None:
return None
if self.path is None:
path = None
else:
path = [self.path]
try:
file, filename, etc = pkgutil.imp.find_module(subname,
path)
except ImportError:
return None
return pkgutil.ImpLoader(fullname, file, filename, etc)

pkgutil.ImpImporter.find_module = find_module_patched

try:
loader = pkgutil.find_loader(x)
except ImportError:
return x
if loader is None:
return x
# This method is sometimes invoked when AssertionRewritingHook, which
# does not define a get_filename method, is already in place:
try:
path = loader.get_filename(x)
except AttributeError:
# Retrieve path from AssertionRewritingHook:
path = loader.modules[x][0].co_filename
if loader.is_package(x):
path = os.path.dirname(path)
return path
else:
# this is not available (even as backport) for python 2.7
import importlib.util

try:
spec = importlib.util.find_spec(x)
except (
ImportError, # import failed
AttributeError, # parent not package
ValueError # (invalid) relative import
):
return x

if spec is None:
return x
elif spec.submodule_search_locations: # if package
return os.path.dirname(spec.origin)
elif spec.has_location: # origin is a valid location
return spec.origin
else: # the import might have involved AssertionRewritingHook
return spec.loader.modules[x][0].co_filename

def _parsearg(self, arg):
""" return (fspath, names) tuple after checking the file exists. """
Expand Down
1 change: 1 addition & 0 deletions changelog/2985.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix conversion of pyargs to filename to not convert symlinks and not use deprecated features on Python 3.
64 changes: 64 additions & 0 deletions testing/acceptance_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import os
import sys

import six

import _pytest._code
import py
import pytest
Expand Down Expand Up @@ -645,6 +647,68 @@ def join_pythonpath(*dirs):
"*1 passed*"
])

def test_cmdline_python_package_symlink(self, testdir, monkeypatch):
"""
test --pyargs option with packages with path containing symlink can
have conftest.py in their package (#2985)
"""
monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', raising=False)

search_path = ["lib", os.path.join("local", "lib")]

dirname = "lib"
d = testdir.mkdir(dirname)
foo = d.mkdir("foo")
foo.ensure("__init__.py")
lib = foo.mkdir('bar')
lib.ensure("__init__.py")
lib.join("test_bar.py"). \
write("def test_bar(): pass\n"
"def test_other(a_fixture):pass")
lib.join("conftest.py"). \
write("import pytest\n"
"@pytest.fixture\n"
"def a_fixture():pass")

d_local = testdir.mkdir("local")
symlink_location = os.path.join(str(d_local), "lib")
if six.PY2:
os.symlink(str(d), symlink_location)
else:
os.symlink(str(d), symlink_location, target_is_directory=True)

# The structure of the test directory is now:
# .
# ├── local
# │ └── lib -> ../world
# └── lib
# └── foo
# ├── __init__.py
# └── bar
# ├── __init__.py
# ├── conftest.py
# └── test_world.py

def join_pythonpath(*dirs):
cur = py.std.os.environ.get('PYTHONPATH')
if cur:
dirs += (cur,)
return os.pathsep.join(str(p) for p in dirs)

monkeypatch.setenv('PYTHONPATH', join_pythonpath(*search_path))
for p in search_path:
monkeypatch.syspath_prepend(p)
print(sys.path)
# module picked up in symlinked directory:
result = testdir.runpytest("--pyargs", "-v", "foo.bar")
testdir.chdir()
assert result.ret == 0
result.stdout.fnmatch_lines([
"*lib/foo/bar/test_bar.py::test_bar*PASSED*",
"*lib/foo/bar/test_bar.py::test_other*PASSED*",
"*2 passed*"
])

def test_cmdline_python_package_not_exists(self, testdir):
result = testdir.runpytest("--pyargs", "tpkgwhatv")
assert result.ret
Expand Down

0 comments on commit 7ccc72d

Please sign in to comment.