Skip to content

Commit 4b6d2fb

Browse files
Merge remote-tracking branch 'upstream/Improvement-move-add-pseudo-funcarg-to-metafunc-parametrize' into Improvement-move-add-pseudo-funcarg-to-metafunc-parametrize
2 parents 3f38901 + b16079a commit 4b6d2fb

File tree

25 files changed

+197
-106
lines changed

25 files changed

+197
-106
lines changed

.github/workflows/deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
name: Packages
3939
path: dist
4040
- name: Publish package to PyPI
41-
uses: pypa/[email protected].7
41+
uses: pypa/[email protected].8
4242

4343
release-notes:
4444

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ Ian Bicking
168168
Ian Lesperance
169169
Ilya Konstantinov
170170
Ionuț Turturică
171+
Isaac Virshup
171172
Itxaso Aizpurua
172173
Iwan Briquemont
173174
Jaap Broekhuizen

changelog/11216.improvement.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
If a test is skipped from inside an :ref:`xunit setup fixture <classic xunit>`, the test summary now shows the test location instead of the fixture location.

changelog/11227.improvement.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Allow :func:`pytest.raises` ``match`` argument to match against `PEP-678 <https://peps.python.org/pep-0678/>` ``__notes__``.

src/_pytest/_code/code.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,12 @@ def match(self, regexp: Union[str, Pattern[str]]) -> "Literal[True]":
704704
If it matches `True` is returned, otherwise an `AssertionError` is raised.
705705
"""
706706
__tracebackhide__ = True
707-
value = str(self.value)
707+
value = "\n".join(
708+
[
709+
str(self.value),
710+
*getattr(self.value, "__notes__", []),
711+
]
712+
)
708713
msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}"
709714
if regexp == value:
710715
msg += "\n Did you mean to `re.escape()` the regex?"

src/_pytest/_py/path.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,12 @@
2525
from typing import Any
2626
from typing import Callable
2727
from typing import cast
28+
from typing import Literal
2829
from typing import overload
2930
from typing import TYPE_CHECKING
3031

3132
from . import error
3233

33-
if TYPE_CHECKING:
34-
from typing import Literal
35-
3634
# Moved from local.py.
3735
iswin32 = sys.platform == "win32" or (getattr(os, "_name", False) == "nt")
3836

src/_pytest/compat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def num_mock_patch_args(function) -> int:
102102

103103

104104
def getfuncargnames(
105-
function: Callable[..., Any],
105+
function: Callable[..., object],
106106
*,
107107
name: str = "",
108108
is_method: bool = False,

src/_pytest/config/argparsing.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
from typing import Dict
1010
from typing import final
1111
from typing import List
12+
from typing import Literal
1213
from typing import Mapping
1314
from typing import NoReturn
1415
from typing import Optional
1516
from typing import Sequence
1617
from typing import Tuple
17-
from typing import TYPE_CHECKING
1818
from typing import Union
1919

2020
import _pytest._io
@@ -24,9 +24,6 @@
2424
from _pytest.deprecated import ARGUMENT_TYPE_STR_CHOICE
2525
from _pytest.deprecated import check_ispytest
2626

27-
if TYPE_CHECKING:
28-
from typing_extensions import Literal
29-
3027
FILE_OR_DIR = "file_or_dir"
3128

3229

@@ -177,7 +174,7 @@ def addini(
177174
name: str,
178175
help: str,
179176
type: Optional[
180-
"Literal['string', 'paths', 'pathlist', 'args', 'linelist', 'bool']"
177+
Literal["string", "paths", "pathlist", "args", "linelist", "bool"]
181178
] = None,
182179
default: Any = None,
183180
) -> None:

src/_pytest/fixtures.py

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@
6060
from _pytest.outcomes import TEST_OUTCOME
6161
from _pytest.pathlib import absolutepath
6262
from _pytest.pathlib import bestrelpath
63+
from _pytest.scope import _ScopeName
6364
from _pytest.scope import HIGH_SCOPES
6465
from _pytest.scope import Scope
6566

6667

6768
if TYPE_CHECKING:
6869
from typing import Deque
6970

70-
from _pytest.scope import _ScopeName
7171
from _pytest.main import Session
7272
from _pytest.python import CallSpec2
7373
from _pytest.python import Function
@@ -274,16 +274,27 @@ def reorder_items_atscope(
274274

275275
@dataclasses.dataclass(frozen=True)
276276
class FuncFixtureInfo:
277+
"""Fixture-related information for a fixture-requesting item (e.g. test
278+
function).
279+
280+
This is used to examine the fixtures which an item requests statically
281+
(known during collection). This includes autouse fixtures, fixtures
282+
requested by the `usefixtures` marker, fixtures requested in the function
283+
parameters, and the transitive closure of these.
284+
285+
An item may also request fixtures dynamically (using `request.getfixturevalue`);
286+
these are not reflected here.
287+
"""
288+
277289
__slots__ = ("argnames", "initialnames", "names_closure", "name2fixturedefs")
278290

279-
# Original function argument names, i.e. fixture names that the function
280-
# requests directly.
291+
# Fixture names that the item requests directly by function parameters.
281292
argnames: Tuple[str, ...]
282-
# Fixture names that the function immediately requires. These include
293+
# Fixture names that the item immediately requires. These include
283294
# argnames + fixture names specified via usefixtures and via autouse=True in
284295
# fixture definitions.
285296
initialnames: Tuple[str, ...]
286-
# The transitive closure of the fixture names that the function requires.
297+
# The transitive closure of the fixture names that the item requires.
287298
# Note: can't include dynamic dependencies (`request.getfixturevalue` calls).
288299
names_closure: List[str]
289300
# A map from a fixture name in the transitive closure to the FixtureDefs
@@ -364,7 +375,7 @@ def __init__(self, pyfuncitem: "Function", *, _ispytest: bool = False) -> None:
364375
self.param: Any
365376

366377
@property
367-
def scope(self) -> "_ScopeName":
378+
def scope(self) -> _ScopeName:
368379
"""Scope string, one of "function", "class", "module", "package", "session"."""
369380
return self._scope.value
370381

@@ -467,8 +478,7 @@ def path(self) -> Path:
467478
"""Path where the test function was collected."""
468479
if self.scope not in ("function", "class", "module", "package"):
469480
raise AttributeError(f"path not available in {self.scope}-scoped context")
470-
# TODO: Remove ignore once _pyfuncitem is properly typed.
471-
return self._pyfuncitem.path # type: ignore
481+
return self._pyfuncitem.path
472482

473483
@property
474484
def keywords(self) -> MutableMapping[str, Any]:
@@ -540,20 +550,17 @@ def getfixturevalue(self, argname: str) -> Any:
540550
def _get_active_fixturedef(
541551
self, argname: str
542552
) -> Union["FixtureDef[object]", PseudoFixtureDef[object]]:
543-
try:
544-
return self._fixture_defs[argname]
545-
except KeyError:
553+
fixturedef = self._fixture_defs.get(argname)
554+
if fixturedef is None:
546555
try:
547556
fixturedef = self._getnextfixturedef(argname)
548557
except FixtureLookupError:
549558
if argname == "request":
550559
cached_result = (self, [0], None)
551560
return PseudoFixtureDef(cached_result, Scope.Function)
552561
raise
553-
# Remove indent to prevent the python3 exception
554-
# from leaking into the call.
555-
self._compute_fixture_value(fixturedef)
556-
self._fixture_defs[argname] = fixturedef
562+
self._compute_fixture_value(fixturedef)
563+
self._fixture_defs[argname] = fixturedef
557564
return fixturedef
558565

559566
def _get_fixturestack(self) -> List["FixtureDef[Any]"]:
@@ -869,10 +876,10 @@ def _teardown_yield_fixture(fixturefunc, it) -> None:
869876

870877

871878
def _eval_scope_callable(
872-
scope_callable: "Callable[[str, Config], _ScopeName]",
879+
scope_callable: Callable[[str, Config], _ScopeName],
873880
fixture_name: str,
874881
config: Config,
875-
) -> "_ScopeName":
882+
) -> _ScopeName:
876883
try:
877884
# Type ignored because there is no typing mechanism to specify
878885
# keyword arguments, currently.
@@ -907,7 +914,7 @@ def __init__(
907914
baseid: Optional[str],
908915
argname: str,
909916
func: "_FixtureFunc[FixtureValue]",
910-
scope: Union[Scope, "_ScopeName", Callable[[str, Config], "_ScopeName"], None],
917+
scope: Union[Scope, _ScopeName, Callable[[str, Config], _ScopeName], None],
911918
params: Optional[Sequence[object]],
912919
unittest: bool = False,
913920
ids: Optional[
@@ -960,8 +967,6 @@ def __init__(
960967
# The names requested by the fixtures.
961968
self.argnames: Final = getfuncargnames(func, name=argname, is_method=unittest)
962969
# Whether the fixture was collected from a unittest TestCase class.
963-
# Note that it really only makes sense to define autouse fixtures in
964-
# unittest TestCases.
965970
self.unittest: Final = unittest
966971
# If the fixture was executed, the current value of the fixture.
967972
# Can change if the fixture is executed with different parameters.
@@ -972,7 +977,7 @@ def __init__(
972977
self.is_pseudo = is_pseudo
973978

974979
@property
975-
def scope(self) -> "_ScopeName":
980+
def scope(self) -> _ScopeName:
976981
"""Scope string, one of "function", "class", "module", "package", "session"."""
977982
return self._scope.value
978983

@@ -1086,9 +1091,10 @@ def pytest_fixture_setup(
10861091
try:
10871092
result = call_fixture_func(fixturefunc, request, kwargs)
10881093
except TEST_OUTCOME as e:
1089-
if isinstance(e, skip.Exception) and not fixturefunc.__name__.startswith(
1090-
"xunit_setup"
1091-
):
1094+
if isinstance(e, skip.Exception):
1095+
# The test requested a fixture which caused a skip.
1096+
# Don't show the fixture as the skip location, as then the user
1097+
# wouldn't know which test skipped.
10921098
e._use_item_location = True
10931099
fixturedef.cached_result = (None, my_cache_key, e)
10941100
raise
@@ -1391,8 +1397,26 @@ def _get_direct_parametrize_args(self, node: nodes.Node) -> List[str]:
13911397
return parametrize_argnames
13921398

13931399
def getfixtureinfo(
1394-
self, node: nodes.Node, func, cls, funcargs: bool = True
1400+
self,
1401+
node: nodes.Item,
1402+
func: Callable[..., object],
1403+
cls: Optional[type],
1404+
funcargs: bool = True,
13951405
) -> FuncFixtureInfo:
1406+
"""Calculate the :class:`FuncFixtureInfo` for an item.
1407+
1408+
If ``funcargs`` is false, or if the item sets an attribute
1409+
``nofuncargs = True``, then ``func`` is not examined at all.
1410+
1411+
:param node:
1412+
The item requesting the fixtures.
1413+
:param func:
1414+
The item's function.
1415+
:param cls:
1416+
If the function is a method, the method's class.
1417+
:param funcargs:
1418+
Whether to look into func's parameters as fixture requests.
1419+
"""
13961420
if funcargs and not getattr(node, "nofuncargs", False):
13971421
argnames = getfuncargnames(func, name=node.name, cls=cls)
13981422
else:
@@ -1402,8 +1426,7 @@ def getfixtureinfo(
14021426
arg for mark in node.iter_markers(name="usefixtures") for arg in mark.args
14031427
)
14041428
initialnames = usefixtures + argnames
1405-
fm = node.session._fixturemanager
1406-
initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure(
1429+
initialnames, names_closure, arg2fixturedefs = self.getfixtureclosure(
14071430
initialnames, node, ignore_args=self._get_direct_parametrize_args(node)
14081431
)
14091432
return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs)

src/_pytest/hookspec.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
if TYPE_CHECKING:
1919
import pdb
2020
import warnings
21-
from typing_extensions import Literal
21+
from typing import Literal
2222

2323
from _pytest._code.code import ExceptionRepr
2424
from _pytest._code.code import ExceptionInfo

0 commit comments

Comments
 (0)