code: convert from py.path to pathlib

This commit is contained in:
Ran Benita 2020-12-19 14:11:00 +02:00
parent 7aa2240832
commit 92ba96b061
7 changed files with 90 additions and 74 deletions

View File

@ -0,0 +1,5 @@
The following changes have been made to internal pytest types/functions:
- The ``path`` property of ``_pytest.code.Code`` returns ``Path`` instead of ``py.path.local``.
- The ``path`` property of ``_pytest.code.TracebackEntry`` returns ``Path`` instead of ``py.path.local``.
- The ``_pytest.code.getfslineno()`` function returns ``Path`` instead of ``py.path.local``.

View File

@ -43,6 +43,8 @@ from _pytest._io.saferepr import safeformat
from _pytest._io.saferepr import saferepr
from _pytest.compat import final
from _pytest.compat import get_real_func
from _pytest.pathlib import absolutepath
from _pytest.pathlib import bestrelpath
if TYPE_CHECKING:
from typing_extensions import Literal
@ -78,16 +80,16 @@ class Code:
return self.raw.co_name
@property
def path(self) -> Union[py.path.local, str]:
def path(self) -> Union[Path, str]:
"""Return a path object pointing to source code, or an ``str`` in
case of ``OSError`` / non-existing file."""
if not self.raw.co_filename:
return ""
try:
p = py.path.local(self.raw.co_filename)
p = absolutepath(self.raw.co_filename)
# maybe don't try this checking
if not p.check():
raise OSError("py.path check failed.")
if not p.exists():
raise OSError("path check failed.")
return p
except OSError:
# XXX maybe try harder like the weird logic
@ -223,7 +225,7 @@ class TracebackEntry:
return source.getstatement(self.lineno)
@property
def path(self) -> Union[py.path.local, str]:
def path(self) -> Union[Path, str]:
"""Path to the source code."""
return self.frame.code.path
@ -336,10 +338,10 @@ class Traceback(List[TracebackEntry]):
def cut(
self,
path=None,
path: Optional[Union[Path, str]] = None,
lineno: Optional[int] = None,
firstlineno: Optional[int] = None,
excludepath: Optional[py.path.local] = None,
excludepath: Optional[Path] = None,
) -> "Traceback":
"""Return a Traceback instance wrapping part of this Traceback.
@ -353,17 +355,19 @@ class Traceback(List[TracebackEntry]):
for x in self:
code = x.frame.code
codepath = code.path
if path is not None and codepath != path:
continue
if (
(path is None or codepath == path)
and (
excludepath is None
or not isinstance(codepath, py.path.local)
or not codepath.relto(excludepath)
)
and (lineno is None or x.lineno == lineno)
and (firstlineno is None or x.frame.code.firstlineno == firstlineno)
excludepath is not None
and isinstance(codepath, Path)
and excludepath in codepath.parents
):
return Traceback(x._rawentry, self._excinfo)
continue
if lineno is not None and x.lineno != lineno:
continue
if firstlineno is not None and x.frame.code.firstlineno != firstlineno:
continue
return Traceback(x._rawentry, self._excinfo)
return self
@overload
@ -801,7 +805,8 @@ class FormattedExcinfo:
message = "in %s" % (entry.name)
else:
message = excinfo and excinfo.typename or ""
path = self._makepath(entry.path)
entry_path = entry.path
path = self._makepath(entry_path)
reprfileloc = ReprFileLocation(path, entry.lineno + 1, message)
localsrepr = self.repr_locals(entry.locals)
return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style)
@ -814,15 +819,15 @@ class FormattedExcinfo:
lines.extend(self.get_exconly(excinfo, indent=4))
return ReprEntry(lines, None, None, None, style)
def _makepath(self, path):
if not self.abspath:
def _makepath(self, path: Union[Path, str]) -> str:
if not self.abspath and isinstance(path, Path):
try:
np = py.path.local().bestrelpath(path)
np = bestrelpath(Path.cwd(), path)
except OSError:
return path
return str(path)
if len(np) < len(str(path)):
path = np
return path
return np
return str(path)
def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> "ReprTraceback":
traceback = excinfo.traceback
@ -1181,7 +1186,7 @@ class ReprFuncArgs(TerminalRepr):
tw.line("")
def getfslineno(obj: object) -> Tuple[Union[str, py.path.local], int]:
def getfslineno(obj: object) -> Tuple[Union[str, Path], int]:
"""Return source location (path, lineno) for the given object.
If the source cannot be determined return ("", -1).
@ -1203,7 +1208,7 @@ def getfslineno(obj: object) -> Tuple[Union[str, py.path.local], int]:
except TypeError:
return "", -1
fspath = fn and py.path.local(fn) or ""
fspath = fn and absolutepath(fn) or ""
lineno = -1
if fspath:
try:

View File

@ -5,6 +5,7 @@ import sys
import warnings
from collections import defaultdict
from collections import deque
from pathlib import Path
from types import TracebackType
from typing import Any
from typing import Callable
@ -58,6 +59,7 @@ from _pytest.mark.structures import MarkDecorator
from _pytest.outcomes import fail
from _pytest.outcomes import TEST_OUTCOME
from _pytest.pathlib import absolutepath
from _pytest.pathlib import bestrelpath
from _pytest.store import StoreKey
if TYPE_CHECKING:
@ -718,7 +720,11 @@ class FixtureRequest:
for fixturedef in self._get_fixturestack():
factory = fixturedef.func
fs, lineno = getfslineno(factory)
p = self._pyfuncitem.session.fspath.bestrelpath(fs)
if isinstance(fs, Path):
session: Session = self._pyfuncitem.session
p = bestrelpath(Path(session.fspath), fs)
else:
p = fs
args = _format_args(factory)
lines.append("%s:%d: def %s%s" % (p, lineno + 1, factory.__name__, args))
return lines

View File

@ -39,7 +39,7 @@ if TYPE_CHECKING:
SEP = "/"
tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
tracebackcutdir = Path(_pytest.__file__).parent
def iterparentnodeids(nodeid: str) -> Iterator[str]:
@ -416,9 +416,7 @@ class Node(metaclass=NodeMeta):
return self._repr_failure_py(excinfo, style)
def get_fslocation_from_item(
node: "Node",
) -> Tuple[Union[str, py.path.local], Optional[int]]:
def get_fslocation_from_item(node: "Node") -> Tuple[Union[str, Path], Optional[int]]:
"""Try to extract the actual location from a node, depending on available attributes:
* "location": a pair (path, lineno)
@ -474,7 +472,7 @@ class Collector(Node):
def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None:
if hasattr(self, "fspath"):
traceback = excinfo.traceback
ntraceback = traceback.cut(path=self.fspath)
ntraceback = traceback.cut(path=Path(self.fspath))
if ntraceback == traceback:
ntraceback = ntraceback.cut(excludepath=tracebackcutdir)
excinfo.traceback = ntraceback.filter()

View File

@ -340,7 +340,11 @@ class PyobjMixin:
fspath: Union[py.path.local, str] = file_path
lineno = compat_co_firstlineno
else:
fspath, lineno = getfslineno(obj)
path, lineno = getfslineno(obj)
if isinstance(path, Path):
fspath = py.path.local(path)
else:
fspath = path
modpath = self.getmodpath()
assert isinstance(lineno, int)
return fspath, lineno, modpath

View File

@ -1,7 +1,6 @@
import importlib
import io
import operator
import os
import queue
import sys
import textwrap
@ -12,14 +11,14 @@ from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
import py
import _pytest
import pytest
from _pytest._code.code import ExceptionChainRepr
from _pytest._code.code import ExceptionInfo
from _pytest._code.code import FormattedExcinfo
from _pytest._io import TerminalWriter
from _pytest.monkeypatch import MonkeyPatch
from _pytest.pathlib import bestrelpath
from _pytest.pathlib import import_path
from _pytest.pytester import LineMatcher
from _pytest.pytester import Pytester
@ -150,9 +149,10 @@ class TestTraceback_f_g_h:
" except somenoname: # type: ignore[name-defined] # noqa: F821",
]
def test_traceback_cut(self):
def test_traceback_cut(self) -> None:
co = _pytest._code.Code.from_function(f)
path, firstlineno = co.path, co.firstlineno
assert isinstance(path, Path)
traceback = self.excinfo.traceback
newtraceback = traceback.cut(path=path, firstlineno=firstlineno)
assert len(newtraceback) == 1
@ -163,11 +163,11 @@ class TestTraceback_f_g_h:
p = pytester.makepyfile("def f(): raise ValueError")
with pytest.raises(ValueError) as excinfo:
import_path(p).f() # type: ignore[attr-defined]
basedir = py.path.local(pytest.__file__).dirpath()
basedir = Path(pytest.__file__).parent
newtraceback = excinfo.traceback.cut(excludepath=basedir)
for x in newtraceback:
if hasattr(x, "path"):
assert not py.path.local(x.path).relto(basedir)
assert isinstance(x.path, Path)
assert basedir not in x.path.parents
assert newtraceback[-1].frame.code.path == p
def test_traceback_filter(self):
@ -376,7 +376,7 @@ def test_excinfo_no_python_sourcecode(tmpdir):
for item in excinfo.traceback:
print(item) # XXX: for some reason jinja.Template.render is printed in full
item.source # shouldn't fail
if isinstance(item.path, py.path.local) and item.path.basename == "test.txt":
if isinstance(item.path, Path) and item.path.name == "test.txt":
assert str(item.source) == "{{ h()}}:"
@ -392,16 +392,16 @@ def test_entrysource_Queue_example():
assert s.startswith("def get")
def test_codepath_Queue_example():
def test_codepath_Queue_example() -> None:
try:
queue.Queue().get(timeout=0.001)
except queue.Empty:
excinfo = _pytest._code.ExceptionInfo.from_current()
entry = excinfo.traceback[-1]
path = entry.path
assert isinstance(path, py.path.local)
assert path.basename.lower() == "queue.py"
assert path.check()
assert isinstance(path, Path)
assert path.name.lower() == "queue.py"
assert path.exists()
def test_match_succeeds():
@ -805,21 +805,21 @@ raise ValueError()
raised = 0
orig_getcwd = os.getcwd
orig_path_cwd = Path.cwd
def raiseos():
nonlocal raised
upframe = sys._getframe().f_back
assert upframe is not None
if upframe.f_code.co_name == "checked_call":
if upframe.f_code.co_name == "_makepath":
# Only raise with expected calls, but not via e.g. inspect for
# py38-windows.
raised += 1
raise OSError(2, "custom_oserror")
return orig_getcwd()
return orig_path_cwd()
monkeypatch.setattr(os, "getcwd", raiseos)
assert p._makepath(__file__) == __file__
monkeypatch.setattr(Path, "cwd", raiseos)
assert p._makepath(Path(__file__)) == __file__
assert raised == 1
repr_tb = p.repr_traceback(excinfo)
@ -1015,7 +1015,9 @@ raise ValueError()
assert line.endswith("mod.py")
assert tw_mock.lines[10] == ":3: ValueError"
def test_toterminal_long_filenames(self, importasmod, tw_mock):
def test_toterminal_long_filenames(
self, importasmod, tw_mock, monkeypatch: MonkeyPatch
) -> None:
mod = importasmod(
"""
def f():
@ -1023,25 +1025,22 @@ raise ValueError()
"""
)
excinfo = pytest.raises(ValueError, mod.f)
path = py.path.local(mod.__file__)
old = path.dirpath().chdir()
try:
repr = excinfo.getrepr(abspath=False)
repr.toterminal(tw_mock)
x = py.path.local().bestrelpath(path)
if len(x) < len(str(path)):
msg = tw_mock.get_write_msg(-2)
assert msg == "mod.py"
assert tw_mock.lines[-1] == ":3: ValueError"
repr = excinfo.getrepr(abspath=True)
repr.toterminal(tw_mock)
path = Path(mod.__file__)
monkeypatch.chdir(path.parent)
repr = excinfo.getrepr(abspath=False)
repr.toterminal(tw_mock)
x = bestrelpath(Path.cwd(), path)
if len(x) < len(str(path)):
msg = tw_mock.get_write_msg(-2)
assert msg == path
line = tw_mock.lines[-1]
assert line == ":3: ValueError"
finally:
old.chdir()
assert msg == "mod.py"
assert tw_mock.lines[-1] == ":3: ValueError"
repr = excinfo.getrepr(abspath=True)
repr.toterminal(tw_mock)
msg = tw_mock.get_write_msg(-2)
assert msg == str(path)
line = tw_mock.lines[-1]
assert line == ":3: ValueError"
@pytest.mark.parametrize(
"reproptions",

View File

@ -6,13 +6,12 @@ import inspect
import linecache
import sys
import textwrap
from pathlib import Path
from types import CodeType
from typing import Any
from typing import Dict
from typing import Optional
import py.path
import pytest
from _pytest._code import Code
from _pytest._code import Frame
@ -352,8 +351,8 @@ def test_getfslineno() -> None:
fspath, lineno = getfslineno(f)
assert isinstance(fspath, py.path.local)
assert fspath.basename == "test_source.py"
assert isinstance(fspath, Path)
assert fspath.name == "test_source.py"
assert lineno == f.__code__.co_firstlineno - 1 # see findsource
class A:
@ -362,8 +361,8 @@ def test_getfslineno() -> None:
fspath, lineno = getfslineno(A)
_, A_lineno = inspect.findsource(A)
assert isinstance(fspath, py.path.local)
assert fspath.basename == "test_source.py"
assert isinstance(fspath, Path)
assert fspath.name == "test_source.py"
assert lineno == A_lineno
assert getfslineno(3) == ("", -1)