diff --git a/AUTHORS b/AUTHORS index bd1e27cb37d..3842077e7bc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -382,6 +382,7 @@ Omri Golan Ondřej Súkup Onkesh Bansal Oscar Benjamin +Paneendra Kumar Parth Patel Patrick Hayes Patrick Lannigan diff --git a/changelog/13431.improvement.rst b/changelog/13431.improvement.rst new file mode 100644 index 00000000000..299642570d5 --- /dev/null +++ b/changelog/13431.improvement.rst @@ -0,0 +1,2 @@ +Exposed :class:`pytest.ParameterSet` as the type of the return value of :func:`pytest.param()`. +This can be used in type annotations and ``isinstance`` checks. diff --git a/doc/en/conf.py b/doc/en/conf.py index 803a5b2a982..2083e72dfbf 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -67,6 +67,7 @@ ("py:class", "HookCaller"), ("py:class", "HookspecMarker"), ("py:exc", "PluginValidationError"), + ("py:class", "pluggy._compat.DistFacade"), # Might want to expose/TODO (https://github.com/pytest-dev/pytest/issues/7469) ("py:class", "ExceptionRepr"), ("py:class", "Exit"), @@ -78,7 +79,6 @@ ("py:class", "_pytest.fixtures.FixtureFunctionMarker"), ("py:class", "_pytest.fixtures.FixtureFunctionDefinition"), ("py:class", "_pytest.logging.LogCaptureHandler"), - ("py:class", "_pytest.mark.structures.ParameterSet"), # Intentionally undocumented/private ("py:class", "_pytest._code.code.Traceback"), ("py:class", "_pytest._py.path.LocalPath"), diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 4e0f40f4f93..87abcc5f74d 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -1049,6 +1049,11 @@ OptionGroup .. autoclass:: pytest.OptionGroup() :members: +ParameterSet +~~~~~~~~~~~~ + +.. autoclass:: pytest.ParameterSet() + PytestPluginManager ~~~~~~~~~~~~~~~~~~~ diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 34a004f4881..aaee55771ee 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -81,33 +81,37 @@ def get_empty_parameterset_mark( class ParameterSet(NamedTuple): - """A set of values for a set of parameters along with associated marks and - an optional ID for the set. - - Examples:: - - pytest.param(1, 2, 3) - # ParameterSet(values=(1, 2, 3), marks=(), id=None) + """The return type of :func:`pytest.param`. - pytest.param("hello", id="greeting") - # ParameterSet(values=("hello",), marks=(), id="greeting") - - # Parameter set with marks - pytest.param(42, marks=pytest.mark.xfail) - # ParameterSet(values=(42,), marks=(MarkDecorator(...),), id=None) + A set of values for a set of parameters along with associated marks and + an optional ID for the set. - # From parametrize mark (parameter names + list of parameter sets) - pytest.mark.parametrize( - ("a", "b", "expected"), - [ - (1, 2, 3), - pytest.param(40, 2, 42, id="everything"), - ], - ) - # ParameterSet(values=(1, 2, 3), marks=(), id=None) - # ParameterSet(values=(40, 2, 42), marks=(), id="everything") + .. versionadded:: 9.2 """ + # Examples: + # + # pytest.param(1, 2, 3) + # # ParameterSet(values=(1, 2, 3), marks=(), id=None) + # + # pytest.param("hello", id="greeting") + # # ParameterSet(values=("hello",), marks=(), id="greeting") + # + # # Parameter set with marks + # pytest.param(42, marks=pytest.mark.xfail) + # # ParameterSet(values=(42,), marks=(MarkDecorator(...),), id=None) + # + # # From parametrize mark (parameter names + list of parameter sets) + # pytest.mark.parametrize( + # ("a", "b", "expected"), + # [ + # (1, 2, 3), + # pytest.param(40, 2, 42, id="everything"), + # ], + # ) + # # ParameterSet(values=(1, 2, 3), marks=(), id=None) + # # ParameterSet(values=(40, 2, 42), marks=(), id="everything") + values: Sequence[object | NotSetType] marks: Collection[MarkDecorator | Mark] id: str | _HiddenParam | None @@ -119,6 +123,7 @@ def param( marks: MarkDecorator | Collection[MarkDecorator | Mark] = (), id: str | _HiddenParam | None = None, ) -> ParameterSet: + """:meta private:""" if isinstance(marks, MarkDecorator): marks = (marks,) else: @@ -145,6 +150,8 @@ def extract_from( ) -> ParameterSet: """Extract from an object or objects. + :meta private: + :param parameterset: A legacy style parameterset that may or may not be a tuple, and may or may not be wrapped into a mess of mark objects. @@ -259,6 +266,10 @@ def _for_parametrize( return argnames, parameters +# mypy doesn't like `__module__` defined inline in the NamedTuple. +ParameterSet.__module__ = "pytest" + + @final @dataclasses.dataclass(frozen=True) class Mark: diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index f6ea24f95a1..ecd075ff5d3 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -42,6 +42,7 @@ from _pytest.mark import MarkDecorator from _pytest.mark import MarkGenerator from _pytest.mark import param +from _pytest.mark import ParameterSet from _pytest.monkeypatch import MonkeyPatch from _pytest.nodes import Collector from _pytest.nodes import Directory @@ -130,6 +131,7 @@ "MonkeyPatch", "OptionGroup", "Package", + "ParameterSet", "Parser", "PytestApproxDecimalToleranceWarning", "PytestAssertRewriteWarning",