Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/_pytest/_code/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
from .code import getrawcode # noqa
from .code import Traceback # noqa
from .source import compile_ as compile # noqa
from .source import getfslineno # noqa
from .source import get_path_and_lineno # noqa
from .source import Source # noqa
14 changes: 8 additions & 6 deletions src/_pytest/_code/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
import textwrap
import tokenize
import warnings
from ast import PyCF_ONLY_AST as _AST_FLAG
from ast import PyCF_ONLY_AST as _AST_FLAG # NOQA
from bisect import bisect_right

import py
import six

from ..pathlib import Path


class Source(object):
""" an immutable object holding a source code fragment,
Expand Down Expand Up @@ -201,7 +203,7 @@ def compile_(source, filename=None, mode="exec", flags=0, dont_inherit=0):
return co


def getfslineno(obj):
def get_path_and_lineno(obj):
""" Return source location (path, lineno) for the given object.
If the source cannot be determined return ("", -1)
"""
Expand All @@ -215,18 +217,18 @@ def getfslineno(obj):
except TypeError:
return "", -1

fspath = fn and py.path.local(fn) or None
path = Path(fn) if fn else None
lineno = -1
if fspath:
if path:
try:
_, lineno = findsource(obj)
except IOError:
pass
else:
fspath = code.path
path = Path(code.path)
lineno = code.firstlineno
assert isinstance(lineno, int)
return fspath, lineno
return path, lineno


#
Expand Down
18 changes: 14 additions & 4 deletions src/_pytest/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ def _format_args(func):
return str(signature(func))


def legacy_path(p):
return py.path.local(p)


def legacy_path_alias(pathlib_path_name):
@property
def fspath(self):
# TODO: deprecationwarning
return legacy_path(getattr(self, pathlib_path_name))


isfunction = inspect.isfunction
isclass = inspect.isclass
# used to work around a python2 exception info leak
Expand Down Expand Up @@ -318,12 +329,11 @@ def get_real_method(obj, holder):
return obj


def getfslineno(obj):
def get_path_and_lineno(obj):
# xxx let decorators etc specify a sane ordering
obj = get_real_func(obj)
if hasattr(obj, "place_as"):
obj = obj.place_as
fslineno = _pytest._code.getfslineno(obj)
obj = getattr(obj, "place_as", obj)
fslineno = _pytest._code.get_path_and_lineno(obj)
assert isinstance(fslineno[1], int), obj
return fslineno

Expand Down
11 changes: 6 additions & 5 deletions src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@
from _pytest.compat import _PytestWrapper
from _pytest.compat import exc_clear
from _pytest.compat import FuncargnamesCompatAttr
from _pytest.compat import get_path_and_lineno
from _pytest.compat import get_real_func
from _pytest.compat import get_real_method
from _pytest.compat import getfslineno
from _pytest.compat import getfuncargnames
from _pytest.compat import getimfunc
from _pytest.compat import getlocation
from _pytest.compat import is_generator
from _pytest.compat import isclass
from _pytest.compat import legacy_path
from _pytest.compat import NOTSET
from _pytest.compat import safe_getattr
from _pytest.deprecated import FIXTURE_FUNCTION_CALL
Expand Down Expand Up @@ -609,8 +610,8 @@ def _factorytraceback(self):
lines = []
for fixturedef in self._get_fixturestack():
factory = fixturedef.func
fs, lineno = getfslineno(factory)
p = self._pyfuncitem.session.fspath.bestrelpath(fs)
fs, lineno = get_path_and_lineno(factory)
p = self._pyfuncitem.session.fspath.bestrelpath(legacy_path(fs))
args = _format_args(factory)
lines.append("%s:%d: def %s%s" % (p, lineno, factory.__name__, args))
return lines
Expand Down Expand Up @@ -703,7 +704,7 @@ def formatrepr(self):
# it at the requesting side
stack = stack[:-1]
for function in stack:
fspath, lineno = getfslineno(function)
fspath, lineno = get_path_and_lineno(function)
try:
lines, _ = inspect.getsourcelines(get_real_func(function))
except (IOError, IndexError, TypeError):
Expand Down Expand Up @@ -765,7 +766,7 @@ def toterminal(self, tw):


def fail_fixturefunc(fixturefunc, msg):
fs, lineno = getfslineno(fixturefunc)
fs, lineno = get_path_and_lineno(fixturefunc)
location = "%s:%s" % (fs, lineno + 1)
source = _pytest._code.Source(fixturefunc)
fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, pytrace=False)
Expand Down
6 changes: 3 additions & 3 deletions src/_pytest/mark/structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import six

from ..compat import ascii_escaped
from ..compat import getfslineno
from ..compat import get_path_and_lineno
from ..compat import MappingMixin
from ..compat import NOTSET
from _pytest.outcomes import fail
Expand Down Expand Up @@ -43,13 +43,13 @@ def get_empty_parameterset_mark(config, argnames, func):
mark = MARK_GEN.xfail(run=False)
elif requested_mark == "fail_at_collect":
f_name = func.__name__
_, lineno = getfslineno(func)
_, lineno = get_path_and_lineno(func)
raise Collector.CollectError(
"Empty parameter set in '%s' at line %d" % (f_name, lineno)
)
else:
raise LookupError(requested_mark)
fs, lineno = getfslineno(func)
fs, lineno = get_path_and_lineno(func)
reason = "got empty parameter set %r, function %s at %s:%d" % (
argnames,
func.__name__,
Expand Down
14 changes: 9 additions & 5 deletions src/_pytest/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
import six

import _pytest._code
from _pytest.compat import getfslineno
from _pytest.compat import get_path_and_lineno
from _pytest.mark.structures import NodeKeywords
from _pytest.outcomes import fail
from _pytest.pathlib import Path

SEP = "/"

Expand Down Expand Up @@ -127,7 +128,7 @@ def warn(self, warning):
warning
)
)
path, lineno = get_fslocation_from_item(self)
path, lineno = get_path_location_from_item(self)
warnings.warn_explicit(
warning,
category=None,
Expand Down Expand Up @@ -287,7 +288,7 @@ def _repr_failure_py(self, excinfo, style=None):
repr_failure = _repr_failure_py


def get_fslocation_from_item(item):
def get_path_location_from_item(item):
"""Tries to extract the actual location from an item, depending on available attributes:

* "fslocation": a pair (path, lineno)
Expand All @@ -301,8 +302,11 @@ def get_fslocation_from_item(item):
return result[:2]
obj = getattr(item, "obj", None)
if obj is not None:
return getfslineno(obj)
return getattr(item, "fspath", "unknown location"), -1
return get_path_and_lineno(obj)
try:
return Path(item.fspath), -1
except AttributeError:
return "unknown location", -1


class Collector(Node):
Expand Down
8 changes: 4 additions & 4 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
from _pytest.compat import ascii_escaped
from _pytest.compat import enum
from _pytest.compat import get_default_arg_names
from _pytest.compat import get_path_and_lineno
from _pytest.compat import get_real_func
from _pytest.compat import getfslineno
from _pytest.compat import getimfunc
from _pytest.compat import getlocation
from _pytest.compat import is_generator
Expand Down Expand Up @@ -207,7 +207,7 @@ def pytest_pycollect_makeitem(collector, name, obj):
# or a funtools.wrapped.
# We musn't if it's been wrapped with mock.patch (python 2 only)
if not (isfunction(obj) or isfunction(get_real_func(obj))):
filename, lineno = getfslineno(obj)
filename, lineno = get_path_and_lineno(obj)
warnings.warn_explicit(
message=PytestWarning(
"cannot collect %r because it is not a function." % name
Expand Down Expand Up @@ -295,10 +295,10 @@ def reportinfo(self):
fspath = fspath[:-1]
lineno = compat_co_firstlineno
else:
fspath, lineno = getfslineno(obj)
fspath, lineno = get_path_and_lineno(obj)
modpath = self.getmodpath()
assert isinstance(lineno, int)
return fspath, lineno, modpath
return str(fspath), lineno, modpath


class PyCollector(PyobjMixin, nodes.Collector):
Expand Down
14 changes: 7 additions & 7 deletions testing/code/test_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,24 +479,24 @@ def x():
assert src[lineno] == " def x():"


def test_getfslineno():
from _pytest._code import getfslineno
def test_get_path_and_lineno():
from _pytest._code import get_path_and_lineno

def f(x):
pass

fspath, lineno = getfslineno(f)
fspath, lineno = get_path_and_lineno(f)

assert fspath.basename == "test_source.py"
assert fspath.name == "test_source.py"
assert lineno == _pytest._code.getrawcode(f).co_firstlineno - 1 # see findsource

class A(object):
pass

fspath, lineno = getfslineno(A)
fspath, lineno = get_path_and_lineno(A)

_, A_lineno = inspect.findsource(A)
assert fspath.basename == "test_source.py"
assert fspath.name == "test_source.py"
assert lineno == A_lineno

assert getfslineno(3) == ("", -1)
Expand All @@ -505,7 +505,7 @@ class B(object):
pass

B.__name__ = "B2"
assert getfslineno(B)[1] == -1
assert get_path_and_lineno(B)[1] == -1


def test_code_of_object_instance_with_call():
Expand Down
4 changes: 2 additions & 2 deletions testing/python/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ def wrap(f):
def wrapped_func(x, y, z):
pass

fs, lineno = python.getfslineno(wrapped_func)
fs2, lineno2 = python.getfslineno(wrap)
fs, lineno = python.get_path_and_lineno(wrapped_func)
fs2, lineno2 = python.get_path_and_lineno(wrap)
assert lineno > lineno2, "getfslineno does not unwrap correctly"


Expand Down
4 changes: 2 additions & 2 deletions testing/test_mark.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,13 +859,13 @@ def test_parameterset_for_fail_at_collect(testdir):

config = testdir.parseconfig()
from _pytest.mark import pytest_configure, get_empty_parameterset_mark
from _pytest.compat import getfslineno
from _pytest.compat import get_path_and_lineno

pytest_configure(config)

test_func = all
func_name = test_func.__name__
_, func_lineno = getfslineno(test_func)
_, func_lineno = get_path_and_lineno(test_func)
expected_errmsg = r"Empty parameter set in '%s' at line %d" % (
func_name,
func_lineno,
Expand Down