From b0d45267c58859bcb79f7ab980f4b410c4bbd109 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Sun, 2 Feb 2020 03:42:53 +0100 Subject: [PATCH 1/4] internal: clean up getfslineno Everything was using `_pytest.compat.getfslineno` basically, which wrapped `_pytest._code.source.getfslineno`. This moves the extra code from there into it directly, and uses the latter everywhere. This helps to eventually remove the one in compat eventually, and also causes less cyclic imports. --- src/_pytest/_code/source.py | 8 ++++++++ src/_pytest/compat.py | 12 ++++-------- src/_pytest/fixtures.py | 2 +- src/_pytest/mark/structures.py | 2 +- src/_pytest/nodes.py | 2 +- src/_pytest/python.py | 2 +- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/_pytest/_code/source.py b/src/_pytest/_code/source.py index 379393b10cd..b5e18863ffc 100644 --- a/src/_pytest/_code/source.py +++ b/src/_pytest/_code/source.py @@ -17,6 +17,7 @@ import py +from _pytest.compat import get_real_func from _pytest.compat import overload from _pytest.compat import TYPE_CHECKING @@ -290,6 +291,13 @@ def getfslineno(obj) -> Tuple[Optional[Union["Literal['']", py.path.local]], int """ from .code import Code + # xxx let decorators etc specify a sane ordering + # NOTE: this used to be done in _pytest.compat.getfslineno, initially added + # in 6ec13a2b9. It ("place_as") appears to be something very custom. + obj = get_real_func(obj) + if hasattr(obj, "place_as"): + obj = obj.place_as + try: code = Code(obj) except TypeError: diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 085f634a4eb..d6ee1d522fc 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -22,7 +22,6 @@ import attr import py -import _pytest from _pytest._io.saferepr import saferepr from _pytest.outcomes import fail from _pytest.outcomes import TEST_OUTCOME @@ -308,13 +307,10 @@ def get_real_method(obj, holder): def getfslineno(obj) -> Tuple[Union[str, py.path.local], int]: - # 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) - assert isinstance(fslineno[1], int), obj - return fslineno + """(**Deprecated**, use _pytest._code.source.getfslineno directly)""" + from _pytest._code.source import getfslineno + + return getfslineno(obj) def getimfunc(func): diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 5b3686b5807..a6bfeb6d303 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -16,12 +16,12 @@ import _pytest from _pytest._code.code import FormattedExcinfo from _pytest._code.code import TerminalRepr +from _pytest._code.source import getfslineno from _pytest._io import TerminalWriter from _pytest.compat import _format_args from _pytest.compat import _PytestWrapper 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 diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 3002f8abc41..de4333a624b 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -6,9 +6,9 @@ import attr +from .._code.source import getfslineno from ..compat import ascii_escaped from ..compat import ATTRS_EQ_FIELD -from ..compat import getfslineno from ..compat import NOTSET from _pytest.outcomes import fail from _pytest.warning_types import PytestUnknownMarkWarning diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 5447f254173..218684e1481 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -15,8 +15,8 @@ from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionInfo from _pytest._code.code import ReprExceptionInfo +from _pytest._code.source import getfslineno from _pytest.compat import cached_property -from _pytest.compat import getfslineno from _pytest.compat import TYPE_CHECKING from _pytest.config import Config from _pytest.config import PytestPluginManager diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 65ef1272b78..525498de22a 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -20,10 +20,10 @@ from _pytest import fixtures from _pytest import nodes from _pytest._code import filter_traceback +from _pytest._code.source import getfslineno from _pytest.compat import ascii_escaped from _pytest.compat import get_default_arg_names 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 From 61f2a26675561d510ab4f736a5b3c5d4f8aa043c Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 3 Feb 2020 18:40:23 +0100 Subject: [PATCH 2/4] Code/getfslineno: keep empty co_filename Previously this would be turned via `py.path.local("")` into the current working directory. This appears to be what `fspath = fn and py.path.local(fn) or None` tries to avoid in `getfslineno`'s `TypeError` handling already, if `Code` would raise it. --- src/_pytest/_code/code.py | 2 ++ testing/code/test_source.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index b176dde98b9..cafd870f04b 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -72,6 +72,8 @@ def path(self) -> Union[py.path.local, str]: """ return a path object pointing to source code (or a str in case of OSError / non-existing file). """ + if not self.raw.co_filename: + return "" try: p = py.path.local(self.raw.co_filename) # maybe don't try this checking diff --git a/testing/code/test_source.py b/testing/code/test_source.py index b5efdb31702..cf09309744a 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -524,6 +524,14 @@ class B: B.__name__ = "B2" assert getfslineno(B)[1] == -1 + co = compile("...", "", "eval") + assert co.co_filename == "" + + if hasattr(sys, "pypy_version_info"): + assert getfslineno(co) == ("", -1) + else: + assert getfslineno(co) == ("", 0) + def test_code_of_object_instance_with_call() -> None: class A: From dab90ef726cf33579e692820f82797d8e906ff8a Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 3 Feb 2020 18:50:12 +0100 Subject: [PATCH 3/4] typing: fix getfslineno Closes https://github.com/pytest-dev/pytest/pull/6590. --- src/_pytest/_code/source.py | 11 +++++------ src/_pytest/compat.py | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/_pytest/_code/source.py b/src/_pytest/_code/source.py index b5e18863ffc..432e1cbe823 100644 --- a/src/_pytest/_code/source.py +++ b/src/_pytest/_code/source.py @@ -8,6 +8,7 @@ from bisect import bisect_right from types import CodeType from types import FrameType +from typing import Any from typing import Iterator from typing import List from typing import Optional @@ -283,7 +284,7 @@ def compile_( # noqa: F811 return s.compile(filename, mode, flags, _genframe=_genframe) -def getfslineno(obj) -> Tuple[Optional[Union["Literal['']", py.path.local]], int]: +def getfslineno(obj: Any) -> Tuple[Union[str, py.path.local], int]: """ Return source location (path, lineno) for the given object. If the source cannot be determined return ("", -1). @@ -306,18 +307,16 @@ def getfslineno(obj) -> Tuple[Optional[Union["Literal['']", py.path.local]], int except TypeError: return "", -1 - fspath = fn and py.path.local(fn) or None + fspath = fn and py.path.local(fn) or "" lineno = -1 if fspath: try: _, lineno = findsource(obj) except IOError: pass + return fspath, lineno else: - fspath = code.path - lineno = code.firstlineno - assert isinstance(lineno, int) - return fspath, lineno + return code.path, code.firstlineno # diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index d6ee1d522fc..3a3645c5a3f 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -308,9 +308,9 @@ def get_real_method(obj, holder): def getfslineno(obj) -> Tuple[Union[str, py.path.local], int]: """(**Deprecated**, use _pytest._code.source.getfslineno directly)""" - from _pytest._code.source import getfslineno + import _pytest._code.source - return getfslineno(obj) + return _pytest._code.source.getfslineno(obj) def getimfunc(func): From 9c7f1d9b329f97914d75c2891f20def973429fa5 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Tue, 4 Feb 2020 02:40:59 +0100 Subject: [PATCH 4/4] Remove compat.getfslineno --- src/_pytest/compat.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 3a3645c5a3f..f204dbd2dfe 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -306,13 +306,6 @@ def get_real_method(obj, holder): return obj -def getfslineno(obj) -> Tuple[Union[str, py.path.local], int]: - """(**Deprecated**, use _pytest._code.source.getfslineno directly)""" - import _pytest._code.source - - return _pytest._code.source.getfslineno(obj) - - def getimfunc(func): try: return func.__func__