Skip to content

Commit 6cb425a

Browse files
[RFC] code location as a namedtuple
1 parent afd53ed commit 6cb425a

File tree

4 files changed

+37
-17
lines changed

4 files changed

+37
-17
lines changed

src/_pytest/compat.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from typing import Any
1212
from typing import Callable
1313
from typing import Generic
14+
from typing import NamedTuple
1415
from typing import Optional
1516
from typing import Tuple
1617
from typing import TYPE_CHECKING
@@ -76,18 +77,35 @@ def is_async_function(func: object) -> bool:
7677
return iscoroutinefunction(func) or inspect.isasyncgenfunction(func)
7778

7879

79-
def getlocation(function, curdir: Optional[str] = None) -> str:
80+
class CodeLocation(NamedTuple):
81+
path: Path
82+
lineno: int
83+
84+
85+
def CodeLocation__str__(self: CodeLocation) -> str:
86+
"""Python 3.6 hack for NamedTuple __str__"""
87+
return f"{self.path}:{self.lineno}"
88+
89+
90+
setattr(CodeLocation, "__str__", CodeLocation__str__)
91+
92+
93+
def getlocation(function, curdir: Optional[Path]) -> CodeLocation:
8094
function = get_real_func(function)
8195
fn = Path(inspect.getfile(function))
8296
lineno = function.__code__.co_firstlineno
97+
98+
# TODO: this cycle indicates a larger issue
99+
from .pathlib import bestrelpath
100+
83101
if curdir is not None:
84102
try:
85-
relfn = fn.relative_to(curdir)
103+
relfn = Path(bestrelpath(curdir, fn))
86104
except ValueError:
87105
pass
88106
else:
89-
return "%s:%d" % (relfn, lineno + 1)
90-
return "%s:%d" % (fn, lineno + 1)
107+
return CodeLocation(relfn, lineno + 1)
108+
return CodeLocation(fn, lineno + 1)
91109

92110

93111
def num_mock_patch_args(function) -> int:

src/_pytest/fixtures.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None:
660660
"\n\nRequested here:\n{}:{}".format(
661661
funcitem.nodeid,
662662
fixturedef.argname,
663-
getlocation(fixturedef.func, funcitem.config.rootdir),
663+
getlocation(fixturedef.func, funcitem.config.rootpath),
664664
source_path_str,
665665
source_lineno,
666666
)
@@ -1202,7 +1202,7 @@ def __call__(self, function: _FixtureFunction) -> _FixtureFunction:
12021202

12031203
name = self.name or function.__name__
12041204
if name == "request":
1205-
location = getlocation(function)
1205+
location = getlocation(function, None)
12061206
fail(
12071207
"'request' is a reserved word for fixtures, use another name:\n {}".format(
12081208
location

src/_pytest/pathlib.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
import py
3434

35-
from _pytest.compat import assert_never
35+
from _pytest import compat
3636
from _pytest.outcomes import skip
3737
from _pytest.warning_types import PytestWarning
3838

@@ -526,7 +526,7 @@ def import_path(
526526
if str(pkg_root) != sys.path[0]:
527527
sys.path.insert(0, str(pkg_root))
528528
else:
529-
assert_never(mode)
529+
compat.assert_never(mode)
530530

531531
importlib.import_module(module_name)
532532

src/_pytest/python.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
import inspect
55
import itertools
66
import os
7+
import pathlib
78
import sys
89
import types
910
import warnings
1011
from collections import Counter
1112
from collections import defaultdict
1213
from functools import partial
14+
from pathlib import Path
1315
from typing import Any
1416
from typing import Callable
1517
from typing import Dict
@@ -38,6 +40,7 @@
3840
from _pytest._io import TerminalWriter
3941
from _pytest._io.saferepr import saferepr
4042
from _pytest.compat import ascii_escaped
43+
from _pytest.compat import CodeLocation
4144
from _pytest.compat import final
4245
from _pytest.compat import get_default_arg_names
4346
from _pytest.compat import get_real_func
@@ -1405,13 +1408,12 @@ def _show_fixtures_per_test(config: Config, session: Session) -> None:
14051408
import _pytest.config
14061409

14071410
session.perform_collect()
1408-
curdir = py.path.local()
1411+
curdir = pathlib.Path.cwd()
14091412
tw = _pytest.config.create_terminal_writer(config)
14101413
verbose = config.getvalue("verbose")
14111414

14121415
def get_best_relpath(func):
1413-
loc = getlocation(func, str(curdir))
1414-
return curdir.bestrelpath(py.path.local(loc))
1416+
return getlocation(func, curdir)
14151417

14161418
def write_fixture(fixture_def: fixtures.FixtureDef[object]) -> None:
14171419
argname = fixture_def.argname
@@ -1461,29 +1463,29 @@ def _showfixtures_main(config: Config, session: Session) -> None:
14611463
import _pytest.config
14621464

14631465
session.perform_collect()
1464-
curdir = py.path.local()
1466+
curdir = Path.cwd()
14651467
tw = _pytest.config.create_terminal_writer(config)
14661468
verbose = config.getvalue("verbose")
14671469

14681470
fm = session._fixturemanager
14691471

14701472
available = []
1471-
seen: Set[Tuple[str, str]] = set()
1473+
seen: Set[Tuple[str, CodeLocation]] = set()
14721474

14731475
for argname, fixturedefs in fm._arg2fixturedefs.items():
14741476
assert fixturedefs is not None
14751477
if not fixturedefs:
14761478
continue
14771479
for fixturedef in fixturedefs:
1478-
loc = getlocation(fixturedef.func, str(curdir))
1480+
loc = getlocation(fixturedef.func, curdir)
14791481
if (fixturedef.argname, loc) in seen:
14801482
continue
14811483
seen.add((fixturedef.argname, loc))
14821484
available.append(
14831485
(
14841486
len(fixturedef.baseid),
14851487
fixturedef.func.__module__,
1486-
curdir.bestrelpath(py.path.local(loc)),
1488+
loc,
14871489
fixturedef.argname,
14881490
fixturedef,
14891491
)
@@ -1503,9 +1505,9 @@ def _showfixtures_main(config: Config, session: Session) -> None:
15031505
if fixturedef.scope != "function":
15041506
tw.write(" [%s scope]" % fixturedef.scope, cyan=True)
15051507
if verbose > 0:
1506-
tw.write(" -- %s" % bestrel, yellow=True)
1508+
tw.write(" -- %s" % str(bestrel), yellow=True)
15071509
tw.write("\n")
1508-
loc = getlocation(fixturedef.func, str(curdir))
1510+
loc = getlocation(fixturedef.func, curdir)
15091511
doc = inspect.getdoc(fixturedef.func)
15101512
if doc:
15111513
write_docstring(tw, doc)

0 commit comments

Comments
 (0)