diff --git a/AUTHORS b/AUTHORS index 5ccff644c..f536e54ec 100644 --- a/AUTHORS +++ b/AUTHORS @@ -354,6 +354,7 @@ Simon Gomizelj Simon Holesch Simon Kerr Skylar Downes +Songpeng Liu Srinivas Reddy Thatiparthy Stefaan Lippens Stefan Farmbauer diff --git a/changelog/11495.feature.rst b/changelog/11495.feature.rst new file mode 100644 index 000000000..ad282a406 --- /dev/null +++ b/changelog/11495.feature.rst @@ -0,0 +1,4 @@ +Add denotation, users now can use annotation to denote a test: +@pytest.mark.test +def hello(): + assert 5 == 5 diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 258ed9f9a..788f40711 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -69,6 +69,7 @@ class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader) try: self.fnpats = config.getini("python_files") except ValueError: + print("debug in 75") self.fnpats = ["test_*.py", "*_test.py"] self.session: Optional[Session] = None self._rewritten_names: Dict[str, Path] = {} diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py old mode 100755 new mode 100644 diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 5cee8e89b..34cacac91 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -661,9 +661,8 @@ class Session(nodes.FSCollector): self._notfound: List[Tuple[str, Sequence[nodes.Collector]]] = [] self._initial_parts: List[Tuple[Path, List[str]]] = [] self.items: List[nodes.Item] = [] - + #import pdb; pdb.set_trace() hook = self.config.hook - items: Sequence[Union[nodes.Item, nodes.Collector]] = self.items try: initialpaths: List[Path] = [] @@ -705,6 +704,12 @@ class Session(nodes.FSCollector): hook.pytest_collection_finish(session=self) self.testscollected = len(items) + +# print("[lizhicheng]") +# print(len(items)) +# print(type(items)) +# print(items) +# # import pdb;pdb.set_trace() return items def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index bbde68644..3fd75c736 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -625,3 +625,70 @@ class NodeKeywords(MutableMapping[str, Any]): def __repr__(self) -> str: return f"" + +@final +@dataclasses.dataclass(frozen=True) +class Test: + """A pytest test.""" + + #: Name of the test. + name: str + #: Positional arguments of the mark decorator. + args: Tuple[Any, ...] + #: Keyword arguments of the mark decorator. + kwargs: Mapping[str, Any] + + _param_ids_from: Optional["Test"] = dataclasses.field(default=None, repr=False) + #: Resolved/generated ids with parametrize Marks. + _param_ids_generated: Optional[Sequence[str]] = dataclasses.field( + default=None, repr=False + ) + + def __init__( + self, + name: str, + args: Tuple[Any, ...], + kwargs: Mapping[str, Any], + param_ids_from: Optional["Mark"] = None, + param_ids_generated: Optional[Sequence[str]] = None, + *, + _ispytest: bool = False, + ) -> None: + """:meta private:""" + check_ispytest(_ispytest) + # Weirdness to bypass frozen=True. + object.__setattr__(self, "name", name) + object.__setattr__(self, "args", args) + object.__setattr__(self, "kwargs", kwargs) + object.__setattr__(self, "_param_ids_from", param_ids_from) + object.__setattr__(self, "_param_ids_generated", param_ids_generated) + + def _has_param_ids(self) -> bool: + return "ids" in self.kwargs or len(self.args) >= 4 + + def combined_with(self, other: "Test") -> "Test": + """Return a new Test which is a combination of this + Test and another Test. + + Combines by appending args and merging kwargs. + + """ + assert self.name == other.name + + # Remember source of ids with parametrize Marks. + param_ids_from: Optional[Mark] = None + if self.name == "parametrize": + if other._has_param_ids(): + param_ids_from = other + elif self._has_param_ids(): + param_ids_from = self + + return Test( + self.name, + self.args + other.args, + dict(self.kwargs, **other.kwargs), + param_ids_from=param_ids_from, + _ispytest=True, + ) + + diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 99d33954d..2c0fe361f 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -1034,6 +1034,7 @@ class Pytester: return result def runitem(self, source: str) -> Any: + print(str) """Run the "test_func" Item. The calling test instance (class containing the test method) must diff --git a/src/_pytest/python.py b/src/_pytest/python.py index f54bbb379..9846a190d 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -110,7 +110,7 @@ def pytest_addoption(parser: Parser) -> None: "python_files", type="args", # NOTE: default is also used in AssertionRewritingHook. - default=["test_*.py", "*_test.py"], + default=["test_*.py", "*_test.py", "myselftest_*.py"], help="Glob-style file patterns for Python test module discovery", ) parser.addini( @@ -122,7 +122,7 @@ def pytest_addoption(parser: Parser) -> None: parser.addini( "python_functions", type="args", - default=["test"], + default=["test", "my", "*"], help="Prefixes or glob names for Python test function and method discovery", ) parser.addini( diff --git a/testing/test_denotation.py b/testing/test_denotation.py new file mode 100644 index 000000000..d805f6133 --- /dev/null +++ b/testing/test_denotation.py @@ -0,0 +1,12 @@ +# content of test_sample.py +import pytest + + +@pytest.mark.test +def mul(): + assert 24 == (4 * 6) + +def my_test(): + print("test_answer") + assert 5 == 5 +