Merge pull request #10396 from pytest-dev/pylib-hax

vendor py.path and py.error
This commit is contained in:
Anthony Sottile 2022-10-21 13:37:32 -04:00 committed by GitHub
commit ffe49ac17c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 3163 additions and 5 deletions

View File

@ -66,7 +66,7 @@ jobs:
- name: "windows-py37-pluggy"
python: "3.7"
os: windows-latest
tox_env: "py37-pluggymain-xdist"
tox_env: "py37-pluggymain-pylib-xdist"
- name: "windows-py38"
python: "3.8"
os: windows-latest
@ -93,7 +93,7 @@ jobs:
- name: "ubuntu-py37-pluggy"
python: "3.7"
os: ubuntu-latest
tox_env: "py37-pluggymain-xdist"
tox_env: "py37-pluggymain-pylib-xdist"
- name: "ubuntu-py37-freeze"
python: "3.7"
os: ubuntu-latest

View File

@ -65,7 +65,6 @@ repos:
args: []
additional_dependencies:
- iniconfig>=1.1.0
- py>=1.8.2
- attrs>=19.2.0
- packaging
- tomli

View File

@ -0,0 +1 @@
pytest no longer depends on the ``py`` library. ``pytest`` provides a vendored copy of ``py.error`` and ``py.path`` modules but will use the ``py`` library if it is installed. If you need other ``py.*`` modules, continue to install the deprecated ``py`` library separately, otherwise it can usually be removed as a dependency.

View File

@ -36,16 +36,17 @@ packages =
_pytest
_pytest._code
_pytest._io
_pytest._py
_pytest.assertion
_pytest.config
_pytest.mark
pytest
py_modules = py
install_requires =
attrs>=19.2.0
iniconfig
packaging
pluggy>=0.12,<2.0
py>=1.8.2
colorama;sys_platform=="win32"
exceptiongroup>=1.0.0rc8;python_version<"3.11"
importlib-metadata>=0.12;python_version<"3.8"

View File

109
src/_pytest/_py/error.py Normal file
View File

@ -0,0 +1,109 @@
"""create errno-specific classes for IO or os calls."""
from __future__ import annotations
import errno
import os
import sys
from typing import Callable
from typing import TYPE_CHECKING
from typing import TypeVar
if TYPE_CHECKING:
from typing_extensions import ParamSpec
P = ParamSpec("P")
R = TypeVar("R")
class Error(EnvironmentError):
def __repr__(self) -> str:
return "{}.{} {!r}: {} ".format(
self.__class__.__module__,
self.__class__.__name__,
self.__class__.__doc__,
" ".join(map(str, self.args)),
# repr(self.args)
)
def __str__(self) -> str:
s = "[{}]: {}".format(
self.__class__.__doc__,
" ".join(map(str, self.args)),
)
return s
_winerrnomap = {
2: errno.ENOENT,
3: errno.ENOENT,
17: errno.EEXIST,
18: errno.EXDEV,
13: errno.EBUSY, # empty cd drive, but ENOMEDIUM seems unavailiable
22: errno.ENOTDIR,
20: errno.ENOTDIR,
267: errno.ENOTDIR,
5: errno.EACCES, # anything better?
}
class ErrorMaker:
"""lazily provides Exception classes for each possible POSIX errno
(as defined per the 'errno' module). All such instances
subclass EnvironmentError.
"""
_errno2class: dict[int, type[Error]] = {}
def __getattr__(self, name: str) -> type[Error]:
if name[0] == "_":
raise AttributeError(name)
eno = getattr(errno, name)
cls = self._geterrnoclass(eno)
setattr(self, name, cls)
return cls
def _geterrnoclass(self, eno: int) -> type[Error]:
try:
return self._errno2class[eno]
except KeyError:
clsname = errno.errorcode.get(eno, "UnknownErrno%d" % (eno,))
errorcls = type(
clsname,
(Error,),
{"__module__": "py.error", "__doc__": os.strerror(eno)},
)
self._errno2class[eno] = errorcls
return errorcls
def checked_call(
self, func: Callable[P, R], *args: P.args, **kwargs: P.kwargs
) -> R:
"""Call a function and raise an errno-exception if applicable."""
__tracebackhide__ = True
try:
return func(*args, **kwargs)
except Error:
raise
except OSError as value:
if not hasattr(value, "errno"):
raise
errno = value.errno
if sys.platform == "win32":
try:
cls = self._geterrnoclass(_winerrnomap[errno])
except KeyError:
raise value
else:
# we are not on Windows, or we got a proper OSError
cls = self._geterrnoclass(errno)
raise cls(f"{func.__name__}{args!r}")
_error_maker = ErrorMaker()
checked_call = _error_maker.checked_call
def __getattr__(attr: str) -> type[Error]:
return getattr(_error_maker, attr) # type: ignore[no-any-return]

1474
src/_pytest/_py/path.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,7 @@ from typing import TypeVar
from typing import Union
import attr
import py
# fmt: off

10
src/py.py Normal file
View File

@ -0,0 +1,10 @@
# shim for pylib going away
# if pylib is installed this file will get skipped
# (`py/__init__.py` has higher precedence)
import sys
import _pytest._py.error as error
import _pytest._py.path as path
sys.modules["py.error"] = error
sys.modules["py.path"] = path

1556
testing/_py/test_local.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -91,6 +91,12 @@ class TestImportPath:
yield path
assert path.joinpath("samplefile").exists()
@pytest.fixture(autouse=True)
def preserve_sys(self):
with unittest.mock.patch.dict(sys.modules):
with unittest.mock.patch.object(sys, "path", list(sys.path)):
yield
def setuptestfs(self, path: Path) -> None:
# print "setting up test fs for", repr(path)
samplefile = path / "samplefile"

View File

@ -10,7 +10,7 @@ envlist =
py310
py311
pypy3
py37-{pexpect,xdist,unittestextras,numpy,pluggymain}
py37-{pexpect,xdist,unittestextras,numpy,pluggymain,pylib}
doctesting
plugins
py37-freeze
@ -54,6 +54,7 @@ deps =
numpy: numpy>=1.19.4
pexpect: pexpect>=4.8.0
pluggymain: pluggy @ git+https://github.com/pytest-dev/pluggy.git
pylib: py>=1.8.2
unittestextras: twisted
unittestextras: asynctest
xdist: pytest-xdist>=2.1.0