From ba905a6635536931522860788f6aa1600d922fdc Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 5 Feb 2019 12:12:27 +0100 Subject: [PATCH] port getfslineno to pathlib --- src/_pytest/_code/__init__.py | 2 +- src/_pytest/_code/source.py | 14 ++++++++------ src/_pytest/compat.py | 18 ++++++++++++++---- src/_pytest/fixtures.py | 11 ++++++----- src/_pytest/mark/structures.py | 6 +++--- src/_pytest/nodes.py | 14 +++++++++----- src/_pytest/python.py | 8 ++++---- testing/code/test_source.py | 14 +++++++------- testing/python/integration.py | 4 ++-- testing/test_mark.py | 4 ++-- 10 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/_pytest/_code/__init__.py b/src/_pytest/_code/__init__.py index fe755a39911..103eb15ec72 100644 --- a/src/_pytest/_code/__init__.py +++ b/src/_pytest/_code/__init__.py @@ -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 diff --git a/src/_pytest/_code/source.py b/src/_pytest/_code/source.py index 887f323f969..be6384e8643 100644 --- a/src/_pytest/_code/source.py +++ b/src/_pytest/_code/source.py @@ -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, @@ -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) """ @@ -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 # diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index fa878a485f3..fbc4c0e1550 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -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 @@ -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 diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 9da8f1609a6..220ac1c3696 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -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 @@ -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 @@ -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): @@ -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) diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index c8a25d156f8..58f2ed8cd4f 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -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 @@ -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__, diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 00ec80894c9..7c12e61792e 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -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 = "/" @@ -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, @@ -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) @@ -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): diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 85373f47c4d..bc565bcb3f8 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -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 @@ -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 @@ -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): diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 0103acb7025..f29a378c993 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -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) @@ -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(): diff --git a/testing/python/integration.py b/testing/python/integration.py index 79de048c3c9..22235522197 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -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" diff --git a/testing/test_mark.py b/testing/test_mark.py index f7d8cf6891a..9f746bd1548 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -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,