forked from pex-tool/pex
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
453 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Copyright 2024 Pex project contributors. | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
# Copyright 2024 Pex project contributors. | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from __future__ import absolute_import | ||
|
||
import errno | ||
import hashlib | ||
import os.path | ||
import re | ||
import warnings | ||
|
||
import attr | ||
|
||
from pex.common import safe_delete, safe_mkdir, safe_rmtree | ||
from pex.enum import Enum | ||
from pex.typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from typing import Any, Optional | ||
|
||
_MAX_ATTEMPTS = 50 | ||
|
||
|
||
class RetentionPolicy(Enum["RetentionPolicy.Value"]): | ||
class Value(Enum.Value): | ||
pass | ||
|
||
ALL = Value("all") | ||
FAILED = Value("failed") | ||
NONE = Value("none") | ||
|
||
|
||
@attr.s(frozen=True) | ||
class Tempdir(object): | ||
path = attr.ib() # type: str | ||
symlink = attr.ib(default=None) # type: Optional[str] | ||
|
||
def join(self, *components): | ||
# type: (*str) -> str | ||
return os.path.join(self.path, *components) | ||
|
||
def safe_remove(self): | ||
# type: () -> None | ||
if self.symlink: | ||
safe_delete(self.symlink) | ||
safe_rmtree(self.path) | ||
|
||
def __str__(self): | ||
# type: () -> str | ||
return self.path | ||
|
||
|
||
@attr.s(frozen=True) | ||
class TempdirFactory(object): | ||
path = attr.ib() # type: str | ||
retention_policy = attr.ib() # type: RetentionPolicy.Value | ||
|
||
def getbasetemp(self): | ||
# type: () -> str | ||
return self.path | ||
|
||
def mktemp( | ||
self, | ||
name, # type: str | ||
request=None, # type: Optional[Any] | ||
): | ||
# type: (...) -> Tempdir | ||
|
||
long_name = None # type: Optional[str] | ||
name = "{name}-{node}".format(name=name, node=request.node.name) if request else name | ||
normalized_name = re.sub(r"\W", "_", name) | ||
if len(normalized_name) > 30: | ||
# The pytest implementation simply truncates at 30 which leads to collisions and this | ||
# causes issues when tmpdir teardown is active - 1 test with the same 30 character | ||
# prefix in its test name as another test can have its directories torn down out from | ||
# underneath it! Here we ~ensure unique tmpdir names while preserving a filename length | ||
# limit to play well with various file systems. | ||
long_name = normalized_name | ||
|
||
# This is yields a ~1 in a million (5 hex chars at 4 bits a piece -> 2^20) chance of | ||
# collision if the 1st 24 characters of the test name match. | ||
prefix = normalized_name[:24] | ||
fingerprint = hashlib.sha1(normalized_name.encode("utf-8")).hexdigest()[:5] | ||
normalized_name = "{prefix}-{hash}".format(prefix=prefix, hash=fingerprint) | ||
for index in range(_MAX_ATTEMPTS): | ||
tempdir_name = "{name}{index}".format(name=normalized_name, index=index) | ||
tempdir = os.path.join(self.path, tempdir_name) | ||
try: | ||
os.makedirs(tempdir) | ||
symlink = None # type: Optional[str] | ||
if long_name: | ||
symlink = os.path.join(self.path, long_name) | ||
safe_delete(symlink) | ||
os.symlink(tempdir_name, symlink) | ||
return Tempdir(tempdir, symlink=symlink) | ||
except OSError as e: | ||
if e.errno == errno.EEXIST: | ||
continue | ||
raise OSError( | ||
"Could not create numbered dir with prefix {prefix} in {root} after {max} tries".format( | ||
prefix=normalized_name, root=self.path, max=_MAX_ATTEMPTS | ||
) | ||
) | ||
|
||
|
||
def tmpdir_factory( | ||
basetemp, # type: str | ||
retention_count, # type: int | ||
retention_policy, # type: RetentionPolicy.Value | ||
): | ||
# type: (...) -> TempdirFactory | ||
|
||
safe_rmtree(basetemp) | ||
if retention_count > 1: | ||
warnings.warn( | ||
"Ignoring temp dir retention count of {count}: only temp dirs from the current run " | ||
"will be retained.".format(count=retention_count) | ||
) | ||
safe_mkdir(basetemp) | ||
return TempdirFactory(path=basetemp, retention_policy=retention_policy) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Copyright 2024 Pex project contributors. | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from __future__ import absolute_import | ||
|
||
import sys | ||
|
||
from pex.compatibility import PY2 | ||
from pex.typing import TYPE_CHECKING | ||
from testing.pytest.tmp import RetentionPolicy | ||
|
||
if TYPE_CHECKING: | ||
from typing import Dict | ||
|
||
from _pytest.config.argparsing import Parser # type: ignore[import] | ||
from _pytest.fixtures import FixtureRequest # type: ignore[import] | ||
from _pytest.nodes import Item # type: ignore[import] | ||
from _pytest.reports import TestReport # type: ignore[import] | ||
|
||
|
||
_PASSED_STATUS = {} # type: Dict[str, bool] | ||
|
||
|
||
def mark_passed(node): | ||
# type: (Item) -> None | ||
_PASSED_STATUS[node.nodeid] = True | ||
|
||
|
||
def passed(node): | ||
# type: (Item) -> bool | ||
return _PASSED_STATUS.pop(node.nodeid, False) | ||
|
||
|
||
if PY2: | ||
from testing.pytest.track_status_hook_py2 import track_status_hook as _track_status_hook | ||
else: | ||
from testing.pytest.track_status_hook_py3 import track_status_hook as _track_status_hook | ||
|
||
hook = _track_status_hook | ||
|
||
|
||
if sys.version_info[:2] < (3, 7): | ||
|
||
def pytest_addoption(parser): | ||
# type: (Parser) -> None | ||
parser.addini( | ||
"tmp_path_retention_count", | ||
help=( | ||
"How many sessions should we keep the `tmpdir` directories, according to" | ||
"`tmp_path_retention_policy`." | ||
), | ||
default=3, | ||
) | ||
|
||
parser.addini( | ||
"tmp_path_retention_policy", | ||
help=( | ||
"Controls which directories created by the `tmpdir` fixture are kept around, based " | ||
"on test outcome. ({values})".format( | ||
values="/".join(map(str, RetentionPolicy.values())) | ||
) | ||
), | ||
default="all", | ||
) | ||
|
||
else: | ||
|
||
def pytest_addoption(parser): | ||
# type: (Parser) -> None | ||
# The `tmp_path_retention_count` and `tmp_path_retention_policy` options are already setup | ||
# under the newer pytests used by our Python>=3.7 test environments. | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# Copyright 2024 Pex project contributors. | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from __future__ import absolute_import, print_function | ||
|
||
from _pytest.config import hookimpl # type: ignore[import] | ||
|
||
from pex.typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from typing import Any, Generator | ||
|
||
from _pytest.nodes import Item # type: ignore[import] | ||
from pluggy.callers import _Result # type: ignore[import] | ||
|
||
|
||
@hookimpl(hookwrapper=True, tryfirst=True) | ||
def track_status_hook( | ||
item, # type: Item | ||
call, # type: Any | ||
): | ||
# type: (...) -> Generator[None, _Result, None] | ||
|
||
from testing.pytest.track_status_hook import mark_passed | ||
|
||
report = yield | ||
result = report.get_result() | ||
if result.when == "call" and result.passed: | ||
mark_passed(item) | ||
return |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# Copyright 2024 Pex project contributors. | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
from __future__ import absolute_import | ||
|
||
import sys | ||
|
||
from _pytest.config import hookimpl # type: ignore[import] | ||
|
||
from pex.typing import TYPE_CHECKING | ||
|
||
if TYPE_CHECKING: | ||
from typing import Any, Generator | ||
|
||
from _pytest.nodes import Item # type: ignore[import] | ||
from _pytest.reports import TestReport # type: ignore[import] | ||
|
||
|
||
@hookimpl(tryfirst=True, **{"wrapper" if sys.version_info[:2] >= (3, 7) else "hookwrapper": True}) | ||
def track_status_hook( | ||
item, # type: Item | ||
call, # type: Any | ||
): | ||
# type: (...) -> Generator[None, TestReport, TestReport] | ||
|
||
from testing.pytest.track_status_hook import mark_passed | ||
|
||
report = yield | ||
if sys.version_info[:2] < (3, 7): | ||
report = report.get_result() | ||
if report.when == "call" and report.passed: | ||
mark_passed(item) | ||
return report |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.