Fix crash when passing a very long cmdline argument (#11404)
Fixes #11394
This commit is contained in:
parent
333e4eba6b
commit
28ccf476b9
|
@ -0,0 +1 @@
|
|||
Fixed crash when parsing long command line arguments that might be interpreted as files.
|
|
@ -36,6 +36,7 @@ from _pytest.outcomes import exit
|
|||
from _pytest.pathlib import absolutepath
|
||||
from _pytest.pathlib import bestrelpath
|
||||
from _pytest.pathlib import fnmatch_ex
|
||||
from _pytest.pathlib import safe_exists
|
||||
from _pytest.pathlib import visit
|
||||
from _pytest.reports import CollectReport
|
||||
from _pytest.reports import TestReport
|
||||
|
@ -888,7 +889,7 @@ def resolve_collection_argument(
|
|||
strpath = search_pypath(strpath)
|
||||
fspath = invocation_path / strpath
|
||||
fspath = absolutepath(fspath)
|
||||
if not fspath.exists():
|
||||
if not safe_exists(fspath):
|
||||
msg = (
|
||||
"module or package not found: {arg} (missing __init__.py?)"
|
||||
if as_pypath
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import atexit
|
||||
import contextlib
|
||||
import errno
|
||||
import fnmatch
|
||||
import importlib.util
|
||||
import itertools
|
||||
|
@ -773,3 +774,13 @@ def bestrelpath(directory: Path, dest: Path) -> str:
|
|||
# Forward from base to dest.
|
||||
*reldest.parts,
|
||||
)
|
||||
|
||||
|
||||
def safe_exists(p: Path) -> bool:
|
||||
"""Like Path.exists(), but account for input arguments that might be too long (#11394)."""
|
||||
try:
|
||||
return p.exists()
|
||||
except OSError as e:
|
||||
if e.errno == errno.ENAMETOOLONG:
|
||||
return False
|
||||
raise
|
||||
|
|
|
@ -262,3 +262,34 @@ def test_module_full_path_without_drive(pytester: Pytester) -> None:
|
|||
"* 1 passed in *",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def test_very_long_cmdline_arg(pytester: Pytester) -> None:
|
||||
"""
|
||||
Regression test for #11394.
|
||||
|
||||
Note: we could not manage to actually reproduce the error with this code, we suspect
|
||||
GitHub runners are configured to support very long paths, however decided to leave
|
||||
the test in place in case this ever regresses in the future.
|
||||
"""
|
||||
pytester.makeconftest(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--long-list", dest="long_list", action="store", default="all", help="List of things")
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def specified_feeds(request):
|
||||
list_string = request.config.getoption("--long-list")
|
||||
return list_string.split(',')
|
||||
"""
|
||||
)
|
||||
pytester.makepyfile(
|
||||
"""
|
||||
def test_foo(specified_feeds):
|
||||
assert len(specified_feeds) == 100_000
|
||||
"""
|
||||
)
|
||||
result = pytester.runpytest("--long-list", ",".join(["helloworld"] * 100_000))
|
||||
result.stdout.fnmatch_lines("* 1 passed *")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import errno
|
||||
import os.path
|
||||
import pickle
|
||||
import sys
|
||||
|
@ -24,6 +25,7 @@ from _pytest.pathlib import insert_missing_modules
|
|||
from _pytest.pathlib import maybe_delete_a_numbered_dir
|
||||
from _pytest.pathlib import module_name_from_path
|
||||
from _pytest.pathlib import resolve_package_path
|
||||
from _pytest.pathlib import safe_exists
|
||||
from _pytest.pathlib import symlink_or_skip
|
||||
from _pytest.pathlib import visit
|
||||
from _pytest.tmpdir import TempPathFactory
|
||||
|
@ -660,3 +662,33 @@ class TestImportLibMode:
|
|||
|
||||
mod = import_path(init, root=tmp_path, mode=ImportMode.importlib)
|
||||
assert len(mod.instance.INSTANCES) == 1
|
||||
|
||||
|
||||
def test_safe_exists(tmp_path: Path) -> None:
|
||||
d = tmp_path.joinpath("some_dir")
|
||||
d.mkdir()
|
||||
assert safe_exists(d) is True
|
||||
|
||||
f = tmp_path.joinpath("some_file")
|
||||
f.touch()
|
||||
assert safe_exists(f) is True
|
||||
|
||||
# Use unittest.mock() as a context manager to have a very narrow
|
||||
# patch lifetime.
|
||||
p = tmp_path.joinpath("some long filename" * 100)
|
||||
with unittest.mock.patch.object(
|
||||
Path,
|
||||
"exists",
|
||||
autospec=True,
|
||||
side_effect=OSError(errno.ENAMETOOLONG, "name too long"),
|
||||
):
|
||||
assert safe_exists(p) is False
|
||||
|
||||
with unittest.mock.patch.object(
|
||||
Path,
|
||||
"exists",
|
||||
autospec=True,
|
||||
side_effect=OSError(errno.EIO, "another kind of error"),
|
||||
):
|
||||
with pytest.raises(OSError):
|
||||
_ = safe_exists(p)
|
||||
|
|
Loading…
Reference in New Issue