pytest
pytest: unit and functional testing with Python.
1# PYTHON_ARGCOMPLETE_OK 2"""pytest: unit and functional testing with Python.""" 3 4from _pytest import __version__ 5from _pytest import version_tuple 6from _pytest._code import ExceptionInfo 7from _pytest.assertion import register_assert_rewrite 8from _pytest.cacheprovider import Cache 9from _pytest.capture import CaptureFixture 10from _pytest.config import cmdline 11from _pytest.config import Config 12from _pytest.config import console_main 13from _pytest.config import ExitCode 14from _pytest.config import hookimpl 15from _pytest.config import hookspec 16from _pytest.config import main 17from _pytest.config import PytestPluginManager 18from _pytest.config import UsageError 19from _pytest.config.argparsing import OptionGroup 20from _pytest.config.argparsing import Parser 21from _pytest.debugging import pytestPDB as __pytestPDB 22from _pytest.doctest import DoctestItem 23from _pytest.fixtures import fixture 24from _pytest.fixtures import FixtureDef 25from _pytest.fixtures import FixtureLookupError 26from _pytest.fixtures import FixtureRequest 27from _pytest.fixtures import yield_fixture 28from _pytest.freeze_support import freeze_includes 29from _pytest.legacypath import TempdirFactory 30from _pytest.legacypath import Testdir 31from _pytest.logging import LogCaptureFixture 32from _pytest.main import Dir 33from _pytest.main import Session 34from _pytest.mark import Mark 35from _pytest.mark import MARK_GEN as mark 36from _pytest.mark import MarkDecorator 37from _pytest.mark import MarkGenerator 38from _pytest.mark import param 39from _pytest.monkeypatch import MonkeyPatch 40from _pytest.nodes import Collector 41from _pytest.nodes import Directory 42from _pytest.nodes import File 43from _pytest.nodes import Item 44from _pytest.outcomes import exit 45from _pytest.outcomes import fail 46from _pytest.outcomes import importorskip 47from _pytest.outcomes import skip 48from _pytest.outcomes import xfail 49from _pytest.pytester import HookRecorder 50from _pytest.pytester import LineMatcher 51from _pytest.pytester import Pytester 52from _pytest.pytester import RecordedHookCall 53from _pytest.pytester import RunResult 54from _pytest.python import Class 55from _pytest.python import Function 56from _pytest.python import Metafunc 57from _pytest.python import Module 58from _pytest.python import Package 59from _pytest.python_api import approx 60from _pytest.python_api import raises 61from _pytest.recwarn import deprecated_call 62from _pytest.recwarn import WarningsRecorder 63from _pytest.recwarn import warns 64from _pytest.reports import CollectReport 65from _pytest.reports import TestReport 66from _pytest.runner import CallInfo 67from _pytest.stash import Stash 68from _pytest.stash import StashKey 69from _pytest.terminal import TestShortLogReport 70from _pytest.tmpdir import TempPathFactory 71from _pytest.warning_types import PytestAssertRewriteWarning 72from _pytest.warning_types import PytestCacheWarning 73from _pytest.warning_types import PytestCollectionWarning 74from _pytest.warning_types import PytestConfigWarning 75from _pytest.warning_types import PytestDeprecationWarning 76from _pytest.warning_types import PytestExperimentalApiWarning 77from _pytest.warning_types import PytestRemovedIn9Warning 78from _pytest.warning_types import PytestReturnNotNoneWarning 79from _pytest.warning_types import PytestUnhandledCoroutineWarning 80from _pytest.warning_types import PytestUnhandledThreadExceptionWarning 81from _pytest.warning_types import PytestUnknownMarkWarning 82from _pytest.warning_types import PytestUnraisableExceptionWarning 83from _pytest.warning_types import PytestWarning 84 85 86set_trace = __pytestPDB.set_trace 87 88 89__all__ = [ 90 "__version__", 91 "approx", 92 "Cache", 93 "CallInfo", 94 "CaptureFixture", 95 "Class", 96 "cmdline", 97 "Collector", 98 "CollectReport", 99 "Config", 100 "console_main", 101 "deprecated_call", 102 "Dir", 103 "Directory", 104 "DoctestItem", 105 "exit", 106 "ExceptionInfo", 107 "ExitCode", 108 "fail", 109 "File", 110 "fixture", 111 "FixtureDef", 112 "FixtureLookupError", 113 "FixtureRequest", 114 "freeze_includes", 115 "Function", 116 "hookimpl", 117 "HookRecorder", 118 "hookspec", 119 "importorskip", 120 "Item", 121 "LineMatcher", 122 "LogCaptureFixture", 123 "main", 124 "mark", 125 "Mark", 126 "MarkDecorator", 127 "MarkGenerator", 128 "Metafunc", 129 "Module", 130 "MonkeyPatch", 131 "OptionGroup", 132 "Package", 133 "param", 134 "Parser", 135 "PytestAssertRewriteWarning", 136 "PytestCacheWarning", 137 "PytestCollectionWarning", 138 "PytestConfigWarning", 139 "PytestDeprecationWarning", 140 "PytestExperimentalApiWarning", 141 "PytestRemovedIn9Warning", 142 "PytestReturnNotNoneWarning", 143 "Pytester", 144 "PytestPluginManager", 145 "PytestUnhandledCoroutineWarning", 146 "PytestUnhandledThreadExceptionWarning", 147 "PytestUnknownMarkWarning", 148 "PytestUnraisableExceptionWarning", 149 "PytestWarning", 150 "raises", 151 "RecordedHookCall", 152 "register_assert_rewrite", 153 "RunResult", 154 "Session", 155 "set_trace", 156 "skip", 157 "Stash", 158 "StashKey", 159 "version_tuple", 160 "TempdirFactory", 161 "TempPathFactory", 162 "Testdir", 163 "TestReport", 164 "TestShortLogReport", 165 "UsageError", 166 "WarningsRecorder", 167 "warns", 168 "xfail", 169 "yield_fixture", 170]
519def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: 520 """Assert that two numbers (or two ordered sequences of numbers) are equal to each other 521 within some tolerance. 522 523 Due to the :doc:`python:tutorial/floatingpoint`, numbers that we 524 would intuitively expect to be equal are not always so:: 525 526 >>> 0.1 + 0.2 == 0.3 527 False 528 529 This problem is commonly encountered when writing tests, e.g. when making 530 sure that floating-point values are what you expect them to be. One way to 531 deal with this problem is to assert that two floating-point numbers are 532 equal to within some appropriate tolerance:: 533 534 >>> abs((0.1 + 0.2) - 0.3) < 1e-6 535 True 536 537 However, comparisons like this are tedious to write and difficult to 538 understand. Furthermore, absolute comparisons like the one above are 539 usually discouraged because there's no tolerance that works well for all 540 situations. ``1e-6`` is good for numbers around ``1``, but too small for 541 very big numbers and too big for very small ones. It's better to express 542 the tolerance as a fraction of the expected value, but relative comparisons 543 like that are even more difficult to write correctly and concisely. 544 545 The ``approx`` class performs floating-point comparisons using a syntax 546 that's as intuitive as possible:: 547 548 >>> from pytest import approx 549 >>> 0.1 + 0.2 == approx(0.3) 550 True 551 552 The same syntax also works for ordered sequences of numbers:: 553 554 >>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6)) 555 True 556 557 ``numpy`` arrays:: 558 559 >>> import numpy as np # doctest: +SKIP 560 >>> np.array([0.1, 0.2]) + np.array([0.2, 0.4]) == approx(np.array([0.3, 0.6])) # doctest: +SKIP 561 True 562 563 And for a ``numpy`` array against a scalar:: 564 565 >>> import numpy as np # doctest: +SKIP 566 >>> np.array([0.1, 0.2]) + np.array([0.2, 0.1]) == approx(0.3) # doctest: +SKIP 567 True 568 569 Only ordered sequences are supported, because ``approx`` needs 570 to infer the relative position of the sequences without ambiguity. This means 571 ``sets`` and other unordered sequences are not supported. 572 573 Finally, dictionary *values* can also be compared:: 574 575 >>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6}) 576 True 577 578 The comparison will be true if both mappings have the same keys and their 579 respective values match the expected tolerances. 580 581 **Tolerances** 582 583 By default, ``approx`` considers numbers within a relative tolerance of 584 ``1e-6`` (i.e. one part in a million) of its expected value to be equal. 585 This treatment would lead to surprising results if the expected value was 586 ``0.0``, because nothing but ``0.0`` itself is relatively close to ``0.0``. 587 To handle this case less surprisingly, ``approx`` also considers numbers 588 within an absolute tolerance of ``1e-12`` of its expected value to be 589 equal. Infinity and NaN are special cases. Infinity is only considered 590 equal to itself, regardless of the relative tolerance. NaN is not 591 considered equal to anything by default, but you can make it be equal to 592 itself by setting the ``nan_ok`` argument to True. (This is meant to 593 facilitate comparing arrays that use NaN to mean "no data".) 594 595 Both the relative and absolute tolerances can be changed by passing 596 arguments to the ``approx`` constructor:: 597 598 >>> 1.0001 == approx(1) 599 False 600 >>> 1.0001 == approx(1, rel=1e-3) 601 True 602 >>> 1.0001 == approx(1, abs=1e-3) 603 True 604 605 If you specify ``abs`` but not ``rel``, the comparison will not consider 606 the relative tolerance at all. In other words, two numbers that are within 607 the default relative tolerance of ``1e-6`` will still be considered unequal 608 if they exceed the specified absolute tolerance. If you specify both 609 ``abs`` and ``rel``, the numbers will be considered equal if either 610 tolerance is met:: 611 612 >>> 1 + 1e-8 == approx(1) 613 True 614 >>> 1 + 1e-8 == approx(1, abs=1e-12) 615 False 616 >>> 1 + 1e-8 == approx(1, rel=1e-6, abs=1e-12) 617 True 618 619 You can also use ``approx`` to compare nonnumeric types, or dicts and 620 sequences containing nonnumeric types, in which case it falls back to 621 strict equality. This can be useful for comparing dicts and sequences that 622 can contain optional values:: 623 624 >>> {"required": 1.0000005, "optional": None} == approx({"required": 1, "optional": None}) 625 True 626 >>> [None, 1.0000005] == approx([None,1]) 627 True 628 >>> ["foo", 1.0000005] == approx([None,1]) 629 False 630 631 If you're thinking about using ``approx``, then you might want to know how 632 it compares to other good ways of comparing floating-point numbers. All of 633 these algorithms are based on relative and absolute tolerances and should 634 agree for the most part, but they do have meaningful differences: 635 636 - ``math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)``: True if the relative 637 tolerance is met w.r.t. either ``a`` or ``b`` or if the absolute 638 tolerance is met. Because the relative tolerance is calculated w.r.t. 639 both ``a`` and ``b``, this test is symmetric (i.e. neither ``a`` nor 640 ``b`` is a "reference value"). You have to specify an absolute tolerance 641 if you want to compare to ``0.0`` because there is no tolerance by 642 default. More information: :py:func:`math.isclose`. 643 644 - ``numpy.isclose(a, b, rtol=1e-5, atol=1e-8)``: True if the difference 645 between ``a`` and ``b`` is less that the sum of the relative tolerance 646 w.r.t. ``b`` and the absolute tolerance. Because the relative tolerance 647 is only calculated w.r.t. ``b``, this test is asymmetric and you can 648 think of ``b`` as the reference value. Support for comparing sequences 649 is provided by :py:func:`numpy.allclose`. More information: 650 :std:doc:`numpy:reference/generated/numpy.isclose`. 651 652 - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b`` 653 are within an absolute tolerance of ``1e-7``. No relative tolerance is 654 considered , so this function is not appropriate for very large or very 655 small numbers. Also, it's only available in subclasses of ``unittest.TestCase`` 656 and it's ugly because it doesn't follow PEP8. More information: 657 :py:meth:`unittest.TestCase.assertAlmostEqual`. 658 659 - ``a == pytest.approx(b, rel=1e-6, abs=1e-12)``: True if the relative 660 tolerance is met w.r.t. ``b`` or if the absolute tolerance is met. 661 Because the relative tolerance is only calculated w.r.t. ``b``, this test 662 is asymmetric and you can think of ``b`` as the reference value. In the 663 special case that you explicitly specify an absolute tolerance but not a 664 relative tolerance, only the absolute tolerance is considered. 665 666 .. note:: 667 668 ``approx`` can handle numpy arrays, but we recommend the 669 specialised test helpers in :std:doc:`numpy:reference/routines.testing` 670 if you need support for comparisons, NaNs, or ULP-based tolerances. 671 672 To match strings using regex, you can use 673 `Matches <https://github.com/asottile/re-assert#re_assertmatchespattern-str-args-kwargs>`_ 674 from the 675 `re_assert package <https://github.com/asottile/re-assert>`_. 676 677 .. warning:: 678 679 .. versionchanged:: 3.2 680 681 In order to avoid inconsistent behavior, :py:exc:`TypeError` is 682 raised for ``>``, ``>=``, ``<`` and ``<=`` comparisons. 683 The example below illustrates the problem:: 684 685 assert approx(0.1) > 0.1 + 1e-10 # calls approx(0.1).__gt__(0.1 + 1e-10) 686 assert 0.1 + 1e-10 > approx(0.1) # calls approx(0.1).__lt__(0.1 + 1e-10) 687 688 In the second example one expects ``approx(0.1).__le__(0.1 + 1e-10)`` 689 to be called. But instead, ``approx(0.1).__lt__(0.1 + 1e-10)`` is used to 690 comparison. This is because the call hierarchy of rich comparisons 691 follows a fixed behavior. More information: :py:meth:`object.__ge__` 692 693 .. versionchanged:: 3.7.1 694 ``approx`` raises ``TypeError`` when it encounters a dict value or 695 sequence element of nonnumeric type. 696 697 .. versionchanged:: 6.1.0 698 ``approx`` falls back to strict equality for nonnumeric types instead 699 of raising ``TypeError``. 700 """ 701 # Delegate the comparison to a class that knows how to deal with the type 702 # of the expected value (e.g. int, float, list, dict, numpy.array, etc). 703 # 704 # The primary responsibility of these classes is to implement ``__eq__()`` 705 # and ``__repr__()``. The former is used to actually check if some 706 # "actual" value is equivalent to the given expected value within the 707 # allowed tolerance. The latter is used to show the user the expected 708 # value and tolerance, in the case that a test failed. 709 # 710 # The actual logic for making approximate comparisons can be found in 711 # ApproxScalar, which is used to compare individual numbers. All of the 712 # other Approx classes eventually delegate to this class. The ApproxBase 713 # class provides some convenient methods and overloads, but isn't really 714 # essential. 715 716 __tracebackhide__ = True 717 718 if isinstance(expected, Decimal): 719 cls: Type[ApproxBase] = ApproxDecimal 720 elif isinstance(expected, Mapping): 721 cls = ApproxMapping 722 elif _is_numpy_array(expected): 723 expected = _as_numpy_array(expected) 724 cls = ApproxNumpy 725 elif ( 726 hasattr(expected, "__getitem__") 727 and isinstance(expected, Sized) 728 and not isinstance(expected, (str, bytes)) 729 ): 730 cls = ApproxSequenceLike 731 elif isinstance(expected, Collection) and not isinstance(expected, (str, bytes)): 732 msg = f"pytest.approx() only supports ordered sequences, but got: {expected!r}" 733 raise TypeError(msg) 734 else: 735 cls = ApproxScalar 736 737 return cls(expected, rel, abs, nan_ok)
Assert that two numbers (or two ordered sequences of numbers) are equal to each other within some tolerance.
Due to the :doc:python:tutorial/floatingpoint
, numbers that we
would intuitively expect to be equal are not always so::
>>> 0.1 + 0.2 == 0.3
False
This problem is commonly encountered when writing tests, e.g. when making sure that floating-point values are what you expect them to be. One way to deal with this problem is to assert that two floating-point numbers are equal to within some appropriate tolerance::
>>> abs((0.1 + 0.2) - 0.3) < 1e-6
True
However, comparisons like this are tedious to write and difficult to
understand. Furthermore, absolute comparisons like the one above are
usually discouraged because there's no tolerance that works well for all
situations. 1e-6
is good for numbers around 1
, but too small for
very big numbers and too big for very small ones. It's better to express
the tolerance as a fraction of the expected value, but relative comparisons
like that are even more difficult to write correctly and concisely.
The approx
class performs floating-point comparisons using a syntax
that's as intuitive as possible::
>>> from pytest import approx
>>> 0.1 + 0.2 == approx(0.3)
True
The same syntax also works for ordered sequences of numbers::
>>> (0.1 + 0.2, 0.2 + 0.4) == approx((0.3, 0.6))
True
numpy
arrays::
>>> import numpy as np # doctest: +SKIP
>>> np.array([0.1, 0.2]) + np.array([0.2, 0.4]) == approx(np.array([0.3, 0.6])) # doctest: +SKIP
True
And for a numpy
array against a scalar::
>>> import numpy as np # doctest: +SKIP
>>> np.array([0.1, 0.2]) + np.array([0.2, 0.1]) == approx(0.3) # doctest: +SKIP
True
Only ordered sequences are supported, because approx
needs
to infer the relative position of the sequences without ambiguity. This means
sets
and other unordered sequences are not supported.
Finally, dictionary values can also be compared::
>>> {'a': 0.1 + 0.2, 'b': 0.2 + 0.4} == approx({'a': 0.3, 'b': 0.6})
True
The comparison will be true if both mappings have the same keys and their respective values match the expected tolerances.
Tolerances
By default, approx
considers numbers within a relative tolerance of
1e-6
(i.e. one part in a million) of its expected value to be equal.
This treatment would lead to surprising results if the expected value was
0.0
, because nothing but 0.0
itself is relatively close to 0.0
.
To handle this case less surprisingly, approx
also considers numbers
within an absolute tolerance of 1e-12
of its expected value to be
equal. Infinity and NaN are special cases. Infinity is only considered
equal to itself, regardless of the relative tolerance. NaN is not
considered equal to anything by default, but you can make it be equal to
itself by setting the nan_ok
argument to True. (This is meant to
facilitate comparing arrays that use NaN to mean "no data".)
Both the relative and absolute tolerances can be changed by passing
arguments to the approx
constructor::
>>> 1.0001 == approx(1)
False
>>> 1.0001 == approx(1, rel=1e-3)
True
>>> 1.0001 == approx(1, abs=1e-3)
True
If you specify abs
but not rel
, the comparison will not consider
the relative tolerance at all. In other words, two numbers that are within
the default relative tolerance of 1e-6
will still be considered unequal
if they exceed the specified absolute tolerance. If you specify both
abs
and rel
, the numbers will be considered equal if either
tolerance is met::
>>> 1 + 1e-8 == approx(1)
True
>>> 1 + 1e-8 == approx(1, abs=1e-12)
False
>>> 1 + 1e-8 == approx(1, rel=1e-6, abs=1e-12)
True
You can also use approx
to compare nonnumeric types, or dicts and
sequences containing nonnumeric types, in which case it falls back to
strict equality. This can be useful for comparing dicts and sequences that
can contain optional values::
>>> {"required": 1.0000005, "optional": None} == approx({"required": 1, "optional": None})
True
>>> [None, 1.0000005] == approx([None,1])
True
>>> ["foo", 1.0000005] == approx([None,1])
False
If you're thinking about using approx
, then you might want to know how
it compares to other good ways of comparing floating-point numbers. All of
these algorithms are based on relative and absolute tolerances and should
agree for the most part, but they do have meaningful differences:
math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)
: True if the relative tolerance is met w.r.t. eithera
orb
or if the absolute tolerance is met. Because the relative tolerance is calculated w.r.t. botha
andb
, this test is symmetric (i.e. neithera
norb
is a "reference value"). You have to specify an absolute tolerance if you want to compare to0.0
because there is no tolerance by default. More information:math.isclose()
.numpy.isclose(a, b, rtol=1e-5, atol=1e-8)
: True if the difference betweena
andb
is less that the sum of the relative tolerance w.r.t.b
and the absolute tolerance. Because the relative tolerance is only calculated w.r.t.b
, this test is asymmetric and you can think ofb
as the reference value. Support for comparing sequences is provided bynumpy.allclose()
. More information: :std:doc:numpy:reference/generated/numpy.isclose
.unittest.TestCase.assertAlmostEqual(a, b)
: True ifa
andb
are within an absolute tolerance of1e-7
. No relative tolerance is considered , so this function is not appropriate for very large or very small numbers. Also, it's only available in subclasses ofunittest.TestCase
and it's ugly because it doesn't follow PEP8. More information:unittest.TestCase.assertAlmostEqual()
.a == pytest.approx(b, rel=1e-6, abs=1e-12)
: True if the relative tolerance is met w.r.t.b
or if the absolute tolerance is met. Because the relative tolerance is only calculated w.r.t.b
, this test is asymmetric and you can think ofb
as the reference value. In the special case that you explicitly specify an absolute tolerance but not a relative tolerance, only the absolute tolerance is considered.
approx
can handle numpy arrays, but we recommend the
specialised test helpers in :std:doc:numpy:reference/routines.testing
if you need support for comparisons, NaNs, or ULP-based tolerances.
To match strings using regex, you can use Matches from the re_assert package .
.. versionchanged:: 3.2
In order to avoid inconsistent behavior, TypeError
is
raised for >
, >=
, <
and <=
comparisons.
The example below illustrates the problem::
assert approx(0.1) > 0.1 + 1e-10 # calls approx(0.1).__gt__(0.1 + 1e-10)
assert 0.1 + 1e-10 > approx(0.1) # calls approx(0.1).__lt__(0.1 + 1e-10)
In the second example one expects approx(0.1).__le__(0.1 + 1e-10)
to be called. But instead, approx(0.1).__lt__(0.1 + 1e-10)
is used to
comparison. This is because the call hierarchy of rich comparisons
follows a fixed behavior. More information: object.__ge__()
Changed in version 3.7.1:
approx
raises TypeError
when it encounters a dict value or
sequence element of nonnumeric type.
Changed in version 6.1.0:
approx
falls back to strict equality for nonnumeric types instead
of raising TypeError
.
58@final 59@dataclasses.dataclass 60class Cache: 61 """Instance of the `cache` fixture.""" 62 63 _cachedir: Path = dataclasses.field(repr=False) 64 _config: Config = dataclasses.field(repr=False) 65 66 # Sub-directory under cache-dir for directories created by `mkdir()`. 67 _CACHE_PREFIX_DIRS = "d" 68 69 # Sub-directory under cache-dir for values created by `set()`. 70 _CACHE_PREFIX_VALUES = "v" 71 72 def __init__( 73 self, cachedir: Path, config: Config, *, _ispytest: bool = False 74 ) -> None: 75 check_ispytest(_ispytest) 76 self._cachedir = cachedir 77 self._config = config 78 79 @classmethod 80 def for_config(cls, config: Config, *, _ispytest: bool = False) -> "Cache": 81 """Create the Cache instance for a Config. 82 83 :meta private: 84 """ 85 check_ispytest(_ispytest) 86 cachedir = cls.cache_dir_from_config(config, _ispytest=True) 87 if config.getoption("cacheclear") and cachedir.is_dir(): 88 cls.clear_cache(cachedir, _ispytest=True) 89 return cls(cachedir, config, _ispytest=True) 90 91 @classmethod 92 def clear_cache(cls, cachedir: Path, _ispytest: bool = False) -> None: 93 """Clear the sub-directories used to hold cached directories and values. 94 95 :meta private: 96 """ 97 check_ispytest(_ispytest) 98 for prefix in (cls._CACHE_PREFIX_DIRS, cls._CACHE_PREFIX_VALUES): 99 d = cachedir / prefix 100 if d.is_dir(): 101 rm_rf(d) 102 103 @staticmethod 104 def cache_dir_from_config(config: Config, *, _ispytest: bool = False) -> Path: 105 """Get the path to the cache directory for a Config. 106 107 :meta private: 108 """ 109 check_ispytest(_ispytest) 110 return resolve_from_str(config.getini("cache_dir"), config.rootpath) 111 112 def warn(self, fmt: str, *, _ispytest: bool = False, **args: object) -> None: 113 """Issue a cache warning. 114 115 :meta private: 116 """ 117 check_ispytest(_ispytest) 118 import warnings 119 120 from _pytest.warning_types import PytestCacheWarning 121 122 warnings.warn( 123 PytestCacheWarning(fmt.format(**args) if args else fmt), 124 self._config.hook, 125 stacklevel=3, 126 ) 127 128 def _mkdir(self, path: Path) -> None: 129 self._ensure_cache_dir_and_supporting_files() 130 path.mkdir(exist_ok=True, parents=True) 131 132 def mkdir(self, name: str) -> Path: 133 """Return a directory path object with the given name. 134 135 If the directory does not yet exist, it will be created. You can use 136 it to manage files to e.g. store/retrieve database dumps across test 137 sessions. 138 139 .. versionadded:: 7.0 140 141 :param name: 142 Must be a string not containing a ``/`` separator. 143 Make sure the name contains your plugin or application 144 identifiers to prevent clashes with other cache users. 145 """ 146 path = Path(name) 147 if len(path.parts) > 1: 148 raise ValueError("name is not allowed to contain path separators") 149 res = self._cachedir.joinpath(self._CACHE_PREFIX_DIRS, path) 150 self._mkdir(res) 151 return res 152 153 def _getvaluepath(self, key: str) -> Path: 154 return self._cachedir.joinpath(self._CACHE_PREFIX_VALUES, Path(key)) 155 156 def get(self, key: str, default): 157 """Return the cached value for the given key. 158 159 If no value was yet cached or the value cannot be read, the specified 160 default is returned. 161 162 :param key: 163 Must be a ``/`` separated value. Usually the first 164 name is the name of your plugin or your application. 165 :param default: 166 The value to return in case of a cache-miss or invalid cache value. 167 """ 168 path = self._getvaluepath(key) 169 try: 170 with path.open("r", encoding="UTF-8") as f: 171 return json.load(f) 172 except (ValueError, OSError): 173 return default 174 175 def set(self, key: str, value: object) -> None: 176 """Save value for the given key. 177 178 :param key: 179 Must be a ``/`` separated value. Usually the first 180 name is the name of your plugin or your application. 181 :param value: 182 Must be of any combination of basic python types, 183 including nested types like lists of dictionaries. 184 """ 185 path = self._getvaluepath(key) 186 try: 187 self._mkdir(path.parent) 188 except OSError as exc: 189 self.warn( 190 f"could not create cache path {path}: {exc}", 191 _ispytest=True, 192 ) 193 return 194 data = json.dumps(value, ensure_ascii=False, indent=2) 195 try: 196 f = path.open("w", encoding="UTF-8") 197 except OSError as exc: 198 self.warn( 199 f"cache could not write path {path}: {exc}", 200 _ispytest=True, 201 ) 202 else: 203 with f: 204 f.write(data) 205 206 def _ensure_cache_dir_and_supporting_files(self) -> None: 207 """Create the cache dir and its supporting files.""" 208 if self._cachedir.is_dir(): 209 return 210 211 self._cachedir.parent.mkdir(parents=True, exist_ok=True) 212 with tempfile.TemporaryDirectory( 213 prefix="pytest-cache-files-", 214 dir=self._cachedir.parent, 215 ) as newpath: 216 path = Path(newpath) 217 with open(path.joinpath("README.md"), "xt", encoding="UTF-8") as f: 218 f.write(README_CONTENT) 219 with open(path.joinpath(".gitignore"), "xt", encoding="UTF-8") as f: 220 f.write("# Created by pytest automatically.\n*\n") 221 with open(path.joinpath("CACHEDIR.TAG"), "xb") as f: 222 f.write(CACHEDIR_TAG_CONTENT) 223 224 path.rename(self._cachedir) 225 # Create a directory in place of the one we just moved so that `TemporaryDirectory`'s 226 # cleanup doesn't complain. 227 # 228 # TODO: pass ignore_cleanup_errors=True when we no longer support python < 3.10. See 229 # https://github.com/python/cpython/issues/74168. Note that passing delete=False would 230 # do the wrong thing in case of errors and isn't supported until python 3.12. 231 path.mkdir()
Instance of the cache
fixture.
79 @classmethod 80 def for_config(cls, config: Config, *, _ispytest: bool = False) -> "Cache": 81 """Create the Cache instance for a Config. 82 83 :meta private: 84 """ 85 check_ispytest(_ispytest) 86 cachedir = cls.cache_dir_from_config(config, _ispytest=True) 87 if config.getoption("cacheclear") and cachedir.is_dir(): 88 cls.clear_cache(cachedir, _ispytest=True) 89 return cls(cachedir, config, _ispytest=True)
Create the Cache instance for a Config.
:meta private:
91 @classmethod 92 def clear_cache(cls, cachedir: Path, _ispytest: bool = False) -> None: 93 """Clear the sub-directories used to hold cached directories and values. 94 95 :meta private: 96 """ 97 check_ispytest(_ispytest) 98 for prefix in (cls._CACHE_PREFIX_DIRS, cls._CACHE_PREFIX_VALUES): 99 d = cachedir / prefix 100 if d.is_dir(): 101 rm_rf(d)
Clear the sub-directories used to hold cached directories and values.
:meta private:
103 @staticmethod 104 def cache_dir_from_config(config: Config, *, _ispytest: bool = False) -> Path: 105 """Get the path to the cache directory for a Config. 106 107 :meta private: 108 """ 109 check_ispytest(_ispytest) 110 return resolve_from_str(config.getini("cache_dir"), config.rootpath)
Get the path to the cache directory for a Config.
:meta private:
112 def warn(self, fmt: str, *, _ispytest: bool = False, **args: object) -> None: 113 """Issue a cache warning. 114 115 :meta private: 116 """ 117 check_ispytest(_ispytest) 118 import warnings 119 120 from _pytest.warning_types import PytestCacheWarning 121 122 warnings.warn( 123 PytestCacheWarning(fmt.format(**args) if args else fmt), 124 self._config.hook, 125 stacklevel=3, 126 )
Issue a cache warning.
:meta private:
132 def mkdir(self, name: str) -> Path: 133 """Return a directory path object with the given name. 134 135 If the directory does not yet exist, it will be created. You can use 136 it to manage files to e.g. store/retrieve database dumps across test 137 sessions. 138 139 .. versionadded:: 7.0 140 141 :param name: 142 Must be a string not containing a ``/`` separator. 143 Make sure the name contains your plugin or application 144 identifiers to prevent clashes with other cache users. 145 """ 146 path = Path(name) 147 if len(path.parts) > 1: 148 raise ValueError("name is not allowed to contain path separators") 149 res = self._cachedir.joinpath(self._CACHE_PREFIX_DIRS, path) 150 self._mkdir(res) 151 return res
Return a directory path object with the given name.
If the directory does not yet exist, it will be created. You can use it to manage files to e.g. store/retrieve database dumps across test sessions.
New in version 7.0.
Parameters
- name:
Must be a string not containing a
/
separator. Make sure the name contains your plugin or application identifiers to prevent clashes with other cache users.
156 def get(self, key: str, default): 157 """Return the cached value for the given key. 158 159 If no value was yet cached or the value cannot be read, the specified 160 default is returned. 161 162 :param key: 163 Must be a ``/`` separated value. Usually the first 164 name is the name of your plugin or your application. 165 :param default: 166 The value to return in case of a cache-miss or invalid cache value. 167 """ 168 path = self._getvaluepath(key) 169 try: 170 with path.open("r", encoding="UTF-8") as f: 171 return json.load(f) 172 except (ValueError, OSError): 173 return default
Return the cached value for the given key.
If no value was yet cached or the value cannot be read, the specified default is returned.
Parameters
- key:
Must be a
/
separated value. Usually the first name is the name of your plugin or your application. - default: The value to return in case of a cache-miss or invalid cache value.
175 def set(self, key: str, value: object) -> None: 176 """Save value for the given key. 177 178 :param key: 179 Must be a ``/`` separated value. Usually the first 180 name is the name of your plugin or your application. 181 :param value: 182 Must be of any combination of basic python types, 183 including nested types like lists of dictionaries. 184 """ 185 path = self._getvaluepath(key) 186 try: 187 self._mkdir(path.parent) 188 except OSError as exc: 189 self.warn( 190 f"could not create cache path {path}: {exc}", 191 _ispytest=True, 192 ) 193 return 194 data = json.dumps(value, ensure_ascii=False, indent=2) 195 try: 196 f = path.open("w", encoding="UTF-8") 197 except OSError as exc: 198 self.warn( 199 f"cache could not write path {path}: {exc}", 200 _ispytest=True, 201 ) 202 else: 203 with f: 204 f.write(data)
Save value for the given key.
Parameters
- key:
Must be a
/
separated value. Usually the first name is the name of your plugin or your application. - value: Must be of any combination of basic python types, including nested types like lists of dictionaries.
270@final 271@dataclasses.dataclass 272class CallInfo(Generic[TResult]): 273 """Result/Exception info of a function invocation.""" 274 275 _result: Optional[TResult] 276 #: The captured exception of the call, if it raised. 277 excinfo: Optional[ExceptionInfo[BaseException]] 278 #: The system time when the call started, in seconds since the epoch. 279 start: float 280 #: The system time when the call ended, in seconds since the epoch. 281 stop: float 282 #: The call duration, in seconds. 283 duration: float 284 #: The context of invocation: "collect", "setup", "call" or "teardown". 285 when: Literal["collect", "setup", "call", "teardown"] 286 287 def __init__( 288 self, 289 result: Optional[TResult], 290 excinfo: Optional[ExceptionInfo[BaseException]], 291 start: float, 292 stop: float, 293 duration: float, 294 when: Literal["collect", "setup", "call", "teardown"], 295 *, 296 _ispytest: bool = False, 297 ) -> None: 298 check_ispytest(_ispytest) 299 self._result = result 300 self.excinfo = excinfo 301 self.start = start 302 self.stop = stop 303 self.duration = duration 304 self.when = when 305 306 @property 307 def result(self) -> TResult: 308 """The return value of the call, if it didn't raise. 309 310 Can only be accessed if excinfo is None. 311 """ 312 if self.excinfo is not None: 313 raise AttributeError(f"{self!r} has no valid result") 314 # The cast is safe because an exception wasn't raised, hence 315 # _result has the expected function return type (which may be 316 # None, that's why a cast and not an assert). 317 return cast(TResult, self._result) 318 319 @classmethod 320 def from_call( 321 cls, 322 func: Callable[[], TResult], 323 when: Literal["collect", "setup", "call", "teardown"], 324 reraise: Optional[ 325 Union[Type[BaseException], Tuple[Type[BaseException], ...]] 326 ] = None, 327 ) -> "CallInfo[TResult]": 328 """Call func, wrapping the result in a CallInfo. 329 330 :param func: 331 The function to call. Called without arguments. 332 :param when: 333 The phase in which the function is called. 334 :param reraise: 335 Exception or exceptions that shall propagate if raised by the 336 function, instead of being wrapped in the CallInfo. 337 """ 338 excinfo = None 339 start = timing.time() 340 precise_start = timing.perf_counter() 341 try: 342 result: Optional[TResult] = func() 343 except BaseException: 344 excinfo = ExceptionInfo.from_current() 345 if reraise is not None and isinstance(excinfo.value, reraise): 346 raise 347 result = None 348 # use the perf counter 349 precise_stop = timing.perf_counter() 350 duration = precise_stop - precise_start 351 stop = timing.time() 352 return cls( 353 start=start, 354 stop=stop, 355 duration=duration, 356 when=when, 357 result=result, 358 excinfo=excinfo, 359 _ispytest=True, 360 ) 361 362 def __repr__(self) -> str: 363 if self.excinfo is None: 364 return f"<CallInfo when={self.when!r} result: {self._result!r}>" 365 return f"<CallInfo when={self.when!r} excinfo={self.excinfo!r}>"
Result/Exception info of a function invocation.
287 def __init__( 288 self, 289 result: Optional[TResult], 290 excinfo: Optional[ExceptionInfo[BaseException]], 291 start: float, 292 stop: float, 293 duration: float, 294 when: Literal["collect", "setup", "call", "teardown"], 295 *, 296 _ispytest: bool = False, 297 ) -> None: 298 check_ispytest(_ispytest) 299 self._result = result 300 self.excinfo = excinfo 301 self.start = start 302 self.stop = stop 303 self.duration = duration 304 self.when = when
306 @property 307 def result(self) -> TResult: 308 """The return value of the call, if it didn't raise. 309 310 Can only be accessed if excinfo is None. 311 """ 312 if self.excinfo is not None: 313 raise AttributeError(f"{self!r} has no valid result") 314 # The cast is safe because an exception wasn't raised, hence 315 # _result has the expected function return type (which may be 316 # None, that's why a cast and not an assert). 317 return cast(TResult, self._result)
The return value of the call, if it didn't raise.
Can only be accessed if excinfo is None.
319 @classmethod 320 def from_call( 321 cls, 322 func: Callable[[], TResult], 323 when: Literal["collect", "setup", "call", "teardown"], 324 reraise: Optional[ 325 Union[Type[BaseException], Tuple[Type[BaseException], ...]] 326 ] = None, 327 ) -> "CallInfo[TResult]": 328 """Call func, wrapping the result in a CallInfo. 329 330 :param func: 331 The function to call. Called without arguments. 332 :param when: 333 The phase in which the function is called. 334 :param reraise: 335 Exception or exceptions that shall propagate if raised by the 336 function, instead of being wrapped in the CallInfo. 337 """ 338 excinfo = None 339 start = timing.time() 340 precise_start = timing.perf_counter() 341 try: 342 result: Optional[TResult] = func() 343 except BaseException: 344 excinfo = ExceptionInfo.from_current() 345 if reraise is not None and isinstance(excinfo.value, reraise): 346 raise 347 result = None 348 # use the perf counter 349 precise_stop = timing.perf_counter() 350 duration = precise_stop - precise_start 351 stop = timing.time() 352 return cls( 353 start=start, 354 stop=stop, 355 duration=duration, 356 when=when, 357 result=result, 358 excinfo=excinfo, 359 _ispytest=True, 360 )
Call func, wrapping the result in a CallInfo.
Parameters
- func: The function to call. Called without arguments.
- when: The phase in which the function is called.
- reraise: Exception or exceptions that shall propagate if raised by the function, instead of being wrapped in the CallInfo.
895class CaptureFixture(Generic[AnyStr]): 896 """Object returned by the :fixture:`capsys`, :fixture:`capsysbinary`, 897 :fixture:`capfd` and :fixture:`capfdbinary` fixtures.""" 898 899 def __init__( 900 self, 901 captureclass: Type[CaptureBase[AnyStr]], 902 request: SubRequest, 903 *, 904 _ispytest: bool = False, 905 ) -> None: 906 check_ispytest(_ispytest) 907 self.captureclass: Type[CaptureBase[AnyStr]] = captureclass 908 self.request = request 909 self._capture: Optional[MultiCapture[AnyStr]] = None 910 self._captured_out: AnyStr = self.captureclass.EMPTY_BUFFER 911 self._captured_err: AnyStr = self.captureclass.EMPTY_BUFFER 912 913 def _start(self) -> None: 914 if self._capture is None: 915 self._capture = MultiCapture( 916 in_=None, 917 out=self.captureclass(1), 918 err=self.captureclass(2), 919 ) 920 self._capture.start_capturing() 921 922 def close(self) -> None: 923 if self._capture is not None: 924 out, err = self._capture.pop_outerr_to_orig() 925 self._captured_out += out 926 self._captured_err += err 927 self._capture.stop_capturing() 928 self._capture = None 929 930 def readouterr(self) -> CaptureResult[AnyStr]: 931 """Read and return the captured output so far, resetting the internal 932 buffer. 933 934 :returns: 935 The captured content as a namedtuple with ``out`` and ``err`` 936 string attributes. 937 """ 938 captured_out, captured_err = self._captured_out, self._captured_err 939 if self._capture is not None: 940 out, err = self._capture.readouterr() 941 captured_out += out 942 captured_err += err 943 self._captured_out = self.captureclass.EMPTY_BUFFER 944 self._captured_err = self.captureclass.EMPTY_BUFFER 945 return CaptureResult(captured_out, captured_err) 946 947 def _suspend(self) -> None: 948 """Suspend this fixture's own capturing temporarily.""" 949 if self._capture is not None: 950 self._capture.suspend_capturing() 951 952 def _resume(self) -> None: 953 """Resume this fixture's own capturing temporarily.""" 954 if self._capture is not None: 955 self._capture.resume_capturing() 956 957 def _is_started(self) -> bool: 958 """Whether actively capturing -- not disabled or closed.""" 959 if self._capture is not None: 960 return self._capture.is_started() 961 return False 962 963 @contextlib.contextmanager 964 def disabled(self) -> Generator[None, None, None]: 965 """Temporarily disable capturing while inside the ``with`` block.""" 966 capmanager: CaptureManager = self.request.config.pluginmanager.getplugin( 967 "capturemanager" 968 ) 969 with capmanager.global_and_fixture_disabled(): 970 yield
Object returned by the :fixture:capsys
, :fixture:capsysbinary
,
:fixture:capfd
and :fixture:capfdbinary
fixtures.
899 def __init__( 900 self, 901 captureclass: Type[CaptureBase[AnyStr]], 902 request: SubRequest, 903 *, 904 _ispytest: bool = False, 905 ) -> None: 906 check_ispytest(_ispytest) 907 self.captureclass: Type[CaptureBase[AnyStr]] = captureclass 908 self.request = request 909 self._capture: Optional[MultiCapture[AnyStr]] = None 910 self._captured_out: AnyStr = self.captureclass.EMPTY_BUFFER 911 self._captured_err: AnyStr = self.captureclass.EMPTY_BUFFER
930 def readouterr(self) -> CaptureResult[AnyStr]: 931 """Read and return the captured output so far, resetting the internal 932 buffer. 933 934 :returns: 935 The captured content as a namedtuple with ``out`` and ``err`` 936 string attributes. 937 """ 938 captured_out, captured_err = self._captured_out, self._captured_err 939 if self._capture is not None: 940 out, err = self._capture.readouterr() 941 captured_out += out 942 captured_err += err 943 self._captured_out = self.captureclass.EMPTY_BUFFER 944 self._captured_err = self.captureclass.EMPTY_BUFFER 945 return CaptureResult(captured_out, captured_err)
Read and return the captured output so far, resetting the internal buffer.
:returns:
The captured content as a namedtuple with out
and err
string attributes.
963 @contextlib.contextmanager 964 def disabled(self) -> Generator[None, None, None]: 965 """Temporarily disable capturing while inside the ``with`` block.""" 966 capmanager: CaptureManager = self.request.config.pluginmanager.getplugin( 967 "capturemanager" 968 ) 969 with capmanager.global_and_fixture_disabled(): 970 yield
Temporarily disable capturing while inside the with
block.
726class Class(PyCollector): 727 """Collector for test methods (and nested classes) in a Python class.""" 728 729 @classmethod 730 def from_parent(cls, parent, *, name, obj=None, **kw) -> "Self": # type: ignore[override] 731 """The public constructor.""" 732 return super().from_parent(name=name, parent=parent, **kw) 733 734 def newinstance(self): 735 return self.obj() 736 737 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: 738 if not safe_getattr(self.obj, "__test__", True): 739 return [] 740 if hasinit(self.obj): 741 assert self.parent is not None 742 self.warn( 743 PytestCollectionWarning( 744 f"cannot collect test class {self.obj.__name__!r} because it has a " 745 f"__init__ constructor (from: {self.parent.nodeid})" 746 ) 747 ) 748 return [] 749 elif hasnew(self.obj): 750 assert self.parent is not None 751 self.warn( 752 PytestCollectionWarning( 753 f"cannot collect test class {self.obj.__name__!r} because it has a " 754 f"__new__ constructor (from: {self.parent.nodeid})" 755 ) 756 ) 757 return [] 758 759 self._register_setup_class_fixture() 760 self._register_setup_method_fixture() 761 762 self.session._fixturemanager.parsefactories(self.newinstance(), self.nodeid) 763 764 return super().collect() 765 766 def _register_setup_class_fixture(self) -> None: 767 """Register an autouse, class scoped fixture into the collected class object 768 that invokes setup_class/teardown_class if either or both are available. 769 770 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with 771 other fixtures (#517). 772 """ 773 setup_class = _get_first_non_fixture_func(self.obj, ("setup_class",)) 774 teardown_class = _get_first_non_fixture_func(self.obj, ("teardown_class",)) 775 if setup_class is None and teardown_class is None: 776 return 777 778 def xunit_setup_class_fixture(request) -> Generator[None, None, None]: 779 cls = request.cls 780 if setup_class is not None: 781 func = getimfunc(setup_class) 782 _call_with_optional_argument(func, cls) 783 yield 784 if teardown_class is not None: 785 func = getimfunc(teardown_class) 786 _call_with_optional_argument(func, cls) 787 788 self.session._fixturemanager._register_fixture( 789 # Use a unique name to speed up lookup. 790 name=f"_xunit_setup_class_fixture_{self.obj.__qualname__}", 791 func=xunit_setup_class_fixture, 792 nodeid=self.nodeid, 793 scope="class", 794 autouse=True, 795 ) 796 797 def _register_setup_method_fixture(self) -> None: 798 """Register an autouse, function scoped fixture into the collected class object 799 that invokes setup_method/teardown_method if either or both are available. 800 801 Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with 802 other fixtures (#517). 803 """ 804 setup_name = "setup_method" 805 setup_method = _get_first_non_fixture_func(self.obj, (setup_name,)) 806 teardown_name = "teardown_method" 807 teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,)) 808 if setup_method is None and teardown_method is None: 809 return 810 811 def xunit_setup_method_fixture(request) -> Generator[None, None, None]: 812 instance = request.instance 813 method = request.function 814 if setup_method is not None: 815 func = getattr(instance, setup_name) 816 _call_with_optional_argument(func, method) 817 yield 818 if teardown_method is not None: 819 func = getattr(instance, teardown_name) 820 _call_with_optional_argument(func, method) 821 822 self.session._fixturemanager._register_fixture( 823 # Use a unique name to speed up lookup. 824 name=f"_xunit_setup_method_fixture_{self.obj.__qualname__}", 825 func=xunit_setup_method_fixture, 826 nodeid=self.nodeid, 827 scope="function", 828 autouse=True, 829 )
Collector for test methods (and nested classes) in a Python class.
729 @classmethod 730 def from_parent(cls, parent, *, name, obj=None, **kw) -> "Self": # type: ignore[override] 731 """The public constructor.""" 732 return super().from_parent(name=name, parent=parent, **kw)
The public constructor.
737 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: 738 if not safe_getattr(self.obj, "__test__", True): 739 return [] 740 if hasinit(self.obj): 741 assert self.parent is not None 742 self.warn( 743 PytestCollectionWarning( 744 f"cannot collect test class {self.obj.__name__!r} because it has a " 745 f"__init__ constructor (from: {self.parent.nodeid})" 746 ) 747 ) 748 return [] 749 elif hasnew(self.obj): 750 assert self.parent is not None 751 self.warn( 752 PytestCollectionWarning( 753 f"cannot collect test class {self.obj.__name__!r} because it has a " 754 f"__new__ constructor (from: {self.parent.nodeid})" 755 ) 756 ) 757 return [] 758 759 self._register_setup_class_fixture() 760 self._register_setup_method_fixture() 761 762 self.session._fixturemanager.parsefactories(self.newinstance(), self.nodeid) 763 764 return super().collect()
Collect children (items and collectors) for this collector.
Inherited Members
- _pytest.nodes.Node
- Node
- fspath
- name
- parent
- path
- keywords
- own_markers
- extra_keyword_matches
- stash
- ihook
- warn
- nodeid
- setup
- teardown
- iter_parents
- listchain
- add_marker
- iter_markers
- iter_markers_with_node
- get_closest_marker
- listextrakeywords
- listnames
- addfinalizer
- getparent
- config
- session
- _pytest.python.PyCollector
- funcnamefilter
- isnosetest
- classnamefilter
- istestfunction
- istestclass
- _pytest.python.PyobjMixin
- module
- cls
- instance
- obj
- getmodpath
- reportinfo
143def main( 144 args: Optional[Union[List[str], "os.PathLike[str]"]] = None, 145 plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None, 146) -> Union[int, ExitCode]: 147 """Perform an in-process test run. 148 149 :param args: 150 List of command line arguments. If `None` or not given, defaults to reading 151 arguments directly from the process command line (:data:`sys.argv`). 152 :param plugins: List of plugin objects to be auto-registered during initialization. 153 154 :returns: An exit code. 155 """ 156 old_pytest_version = os.environ.get("PYTEST_VERSION") 157 try: 158 os.environ["PYTEST_VERSION"] = __version__ 159 try: 160 config = _prepareconfig(args, plugins) 161 except ConftestImportFailure as e: 162 exc_info = ExceptionInfo.from_exception(e.cause) 163 tw = TerminalWriter(sys.stderr) 164 tw.line(f"ImportError while loading conftest '{e.path}'.", red=True) 165 exc_info.traceback = exc_info.traceback.filter( 166 filter_traceback_for_conftest_import_failure 167 ) 168 exc_repr = ( 169 exc_info.getrepr(style="short", chain=False) 170 if exc_info.traceback 171 else exc_info.exconly() 172 ) 173 formatted_tb = str(exc_repr) 174 for line in formatted_tb.splitlines(): 175 tw.line(line.rstrip(), red=True) 176 return ExitCode.USAGE_ERROR 177 else: 178 try: 179 ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main( 180 config=config 181 ) 182 try: 183 return ExitCode(ret) 184 except ValueError: 185 return ret 186 finally: 187 config._ensure_unconfigure() 188 except UsageError as e: 189 tw = TerminalWriter(sys.stderr) 190 for msg in e.args: 191 tw.line(f"ERROR: {msg}\n", red=True) 192 return ExitCode.USAGE_ERROR 193 finally: 194 if old_pytest_version is None: 195 os.environ.pop("PYTEST_VERSION", None) 196 else: 197 os.environ["PYTEST_VERSION"] = old_pytest_version
Perform an in-process test run.
Parameters
- args:
List of command line arguments. If
None
or not given, defaults to reading arguments directly from the process command line (sys.argv
). - plugins: List of plugin objects to be auto-registered during initialization.
:returns: An exit code.
505class Collector(Node, abc.ABC): 506 """Base class of all collectors. 507 508 Collector create children through `collect()` and thus iteratively build 509 the collection tree. 510 """ 511 512 class CollectError(Exception): 513 """An error during collection, contains a custom message.""" 514 515 @abc.abstractmethod 516 def collect(self) -> Iterable[Union["Item", "Collector"]]: 517 """Collect children (items and collectors) for this collector.""" 518 raise NotImplementedError("abstract") 519 520 # TODO: This omits the style= parameter which breaks Liskov Substitution. 521 def repr_failure( # type: ignore[override] 522 self, excinfo: ExceptionInfo[BaseException] 523 ) -> Union[str, TerminalRepr]: 524 """Return a representation of a collection failure. 525 526 :param excinfo: Exception information for the failure. 527 """ 528 if isinstance(excinfo.value, self.CollectError) and not self.config.getoption( 529 "fulltrace", False 530 ): 531 exc = excinfo.value 532 return str(exc.args[0]) 533 534 # Respect explicit tbstyle option, but default to "short" 535 # (_repr_failure_py uses "long" with "fulltrace" option always). 536 tbstyle = self.config.getoption("tbstyle", "auto") 537 if tbstyle == "auto": 538 tbstyle = "short" 539 540 return self._repr_failure_py(excinfo, style=tbstyle) 541 542 def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: 543 if hasattr(self, "path"): 544 traceback = excinfo.traceback 545 ntraceback = traceback.cut(path=self.path) 546 if ntraceback == traceback: 547 ntraceback = ntraceback.cut(excludepath=tracebackcutdir) 548 return ntraceback.filter(excinfo) 549 return excinfo.traceback
Base class of all collectors.
Collector create children through collect()
and thus iteratively build
the collection tree.
515 @abc.abstractmethod 516 def collect(self) -> Iterable[Union["Item", "Collector"]]: 517 """Collect children (items and collectors) for this collector.""" 518 raise NotImplementedError("abstract")
Collect children (items and collectors) for this collector.
521 def repr_failure( # type: ignore[override] 522 self, excinfo: ExceptionInfo[BaseException] 523 ) -> Union[str, TerminalRepr]: 524 """Return a representation of a collection failure. 525 526 :param excinfo: Exception information for the failure. 527 """ 528 if isinstance(excinfo.value, self.CollectError) and not self.config.getoption( 529 "fulltrace", False 530 ): 531 exc = excinfo.value 532 return str(exc.args[0]) 533 534 # Respect explicit tbstyle option, but default to "short" 535 # (_repr_failure_py uses "long" with "fulltrace" option always). 536 tbstyle = self.config.getoption("tbstyle", "auto") 537 if tbstyle == "auto": 538 tbstyle = "short" 539 540 return self._repr_failure_py(excinfo, style=tbstyle)
Return a representation of a collection failure.
Parameters
- excinfo: Exception information for the failure.
Inherited Members
- _pytest.nodes.Node
- fspath
- name
- parent
- path
- keywords
- own_markers
- extra_keyword_matches
- stash
- from_parent
- ihook
- warn
- nodeid
- setup
- teardown
- iter_parents
- listchain
- add_marker
- iter_markers
- iter_markers_with_node
- get_closest_marker
- listextrakeywords
- listnames
- addfinalizer
- getparent
- config
- session
An error during collection, contains a custom message.
Inherited Members
- builtins.Exception
- Exception
- builtins.BaseException
- with_traceback
- add_note
- args
386@final 387class CollectReport(BaseReport): 388 """Collection report object. 389 390 Reports can contain arbitrary extra attributes. 391 """ 392 393 when = "collect" 394 395 def __init__( 396 self, 397 nodeid: str, 398 outcome: "Literal['passed', 'failed', 'skipped']", 399 longrepr: Union[ 400 None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr 401 ], 402 result: Optional[List[Union[Item, Collector]]], 403 sections: Iterable[Tuple[str, str]] = (), 404 **extra, 405 ) -> None: 406 #: Normalized collection nodeid. 407 self.nodeid = nodeid 408 409 #: Test outcome, always one of "passed", "failed", "skipped". 410 self.outcome = outcome 411 412 #: None or a failure representation. 413 self.longrepr = longrepr 414 415 #: The collected items and collection nodes. 416 self.result = result or [] 417 418 #: Tuples of str ``(heading, content)`` with extra information 419 #: for the test report. Used by pytest to add text captured 420 #: from ``stdout``, ``stderr``, and intercepted logging events. May 421 #: be used by other plugins to add arbitrary information to reports. 422 self.sections = list(sections) 423 424 self.__dict__.update(extra) 425 426 @property 427 def location( # type:ignore[override] 428 self, 429 ) -> Optional[Tuple[str, Optional[int], str]]: 430 return (self.fspath, None, self.fspath) 431 432 def __repr__(self) -> str: 433 return f"<CollectReport {self.nodeid!r} lenresult={len(self.result)} outcome={self.outcome!r}>"
Collection report object.
Reports can contain arbitrary extra attributes.
395 def __init__( 396 self, 397 nodeid: str, 398 outcome: "Literal['passed', 'failed', 'skipped']", 399 longrepr: Union[ 400 None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr 401 ], 402 result: Optional[List[Union[Item, Collector]]], 403 sections: Iterable[Tuple[str, str]] = (), 404 **extra, 405 ) -> None: 406 #: Normalized collection nodeid. 407 self.nodeid = nodeid 408 409 #: Test outcome, always one of "passed", "failed", "skipped". 410 self.outcome = outcome 411 412 #: None or a failure representation. 413 self.longrepr = longrepr 414 415 #: The collected items and collection nodes. 416 self.result = result or [] 417 418 #: Tuples of str ``(heading, content)`` with extra information 419 #: for the test report. Used by pytest to add text captured 420 #: from ``stdout``, ``stderr``, and intercepted logging events. May 421 #: be used by other plugins to add arbitrary information to reports. 422 self.sections = list(sections) 423 424 self.__dict__.update(extra)
Inherited Members
- _pytest.reports.BaseReport
- toterminal
- get_sections
- longreprtext
- caplog
- capstdout
- capstderr
- passed
- failed
- skipped
- fspath
- count_towards_summary
- head_line
974@final 975class Config: 976 """Access to configuration values, pluginmanager and plugin hooks. 977 978 :param PytestPluginManager pluginmanager: 979 A pytest PluginManager. 980 981 :param InvocationParams invocation_params: 982 Object containing parameters regarding the :func:`pytest.main` 983 invocation. 984 """ 985 986 @final 987 @dataclasses.dataclass(frozen=True) 988 class InvocationParams: 989 """Holds parameters passed during :func:`pytest.main`. 990 991 The object attributes are read-only. 992 993 .. versionadded:: 5.1 994 995 .. note:: 996 997 Note that the environment variable ``PYTEST_ADDOPTS`` and the ``addopts`` 998 ini option are handled by pytest, not being included in the ``args`` attribute. 999 1000 Plugins accessing ``InvocationParams`` must be aware of that. 1001 """ 1002 1003 args: Tuple[str, ...] 1004 """The command-line arguments as passed to :func:`pytest.main`.""" 1005 plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] 1006 """Extra plugins, might be `None`.""" 1007 dir: Path 1008 """The directory from which :func:`pytest.main` was invoked.""" 1009 1010 def __init__( 1011 self, 1012 *, 1013 args: Iterable[str], 1014 plugins: Optional[Sequence[Union[str, _PluggyPlugin]]], 1015 dir: Path, 1016 ) -> None: 1017 object.__setattr__(self, "args", tuple(args)) 1018 object.__setattr__(self, "plugins", plugins) 1019 object.__setattr__(self, "dir", dir) 1020 1021 class ArgsSource(enum.Enum): 1022 """Indicates the source of the test arguments. 1023 1024 .. versionadded:: 7.2 1025 """ 1026 1027 #: Command line arguments. 1028 ARGS = enum.auto() 1029 #: Invocation directory. 1030 INVOCATION_DIR = enum.auto() 1031 INCOVATION_DIR = INVOCATION_DIR # backwards compatibility alias 1032 #: 'testpaths' configuration value. 1033 TESTPATHS = enum.auto() 1034 1035 def __init__( 1036 self, 1037 pluginmanager: PytestPluginManager, 1038 *, 1039 invocation_params: Optional[InvocationParams] = None, 1040 ) -> None: 1041 from .argparsing import FILE_OR_DIR 1042 from .argparsing import Parser 1043 1044 if invocation_params is None: 1045 invocation_params = self.InvocationParams( 1046 args=(), plugins=None, dir=Path.cwd() 1047 ) 1048 1049 self.option = argparse.Namespace() 1050 """Access to command line option as attributes. 1051 1052 :type: argparse.Namespace 1053 """ 1054 1055 self.invocation_params = invocation_params 1056 """The parameters with which pytest was invoked. 1057 1058 :type: InvocationParams 1059 """ 1060 1061 _a = FILE_OR_DIR 1062 self._parser = Parser( 1063 usage=f"%(prog)s [options] [{_a}] [{_a}] [...]", 1064 processopt=self._processopt, 1065 _ispytest=True, 1066 ) 1067 self.pluginmanager = pluginmanager 1068 """The plugin manager handles plugin registration and hook invocation. 1069 1070 :type: PytestPluginManager 1071 """ 1072 1073 self.stash = Stash() 1074 """A place where plugins can store information on the config for their 1075 own use. 1076 1077 :type: Stash 1078 """ 1079 # Deprecated alias. Was never public. Can be removed in a few releases. 1080 self._store = self.stash 1081 1082 self.trace = self.pluginmanager.trace.root.get("config") 1083 self.hook: pluggy.HookRelay = PathAwareHookProxy(self.pluginmanager.hook) # type: ignore[assignment] 1084 self._inicache: Dict[str, Any] = {} 1085 self._override_ini: Sequence[str] = () 1086 self._opt2dest: Dict[str, str] = {} 1087 self._cleanup: List[Callable[[], None]] = [] 1088 self.pluginmanager.register(self, "pytestconfig") 1089 self._configured = False 1090 self.hook.pytest_addoption.call_historic( 1091 kwargs=dict(parser=self._parser, pluginmanager=self.pluginmanager) 1092 ) 1093 self.args_source = Config.ArgsSource.ARGS 1094 self.args: List[str] = [] 1095 1096 if TYPE_CHECKING: 1097 from _pytest.cacheprovider import Cache 1098 1099 self.cache: Optional[Cache] = None 1100 1101 @property 1102 def rootpath(self) -> Path: 1103 """The path to the :ref:`rootdir <rootdir>`. 1104 1105 :type: pathlib.Path 1106 1107 .. versionadded:: 6.1 1108 """ 1109 return self._rootpath 1110 1111 @property 1112 def inipath(self) -> Optional[Path]: 1113 """The path to the :ref:`configfile <configfiles>`. 1114 1115 :type: Optional[pathlib.Path] 1116 1117 .. versionadded:: 6.1 1118 """ 1119 return self._inipath 1120 1121 def add_cleanup(self, func: Callable[[], None]) -> None: 1122 """Add a function to be called when the config object gets out of 1123 use (usually coinciding with pytest_unconfigure).""" 1124 self._cleanup.append(func) 1125 1126 def _do_configure(self) -> None: 1127 assert not self._configured 1128 self._configured = True 1129 with warnings.catch_warnings(): 1130 warnings.simplefilter("default") 1131 self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) 1132 1133 def _ensure_unconfigure(self) -> None: 1134 if self._configured: 1135 self._configured = False 1136 self.hook.pytest_unconfigure(config=self) 1137 self.hook.pytest_configure._call_history = [] 1138 while self._cleanup: 1139 fin = self._cleanup.pop() 1140 fin() 1141 1142 def get_terminal_writer(self) -> TerminalWriter: 1143 terminalreporter: Optional[TerminalReporter] = self.pluginmanager.get_plugin( 1144 "terminalreporter" 1145 ) 1146 assert terminalreporter is not None 1147 return terminalreporter._tw 1148 1149 def pytest_cmdline_parse( 1150 self, pluginmanager: PytestPluginManager, args: List[str] 1151 ) -> "Config": 1152 try: 1153 self.parse(args) 1154 except UsageError: 1155 # Handle --version and --help here in a minimal fashion. 1156 # This gets done via helpconfig normally, but its 1157 # pytest_cmdline_main is not called in case of errors. 1158 if getattr(self.option, "version", False) or "--version" in args: 1159 from _pytest.helpconfig import showversion 1160 1161 showversion(self) 1162 elif ( 1163 getattr(self.option, "help", False) or "--help" in args or "-h" in args 1164 ): 1165 self._parser._getparser().print_help() 1166 sys.stdout.write( 1167 "\nNOTE: displaying only minimal help due to UsageError.\n\n" 1168 ) 1169 1170 raise 1171 1172 return self 1173 1174 def notify_exception( 1175 self, 1176 excinfo: ExceptionInfo[BaseException], 1177 option: Optional[argparse.Namespace] = None, 1178 ) -> None: 1179 if option and getattr(option, "fulltrace", False): 1180 style: _TracebackStyle = "long" 1181 else: 1182 style = "native" 1183 excrepr = excinfo.getrepr( 1184 funcargs=True, showlocals=getattr(option, "showlocals", False), style=style 1185 ) 1186 res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo) 1187 if not any(res): 1188 for line in str(excrepr).split("\n"): 1189 sys.stderr.write("INTERNALERROR> %s\n" % line) 1190 sys.stderr.flush() 1191 1192 def cwd_relative_nodeid(self, nodeid: str) -> str: 1193 # nodeid's are relative to the rootpath, compute relative to cwd. 1194 if self.invocation_params.dir != self.rootpath: 1195 fullpath = self.rootpath / nodeid 1196 nodeid = bestrelpath(self.invocation_params.dir, fullpath) 1197 return nodeid 1198 1199 @classmethod 1200 def fromdictargs(cls, option_dict, args) -> "Config": 1201 """Constructor usable for subprocesses.""" 1202 config = get_config(args) 1203 config.option.__dict__.update(option_dict) 1204 config.parse(args, addopts=False) 1205 for x in config.option.plugins: 1206 config.pluginmanager.consider_pluginarg(x) 1207 return config 1208 1209 def _processopt(self, opt: "Argument") -> None: 1210 for name in opt._short_opts + opt._long_opts: 1211 self._opt2dest[name] = opt.dest 1212 1213 if hasattr(opt, "default"): 1214 if not hasattr(self.option, opt.dest): 1215 setattr(self.option, opt.dest, opt.default) 1216 1217 @hookimpl(trylast=True) 1218 def pytest_load_initial_conftests(self, early_config: "Config") -> None: 1219 # We haven't fully parsed the command line arguments yet, so 1220 # early_config.args it not set yet. But we need it for 1221 # discovering the initial conftests. So "pre-run" the logic here. 1222 # It will be done for real in `parse()`. 1223 args, args_source = early_config._decide_args( 1224 args=early_config.known_args_namespace.file_or_dir, 1225 pyargs=early_config.known_args_namespace.pyargs, 1226 testpaths=early_config.getini("testpaths"), 1227 invocation_dir=early_config.invocation_params.dir, 1228 rootpath=early_config.rootpath, 1229 warn=False, 1230 ) 1231 self.pluginmanager._set_initial_conftests( 1232 args=args, 1233 pyargs=early_config.known_args_namespace.pyargs, 1234 noconftest=early_config.known_args_namespace.noconftest, 1235 rootpath=early_config.rootpath, 1236 confcutdir=early_config.known_args_namespace.confcutdir, 1237 invocation_dir=early_config.invocation_params.dir, 1238 importmode=early_config.known_args_namespace.importmode, 1239 consider_namespace_packages=early_config.getini( 1240 "consider_namespace_packages" 1241 ), 1242 ) 1243 1244 def _initini(self, args: Sequence[str]) -> None: 1245 ns, unknown_args = self._parser.parse_known_and_unknown_args( 1246 args, namespace=copy.copy(self.option) 1247 ) 1248 rootpath, inipath, inicfg = determine_setup( 1249 inifile=ns.inifilename, 1250 args=ns.file_or_dir + unknown_args, 1251 rootdir_cmd_arg=ns.rootdir or None, 1252 invocation_dir=self.invocation_params.dir, 1253 ) 1254 self._rootpath = rootpath 1255 self._inipath = inipath 1256 self.inicfg = inicfg 1257 self._parser.extra_info["rootdir"] = str(self.rootpath) 1258 self._parser.extra_info["inifile"] = str(self.inipath) 1259 self._parser.addini("addopts", "Extra command line options", "args") 1260 self._parser.addini("minversion", "Minimally required pytest version") 1261 self._parser.addini( 1262 "required_plugins", 1263 "Plugins that must be present for pytest to run", 1264 type="args", 1265 default=[], 1266 ) 1267 self._override_ini = ns.override_ini or () 1268 1269 def _consider_importhook(self, args: Sequence[str]) -> None: 1270 """Install the PEP 302 import hook if using assertion rewriting. 1271 1272 Needs to parse the --assert=<mode> option from the commandline 1273 and find all the installed plugins to mark them for rewriting 1274 by the importhook. 1275 """ 1276 ns, unknown_args = self._parser.parse_known_and_unknown_args(args) 1277 mode = getattr(ns, "assertmode", "plain") 1278 if mode == "rewrite": 1279 import _pytest.assertion 1280 1281 try: 1282 hook = _pytest.assertion.install_importhook(self) 1283 except SystemError: 1284 mode = "plain" 1285 else: 1286 self._mark_plugins_for_rewrite(hook) 1287 self._warn_about_missing_assertion(mode) 1288 1289 def _mark_plugins_for_rewrite(self, hook) -> None: 1290 """Given an importhook, mark for rewrite any top-level 1291 modules or packages in the distribution package for 1292 all pytest plugins.""" 1293 self.pluginmanager.rewrite_hook = hook 1294 1295 if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): 1296 # We don't autoload from setuptools entry points, no need to continue. 1297 return 1298 1299 package_files = ( 1300 str(file) 1301 for dist in importlib.metadata.distributions() 1302 if any(ep.group == "pytest11" for ep in dist.entry_points) 1303 for file in dist.files or [] 1304 ) 1305 1306 for name in _iter_rewritable_modules(package_files): 1307 hook.mark_rewrite(name) 1308 1309 def _validate_args(self, args: List[str], via: str) -> List[str]: 1310 """Validate known args.""" 1311 self._parser._config_source_hint = via # type: ignore 1312 try: 1313 self._parser.parse_known_and_unknown_args( 1314 args, namespace=copy.copy(self.option) 1315 ) 1316 finally: 1317 del self._parser._config_source_hint # type: ignore 1318 1319 return args 1320 1321 def _decide_args( 1322 self, 1323 *, 1324 args: List[str], 1325 pyargs: bool, 1326 testpaths: List[str], 1327 invocation_dir: Path, 1328 rootpath: Path, 1329 warn: bool, 1330 ) -> Tuple[List[str], ArgsSource]: 1331 """Decide the args (initial paths/nodeids) to use given the relevant inputs. 1332 1333 :param warn: Whether can issue warnings. 1334 1335 :returns: The args and the args source. Guaranteed to be non-empty. 1336 """ 1337 if args: 1338 source = Config.ArgsSource.ARGS 1339 result = args 1340 else: 1341 if invocation_dir == rootpath: 1342 source = Config.ArgsSource.TESTPATHS 1343 if pyargs: 1344 result = testpaths 1345 else: 1346 result = [] 1347 for path in testpaths: 1348 result.extend(sorted(glob.iglob(path, recursive=True))) 1349 if testpaths and not result: 1350 if warn: 1351 warning_text = ( 1352 "No files were found in testpaths; " 1353 "consider removing or adjusting your testpaths configuration. " 1354 "Searching recursively from the current directory instead." 1355 ) 1356 self.issue_config_time_warning( 1357 PytestConfigWarning(warning_text), stacklevel=3 1358 ) 1359 else: 1360 result = [] 1361 if not result: 1362 source = Config.ArgsSource.INVOCATION_DIR 1363 result = [str(invocation_dir)] 1364 return result, source 1365 1366 def _preparse(self, args: List[str], addopts: bool = True) -> None: 1367 if addopts: 1368 env_addopts = os.environ.get("PYTEST_ADDOPTS", "") 1369 if len(env_addopts): 1370 args[:] = ( 1371 self._validate_args(shlex.split(env_addopts), "via PYTEST_ADDOPTS") 1372 + args 1373 ) 1374 self._initini(args) 1375 if addopts: 1376 args[:] = ( 1377 self._validate_args(self.getini("addopts"), "via addopts config") + args 1378 ) 1379 1380 self.known_args_namespace = self._parser.parse_known_args( 1381 args, namespace=copy.copy(self.option) 1382 ) 1383 self._checkversion() 1384 self._consider_importhook(args) 1385 self.pluginmanager.consider_preparse(args, exclude_only=False) 1386 if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): 1387 # Don't autoload from setuptools entry point. Only explicitly specified 1388 # plugins are going to be loaded. 1389 self.pluginmanager.load_setuptools_entrypoints("pytest11") 1390 self.pluginmanager.consider_env() 1391 1392 self.known_args_namespace = self._parser.parse_known_args( 1393 args, namespace=copy.copy(self.known_args_namespace) 1394 ) 1395 1396 self._validate_plugins() 1397 self._warn_about_skipped_plugins() 1398 1399 if self.known_args_namespace.confcutdir is None: 1400 if self.inipath is not None: 1401 confcutdir = str(self.inipath.parent) 1402 else: 1403 confcutdir = str(self.rootpath) 1404 self.known_args_namespace.confcutdir = confcutdir 1405 try: 1406 self.hook.pytest_load_initial_conftests( 1407 early_config=self, args=args, parser=self._parser 1408 ) 1409 except ConftestImportFailure as e: 1410 if self.known_args_namespace.help or self.known_args_namespace.version: 1411 # we don't want to prevent --help/--version to work 1412 # so just let is pass and print a warning at the end 1413 self.issue_config_time_warning( 1414 PytestConfigWarning(f"could not load initial conftests: {e.path}"), 1415 stacklevel=2, 1416 ) 1417 else: 1418 raise 1419 1420 @hookimpl(wrapper=True) 1421 def pytest_collection(self) -> Generator[None, object, object]: 1422 # Validate invalid ini keys after collection is done so we take in account 1423 # options added by late-loading conftest files. 1424 try: 1425 return (yield) 1426 finally: 1427 self._validate_config_options() 1428 1429 def _checkversion(self) -> None: 1430 import pytest 1431 1432 minver = self.inicfg.get("minversion", None) 1433 if minver: 1434 # Imported lazily to improve start-up time. 1435 from packaging.version import Version 1436 1437 if not isinstance(minver, str): 1438 raise pytest.UsageError( 1439 "%s: 'minversion' must be a single value" % self.inipath 1440 ) 1441 1442 if Version(minver) > Version(pytest.__version__): 1443 raise pytest.UsageError( 1444 f"{self.inipath}: 'minversion' requires pytest-{minver}, actual pytest-{pytest.__version__}'" 1445 ) 1446 1447 def _validate_config_options(self) -> None: 1448 for key in sorted(self._get_unknown_ini_keys()): 1449 self._warn_or_fail_if_strict(f"Unknown config option: {key}\n") 1450 1451 def _validate_plugins(self) -> None: 1452 required_plugins = sorted(self.getini("required_plugins")) 1453 if not required_plugins: 1454 return 1455 1456 # Imported lazily to improve start-up time. 1457 from packaging.requirements import InvalidRequirement 1458 from packaging.requirements import Requirement 1459 from packaging.version import Version 1460 1461 plugin_info = self.pluginmanager.list_plugin_distinfo() 1462 plugin_dist_info = {dist.project_name: dist.version for _, dist in plugin_info} 1463 1464 missing_plugins = [] 1465 for required_plugin in required_plugins: 1466 try: 1467 req = Requirement(required_plugin) 1468 except InvalidRequirement: 1469 missing_plugins.append(required_plugin) 1470 continue 1471 1472 if req.name not in plugin_dist_info: 1473 missing_plugins.append(required_plugin) 1474 elif not req.specifier.contains( 1475 Version(plugin_dist_info[req.name]), prereleases=True 1476 ): 1477 missing_plugins.append(required_plugin) 1478 1479 if missing_plugins: 1480 raise UsageError( 1481 "Missing required plugins: {}".format(", ".join(missing_plugins)), 1482 ) 1483 1484 def _warn_or_fail_if_strict(self, message: str) -> None: 1485 if self.known_args_namespace.strict_config: 1486 raise UsageError(message) 1487 1488 self.issue_config_time_warning(PytestConfigWarning(message), stacklevel=3) 1489 1490 def _get_unknown_ini_keys(self) -> List[str]: 1491 parser_inicfg = self._parser._inidict 1492 return [name for name in self.inicfg if name not in parser_inicfg] 1493 1494 def parse(self, args: List[str], addopts: bool = True) -> None: 1495 # Parse given cmdline arguments into this config object. 1496 assert ( 1497 self.args == [] 1498 ), "can only parse cmdline args at most once per Config object" 1499 self.hook.pytest_addhooks.call_historic( 1500 kwargs=dict(pluginmanager=self.pluginmanager) 1501 ) 1502 self._preparse(args, addopts=addopts) 1503 self._parser.after_preparse = True # type: ignore 1504 try: 1505 args = self._parser.parse_setoption( 1506 args, self.option, namespace=self.option 1507 ) 1508 self.args, self.args_source = self._decide_args( 1509 args=args, 1510 pyargs=self.known_args_namespace.pyargs, 1511 testpaths=self.getini("testpaths"), 1512 invocation_dir=self.invocation_params.dir, 1513 rootpath=self.rootpath, 1514 warn=True, 1515 ) 1516 except PrintHelp: 1517 pass 1518 1519 def issue_config_time_warning(self, warning: Warning, stacklevel: int) -> None: 1520 """Issue and handle a warning during the "configure" stage. 1521 1522 During ``pytest_configure`` we can't capture warnings using the ``catch_warnings_for_item`` 1523 function because it is not possible to have hook wrappers around ``pytest_configure``. 1524 1525 This function is mainly intended for plugins that need to issue warnings during 1526 ``pytest_configure`` (or similar stages). 1527 1528 :param warning: The warning instance. 1529 :param stacklevel: stacklevel forwarded to warnings.warn. 1530 """ 1531 if self.pluginmanager.is_blocked("warnings"): 1532 return 1533 1534 cmdline_filters = self.known_args_namespace.pythonwarnings or [] 1535 config_filters = self.getini("filterwarnings") 1536 1537 with warnings.catch_warnings(record=True) as records: 1538 warnings.simplefilter("always", type(warning)) 1539 apply_warning_filters(config_filters, cmdline_filters) 1540 warnings.warn(warning, stacklevel=stacklevel) 1541 1542 if records: 1543 frame = sys._getframe(stacklevel - 1) 1544 location = frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name 1545 self.hook.pytest_warning_recorded.call_historic( 1546 kwargs=dict( 1547 warning_message=records[0], 1548 when="config", 1549 nodeid="", 1550 location=location, 1551 ) 1552 ) 1553 1554 def addinivalue_line(self, name: str, line: str) -> None: 1555 """Add a line to an ini-file option. The option must have been 1556 declared but might not yet be set in which case the line becomes 1557 the first line in its value.""" 1558 x = self.getini(name) 1559 assert isinstance(x, list) 1560 x.append(line) # modifies the cached list inline 1561 1562 def getini(self, name: str): 1563 """Return configuration value from an :ref:`ini file <configfiles>`. 1564 1565 If a configuration value is not defined in an 1566 :ref:`ini file <configfiles>`, then the ``default`` value provided while 1567 registering the configuration through 1568 :func:`parser.addini <pytest.Parser.addini>` will be returned. 1569 Please note that you can even provide ``None`` as a valid 1570 default value. 1571 1572 If ``default`` is not provided while registering using 1573 :func:`parser.addini <pytest.Parser.addini>`, then a default value 1574 based on the ``type`` parameter passed to 1575 :func:`parser.addini <pytest.Parser.addini>` will be returned. 1576 The default values based on ``type`` are: 1577 ``paths``, ``pathlist``, ``args`` and ``linelist`` : empty list ``[]`` 1578 ``bool`` : ``False`` 1579 ``string`` : empty string ``""`` 1580 1581 If neither the ``default`` nor the ``type`` parameter is passed 1582 while registering the configuration through 1583 :func:`parser.addini <pytest.Parser.addini>`, then the configuration 1584 is treated as a string and a default empty string '' is returned. 1585 1586 If the specified name hasn't been registered through a prior 1587 :func:`parser.addini <pytest.Parser.addini>` call (usually from a 1588 plugin), a ValueError is raised. 1589 """ 1590 try: 1591 return self._inicache[name] 1592 except KeyError: 1593 self._inicache[name] = val = self._getini(name) 1594 return val 1595 1596 # Meant for easy monkeypatching by legacypath plugin. 1597 # Can be inlined back (with no cover removed) once legacypath is gone. 1598 def _getini_unknown_type(self, name: str, type: str, value: Union[str, List[str]]): 1599 msg = f"unknown configuration type: {type}" 1600 raise ValueError(msg, value) # pragma: no cover 1601 1602 def _getini(self, name: str): 1603 try: 1604 description, type, default = self._parser._inidict[name] 1605 except KeyError as e: 1606 raise ValueError(f"unknown configuration value: {name!r}") from e 1607 override_value = self._get_override_ini_value(name) 1608 if override_value is None: 1609 try: 1610 value = self.inicfg[name] 1611 except KeyError: 1612 return default 1613 else: 1614 value = override_value 1615 # Coerce the values based on types. 1616 # 1617 # Note: some coercions are only required if we are reading from .ini files, because 1618 # the file format doesn't contain type information, but when reading from toml we will 1619 # get either str or list of str values (see _parse_ini_config_from_pyproject_toml). 1620 # For example: 1621 # 1622 # ini: 1623 # a_line_list = "tests acceptance" 1624 # in this case, we need to split the string to obtain a list of strings. 1625 # 1626 # toml: 1627 # a_line_list = ["tests", "acceptance"] 1628 # in this case, we already have a list ready to use. 1629 # 1630 if type == "paths": 1631 dp = ( 1632 self.inipath.parent 1633 if self.inipath is not None 1634 else self.invocation_params.dir 1635 ) 1636 input_values = shlex.split(value) if isinstance(value, str) else value 1637 return [dp / x for x in input_values] 1638 elif type == "args": 1639 return shlex.split(value) if isinstance(value, str) else value 1640 elif type == "linelist": 1641 if isinstance(value, str): 1642 return [t for t in map(lambda x: x.strip(), value.split("\n")) if t] 1643 else: 1644 return value 1645 elif type == "bool": 1646 return _strtobool(str(value).strip()) 1647 elif type == "string": 1648 return value 1649 elif type is None: 1650 return value 1651 else: 1652 return self._getini_unknown_type(name, type, value) 1653 1654 def _getconftest_pathlist(self, name: str, path: Path) -> Optional[List[Path]]: 1655 try: 1656 mod, relroots = self.pluginmanager._rget_with_confmod(name, path) 1657 except KeyError: 1658 return None 1659 assert mod.__file__ is not None 1660 modpath = Path(mod.__file__).parent 1661 values: List[Path] = [] 1662 for relroot in relroots: 1663 if isinstance(relroot, os.PathLike): 1664 relroot = Path(relroot) 1665 else: 1666 relroot = relroot.replace("/", os.sep) 1667 relroot = absolutepath(modpath / relroot) 1668 values.append(relroot) 1669 return values 1670 1671 def _get_override_ini_value(self, name: str) -> Optional[str]: 1672 value = None 1673 # override_ini is a list of "ini=value" options. 1674 # Always use the last item if multiple values are set for same ini-name, 1675 # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2. 1676 for ini_config in self._override_ini: 1677 try: 1678 key, user_ini_value = ini_config.split("=", 1) 1679 except ValueError as e: 1680 raise UsageError( 1681 f"-o/--override-ini expects option=value style (got: {ini_config!r})." 1682 ) from e 1683 else: 1684 if key == name: 1685 value = user_ini_value 1686 return value 1687 1688 def getoption(self, name: str, default=notset, skip: bool = False): 1689 """Return command line option value. 1690 1691 :param name: Name of the option. You may also specify 1692 the literal ``--OPT`` option instead of the "dest" option name. 1693 :param default: Default value if no option of that name exists. 1694 :param skip: If True, raise pytest.skip if option does not exists 1695 or has a None value. 1696 """ 1697 name = self._opt2dest.get(name, name) 1698 try: 1699 val = getattr(self.option, name) 1700 if val is None and skip: 1701 raise AttributeError(name) 1702 return val 1703 except AttributeError as e: 1704 if default is not notset: 1705 return default 1706 if skip: 1707 import pytest 1708 1709 pytest.skip(f"no {name!r} option found") 1710 raise ValueError(f"no option named {name!r}") from e 1711 1712 def getvalue(self, name: str, path=None): 1713 """Deprecated, use getoption() instead.""" 1714 return self.getoption(name) 1715 1716 def getvalueorskip(self, name: str, path=None): 1717 """Deprecated, use getoption(skip=True) instead.""" 1718 return self.getoption(name, skip=True) 1719 1720 #: Verbosity type for failed assertions (see :confval:`verbosity_assertions`). 1721 VERBOSITY_ASSERTIONS: Final = "assertions" 1722 #: Verbosity type for test case execution (see :confval:`verbosity_test_cases`). 1723 VERBOSITY_TEST_CASES: Final = "test_cases" 1724 _VERBOSITY_INI_DEFAULT: Final = "auto" 1725 1726 def get_verbosity(self, verbosity_type: Optional[str] = None) -> int: 1727 r"""Retrieve the verbosity level for a fine-grained verbosity type. 1728 1729 :param verbosity_type: Verbosity type to get level for. If a level is 1730 configured for the given type, that value will be returned. If the 1731 given type is not a known verbosity type, the global verbosity 1732 level will be returned. If the given type is None (default), the 1733 global verbosity level will be returned. 1734 1735 To configure a level for a fine-grained verbosity type, the 1736 configuration file should have a setting for the configuration name 1737 and a numeric value for the verbosity level. A special value of "auto" 1738 can be used to explicitly use the global verbosity level. 1739 1740 Example: 1741 .. code-block:: ini 1742 1743 # content of pytest.ini 1744 [pytest] 1745 verbosity_assertions = 2 1746 1747 .. code-block:: console 1748 1749 pytest -v 1750 1751 .. code-block:: python 1752 1753 print(config.get_verbosity()) # 1 1754 print(config.get_verbosity(Config.VERBOSITY_ASSERTIONS)) # 2 1755 """ 1756 global_level = self.option.verbose 1757 assert isinstance(global_level, int) 1758 if verbosity_type is None: 1759 return global_level 1760 1761 ini_name = Config._verbosity_ini_name(verbosity_type) 1762 if ini_name not in self._parser._inidict: 1763 return global_level 1764 1765 level = self.getini(ini_name) 1766 if level == Config._VERBOSITY_INI_DEFAULT: 1767 return global_level 1768 1769 return int(level) 1770 1771 @staticmethod 1772 def _verbosity_ini_name(verbosity_type: str) -> str: 1773 return f"verbosity_{verbosity_type}" 1774 1775 @staticmethod 1776 def _add_verbosity_ini(parser: "Parser", verbosity_type: str, help: str) -> None: 1777 """Add a output verbosity configuration option for the given output type. 1778 1779 :param parser: Parser for command line arguments and ini-file values. 1780 :param verbosity_type: Fine-grained verbosity category. 1781 :param help: Description of the output this type controls. 1782 1783 The value should be retrieved via a call to 1784 :py:func:`config.get_verbosity(type) <pytest.Config.get_verbosity>`. 1785 """ 1786 parser.addini( 1787 Config._verbosity_ini_name(verbosity_type), 1788 help=help, 1789 type="string", 1790 default=Config._VERBOSITY_INI_DEFAULT, 1791 ) 1792 1793 def _warn_about_missing_assertion(self, mode: str) -> None: 1794 if not _assertion_supported(): 1795 if mode == "plain": 1796 warning_text = ( 1797 "ASSERTIONS ARE NOT EXECUTED" 1798 " and FAILING TESTS WILL PASS. Are you" 1799 " using python -O?" 1800 ) 1801 else: 1802 warning_text = ( 1803 "assertions not in test modules or" 1804 " plugins will be ignored" 1805 " because assert statements are not executed " 1806 "by the underlying Python interpreter " 1807 "(are you using python -O?)\n" 1808 ) 1809 self.issue_config_time_warning( 1810 PytestConfigWarning(warning_text), 1811 stacklevel=3, 1812 ) 1813 1814 def _warn_about_skipped_plugins(self) -> None: 1815 for module_name, msg in self.pluginmanager.skipped_plugins: 1816 self.issue_config_time_warning( 1817 PytestConfigWarning(f"skipped plugin {module_name!r}: {msg}"), 1818 stacklevel=2, 1819 )
Access to configuration values, pluginmanager and plugin hooks.
Parameters
PytestPluginManager pluginmanager: A pytest PluginManager.
InvocationParams invocation_params: Object containing parameters regarding the
pytest.main()
invocation.
1035 def __init__( 1036 self, 1037 pluginmanager: PytestPluginManager, 1038 *, 1039 invocation_params: Optional[InvocationParams] = None, 1040 ) -> None: 1041 from .argparsing import FILE_OR_DIR 1042 from .argparsing import Parser 1043 1044 if invocation_params is None: 1045 invocation_params = self.InvocationParams( 1046 args=(), plugins=None, dir=Path.cwd() 1047 ) 1048 1049 self.option = argparse.Namespace() 1050 """Access to command line option as attributes. 1051 1052 :type: argparse.Namespace 1053 """ 1054 1055 self.invocation_params = invocation_params 1056 """The parameters with which pytest was invoked. 1057 1058 :type: InvocationParams 1059 """ 1060 1061 _a = FILE_OR_DIR 1062 self._parser = Parser( 1063 usage=f"%(prog)s [options] [{_a}] [{_a}] [...]", 1064 processopt=self._processopt, 1065 _ispytest=True, 1066 ) 1067 self.pluginmanager = pluginmanager 1068 """The plugin manager handles plugin registration and hook invocation. 1069 1070 :type: PytestPluginManager 1071 """ 1072 1073 self.stash = Stash() 1074 """A place where plugins can store information on the config for their 1075 own use. 1076 1077 :type: Stash 1078 """ 1079 # Deprecated alias. Was never public. Can be removed in a few releases. 1080 self._store = self.stash 1081 1082 self.trace = self.pluginmanager.trace.root.get("config") 1083 self.hook: pluggy.HookRelay = PathAwareHookProxy(self.pluginmanager.hook) # type: ignore[assignment] 1084 self._inicache: Dict[str, Any] = {} 1085 self._override_ini: Sequence[str] = () 1086 self._opt2dest: Dict[str, str] = {} 1087 self._cleanup: List[Callable[[], None]] = [] 1088 self.pluginmanager.register(self, "pytestconfig") 1089 self._configured = False 1090 self.hook.pytest_addoption.call_historic( 1091 kwargs=dict(parser=self._parser, pluginmanager=self.pluginmanager) 1092 ) 1093 self.args_source = Config.ArgsSource.ARGS 1094 self.args: List[str] = [] 1095 1096 if TYPE_CHECKING: 1097 from _pytest.cacheprovider import Cache 1098 1099 self.cache: Optional[Cache] = None
1101 @property 1102 def rootpath(self) -> Path: 1103 """The path to the :ref:`rootdir <rootdir>`. 1104 1105 :type: pathlib.Path 1106 1107 .. versionadded:: 6.1 1108 """ 1109 return self._rootpath
The path to the :ref:rootdir <rootdir>
.
New in version 6.1.
1111 @property 1112 def inipath(self) -> Optional[Path]: 1113 """The path to the :ref:`configfile <configfiles>`. 1114 1115 :type: Optional[pathlib.Path] 1116 1117 .. versionadded:: 6.1 1118 """ 1119 return self._inipath
The path to the :ref:configfile <configfiles>
.
New in version 6.1.
1121 def add_cleanup(self, func: Callable[[], None]) -> None: 1122 """Add a function to be called when the config object gets out of 1123 use (usually coinciding with pytest_unconfigure).""" 1124 self._cleanup.append(func)
Add a function to be called when the config object gets out of use (usually coinciding with pytest_unconfigure).
1149 def pytest_cmdline_parse( 1150 self, pluginmanager: PytestPluginManager, args: List[str] 1151 ) -> "Config": 1152 try: 1153 self.parse(args) 1154 except UsageError: 1155 # Handle --version and --help here in a minimal fashion. 1156 # This gets done via helpconfig normally, but its 1157 # pytest_cmdline_main is not called in case of errors. 1158 if getattr(self.option, "version", False) or "--version" in args: 1159 from _pytest.helpconfig import showversion 1160 1161 showversion(self) 1162 elif ( 1163 getattr(self.option, "help", False) or "--help" in args or "-h" in args 1164 ): 1165 self._parser._getparser().print_help() 1166 sys.stdout.write( 1167 "\nNOTE: displaying only minimal help due to UsageError.\n\n" 1168 ) 1169 1170 raise 1171 1172 return self
1174 def notify_exception( 1175 self, 1176 excinfo: ExceptionInfo[BaseException], 1177 option: Optional[argparse.Namespace] = None, 1178 ) -> None: 1179 if option and getattr(option, "fulltrace", False): 1180 style: _TracebackStyle = "long" 1181 else: 1182 style = "native" 1183 excrepr = excinfo.getrepr( 1184 funcargs=True, showlocals=getattr(option, "showlocals", False), style=style 1185 ) 1186 res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo) 1187 if not any(res): 1188 for line in str(excrepr).split("\n"): 1189 sys.stderr.write("INTERNALERROR> %s\n" % line) 1190 sys.stderr.flush()
1192 def cwd_relative_nodeid(self, nodeid: str) -> str: 1193 # nodeid's are relative to the rootpath, compute relative to cwd. 1194 if self.invocation_params.dir != self.rootpath: 1195 fullpath = self.rootpath / nodeid 1196 nodeid = bestrelpath(self.invocation_params.dir, fullpath) 1197 return nodeid
1199 @classmethod 1200 def fromdictargs(cls, option_dict, args) -> "Config": 1201 """Constructor usable for subprocesses.""" 1202 config = get_config(args) 1203 config.option.__dict__.update(option_dict) 1204 config.parse(args, addopts=False) 1205 for x in config.option.plugins: 1206 config.pluginmanager.consider_pluginarg(x) 1207 return config
Constructor usable for subprocesses.
1217 @hookimpl(trylast=True) 1218 def pytest_load_initial_conftests(self, early_config: "Config") -> None: 1219 # We haven't fully parsed the command line arguments yet, so 1220 # early_config.args it not set yet. But we need it for 1221 # discovering the initial conftests. So "pre-run" the logic here. 1222 # It will be done for real in `parse()`. 1223 args, args_source = early_config._decide_args( 1224 args=early_config.known_args_namespace.file_or_dir, 1225 pyargs=early_config.known_args_namespace.pyargs, 1226 testpaths=early_config.getini("testpaths"), 1227 invocation_dir=early_config.invocation_params.dir, 1228 rootpath=early_config.rootpath, 1229 warn=False, 1230 ) 1231 self.pluginmanager._set_initial_conftests( 1232 args=args, 1233 pyargs=early_config.known_args_namespace.pyargs, 1234 noconftest=early_config.known_args_namespace.noconftest, 1235 rootpath=early_config.rootpath, 1236 confcutdir=early_config.known_args_namespace.confcutdir, 1237 invocation_dir=early_config.invocation_params.dir, 1238 importmode=early_config.known_args_namespace.importmode, 1239 consider_namespace_packages=early_config.getini( 1240 "consider_namespace_packages" 1241 ), 1242 )
1420 @hookimpl(wrapper=True) 1421 def pytest_collection(self) -> Generator[None, object, object]: 1422 # Validate invalid ini keys after collection is done so we take in account 1423 # options added by late-loading conftest files. 1424 try: 1425 return (yield) 1426 finally: 1427 self._validate_config_options()
1494 def parse(self, args: List[str], addopts: bool = True) -> None: 1495 # Parse given cmdline arguments into this config object. 1496 assert ( 1497 self.args == [] 1498 ), "can only parse cmdline args at most once per Config object" 1499 self.hook.pytest_addhooks.call_historic( 1500 kwargs=dict(pluginmanager=self.pluginmanager) 1501 ) 1502 self._preparse(args, addopts=addopts) 1503 self._parser.after_preparse = True # type: ignore 1504 try: 1505 args = self._parser.parse_setoption( 1506 args, self.option, namespace=self.option 1507 ) 1508 self.args, self.args_source = self._decide_args( 1509 args=args, 1510 pyargs=self.known_args_namespace.pyargs, 1511 testpaths=self.getini("testpaths"), 1512 invocation_dir=self.invocation_params.dir, 1513 rootpath=self.rootpath, 1514 warn=True, 1515 ) 1516 except PrintHelp: 1517 pass
1519 def issue_config_time_warning(self, warning: Warning, stacklevel: int) -> None: 1520 """Issue and handle a warning during the "configure" stage. 1521 1522 During ``pytest_configure`` we can't capture warnings using the ``catch_warnings_for_item`` 1523 function because it is not possible to have hook wrappers around ``pytest_configure``. 1524 1525 This function is mainly intended for plugins that need to issue warnings during 1526 ``pytest_configure`` (or similar stages). 1527 1528 :param warning: The warning instance. 1529 :param stacklevel: stacklevel forwarded to warnings.warn. 1530 """ 1531 if self.pluginmanager.is_blocked("warnings"): 1532 return 1533 1534 cmdline_filters = self.known_args_namespace.pythonwarnings or [] 1535 config_filters = self.getini("filterwarnings") 1536 1537 with warnings.catch_warnings(record=True) as records: 1538 warnings.simplefilter("always", type(warning)) 1539 apply_warning_filters(config_filters, cmdline_filters) 1540 warnings.warn(warning, stacklevel=stacklevel) 1541 1542 if records: 1543 frame = sys._getframe(stacklevel - 1) 1544 location = frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name 1545 self.hook.pytest_warning_recorded.call_historic( 1546 kwargs=dict( 1547 warning_message=records[0], 1548 when="config", 1549 nodeid="", 1550 location=location, 1551 ) 1552 )
Issue and handle a warning during the "configure" stage.
During pytest_configure
we can't capture warnings using the catch_warnings_for_item
function because it is not possible to have hook wrappers around pytest_configure
.
This function is mainly intended for plugins that need to issue warnings during
pytest_configure
(or similar stages).
Parameters
- warning: The warning instance.
- stacklevel: stacklevel forwarded to warnings.warn.
1554 def addinivalue_line(self, name: str, line: str) -> None: 1555 """Add a line to an ini-file option. The option must have been 1556 declared but might not yet be set in which case the line becomes 1557 the first line in its value.""" 1558 x = self.getini(name) 1559 assert isinstance(x, list) 1560 x.append(line) # modifies the cached list inline
Add a line to an ini-file option. The option must have been declared but might not yet be set in which case the line becomes the first line in its value.
1562 def getini(self, name: str): 1563 """Return configuration value from an :ref:`ini file <configfiles>`. 1564 1565 If a configuration value is not defined in an 1566 :ref:`ini file <configfiles>`, then the ``default`` value provided while 1567 registering the configuration through 1568 :func:`parser.addini <pytest.Parser.addini>` will be returned. 1569 Please note that you can even provide ``None`` as a valid 1570 default value. 1571 1572 If ``default`` is not provided while registering using 1573 :func:`parser.addini <pytest.Parser.addini>`, then a default value 1574 based on the ``type`` parameter passed to 1575 :func:`parser.addini <pytest.Parser.addini>` will be returned. 1576 The default values based on ``type`` are: 1577 ``paths``, ``pathlist``, ``args`` and ``linelist`` : empty list ``[]`` 1578 ``bool`` : ``False`` 1579 ``string`` : empty string ``""`` 1580 1581 If neither the ``default`` nor the ``type`` parameter is passed 1582 while registering the configuration through 1583 :func:`parser.addini <pytest.Parser.addini>`, then the configuration 1584 is treated as a string and a default empty string '' is returned. 1585 1586 If the specified name hasn't been registered through a prior 1587 :func:`parser.addini <pytest.Parser.addini>` call (usually from a 1588 plugin), a ValueError is raised. 1589 """ 1590 try: 1591 return self._inicache[name] 1592 except KeyError: 1593 self._inicache[name] = val = self._getini(name) 1594 return val
Return configuration value from an :ref:ini file <configfiles>
.
If a configuration value is not defined in an
:ref:ini file <configfiles>
, then the default
value provided while
registering the configuration through
parser.addini <pytest.Parser.addini>()
will be returned.
Please note that you can even provide None
as a valid
default value.
If default
is not provided while registering using
parser.addini <pytest.Parser.addini>()
, then a default value
based on the type
parameter passed to
parser.addini <pytest.Parser.addini>()
will be returned.
The default values based on type
are:
paths
, pathlist
, args
and linelist
: empty list []
bool
: False
string
: empty string ""
If neither the default
nor the type
parameter is passed
while registering the configuration through
parser.addini <pytest.Parser.addini>()
, then the configuration
is treated as a string and a default empty string '' is returned.
If the specified name hasn't been registered through a prior
parser.addini <pytest.Parser.addini>()
call (usually from a
plugin), a ValueError is raised.
1688 def getoption(self, name: str, default=notset, skip: bool = False): 1689 """Return command line option value. 1690 1691 :param name: Name of the option. You may also specify 1692 the literal ``--OPT`` option instead of the "dest" option name. 1693 :param default: Default value if no option of that name exists. 1694 :param skip: If True, raise pytest.skip if option does not exists 1695 or has a None value. 1696 """ 1697 name = self._opt2dest.get(name, name) 1698 try: 1699 val = getattr(self.option, name) 1700 if val is None and skip: 1701 raise AttributeError(name) 1702 return val 1703 except AttributeError as e: 1704 if default is not notset: 1705 return default 1706 if skip: 1707 import pytest 1708 1709 pytest.skip(f"no {name!r} option found") 1710 raise ValueError(f"no option named {name!r}") from e
Return command line option value.
Parameters
- name: Name of the option. You may also specify
the literal
--OPT
option instead of the "dest" option name. - default: Default value if no option of that name exists.
- skip: If True, raise pytest.skip if option does not exists or has a None value.
1712 def getvalue(self, name: str, path=None): 1713 """Deprecated, use getoption() instead.""" 1714 return self.getoption(name)
Deprecated, use getoption() instead.
1716 def getvalueorskip(self, name: str, path=None): 1717 """Deprecated, use getoption(skip=True) instead.""" 1718 return self.getoption(name, skip=True)
Deprecated, use getoption(skip=True) instead.
1726 def get_verbosity(self, verbosity_type: Optional[str] = None) -> int: 1727 r"""Retrieve the verbosity level for a fine-grained verbosity type. 1728 1729 :param verbosity_type: Verbosity type to get level for. If a level is 1730 configured for the given type, that value will be returned. If the 1731 given type is not a known verbosity type, the global verbosity 1732 level will be returned. If the given type is None (default), the 1733 global verbosity level will be returned. 1734 1735 To configure a level for a fine-grained verbosity type, the 1736 configuration file should have a setting for the configuration name 1737 and a numeric value for the verbosity level. A special value of "auto" 1738 can be used to explicitly use the global verbosity level. 1739 1740 Example: 1741 .. code-block:: ini 1742 1743 # content of pytest.ini 1744 [pytest] 1745 verbosity_assertions = 2 1746 1747 .. code-block:: console 1748 1749 pytest -v 1750 1751 .. code-block:: python 1752 1753 print(config.get_verbosity()) # 1 1754 print(config.get_verbosity(Config.VERBOSITY_ASSERTIONS)) # 2 1755 """ 1756 global_level = self.option.verbose 1757 assert isinstance(global_level, int) 1758 if verbosity_type is None: 1759 return global_level 1760 1761 ini_name = Config._verbosity_ini_name(verbosity_type) 1762 if ini_name not in self._parser._inidict: 1763 return global_level 1764 1765 level = self.getini(ini_name) 1766 if level == Config._VERBOSITY_INI_DEFAULT: 1767 return global_level 1768 1769 return int(level)
Retrieve the verbosity level for a fine-grained verbosity type.
Parameters
- verbosity_type: Verbosity type to get level for. If a level is configured for the given type, that value will be returned. If the given type is not a known verbosity type, the global verbosity level will be returned. If the given type is None (default), the global verbosity level will be returned.
To configure a level for a fine-grained verbosity type, the configuration file should have a setting for the configuration name and a numeric value for the verbosity level. A special value of "auto" can be used to explicitly use the global verbosity level.
Example:
# content of pytest.ini
[pytest]
verbosity_assertions = 2
pytest -v
print(config.get_verbosity()) # 1
print(config.get_verbosity(Config.VERBOSITY_ASSERTIONS)) # 2
986 @final 987 @dataclasses.dataclass(frozen=True) 988 class InvocationParams: 989 """Holds parameters passed during :func:`pytest.main`. 990 991 The object attributes are read-only. 992 993 .. versionadded:: 5.1 994 995 .. note:: 996 997 Note that the environment variable ``PYTEST_ADDOPTS`` and the ``addopts`` 998 ini option are handled by pytest, not being included in the ``args`` attribute. 999 1000 Plugins accessing ``InvocationParams`` must be aware of that. 1001 """ 1002 1003 args: Tuple[str, ...] 1004 """The command-line arguments as passed to :func:`pytest.main`.""" 1005 plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] 1006 """Extra plugins, might be `None`.""" 1007 dir: Path 1008 """The directory from which :func:`pytest.main` was invoked.""" 1009 1010 def __init__( 1011 self, 1012 *, 1013 args: Iterable[str], 1014 plugins: Optional[Sequence[Union[str, _PluggyPlugin]]], 1015 dir: Path, 1016 ) -> None: 1017 object.__setattr__(self, "args", tuple(args)) 1018 object.__setattr__(self, "plugins", plugins) 1019 object.__setattr__(self, "dir", dir)
Holds parameters passed during pytest.main()
.
The object attributes are read-only.
New in version 5.1.
Note that the environment variable PYTEST_ADDOPTS
and the addopts
ini option are handled by pytest, not being included in the args
attribute.
Plugins accessing InvocationParams
must be aware of that.
1021 class ArgsSource(enum.Enum): 1022 """Indicates the source of the test arguments. 1023 1024 .. versionadded:: 7.2 1025 """ 1026 1027 #: Command line arguments. 1028 ARGS = enum.auto() 1029 #: Invocation directory. 1030 INVOCATION_DIR = enum.auto() 1031 INCOVATION_DIR = INVOCATION_DIR # backwards compatibility alias 1032 #: 'testpaths' configuration value. 1033 TESTPATHS = enum.auto()
Indicates the source of the test arguments.
New in version 7.2.
Inherited Members
- enum.Enum
- name
- value
200def console_main() -> int: 201 """The CLI entry point of pytest. 202 203 This function is not meant for programmable use; use `main()` instead. 204 """ 205 # https://docs.python.org/3/library/signal.html#note-on-sigpipe 206 try: 207 code = main() 208 sys.stdout.flush() 209 return code 210 except BrokenPipeError: 211 # Python flushes standard streams on exit; redirect remaining output 212 # to devnull to avoid another BrokenPipeError at shutdown 213 devnull = os.open(os.devnull, os.O_WRONLY) 214 os.dup2(devnull, sys.stdout.fileno()) 215 return 1 # Python exits with error code 1 on EPIPE
The CLI entry point of pytest.
This function is not meant for programmable use; use main()
instead.
55def deprecated_call( 56 func: Optional[Callable[..., Any]] = None, *args: Any, **kwargs: Any 57) -> Union["WarningsRecorder", Any]: 58 """Assert that code produces a ``DeprecationWarning`` or ``PendingDeprecationWarning`` or ``FutureWarning``. 59 60 This function can be used as a context manager:: 61 62 >>> import warnings 63 >>> def api_call_v2(): 64 ... warnings.warn('use v3 of this api', DeprecationWarning) 65 ... return 200 66 67 >>> import pytest 68 >>> with pytest.deprecated_call(): 69 ... assert api_call_v2() == 200 70 71 It can also be used by passing a function and ``*args`` and ``**kwargs``, 72 in which case it will ensure calling ``func(*args, **kwargs)`` produces one of 73 the warnings types above. The return value is the return value of the function. 74 75 In the context manager form you may use the keyword argument ``match`` to assert 76 that the warning matches a text or regex. 77 78 The context manager produces a list of :class:`warnings.WarningMessage` objects, 79 one for each warning raised. 80 """ 81 __tracebackhide__ = True 82 if func is not None: 83 args = (func, *args) 84 return warns( 85 (DeprecationWarning, PendingDeprecationWarning, FutureWarning), *args, **kwargs 86 )
Assert that code produces a DeprecationWarning
or PendingDeprecationWarning
or FutureWarning
.
This function can be used as a context manager::
>>> import warnings
>>> def api_call_v2():
... warnings.warn('use v3 of this api', DeprecationWarning)
... return 200
>>> import pytest
>>> with pytest.deprecated_call():
... assert api_call_v2() == 200
It can also be used by passing a function and *args
and **kwargs
,
in which case it will ensure calling func(*args, **kwargs)
produces one of
the warnings types above. The return value is the return value of the function.
In the context manager form you may use the keyword argument match
to assert
that the warning matches a text or regex.
The context manager produces a list of warnings.WarningMessage
objects,
one for each warning raised.
492@final 493class Dir(nodes.Directory): 494 """Collector of files in a file system directory. 495 496 .. versionadded:: 8.0 497 498 .. note:: 499 500 Python directories with an `__init__.py` file are instead collected by 501 :class:`~pytest.Package` by default. Both are :class:`~pytest.Directory` 502 collectors. 503 """ 504 505 @classmethod 506 def from_parent( # type: ignore[override] 507 cls, 508 parent: nodes.Collector, 509 *, 510 path: Path, 511 ) -> "Self": 512 """The public constructor. 513 514 :param parent: The parent collector of this Dir. 515 :param path: The directory's path. 516 """ 517 return super().from_parent(parent=parent, path=path) 518 519 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: 520 config = self.config 521 col: Optional[nodes.Collector] 522 cols: Sequence[nodes.Collector] 523 ihook = self.ihook 524 for direntry in scandir(self.path): 525 if direntry.is_dir(): 526 path = Path(direntry.path) 527 if not self.session.isinitpath(path, with_parents=True): 528 if ihook.pytest_ignore_collect(collection_path=path, config=config): 529 continue 530 col = ihook.pytest_collect_directory(path=path, parent=self) 531 if col is not None: 532 yield col 533 534 elif direntry.is_file(): 535 path = Path(direntry.path) 536 if not self.session.isinitpath(path): 537 if ihook.pytest_ignore_collect(collection_path=path, config=config): 538 continue 539 cols = ihook.pytest_collect_file(file_path=path, parent=self) 540 yield from cols
Collector of files in a file system directory.
New in version 8.0.
Python directories with an __init__.py
file are instead collected by
~pytest.Package
by default. Both are ~pytest.Directory
collectors.
505 @classmethod 506 def from_parent( # type: ignore[override] 507 cls, 508 parent: nodes.Collector, 509 *, 510 path: Path, 511 ) -> "Self": 512 """The public constructor. 513 514 :param parent: The parent collector of this Dir. 515 :param path: The directory's path. 516 """ 517 return super().from_parent(parent=parent, path=path)
The public constructor.
Parameters
- parent: The parent collector of this Dir.
- path: The directory's path.
519 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: 520 config = self.config 521 col: Optional[nodes.Collector] 522 cols: Sequence[nodes.Collector] 523 ihook = self.ihook 524 for direntry in scandir(self.path): 525 if direntry.is_dir(): 526 path = Path(direntry.path) 527 if not self.session.isinitpath(path, with_parents=True): 528 if ihook.pytest_ignore_collect(collection_path=path, config=config): 529 continue 530 col = ihook.pytest_collect_directory(path=path, parent=self) 531 if col is not None: 532 yield col 533 534 elif direntry.is_file(): 535 path = Path(direntry.path) 536 if not self.session.isinitpath(path): 537 if ihook.pytest_ignore_collect(collection_path=path, config=config): 538 continue 539 cols = ihook.pytest_collect_file(file_path=path, parent=self) 540 yield from cols
Collect children (items and collectors) for this collector.
Inherited Members
- _pytest.nodes.FSCollector
- FSCollector
- path
- _pytest.nodes.Node
- fspath
- name
- parent
- keywords
- own_markers
- extra_keyword_matches
- stash
- ihook
- warn
- nodeid
- setup
- teardown
- iter_parents
- listchain
- add_marker
- iter_markers
- iter_markers_with_node
- get_closest_marker
- listextrakeywords
- listnames
- addfinalizer
- getparent
- config
- session
637class Directory(FSCollector, abc.ABC): 638 """Base class for collecting files from a directory. 639 640 A basic directory collector does the following: goes over the files and 641 sub-directories in the directory and creates collectors for them by calling 642 the hooks :hook:`pytest_collect_directory` and :hook:`pytest_collect_file`, 643 after checking that they are not ignored using 644 :hook:`pytest_ignore_collect`. 645 646 The default directory collectors are :class:`~pytest.Dir` and 647 :class:`~pytest.Package`. 648 649 .. versionadded:: 8.0 650 651 :ref:`custom directory collectors`. 652 """
Base class for collecting files from a directory.
A basic directory collector does the following: goes over the files and
sub-directories in the directory and creates collectors for them by calling
the hooks :hook:pytest_collect_directory
and :hook:pytest_collect_file
,
after checking that they are not ignored using
:hook:pytest_ignore_collect
.
The default directory collectors are ~pytest.Dir
and
~pytest.Package
.
New in version 8.0.
:ref:custom directory collectors
.
Inherited Members
- _pytest.nodes.FSCollector
- path
- from_parent
- _pytest.nodes.Node
- fspath
- name
- parent
- keywords
- own_markers
- extra_keyword_matches
- stash
- ihook
- warn
- nodeid
- setup
- teardown
- iter_parents
- listchain
- add_marker
- iter_markers
- iter_markers_with_node
- get_closest_marker
- listextrakeywords
- listnames
- addfinalizer
- getparent
- config
- session
255class DoctestItem(Item): 256 def __init__( 257 self, 258 name: str, 259 parent: "Union[DoctestTextfile, DoctestModule]", 260 runner: "doctest.DocTestRunner", 261 dtest: "doctest.DocTest", 262 ) -> None: 263 super().__init__(name, parent) 264 self.runner = runner 265 self.dtest = dtest 266 267 # Stuff needed for fixture support. 268 self.obj = None 269 fm = self.session._fixturemanager 270 fixtureinfo = fm.getfixtureinfo(node=self, func=None, cls=None) 271 self._fixtureinfo = fixtureinfo 272 self.fixturenames = fixtureinfo.names_closure 273 self._initrequest() 274 275 @classmethod 276 def from_parent( # type: ignore[override] 277 cls, 278 parent: "Union[DoctestTextfile, DoctestModule]", 279 *, 280 name: str, 281 runner: "doctest.DocTestRunner", 282 dtest: "doctest.DocTest", 283 ) -> "Self": 284 # incompatible signature due to imposed limits on subclass 285 """The public named constructor.""" 286 return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest) 287 288 def _initrequest(self) -> None: 289 self.funcargs: Dict[str, object] = {} 290 self._request = TopRequest(self, _ispytest=True) # type: ignore[arg-type] 291 292 def setup(self) -> None: 293 self._request._fillfixtures() 294 globs = dict(getfixture=self._request.getfixturevalue) 295 for name, value in self._request.getfixturevalue("doctest_namespace").items(): 296 globs[name] = value 297 self.dtest.globs.update(globs) 298 299 def runtest(self) -> None: 300 _check_all_skipped(self.dtest) 301 self._disable_output_capturing_for_darwin() 302 failures: List["doctest.DocTestFailure"] = [] 303 # Type ignored because we change the type of `out` from what 304 # doctest expects. 305 self.runner.run(self.dtest, out=failures) # type: ignore[arg-type] 306 if failures: 307 raise MultipleDoctestFailures(failures) 308 309 def _disable_output_capturing_for_darwin(self) -> None: 310 """Disable output capturing. Otherwise, stdout is lost to doctest (#985).""" 311 if platform.system() != "Darwin": 312 return 313 capman = self.config.pluginmanager.getplugin("capturemanager") 314 if capman: 315 capman.suspend_global_capture(in_=True) 316 out, err = capman.read_global_capture() 317 sys.stdout.write(out) 318 sys.stderr.write(err) 319 320 # TODO: Type ignored -- breaks Liskov Substitution. 321 def repr_failure( # type: ignore[override] 322 self, 323 excinfo: ExceptionInfo[BaseException], 324 ) -> Union[str, TerminalRepr]: 325 import doctest 326 327 failures: Optional[ 328 Sequence[Union[doctest.DocTestFailure, doctest.UnexpectedException]] 329 ] = None 330 if isinstance( 331 excinfo.value, (doctest.DocTestFailure, doctest.UnexpectedException) 332 ): 333 failures = [excinfo.value] 334 elif isinstance(excinfo.value, MultipleDoctestFailures): 335 failures = excinfo.value.failures 336 337 if failures is None: 338 return super().repr_failure(excinfo) 339 340 reprlocation_lines = [] 341 for failure in failures: 342 example = failure.example 343 test = failure.test 344 filename = test.filename 345 if test.lineno is None: 346 lineno = None 347 else: 348 lineno = test.lineno + example.lineno + 1 349 message = type(failure).__name__ 350 # TODO: ReprFileLocation doesn't expect a None lineno. 351 reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type] 352 checker = _get_checker() 353 report_choice = _get_report_choice(self.config.getoption("doctestreport")) 354 if lineno is not None: 355 assert failure.test.docstring is not None 356 lines = failure.test.docstring.splitlines(False) 357 # add line numbers to the left of the error message 358 assert test.lineno is not None 359 lines = [ 360 "%03d %s" % (i + test.lineno + 1, x) for (i, x) in enumerate(lines) 361 ] 362 # trim docstring error lines to 10 363 lines = lines[max(example.lineno - 9, 0) : example.lineno + 1] 364 else: 365 lines = [ 366 "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example" 367 ] 368 indent = ">>>" 369 for line in example.source.splitlines(): 370 lines.append(f"??? {indent} {line}") 371 indent = "..." 372 if isinstance(failure, doctest.DocTestFailure): 373 lines += checker.output_difference( 374 example, failure.got, report_choice 375 ).split("\n") 376 else: 377 inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info) 378 lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)] 379 lines += [ 380 x.strip("\n") for x in traceback.format_exception(*failure.exc_info) 381 ] 382 reprlocation_lines.append((reprlocation, lines)) 383 return ReprFailDoctest(reprlocation_lines) 384 385 def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]: 386 return self.path, self.dtest.lineno, "[doctest] %s" % self.name
Base class of all test invocation items.
Note that for a single function there might be multiple test invocation items.
256 def __init__( 257 self, 258 name: str, 259 parent: "Union[DoctestTextfile, DoctestModule]", 260 runner: "doctest.DocTestRunner", 261 dtest: "doctest.DocTest", 262 ) -> None: 263 super().__init__(name, parent) 264 self.runner = runner 265 self.dtest = dtest 266 267 # Stuff needed for fixture support. 268 self.obj = None 269 fm = self.session._fixturemanager 270 fixtureinfo = fm.getfixtureinfo(node=self, func=None, cls=None) 271 self._fixtureinfo = fixtureinfo 272 self.fixturenames = fixtureinfo.names_closure 273 self._initrequest()
275 @classmethod 276 def from_parent( # type: ignore[override] 277 cls, 278 parent: "Union[DoctestTextfile, DoctestModule]", 279 *, 280 name: str, 281 runner: "doctest.DocTestRunner", 282 dtest: "doctest.DocTest", 283 ) -> "Self": 284 # incompatible signature due to imposed limits on subclass 285 """The public named constructor.""" 286 return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest)
The public named constructor.
299 def runtest(self) -> None: 300 _check_all_skipped(self.dtest) 301 self._disable_output_capturing_for_darwin() 302 failures: List["doctest.DocTestFailure"] = [] 303 # Type ignored because we change the type of `out` from what 304 # doctest expects. 305 self.runner.run(self.dtest, out=failures) # type: ignore[arg-type] 306 if failures: 307 raise MultipleDoctestFailures(failures)
Run the test case for this item.
Must be implemented by subclasses.
seealso :ref:non-python tests
.
321 def repr_failure( # type: ignore[override] 322 self, 323 excinfo: ExceptionInfo[BaseException], 324 ) -> Union[str, TerminalRepr]: 325 import doctest 326 327 failures: Optional[ 328 Sequence[Union[doctest.DocTestFailure, doctest.UnexpectedException]] 329 ] = None 330 if isinstance( 331 excinfo.value, (doctest.DocTestFailure, doctest.UnexpectedException) 332 ): 333 failures = [excinfo.value] 334 elif isinstance(excinfo.value, MultipleDoctestFailures): 335 failures = excinfo.value.failures 336 337 if failures is None: 338 return super().repr_failure(excinfo) 339 340 reprlocation_lines = [] 341 for failure in failures: 342 example = failure.example 343 test = failure.test 344 filename = test.filename 345 if test.lineno is None: 346 lineno = None 347 else: 348 lineno = test.lineno + example.lineno + 1 349 message = type(failure).__name__ 350 # TODO: ReprFileLocation doesn't expect a None lineno. 351 reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type] 352 checker = _get_checker() 353 report_choice = _get_report_choice(self.config.getoption("doctestreport")) 354 if lineno is not None: 355 assert failure.test.docstring is not None 356 lines = failure.test.docstring.splitlines(False) 357 # add line numbers to the left of the error message 358 assert test.lineno is not None 359 lines = [ 360 "%03d %s" % (i + test.lineno + 1, x) for (i, x) in enumerate(lines) 361 ] 362 # trim docstring error lines to 10 363 lines = lines[max(example.lineno - 9, 0) : example.lineno + 1] 364 else: 365 lines = [ 366 "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example" 367 ] 368 indent = ">>>" 369 for line in example.source.splitlines(): 370 lines.append(f"??? {indent} {line}") 371 indent = "..." 372 if isinstance(failure, doctest.DocTestFailure): 373 lines += checker.output_difference( 374 example, failure.got, report_choice 375 ).split("\n") 376 else: 377 inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info) 378 lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)] 379 lines += [ 380 x.strip("\n") for x in traceback.format_exception(*failure.exc_info) 381 ] 382 reprlocation_lines.append((reprlocation, lines)) 383 return ReprFailDoctest(reprlocation_lines)
Return a representation of a collection or test failure.
seealso :ref:non-python tests
.
Parameters
- excinfo: Exception information for the failure.
385 def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]: 386 return self.path, self.dtest.lineno, "[doctest] %s" % self.name
Get location information for this item for test reports.
Returns a tuple with three elements:
- The path of the test (default
self.path
) - The 0-based line number of the test (default
None
) - A name of the test to be shown (default
""
)
seealso :ref:non-python tests
.
Inherited Members
- _pytest.nodes.Node
- fspath
- name
- parent
- path
- keywords
- own_markers
- extra_keyword_matches
- stash
- ihook
- warn
- nodeid
- teardown
- iter_parents
- listchain
- add_marker
- iter_markers
- iter_markers_with_node
- get_closest_marker
- listextrakeywords
- listnames
- addfinalizer
- getparent
- config
- session
105@_with_exception(Exit) 106def exit( 107 reason: str = "", 108 returncode: Optional[int] = None, 109) -> NoReturn: 110 """Exit testing process. 111 112 :param reason: 113 The message to show as the reason for exiting pytest. reason has a default value 114 only because `msg` is deprecated. 115 116 :param returncode: 117 Return code to be used when exiting pytest. None means the same as ``0`` (no error), same as :func:`sys.exit`. 118 """ 119 __tracebackhide__ = True 120 raise Exit(reason, returncode)
Exit testing process.
Parameters
reason: The message to show as the reason for exiting pytest. reason has a default value only because
msg
is deprecated.returncode: Return code to be used when exiting pytest. None means the same as
0
(no error), same assys.exit()
.
443@final 444@dataclasses.dataclass 445class ExceptionInfo(Generic[E]): 446 """Wraps sys.exc_info() objects and offers help for navigating the traceback.""" 447 448 _assert_start_repr: ClassVar = "AssertionError('assert " 449 450 _excinfo: Optional[Tuple[Type["E"], "E", TracebackType]] 451 _striptext: str 452 _traceback: Optional[Traceback] 453 454 def __init__( 455 self, 456 excinfo: Optional[Tuple[Type["E"], "E", TracebackType]], 457 striptext: str = "", 458 traceback: Optional[Traceback] = None, 459 *, 460 _ispytest: bool = False, 461 ) -> None: 462 check_ispytest(_ispytest) 463 self._excinfo = excinfo 464 self._striptext = striptext 465 self._traceback = traceback 466 467 @classmethod 468 def from_exception( 469 cls, 470 # Ignoring error: "Cannot use a covariant type variable as a parameter". 471 # This is OK to ignore because this class is (conceptually) readonly. 472 # See https://github.com/python/mypy/issues/7049. 473 exception: E, # type: ignore[misc] 474 exprinfo: Optional[str] = None, 475 ) -> "ExceptionInfo[E]": 476 """Return an ExceptionInfo for an existing exception. 477 478 The exception must have a non-``None`` ``__traceback__`` attribute, 479 otherwise this function fails with an assertion error. This means that 480 the exception must have been raised, or added a traceback with the 481 :py:meth:`~BaseException.with_traceback()` method. 482 483 :param exprinfo: 484 A text string helping to determine if we should strip 485 ``AssertionError`` from the output. Defaults to the exception 486 message/``__str__()``. 487 488 .. versionadded:: 7.4 489 """ 490 assert exception.__traceback__, ( 491 "Exceptions passed to ExcInfo.from_exception(...)" 492 " must have a non-None __traceback__." 493 ) 494 exc_info = (type(exception), exception, exception.__traceback__) 495 return cls.from_exc_info(exc_info, exprinfo) 496 497 @classmethod 498 def from_exc_info( 499 cls, 500 exc_info: Tuple[Type[E], E, TracebackType], 501 exprinfo: Optional[str] = None, 502 ) -> "ExceptionInfo[E]": 503 """Like :func:`from_exception`, but using old-style exc_info tuple.""" 504 _striptext = "" 505 if exprinfo is None and isinstance(exc_info[1], AssertionError): 506 exprinfo = getattr(exc_info[1], "msg", None) 507 if exprinfo is None: 508 exprinfo = saferepr(exc_info[1]) 509 if exprinfo and exprinfo.startswith(cls._assert_start_repr): 510 _striptext = "AssertionError: " 511 512 return cls(exc_info, _striptext, _ispytest=True) 513 514 @classmethod 515 def from_current( 516 cls, exprinfo: Optional[str] = None 517 ) -> "ExceptionInfo[BaseException]": 518 """Return an ExceptionInfo matching the current traceback. 519 520 .. warning:: 521 522 Experimental API 523 524 :param exprinfo: 525 A text string helping to determine if we should strip 526 ``AssertionError`` from the output. Defaults to the exception 527 message/``__str__()``. 528 """ 529 tup = sys.exc_info() 530 assert tup[0] is not None, "no current exception" 531 assert tup[1] is not None, "no current exception" 532 assert tup[2] is not None, "no current exception" 533 exc_info = (tup[0], tup[1], tup[2]) 534 return ExceptionInfo.from_exc_info(exc_info, exprinfo) 535 536 @classmethod 537 def for_later(cls) -> "ExceptionInfo[E]": 538 """Return an unfilled ExceptionInfo.""" 539 return cls(None, _ispytest=True) 540 541 def fill_unfilled(self, exc_info: Tuple[Type[E], E, TracebackType]) -> None: 542 """Fill an unfilled ExceptionInfo created with ``for_later()``.""" 543 assert self._excinfo is None, "ExceptionInfo was already filled" 544 self._excinfo = exc_info 545 546 @property 547 def type(self) -> Type[E]: 548 """The exception class.""" 549 assert ( 550 self._excinfo is not None 551 ), ".type can only be used after the context manager exits" 552 return self._excinfo[0] 553 554 @property 555 def value(self) -> E: 556 """The exception value.""" 557 assert ( 558 self._excinfo is not None 559 ), ".value can only be used after the context manager exits" 560 return self._excinfo[1] 561 562 @property 563 def tb(self) -> TracebackType: 564 """The exception raw traceback.""" 565 assert ( 566 self._excinfo is not None 567 ), ".tb can only be used after the context manager exits" 568 return self._excinfo[2] 569 570 @property 571 def typename(self) -> str: 572 """The type name of the exception.""" 573 assert ( 574 self._excinfo is not None 575 ), ".typename can only be used after the context manager exits" 576 return self.type.__name__ 577 578 @property 579 def traceback(self) -> Traceback: 580 """The traceback.""" 581 if self._traceback is None: 582 self._traceback = Traceback(self.tb) 583 return self._traceback 584 585 @traceback.setter 586 def traceback(self, value: Traceback) -> None: 587 self._traceback = value 588 589 def __repr__(self) -> str: 590 if self._excinfo is None: 591 return "<ExceptionInfo for raises contextmanager>" 592 return f"<{self.__class__.__name__} {saferepr(self._excinfo[1])} tblen={len(self.traceback)}>" 593 594 def exconly(self, tryshort: bool = False) -> str: 595 """Return the exception as a string. 596 597 When 'tryshort' resolves to True, and the exception is an 598 AssertionError, only the actual exception part of the exception 599 representation is returned (so 'AssertionError: ' is removed from 600 the beginning). 601 """ 602 lines = format_exception_only(self.type, self.value) 603 text = "".join(lines) 604 text = text.rstrip() 605 if tryshort: 606 if text.startswith(self._striptext): 607 text = text[len(self._striptext) :] 608 return text 609 610 def errisinstance( 611 self, exc: Union[Type[BaseException], Tuple[Type[BaseException], ...]] 612 ) -> bool: 613 """Return True if the exception is an instance of exc. 614 615 Consider using ``isinstance(excinfo.value, exc)`` instead. 616 """ 617 return isinstance(self.value, exc) 618 619 def _getreprcrash(self) -> Optional["ReprFileLocation"]: 620 # Find last non-hidden traceback entry that led to the exception of the 621 # traceback, or None if all hidden. 622 for i in range(-1, -len(self.traceback) - 1, -1): 623 entry = self.traceback[i] 624 if not entry.ishidden(self): 625 path, lineno = entry.frame.code.raw.co_filename, entry.lineno 626 exconly = self.exconly(tryshort=True) 627 return ReprFileLocation(path, lineno + 1, exconly) 628 return None 629 630 def getrepr( 631 self, 632 showlocals: bool = False, 633 style: _TracebackStyle = "long", 634 abspath: bool = False, 635 tbfilter: Union[ 636 bool, Callable[["ExceptionInfo[BaseException]"], Traceback] 637 ] = True, 638 funcargs: bool = False, 639 truncate_locals: bool = True, 640 chain: bool = True, 641 ) -> Union["ReprExceptionInfo", "ExceptionChainRepr"]: 642 """Return str()able representation of this exception info. 643 644 :param bool showlocals: 645 Show locals per traceback entry. 646 Ignored if ``style=="native"``. 647 648 :param str style: 649 long|short|line|no|native|value traceback style. 650 651 :param bool abspath: 652 If paths should be changed to absolute or left unchanged. 653 654 :param tbfilter: 655 A filter for traceback entries. 656 657 * If false, don't hide any entries. 658 * If true, hide internal entries and entries that contain a local 659 variable ``__tracebackhide__ = True``. 660 * If a callable, delegates the filtering to the callable. 661 662 Ignored if ``style`` is ``"native"``. 663 664 :param bool funcargs: 665 Show fixtures ("funcargs" for legacy purposes) per traceback entry. 666 667 :param bool truncate_locals: 668 With ``showlocals==True``, make sure locals can be safely represented as strings. 669 670 :param bool chain: 671 If chained exceptions in Python 3 should be shown. 672 673 .. versionchanged:: 3.9 674 675 Added the ``chain`` parameter. 676 """ 677 if style == "native": 678 return ReprExceptionInfo( 679 reprtraceback=ReprTracebackNative( 680 traceback.format_exception( 681 self.type, 682 self.value, 683 self.traceback[0]._rawentry if self.traceback else None, 684 ) 685 ), 686 reprcrash=self._getreprcrash(), 687 ) 688 689 fmt = FormattedExcinfo( 690 showlocals=showlocals, 691 style=style, 692 abspath=abspath, 693 tbfilter=tbfilter, 694 funcargs=funcargs, 695 truncate_locals=truncate_locals, 696 chain=chain, 697 ) 698 return fmt.repr_excinfo(self) 699 700 def _stringify_exception(self, exc: BaseException) -> str: 701 try: 702 notes = getattr(exc, "__notes__", []) 703 except KeyError: 704 # Workaround for https://github.com/python/cpython/issues/98778 on 705 # Python <= 3.9, and some 3.10 and 3.11 patch versions. 706 HTTPError = getattr(sys.modules.get("urllib.error", None), "HTTPError", ()) 707 if sys.version_info < (3, 12) and isinstance(exc, HTTPError): 708 notes = [] 709 else: 710 raise 711 712 return "\n".join( 713 [ 714 str(exc), 715 *notes, 716 ] 717 ) 718 719 def match(self, regexp: Union[str, Pattern[str]]) -> "Literal[True]": 720 """Check whether the regular expression `regexp` matches the string 721 representation of the exception using :func:`python:re.search`. 722 723 If it matches `True` is returned, otherwise an `AssertionError` is raised. 724 """ 725 __tracebackhide__ = True 726 value = self._stringify_exception(self.value) 727 msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}" 728 if regexp == value: 729 msg += "\n Did you mean to `re.escape()` the regex?" 730 assert re.search(regexp, value), msg 731 # Return True to allow for "assert excinfo.match()". 732 return True 733 734 def _group_contains( 735 self, 736 exc_group: BaseExceptionGroup[BaseException], 737 expected_exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]], 738 match: Union[str, Pattern[str], None], 739 target_depth: Optional[int] = None, 740 current_depth: int = 1, 741 ) -> bool: 742 """Return `True` if a `BaseExceptionGroup` contains a matching exception.""" 743 if (target_depth is not None) and (current_depth > target_depth): 744 # already descended past the target depth 745 return False 746 for exc in exc_group.exceptions: 747 if isinstance(exc, BaseExceptionGroup): 748 if self._group_contains( 749 exc, expected_exception, match, target_depth, current_depth + 1 750 ): 751 return True 752 if (target_depth is not None) and (current_depth != target_depth): 753 # not at the target depth, no match 754 continue 755 if not isinstance(exc, expected_exception): 756 continue 757 if match is not None: 758 value = self._stringify_exception(exc) 759 if not re.search(match, value): 760 continue 761 return True 762 return False 763 764 def group_contains( 765 self, 766 expected_exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]], 767 *, 768 match: Union[str, Pattern[str], None] = None, 769 depth: Optional[int] = None, 770 ) -> bool: 771 """Check whether a captured exception group contains a matching exception. 772 773 :param Type[BaseException] | Tuple[Type[BaseException]] expected_exception: 774 The expected exception type, or a tuple if one of multiple possible 775 exception types are expected. 776 777 :param str | Pattern[str] | None match: 778 If specified, a string containing a regular expression, 779 or a regular expression object, that is tested against the string 780 representation of the exception and its `PEP-678 <https://peps.python.org/pep-0678/>` `__notes__` 781 using :func:`re.search`. 782 783 To match a literal string that may contain :ref:`special characters 784 <re-syntax>`, the pattern can first be escaped with :func:`re.escape`. 785 786 :param Optional[int] depth: 787 If `None`, will search for a matching exception at any nesting depth. 788 If >= 1, will only match an exception if it's at the specified depth (depth = 1 being 789 the exceptions contained within the topmost exception group). 790 791 .. versionadded:: 8.0 792 """ 793 msg = "Captured exception is not an instance of `BaseExceptionGroup`" 794 assert isinstance(self.value, BaseExceptionGroup), msg 795 msg = "`depth` must be >= 1 if specified" 796 assert (depth is None) or (depth >= 1), msg 797 return self._group_contains(self.value, expected_exception, match, depth)
Wraps sys.exc_info() objects and offers help for navigating the traceback.
454 def __init__( 455 self, 456 excinfo: Optional[Tuple[Type["E"], "E", TracebackType]], 457 striptext: str = "", 458 traceback: Optional[Traceback] = None, 459 *, 460 _ispytest: bool = False, 461 ) -> None: 462 check_ispytest(_ispytest) 463 self._excinfo = excinfo 464 self._striptext = striptext 465 self._traceback = traceback
467 @classmethod 468 def from_exception( 469 cls, 470 # Ignoring error: "Cannot use a covariant type variable as a parameter". 471 # This is OK to ignore because this class is (conceptually) readonly. 472 # See https://github.com/python/mypy/issues/7049. 473 exception: E, # type: ignore[misc] 474 exprinfo: Optional[str] = None, 475 ) -> "ExceptionInfo[E]": 476 """Return an ExceptionInfo for an existing exception. 477 478 The exception must have a non-``None`` ``__traceback__`` attribute, 479 otherwise this function fails with an assertion error. This means that 480 the exception must have been raised, or added a traceback with the 481 :py:meth:`~BaseException.with_traceback()` method. 482 483 :param exprinfo: 484 A text string helping to determine if we should strip 485 ``AssertionError`` from the output. Defaults to the exception 486 message/``__str__()``. 487 488 .. versionadded:: 7.4 489 """ 490 assert exception.__traceback__, ( 491 "Exceptions passed to ExcInfo.from_exception(...)" 492 " must have a non-None __traceback__." 493 ) 494 exc_info = (type(exception), exception, exception.__traceback__) 495 return cls.from_exc_info(exc_info, exprinfo)
Return an ExceptionInfo for an existing exception.
The exception must have a non-None
__traceback__
attribute,
otherwise this function fails with an assertion error. This means that
the exception must have been raised, or added a traceback with the
~BaseException.with_traceback()()
method.
Parameters
- exprinfo:
A text string helping to determine if we should strip
AssertionError
from the output. Defaults to the exception message/__str__()
.
New in version 7.4.
497 @classmethod 498 def from_exc_info( 499 cls, 500 exc_info: Tuple[Type[E], E, TracebackType], 501 exprinfo: Optional[str] = None, 502 ) -> "ExceptionInfo[E]": 503 """Like :func:`from_exception`, but using old-style exc_info tuple.""" 504 _striptext = "" 505 if exprinfo is None and isinstance(exc_info[1], AssertionError): 506 exprinfo = getattr(exc_info[1], "msg", None) 507 if exprinfo is None: 508 exprinfo = saferepr(exc_info[1]) 509 if exprinfo and exprinfo.startswith(cls._assert_start_repr): 510 _striptext = "AssertionError: " 511 512 return cls(exc_info, _striptext, _ispytest=True)
Like from_exception()
, but using old-style exc_info tuple.
514 @classmethod 515 def from_current( 516 cls, exprinfo: Optional[str] = None 517 ) -> "ExceptionInfo[BaseException]": 518 """Return an ExceptionInfo matching the current traceback. 519 520 .. warning:: 521 522 Experimental API 523 524 :param exprinfo: 525 A text string helping to determine if we should strip 526 ``AssertionError`` from the output. Defaults to the exception 527 message/``__str__()``. 528 """ 529 tup = sys.exc_info() 530 assert tup[0] is not None, "no current exception" 531 assert tup[1] is not None, "no current exception" 532 assert tup[2] is not None, "no current exception" 533 exc_info = (tup[0], tup[1], tup[2]) 534 return ExceptionInfo.from_exc_info(exc_info, exprinfo)
Return an ExceptionInfo matching the current traceback.
Experimental API
Parameters
- exprinfo:
A text string helping to determine if we should strip
AssertionError
from the output. Defaults to the exception message/__str__()
.
536 @classmethod 537 def for_later(cls) -> "ExceptionInfo[E]": 538 """Return an unfilled ExceptionInfo.""" 539 return cls(None, _ispytest=True)
Return an unfilled ExceptionInfo.
541 def fill_unfilled(self, exc_info: Tuple[Type[E], E, TracebackType]) -> None: 542 """Fill an unfilled ExceptionInfo created with ``for_later()``.""" 543 assert self._excinfo is None, "ExceptionInfo was already filled" 544 self._excinfo = exc_info
Fill an unfilled ExceptionInfo created with for_later()
.
546 @property 547 def type(self) -> Type[E]: 548 """The exception class.""" 549 assert ( 550 self._excinfo is not None 551 ), ".type can only be used after the context manager exits" 552 return self._excinfo[0]
The exception class.
554 @property 555 def value(self) -> E: 556 """The exception value.""" 557 assert ( 558 self._excinfo is not None 559 ), ".value can only be used after the context manager exits" 560 return self._excinfo[1]
The exception value.
562 @property 563 def tb(self) -> TracebackType: 564 """The exception raw traceback.""" 565 assert ( 566 self._excinfo is not None 567 ), ".tb can only be used after the context manager exits" 568 return self._excinfo[2]
The exception raw traceback.
570 @property 571 def typename(self) -> str: 572 """The type name of the exception.""" 573 assert ( 574 self._excinfo is not None 575 ), ".typename can only be used after the context manager exits" 576 return self.type.__name__
The type name of the exception.
578 @property 579 def traceback(self) -> Traceback: 580 """The traceback.""" 581 if self._traceback is None: 582 self._traceback = Traceback(self.tb) 583 return self._traceback
The traceback.
594 def exconly(self, tryshort: bool = False) -> str: 595 """Return the exception as a string. 596 597 When 'tryshort' resolves to True, and the exception is an 598 AssertionError, only the actual exception part of the exception 599 representation is returned (so 'AssertionError: ' is removed from 600 the beginning). 601 """ 602 lines = format_exception_only(self.type, self.value) 603 text = "".join(lines) 604 text = text.rstrip() 605 if tryshort: 606 if text.startswith(self._striptext): 607 text = text[len(self._striptext) :] 608 return text
Return the exception as a string.
When 'tryshort' resolves to True, and the exception is an AssertionError, only the actual exception part of the exception representation is returned (so 'AssertionError: ' is removed from the beginning).
610 def errisinstance( 611 self, exc: Union[Type[BaseException], Tuple[Type[BaseException], ...]] 612 ) -> bool: 613 """Return True if the exception is an instance of exc. 614 615 Consider using ``isinstance(excinfo.value, exc)`` instead. 616 """ 617 return isinstance(self.value, exc)
Return True if the exception is an instance of exc.
Consider using isinstance(excinfo.value, exc)
instead.
630 def getrepr( 631 self, 632 showlocals: bool = False, 633 style: _TracebackStyle = "long", 634 abspath: bool = False, 635 tbfilter: Union[ 636 bool, Callable[["ExceptionInfo[BaseException]"], Traceback] 637 ] = True, 638 funcargs: bool = False, 639 truncate_locals: bool = True, 640 chain: bool = True, 641 ) -> Union["ReprExceptionInfo", "ExceptionChainRepr"]: 642 """Return str()able representation of this exception info. 643 644 :param bool showlocals: 645 Show locals per traceback entry. 646 Ignored if ``style=="native"``. 647 648 :param str style: 649 long|short|line|no|native|value traceback style. 650 651 :param bool abspath: 652 If paths should be changed to absolute or left unchanged. 653 654 :param tbfilter: 655 A filter for traceback entries. 656 657 * If false, don't hide any entries. 658 * If true, hide internal entries and entries that contain a local 659 variable ``__tracebackhide__ = True``. 660 * If a callable, delegates the filtering to the callable. 661 662 Ignored if ``style`` is ``"native"``. 663 664 :param bool funcargs: 665 Show fixtures ("funcargs" for legacy purposes) per traceback entry. 666 667 :param bool truncate_locals: 668 With ``showlocals==True``, make sure locals can be safely represented as strings. 669 670 :param bool chain: 671 If chained exceptions in Python 3 should be shown. 672 673 .. versionchanged:: 3.9 674 675 Added the ``chain`` parameter. 676 """ 677 if style == "native": 678 return ReprExceptionInfo( 679 reprtraceback=ReprTracebackNative( 680 traceback.format_exception( 681 self.type, 682 self.value, 683 self.traceback[0]._rawentry if self.traceback else None, 684 ) 685 ), 686 reprcrash=self._getreprcrash(), 687 ) 688 689 fmt = FormattedExcinfo( 690 showlocals=showlocals, 691 style=style, 692 abspath=abspath, 693 tbfilter=tbfilter, 694 funcargs=funcargs, 695 truncate_locals=truncate_locals, 696 chain=chain, 697 ) 698 return fmt.repr_excinfo(self)
Return str()able representation of this exception info.
Parameters
bool showlocals: Show locals per traceback entry. Ignored if
style=="native"
.str style: long|short|line|no|native|value traceback style.
bool abspath: If paths should be changed to absolute or left unchanged.
tbfilter: A filter for traceback entries.
- If false, don't hide any entries.
- If true, hide internal entries and entries that contain a local
variable
__tracebackhide__ = True
. - If a callable, delegates the filtering to the callable.
Ignored if
style
is"native"
.bool funcargs: Show fixtures ("funcargs" for legacy purposes) per traceback entry.
bool truncate_locals: With
showlocals==True
, make sure locals can be safely represented as strings.bool chain: If chained exceptions in Python 3 should be shown.
Changed in version 3.9:
Added the chain
parameter.
719 def match(self, regexp: Union[str, Pattern[str]]) -> "Literal[True]": 720 """Check whether the regular expression `regexp` matches the string 721 representation of the exception using :func:`python:re.search`. 722 723 If it matches `True` is returned, otherwise an `AssertionError` is raised. 724 """ 725 __tracebackhide__ = True 726 value = self._stringify_exception(self.value) 727 msg = f"Regex pattern did not match.\n Regex: {regexp!r}\n Input: {value!r}" 728 if regexp == value: 729 msg += "\n Did you mean to `re.escape()` the regex?" 730 assert re.search(regexp, value), msg 731 # Return True to allow for "assert excinfo.match()". 732 return True
Check whether the regular expression regexp
matches the string
representation of the exception using python:re.search()
.
If it matches True
is returned, otherwise an AssertionError
is raised.
764 def group_contains( 765 self, 766 expected_exception: Union[Type[BaseException], Tuple[Type[BaseException], ...]], 767 *, 768 match: Union[str, Pattern[str], None] = None, 769 depth: Optional[int] = None, 770 ) -> bool: 771 """Check whether a captured exception group contains a matching exception. 772 773 :param Type[BaseException] | Tuple[Type[BaseException]] expected_exception: 774 The expected exception type, or a tuple if one of multiple possible 775 exception types are expected. 776 777 :param str | Pattern[str] | None match: 778 If specified, a string containing a regular expression, 779 or a regular expression object, that is tested against the string 780 representation of the exception and its `PEP-678 <https://peps.python.org/pep-0678/>` `__notes__` 781 using :func:`re.search`. 782 783 To match a literal string that may contain :ref:`special characters 784 <re-syntax>`, the pattern can first be escaped with :func:`re.escape`. 785 786 :param Optional[int] depth: 787 If `None`, will search for a matching exception at any nesting depth. 788 If >= 1, will only match an exception if it's at the specified depth (depth = 1 being 789 the exceptions contained within the topmost exception group). 790 791 .. versionadded:: 8.0 792 """ 793 msg = "Captured exception is not an instance of `BaseExceptionGroup`" 794 assert isinstance(self.value, BaseExceptionGroup), msg 795 msg = "`depth` must be >= 1 if specified" 796 assert (depth is None) or (depth >= 1), msg 797 return self._group_contains(self.value, expected_exception, match, depth)
Check whether a captured exception group contains a matching exception.
Parameters
Type[BaseException] | Tuple[Type[BaseException]] expected_exception: The expected exception type, or a tuple if one of multiple possible exception types are expected.
str | Pattern[str] | None match: If specified, a string containing a regular expression, or a regular expression object, that is tested against the string representation of the exception and its
PEP-678 <https://peps.python.org/pep-0678/>
__notes__
usingre.search()
.To match a literal string that may contain :ref:
special characters <re-syntax>
, the pattern can first be escaped withre.escape()
.Optional[int] depth: If
None
, will search for a matching exception at any nesting depth. If >= 1, will only match an exception if it's at the specified depth (depth = 1 being the exceptions contained within the topmost exception group).
New in version 8.0.
95@final 96class ExitCode(enum.IntEnum): 97 """Encodes the valid exit codes by pytest. 98 99 Currently users and plugins may supply other exit codes as well. 100 101 .. versionadded:: 5.0 102 """ 103 104 #: Tests passed. 105 OK = 0 106 #: Tests failed. 107 TESTS_FAILED = 1 108 #: pytest was interrupted. 109 INTERRUPTED = 2 110 #: An internal error got in the way. 111 INTERNAL_ERROR = 3 112 #: pytest was misused. 113 USAGE_ERROR = 4 114 #: pytest couldn't find tests. 115 NO_TESTS_COLLECTED = 5
Encodes the valid exit codes by pytest.
Currently users and plugins may supply other exit codes as well.
New in version 5.0.
Inherited Members
- enum.Enum
- name
- value
- builtins.int
- conjugate
- bit_length
- bit_count
- to_bytes
- from_bytes
- as_integer_ratio
- is_integer
- real
- imag
- numerator
- denominator
157@_with_exception(Failed) 158def fail(reason: str = "", pytrace: bool = True) -> NoReturn: 159 """Explicitly fail an executing test with the given message. 160 161 :param reason: 162 The message to show the user as reason for the failure. 163 164 :param pytrace: 165 If False, msg represents the full failure information and no 166 python traceback will be reported. 167 """ 168 __tracebackhide__ = True 169 raise Failed(msg=reason, pytrace=pytrace)
Explicitly fail an executing test with the given message.
Parameters
reason: The message to show the user as reason for the failure.
pytrace: If False, msg represents the full failure information and no python traceback will be reported.
630class File(FSCollector, abc.ABC): 631 """Base class for collecting tests from a file. 632 633 :ref:`non-python tests`. 634 """
Base class for collecting tests from a file.
:ref:non-python tests
.
Inherited Members
- _pytest.nodes.FSCollector
- path
- from_parent
- _pytest.nodes.Node
- fspath
- name
- parent
- keywords
- own_markers
- extra_keyword_matches
- stash
- ihook
- warn
- nodeid
- setup
- teardown
- iter_parents
- listchain
- add_marker
- iter_markers
- iter_markers_with_node
- get_closest_marker
- listextrakeywords
- listnames
- addfinalizer
- getparent
- config
- session
1232def fixture( 1233 fixture_function: Optional[FixtureFunction] = None, 1234 *, 1235 scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = "function", 1236 params: Optional[Iterable[object]] = None, 1237 autouse: bool = False, 1238 ids: Optional[ 1239 Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]] 1240 ] = None, 1241 name: Optional[str] = None, 1242) -> Union[FixtureFunctionMarker, FixtureFunction]: 1243 """Decorator to mark a fixture factory function. 1244 1245 This decorator can be used, with or without parameters, to define a 1246 fixture function. 1247 1248 The name of the fixture function can later be referenced to cause its 1249 invocation ahead of running tests: test modules or classes can use the 1250 ``pytest.mark.usefixtures(fixturename)`` marker. 1251 1252 Test functions can directly use fixture names as input arguments in which 1253 case the fixture instance returned from the fixture function will be 1254 injected. 1255 1256 Fixtures can provide their values to test functions using ``return`` or 1257 ``yield`` statements. When using ``yield`` the code block after the 1258 ``yield`` statement is executed as teardown code regardless of the test 1259 outcome, and must yield exactly once. 1260 1261 :param scope: 1262 The scope for which this fixture is shared; one of ``"function"`` 1263 (default), ``"class"``, ``"module"``, ``"package"`` or ``"session"``. 1264 1265 This parameter may also be a callable which receives ``(fixture_name, config)`` 1266 as parameters, and must return a ``str`` with one of the values mentioned above. 1267 1268 See :ref:`dynamic scope` in the docs for more information. 1269 1270 :param params: 1271 An optional list of parameters which will cause multiple invocations 1272 of the fixture function and all of the tests using it. The current 1273 parameter is available in ``request.param``. 1274 1275 :param autouse: 1276 If True, the fixture func is activated for all tests that can see it. 1277 If False (the default), an explicit reference is needed to activate 1278 the fixture. 1279 1280 :param ids: 1281 Sequence of ids each corresponding to the params so that they are 1282 part of the test id. If no ids are provided they will be generated 1283 automatically from the params. 1284 1285 :param name: 1286 The name of the fixture. This defaults to the name of the decorated 1287 function. If a fixture is used in the same module in which it is 1288 defined, the function name of the fixture will be shadowed by the 1289 function arg that requests the fixture; one way to resolve this is to 1290 name the decorated function ``fixture_<fixturename>`` and then use 1291 ``@pytest.fixture(name='<fixturename>')``. 1292 """ 1293 fixture_marker = FixtureFunctionMarker( 1294 scope=scope, 1295 params=tuple(params) if params is not None else None, 1296 autouse=autouse, 1297 ids=None if ids is None else ids if callable(ids) else tuple(ids), 1298 name=name, 1299 _ispytest=True, 1300 ) 1301 1302 # Direct decoration. 1303 if fixture_function: 1304 return fixture_marker(fixture_function) 1305 1306 return fixture_marker
Decorator to mark a fixture factory function.
This decorator can be used, with or without parameters, to define a fixture function.
The name of the fixture function can later be referenced to cause its
invocation ahead of running tests: test modules or classes can use the
pytest.mark.usefixtures(fixturename)
marker.
Test functions can directly use fixture names as input arguments in which case the fixture instance returned from the fixture function will be injected.
Fixtures can provide their values to test functions using return
or
yield
statements. When using yield
the code block after the
yield
statement is executed as teardown code regardless of the test
outcome, and must yield exactly once.
Parameters
scope: The scope for which this fixture is shared; one of
"function"
(default),"class"
,"module"
,"package"
or"session"
.This parameter may also be a callable which receives
(fixture_name, config)
as parameters, and must return astr
with one of the values mentioned above.See :ref:
dynamic scope
in the docs for more information.params: An optional list of parameters which will cause multiple invocations of the fixture function and all of the tests using it. The current parameter is available in
request.param
.autouse: If True, the fixture func is activated for all tests that can see it. If False (the default), an explicit reference is needed to activate the fixture.
ids: Sequence of ids each corresponding to the params so that they are part of the test id. If no ids are provided they will be generated automatically from the params.
name: The name of the fixture. This defaults to the name of the decorated function. If a fixture is used in the same module in which it is defined, the function name of the fixture will be shadowed by the function arg that requests the fixture; one way to resolve this is to name the decorated function
fixture_<fixturename>
and then use@pytest.fixture(name='<fixturename>')
.
930@final 931class FixtureDef(Generic[FixtureValue]): 932 """A container for a fixture definition. 933 934 Note: At this time, only explicitly documented fields and methods are 935 considered public stable API. 936 """ 937 938 def __init__( 939 self, 940 config: Config, 941 baseid: Optional[str], 942 argname: str, 943 func: "_FixtureFunc[FixtureValue]", 944 scope: Union[Scope, _ScopeName, Callable[[str, Config], _ScopeName], None], 945 params: Optional[Sequence[object]], 946 ids: Optional[ 947 Union[Tuple[Optional[object], ...], Callable[[Any], Optional[object]]] 948 ] = None, 949 *, 950 _ispytest: bool = False, 951 ) -> None: 952 check_ispytest(_ispytest) 953 # The "base" node ID for the fixture. 954 # 955 # This is a node ID prefix. A fixture is only available to a node (e.g. 956 # a `Function` item) if the fixture's baseid is a nodeid of a parent of 957 # node. 958 # 959 # For a fixture found in a Collector's object (e.g. a `Module`s module, 960 # a `Class`'s class), the baseid is the Collector's nodeid. 961 # 962 # For a fixture found in a conftest plugin, the baseid is the conftest's 963 # directory path relative to the rootdir. 964 # 965 # For other plugins, the baseid is the empty string (always matches). 966 self.baseid: Final = baseid or "" 967 # Whether the fixture was found from a node or a conftest in the 968 # collection tree. Will be false for fixtures defined in non-conftest 969 # plugins. 970 self.has_location: Final = baseid is not None 971 # The fixture factory function. 972 self.func: Final = func 973 # The name by which the fixture may be requested. 974 self.argname: Final = argname 975 if scope is None: 976 scope = Scope.Function 977 elif callable(scope): 978 scope = _eval_scope_callable(scope, argname, config) 979 if isinstance(scope, str): 980 scope = Scope.from_user( 981 scope, descr=f"Fixture '{func.__name__}'", where=baseid 982 ) 983 self._scope: Final = scope 984 # If the fixture is directly parametrized, the parameter values. 985 self.params: Final = params 986 # If the fixture is directly parametrized, a tuple of explicit IDs to 987 # assign to the parameter values, or a callable to generate an ID given 988 # a parameter value. 989 self.ids: Final = ids 990 # The names requested by the fixtures. 991 self.argnames: Final = getfuncargnames(func, name=argname) 992 # If the fixture was executed, the current value of the fixture. 993 # Can change if the fixture is executed with different parameters. 994 self.cached_result: Optional[_FixtureCachedResult[FixtureValue]] = None 995 self._finalizers: Final[List[Callable[[], object]]] = [] 996 997 @property 998 def scope(self) -> _ScopeName: 999 """Scope string, one of "function", "class", "module", "package", "session".""" 1000 return self._scope.value 1001 1002 def addfinalizer(self, finalizer: Callable[[], object]) -> None: 1003 self._finalizers.append(finalizer) 1004 1005 def finish(self, request: SubRequest) -> None: 1006 exceptions: List[BaseException] = [] 1007 while self._finalizers: 1008 fin = self._finalizers.pop() 1009 try: 1010 fin() 1011 except BaseException as e: 1012 exceptions.append(e) 1013 node = request.node 1014 node.ihook.pytest_fixture_post_finalizer(fixturedef=self, request=request) 1015 # Even if finalization fails, we invalidate the cached fixture 1016 # value and remove all finalizers because they may be bound methods 1017 # which will keep instances alive. 1018 self.cached_result = None 1019 self._finalizers.clear() 1020 if len(exceptions) == 1: 1021 raise exceptions[0] 1022 elif len(exceptions) > 1: 1023 msg = f'errors while tearing down fixture "{self.argname}" of {node}' 1024 raise BaseExceptionGroup(msg, exceptions[::-1]) 1025 1026 def execute(self, request: SubRequest) -> FixtureValue: 1027 """Return the value of this fixture, executing it if not cached.""" 1028 # Ensure that the dependent fixtures requested by this fixture are loaded. 1029 # This needs to be done before checking if we have a cached value, since 1030 # if a dependent fixture has their cache invalidated, e.g. due to 1031 # parametrization, they finalize themselves and fixtures depending on it 1032 # (which will likely include this fixture) setting `self.cached_result = None`. 1033 # See #4871 1034 requested_fixtures_that_should_finalize_us = [] 1035 for argname in self.argnames: 1036 fixturedef = request._get_active_fixturedef(argname) 1037 # Saves requested fixtures in a list so we later can add our finalizer 1038 # to them, ensuring that if a requested fixture gets torn down we get torn 1039 # down first. This is generally handled by SetupState, but still currently 1040 # needed when this fixture is not parametrized but depends on a parametrized 1041 # fixture. 1042 if not isinstance(fixturedef, PseudoFixtureDef): 1043 requested_fixtures_that_should_finalize_us.append(fixturedef) 1044 1045 # Check for (and return) cached value/exception. 1046 my_cache_key = self.cache_key(request) 1047 if self.cached_result is not None: 1048 cache_key = self.cached_result[1] 1049 # note: comparison with `==` can fail (or be expensive) for e.g. 1050 # numpy arrays (#6497). 1051 if my_cache_key is cache_key: 1052 if self.cached_result[2] is not None: 1053 exc = self.cached_result[2] 1054 raise exc 1055 else: 1056 result = self.cached_result[0] 1057 return result 1058 # We have a previous but differently parametrized fixture instance 1059 # so we need to tear it down before creating a new one. 1060 self.finish(request) 1061 assert self.cached_result is None 1062 1063 # Add finalizer to requested fixtures we saved previously. 1064 # We make sure to do this after checking for cached value to avoid 1065 # adding our finalizer multiple times. (#12135) 1066 finalizer = functools.partial(self.finish, request=request) 1067 for parent_fixture in requested_fixtures_that_should_finalize_us: 1068 parent_fixture.addfinalizer(finalizer) 1069 1070 ihook = request.node.ihook 1071 try: 1072 # Setup the fixture, run the code in it, and cache the value 1073 # in self.cached_result 1074 result = ihook.pytest_fixture_setup(fixturedef=self, request=request) 1075 finally: 1076 # schedule our finalizer, even if the setup failed 1077 request.node.addfinalizer(finalizer) 1078 1079 return result 1080 1081 def cache_key(self, request: SubRequest) -> object: 1082 return getattr(request, "param", None) 1083 1084 def __repr__(self) -> str: 1085 return f"<FixtureDef argname={self.argname!r} scope={self.scope!r} baseid={self.baseid!r}>"
A container for a fixture definition.
Note: At this time, only explicitly documented fields and methods are considered public stable API.
938 def __init__( 939 self, 940 config: Config, 941 baseid: Optional[str], 942 argname: str, 943 func: "_FixtureFunc[FixtureValue]", 944 scope: Union[Scope, _ScopeName, Callable[[str, Config], _ScopeName], None], 945 params: Optional[Sequence[object]], 946 ids: Optional[ 947 Union[Tuple[Optional[object], ...], Callable[[Any], Optional[object]]] 948 ] = None, 949 *, 950 _ispytest: bool = False, 951 ) -> None: 952 check_ispytest(_ispytest) 953 # The "base" node ID for the fixture. 954 # 955 # This is a node ID prefix. A fixture is only available to a node (e.g. 956 # a `Function` item) if the fixture's baseid is a nodeid of a parent of 957 # node. 958 # 959 # For a fixture found in a Collector's object (e.g. a `Module`s module, 960 # a `Class`'s class), the baseid is the Collector's nodeid. 961 # 962 # For a fixture found in a conftest plugin, the baseid is the conftest's 963 # directory path relative to the rootdir. 964 # 965 # For other plugins, the baseid is the empty string (always matches). 966 self.baseid: Final = baseid or "" 967 # Whether the fixture was found from a node or a conftest in the 968 # collection tree. Will be false for fixtures defined in non-conftest 969 # plugins. 970 self.has_location: Final = baseid is not None 971 # The fixture factory function. 972 self.func: Final = func 973 # The name by which the fixture may be requested. 974 self.argname: Final = argname 975 if scope is None: 976 scope = Scope.Function 977 elif callable(scope): 978 scope = _eval_scope_callable(scope, argname, config) 979 if isinstance(scope, str): 980 scope = Scope.from_user( 981 scope, descr=f"Fixture '{func.__name__}'", where=baseid 982 ) 983 self._scope: Final = scope 984 # If the fixture is directly parametrized, the parameter values. 985 self.params: Final = params 986 # If the fixture is directly parametrized, a tuple of explicit IDs to 987 # assign to the parameter values, or a callable to generate an ID given 988 # a parameter value. 989 self.ids: Final = ids 990 # The names requested by the fixtures. 991 self.argnames: Final = getfuncargnames(func, name=argname) 992 # If the fixture was executed, the current value of the fixture. 993 # Can change if the fixture is executed with different parameters. 994 self.cached_result: Optional[_FixtureCachedResult[FixtureValue]] = None 995 self._finalizers: Final[List[Callable[[], object]]] = []
997 @property 998 def scope(self) -> _ScopeName: 999 """Scope string, one of "function", "class", "module", "package", "session".""" 1000 return self._scope.value
Scope string, one of "function", "class", "module", "package", "session".
1005 def finish(self, request: SubRequest) -> None: 1006 exceptions: List[BaseException] = [] 1007 while self._finalizers: 1008 fin = self._finalizers.pop() 1009 try: 1010 fin() 1011 except BaseException as e: 1012 exceptions.append(e) 1013 node = request.node 1014 node.ihook.pytest_fixture_post_finalizer(fixturedef=self, request=request) 1015 # Even if finalization fails, we invalidate the cached fixture 1016 # value and remove all finalizers because they may be bound methods 1017 # which will keep instances alive. 1018 self.cached_result = None 1019 self._finalizers.clear() 1020 if len(exceptions) == 1: 1021 raise exceptions[0] 1022 elif len(exceptions) > 1: 1023 msg = f'errors while tearing down fixture "{self.argname}" of {node}' 1024 raise BaseExceptionGroup(msg, exceptions[::-1])
1026 def execute(self, request: SubRequest) -> FixtureValue: 1027 """Return the value of this fixture, executing it if not cached.""" 1028 # Ensure that the dependent fixtures requested by this fixture are loaded. 1029 # This needs to be done before checking if we have a cached value, since 1030 # if a dependent fixture has their cache invalidated, e.g. due to 1031 # parametrization, they finalize themselves and fixtures depending on it 1032 # (which will likely include this fixture) setting `self.cached_result = None`. 1033 # See #4871 1034 requested_fixtures_that_should_finalize_us = [] 1035 for argname in self.argnames: 1036 fixturedef = request._get_active_fixturedef(argname) 1037 # Saves requested fixtures in a list so we later can add our finalizer 1038 # to them, ensuring that if a requested fixture gets torn down we get torn 1039 # down first. This is generally handled by SetupState, but still currently 1040 # needed when this fixture is not parametrized but depends on a parametrized 1041 # fixture. 1042 if not isinstance(fixturedef, PseudoFixtureDef): 1043 requested_fixtures_that_should_finalize_us.append(fixturedef) 1044 1045 # Check for (and return) cached value/exception. 1046 my_cache_key = self.cache_key(request) 1047 if self.cached_result is not None: 1048 cache_key = self.cached_result[1] 1049 # note: comparison with `==` can fail (or be expensive) for e.g. 1050 # numpy arrays (#6497). 1051 if my_cache_key is cache_key: 1052 if self.cached_result[2] is not None: 1053 exc = self.cached_result[2] 1054 raise exc 1055 else: 1056 result = self.cached_result[0] 1057 return result 1058 # We have a previous but differently parametrized fixture instance 1059 # so we need to tear it down before creating a new one. 1060 self.finish(request) 1061 assert self.cached_result is None 1062 1063 # Add finalizer to requested fixtures we saved previously. 1064 # We make sure to do this after checking for cached value to avoid 1065 # adding our finalizer multiple times. (#12135) 1066 finalizer = functools.partial(self.finish, request=request) 1067 for parent_fixture in requested_fixtures_that_should_finalize_us: 1068 parent_fixture.addfinalizer(finalizer) 1069 1070 ihook = request.node.ihook 1071 try: 1072 # Setup the fixture, run the code in it, and cache the value 1073 # in self.cached_result 1074 result = ihook.pytest_fixture_setup(fixturedef=self, request=request) 1075 finally: 1076 # schedule our finalizer, even if the setup failed 1077 request.node.addfinalizer(finalizer) 1078 1079 return result
Return the value of this fixture, executing it if not cached.
777@final 778class FixtureLookupError(LookupError): 779 """Could not return a requested fixture (missing or invalid).""" 780 781 def __init__( 782 self, argname: Optional[str], request: FixtureRequest, msg: Optional[str] = None 783 ) -> None: 784 self.argname = argname 785 self.request = request 786 self.fixturestack = request._get_fixturestack() 787 self.msg = msg 788 789 def formatrepr(self) -> "FixtureLookupErrorRepr": 790 tblines: List[str] = [] 791 addline = tblines.append 792 stack = [self.request._pyfuncitem.obj] 793 stack.extend(map(lambda x: x.func, self.fixturestack)) 794 msg = self.msg 795 if msg is not None: 796 # The last fixture raise an error, let's present 797 # it at the requesting side. 798 stack = stack[:-1] 799 for function in stack: 800 fspath, lineno = getfslineno(function) 801 try: 802 lines, _ = inspect.getsourcelines(get_real_func(function)) 803 except (OSError, IndexError, TypeError): 804 error_msg = "file %s, line %s: source code not available" 805 addline(error_msg % (fspath, lineno + 1)) 806 else: 807 addline(f"file {fspath}, line {lineno + 1}") 808 for i, line in enumerate(lines): 809 line = line.rstrip() 810 addline(" " + line) 811 if line.lstrip().startswith("def"): 812 break 813 814 if msg is None: 815 fm = self.request._fixturemanager 816 available = set() 817 parent = self.request._pyfuncitem.parent 818 assert parent is not None 819 for name, fixturedefs in fm._arg2fixturedefs.items(): 820 faclist = list(fm._matchfactories(fixturedefs, parent)) 821 if faclist: 822 available.add(name) 823 if self.argname in available: 824 msg = ( 825 f" recursive dependency involving fixture '{self.argname}' detected" 826 ) 827 else: 828 msg = f"fixture '{self.argname}' not found" 829 msg += "\n available fixtures: {}".format(", ".join(sorted(available))) 830 msg += "\n use 'pytest --fixtures [testpath]' for help on them." 831 832 return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname)
Could not return a requested fixture (missing or invalid).
789 def formatrepr(self) -> "FixtureLookupErrorRepr": 790 tblines: List[str] = [] 791 addline = tblines.append 792 stack = [self.request._pyfuncitem.obj] 793 stack.extend(map(lambda x: x.func, self.fixturestack)) 794 msg = self.msg 795 if msg is not None: 796 # The last fixture raise an error, let's present 797 # it at the requesting side. 798 stack = stack[:-1] 799 for function in stack: 800 fspath, lineno = getfslineno(function) 801 try: 802 lines, _ = inspect.getsourcelines(get_real_func(function)) 803 except (OSError, IndexError, TypeError): 804 error_msg = "file %s, line %s: source code not available" 805 addline(error_msg % (fspath, lineno + 1)) 806 else: 807 addline(f"file {fspath}, line {lineno + 1}") 808 for i, line in enumerate(lines): 809 line = line.rstrip() 810 addline(" " + line) 811 if line.lstrip().startswith("def"): 812 break 813 814 if msg is None: 815 fm = self.request._fixturemanager 816 available = set() 817 parent = self.request._pyfuncitem.parent 818 assert parent is not None 819 for name, fixturedefs in fm._arg2fixturedefs.items(): 820 faclist = list(fm._matchfactories(fixturedefs, parent)) 821 if faclist: 822 available.add(name) 823 if self.argname in available: 824 msg = ( 825 f" recursive dependency involving fixture '{self.argname}' detected" 826 ) 827 else: 828 msg = f"fixture '{self.argname}' not found" 829 msg += "\n available fixtures: {}".format(", ".join(sorted(available))) 830 msg += "\n use 'pytest --fixtures [testpath]' for help on them." 831 832 return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname)
Inherited Members
- builtins.BaseException
- with_traceback
- add_note
- args
342class FixtureRequest(abc.ABC): 343 """The type of the ``request`` fixture. 344 345 A request object gives access to the requesting test context and has a 346 ``param`` attribute in case the fixture is parametrized. 347 """ 348 349 def __init__( 350 self, 351 pyfuncitem: "Function", 352 fixturename: Optional[str], 353 arg2fixturedefs: Dict[str, Sequence["FixtureDef[Any]"]], 354 fixture_defs: Dict[str, "FixtureDef[Any]"], 355 *, 356 _ispytest: bool = False, 357 ) -> None: 358 check_ispytest(_ispytest) 359 #: Fixture for which this request is being performed. 360 self.fixturename: Final = fixturename 361 self._pyfuncitem: Final = pyfuncitem 362 # The FixtureDefs for each fixture name requested by this item. 363 # Starts from the statically-known fixturedefs resolved during 364 # collection. Dynamically requested fixtures (using 365 # `request.getfixturevalue("foo")`) are added dynamically. 366 self._arg2fixturedefs: Final = arg2fixturedefs 367 # The evaluated argnames so far, mapping to the FixtureDef they resolved 368 # to. 369 self._fixture_defs: Final = fixture_defs 370 # Notes on the type of `param`: 371 # -`request.param` is only defined in parametrized fixtures, and will raise 372 # AttributeError otherwise. Python typing has no notion of "undefined", so 373 # this cannot be reflected in the type. 374 # - Technically `param` is only (possibly) defined on SubRequest, not 375 # FixtureRequest, but the typing of that is still in flux so this cheats. 376 # - In the future we might consider using a generic for the param type, but 377 # for now just using Any. 378 self.param: Any 379 380 @property 381 def _fixturemanager(self) -> "FixtureManager": 382 return self._pyfuncitem.session._fixturemanager 383 384 @property 385 @abc.abstractmethod 386 def _scope(self) -> Scope: 387 raise NotImplementedError() 388 389 @property 390 def scope(self) -> _ScopeName: 391 """Scope string, one of "function", "class", "module", "package", "session".""" 392 return self._scope.value 393 394 @abc.abstractmethod 395 def _check_scope( 396 self, 397 requested_fixturedef: Union["FixtureDef[object]", PseudoFixtureDef[object]], 398 requested_scope: Scope, 399 ) -> None: 400 raise NotImplementedError() 401 402 @property 403 def fixturenames(self) -> List[str]: 404 """Names of all active fixtures in this request.""" 405 result = list(self._pyfuncitem.fixturenames) 406 result.extend(set(self._fixture_defs).difference(result)) 407 return result 408 409 @property 410 @abc.abstractmethod 411 def node(self): 412 """Underlying collection node (depends on current request scope).""" 413 raise NotImplementedError() 414 415 @property 416 def config(self) -> Config: 417 """The pytest config object associated with this request.""" 418 return self._pyfuncitem.config 419 420 @property 421 def function(self): 422 """Test function object if the request has a per-function scope.""" 423 if self.scope != "function": 424 raise AttributeError( 425 f"function not available in {self.scope}-scoped context" 426 ) 427 return self._pyfuncitem.obj 428 429 @property 430 def cls(self): 431 """Class (can be None) where the test function was collected.""" 432 if self.scope not in ("class", "function"): 433 raise AttributeError(f"cls not available in {self.scope}-scoped context") 434 clscol = self._pyfuncitem.getparent(_pytest.python.Class) 435 if clscol: 436 return clscol.obj 437 438 @property 439 def instance(self): 440 """Instance (can be None) on which test function was collected.""" 441 if self.scope != "function": 442 return None 443 return getattr(self._pyfuncitem, "instance", None) 444 445 @property 446 def module(self): 447 """Python module object where the test function was collected.""" 448 if self.scope not in ("function", "class", "module"): 449 raise AttributeError(f"module not available in {self.scope}-scoped context") 450 mod = self._pyfuncitem.getparent(_pytest.python.Module) 451 assert mod is not None 452 return mod.obj 453 454 @property 455 def path(self) -> Path: 456 """Path where the test function was collected.""" 457 if self.scope not in ("function", "class", "module", "package"): 458 raise AttributeError(f"path not available in {self.scope}-scoped context") 459 return self._pyfuncitem.path 460 461 @property 462 def keywords(self) -> MutableMapping[str, Any]: 463 """Keywords/markers dictionary for the underlying node.""" 464 node: nodes.Node = self.node 465 return node.keywords 466 467 @property 468 def session(self) -> "Session": 469 """Pytest session object.""" 470 return self._pyfuncitem.session 471 472 @abc.abstractmethod 473 def addfinalizer(self, finalizer: Callable[[], object]) -> None: 474 """Add finalizer/teardown function to be called without arguments after 475 the last test within the requesting test context finished execution.""" 476 raise NotImplementedError() 477 478 def applymarker(self, marker: Union[str, MarkDecorator]) -> None: 479 """Apply a marker to a single test function invocation. 480 481 This method is useful if you don't want to have a keyword/marker 482 on all function invocations. 483 484 :param marker: 485 An object created by a call to ``pytest.mark.NAME(...)``. 486 """ 487 self.node.add_marker(marker) 488 489 def raiseerror(self, msg: Optional[str]) -> NoReturn: 490 """Raise a FixtureLookupError exception. 491 492 :param msg: 493 An optional custom error message. 494 """ 495 raise FixtureLookupError(None, self, msg) 496 497 def getfixturevalue(self, argname: str) -> Any: 498 """Dynamically run a named fixture function. 499 500 Declaring fixtures via function argument is recommended where possible. 501 But if you can only decide whether to use another fixture at test 502 setup time, you may use this function to retrieve it inside a fixture 503 or test function body. 504 505 This method can be used during the test setup phase or the test run 506 phase, but during the test teardown phase a fixture's value may not 507 be available. 508 509 :param argname: 510 The fixture name. 511 :raises pytest.FixtureLookupError: 512 If the given fixture could not be found. 513 """ 514 # Note that in addition to the use case described in the docstring, 515 # getfixturevalue() is also called by pytest itself during item and fixture 516 # setup to evaluate the fixtures that are requested statically 517 # (using function parameters, autouse, etc). 518 519 fixturedef = self._get_active_fixturedef(argname) 520 assert fixturedef.cached_result is not None, ( 521 f'The fixture value for "{argname}" is not available. ' 522 "This can happen when the fixture has already been torn down." 523 ) 524 return fixturedef.cached_result[0] 525 526 def _iter_chain(self) -> Iterator["SubRequest"]: 527 """Yield all SubRequests in the chain, from self up. 528 529 Note: does *not* yield the TopRequest. 530 """ 531 current = self 532 while isinstance(current, SubRequest): 533 yield current 534 current = current._parent_request 535 536 def _get_active_fixturedef( 537 self, argname: str 538 ) -> Union["FixtureDef[object]", PseudoFixtureDef[object]]: 539 if argname == "request": 540 cached_result = (self, [0], None) 541 return PseudoFixtureDef(cached_result, Scope.Function) 542 543 # If we already finished computing a fixture by this name in this item, 544 # return it. 545 fixturedef = self._fixture_defs.get(argname) 546 if fixturedef is not None: 547 self._check_scope(fixturedef, fixturedef._scope) 548 return fixturedef 549 550 # Find the appropriate fixturedef. 551 fixturedefs = self._arg2fixturedefs.get(argname, None) 552 if fixturedefs is None: 553 # We arrive here because of a dynamic call to 554 # getfixturevalue(argname) which was naturally 555 # not known at parsing/collection time. 556 fixturedefs = self._fixturemanager.getfixturedefs(argname, self._pyfuncitem) 557 if fixturedefs is not None: 558 self._arg2fixturedefs[argname] = fixturedefs 559 # No fixtures defined with this name. 560 if fixturedefs is None: 561 raise FixtureLookupError(argname, self) 562 # The are no fixtures with this name applicable for the function. 563 if not fixturedefs: 564 raise FixtureLookupError(argname, self) 565 # A fixture may override another fixture with the same name, e.g. a 566 # fixture in a module can override a fixture in a conftest, a fixture in 567 # a class can override a fixture in the module, and so on. 568 # An overriding fixture can request its own name (possibly indirectly); 569 # in this case it gets the value of the fixture it overrides, one level 570 # up. 571 # Check how many `argname`s deep we are, and take the next one. 572 # `fixturedefs` is sorted from furthest to closest, so use negative 573 # indexing to go in reverse. 574 index = -1 575 for request in self._iter_chain(): 576 if request.fixturename == argname: 577 index -= 1 578 # If already consumed all of the available levels, fail. 579 if -index > len(fixturedefs): 580 raise FixtureLookupError(argname, self) 581 fixturedef = fixturedefs[index] 582 583 # Prepare a SubRequest object for calling the fixture. 584 try: 585 callspec = self._pyfuncitem.callspec 586 except AttributeError: 587 callspec = None 588 if callspec is not None and argname in callspec.params: 589 param = callspec.params[argname] 590 param_index = callspec.indices[argname] 591 # The parametrize invocation scope overrides the fixture's scope. 592 scope = callspec._arg2scope[argname] 593 else: 594 param = NOTSET 595 param_index = 0 596 scope = fixturedef._scope 597 self._check_fixturedef_without_param(fixturedef) 598 self._check_scope(fixturedef, scope) 599 subrequest = SubRequest( 600 self, scope, param, param_index, fixturedef, _ispytest=True 601 ) 602 603 # Make sure the fixture value is cached, running it if it isn't 604 fixturedef.execute(request=subrequest) 605 606 self._fixture_defs[argname] = fixturedef 607 return fixturedef 608 609 def _check_fixturedef_without_param(self, fixturedef: "FixtureDef[object]") -> None: 610 """Check that this request is allowed to execute this fixturedef without 611 a param.""" 612 funcitem = self._pyfuncitem 613 has_params = fixturedef.params is not None 614 fixtures_not_supported = getattr(funcitem, "nofuncargs", False) 615 if has_params and fixtures_not_supported: 616 msg = ( 617 f"{funcitem.name} does not support fixtures, maybe unittest.TestCase subclass?\n" 618 f"Node id: {funcitem.nodeid}\n" 619 f"Function type: {type(funcitem).__name__}" 620 ) 621 fail(msg, pytrace=False) 622 if has_params: 623 frame = inspect.stack()[3] 624 frameinfo = inspect.getframeinfo(frame[0]) 625 source_path = absolutepath(frameinfo.filename) 626 source_lineno = frameinfo.lineno 627 try: 628 source_path_str = str(source_path.relative_to(funcitem.config.rootpath)) 629 except ValueError: 630 source_path_str = str(source_path) 631 location = getlocation(fixturedef.func, funcitem.config.rootpath) 632 msg = ( 633 "The requested fixture has no parameter defined for test:\n" 634 f" {funcitem.nodeid}\n\n" 635 f"Requested fixture '{fixturedef.argname}' defined in:\n" 636 f"{location}\n\n" 637 f"Requested here:\n" 638 f"{source_path_str}:{source_lineno}" 639 ) 640 fail(msg, pytrace=False) 641 642 def _get_fixturestack(self) -> List["FixtureDef[Any]"]: 643 values = [request._fixturedef for request in self._iter_chain()] 644 values.reverse() 645 return values
The type of the request
fixture.
A request object gives access to the requesting test context and has a
param
attribute in case the fixture is parametrized.
389 @property 390 def scope(self) -> _ScopeName: 391 """Scope string, one of "function", "class", "module", "package", "session".""" 392 return self._scope.value
Scope string, one of "function", "class", "module", "package", "session".
402 @property 403 def fixturenames(self) -> List[str]: 404 """Names of all active fixtures in this request.""" 405 result = list(self._pyfuncitem.fixturenames) 406 result.extend(set(self._fixture_defs).difference(result)) 407 return result
Names of all active fixtures in this request.
409 @property 410 @abc.abstractmethod 411 def node(self): 412 """Underlying collection node (depends on current request scope).""" 413 raise NotImplementedError()
Underlying collection node (depends on current request scope).
415 @property 416 def config(self) -> Config: 417 """The pytest config object associated with this request.""" 418 return self._pyfuncitem.config
The pytest config object associated with this request.
420 @property 421 def function(self): 422 """Test function object if the request has a per-function scope.""" 423 if self.scope != "function": 424 raise AttributeError( 425 f"function not available in {self.scope}-scoped context" 426 ) 427 return self._pyfuncitem.obj
Test function object if the request has a per-function scope.
429 @property 430 def cls(self): 431 """Class (can be None) where the test function was collected.""" 432 if self.scope not in ("class", "function"): 433 raise AttributeError(f"cls not available in {self.scope}-scoped context") 434 clscol = self._pyfuncitem.getparent(_pytest.python.Class) 435 if clscol: 436 return clscol.obj
Class (can be None) where the test function was collected.
438 @property 439 def instance(self): 440 """Instance (can be None) on which test function was collected.""" 441 if self.scope != "function": 442 return None 443 return getattr(self._pyfuncitem, "instance", None)
Instance (can be None) on which test function was collected.
445 @property 446 def module(self): 447 """Python module object where the test function was collected.""" 448 if self.scope not in ("function", "class", "module"): 449 raise AttributeError(f"module not available in {self.scope}-scoped context") 450 mod = self._pyfuncitem.getparent(_pytest.python.Module) 451 assert mod is not None 452 return mod.obj
Python module object where the test function was collected.
454 @property 455 def path(self) -> Path: 456 """Path where the test function was collected.""" 457 if self.scope not in ("function", "class", "module", "package"): 458 raise AttributeError(f"path not available in {self.scope}-scoped context") 459 return self._pyfuncitem.path
Path where the test function was collected.
461 @property 462 def keywords(self) -> MutableMapping[str, Any]: 463 """Keywords/markers dictionary for the underlying node.""" 464 node: nodes.Node = self.node 465 return node.keywords
Keywords/markers dictionary for the underlying node.
467 @property 468 def session(self) -> "Session": 469 """Pytest session object.""" 470 return self._pyfuncitem.session
Pytest session object.
472 @abc.abstractmethod 473 def addfinalizer(self, finalizer: Callable[[], object]) -> None: 474 """Add finalizer/teardown function to be called without arguments after 475 the last test within the requesting test context finished execution.""" 476 raise NotImplementedError()
Add finalizer/teardown function to be called without arguments after the last test within the requesting test context finished execution.
478 def applymarker(self, marker: Union[str, MarkDecorator]) -> None: 479 """Apply a marker to a single test function invocation. 480 481 This method is useful if you don't want to have a keyword/marker 482 on all function invocations. 483 484 :param marker: 485 An object created by a call to ``pytest.mark.NAME(...)``. 486 """ 487 self.node.add_marker(marker)
Apply a marker to a single test function invocation.
This method is useful if you don't want to have a keyword/marker on all function invocations.
Parameters
- marker:
An object created by a call to
pytest.mark.NAME(...)
.
489 def raiseerror(self, msg: Optional[str]) -> NoReturn: 490 """Raise a FixtureLookupError exception. 491 492 :param msg: 493 An optional custom error message. 494 """ 495 raise FixtureLookupError(None, self, msg)
Raise a FixtureLookupError exception.
Parameters
- msg: An optional custom error message.
497 def getfixturevalue(self, argname: str) -> Any: 498 """Dynamically run a named fixture function. 499 500 Declaring fixtures via function argument is recommended where possible. 501 But if you can only decide whether to use another fixture at test 502 setup time, you may use this function to retrieve it inside a fixture 503 or test function body. 504 505 This method can be used during the test setup phase or the test run 506 phase, but during the test teardown phase a fixture's value may not 507 be available. 508 509 :param argname: 510 The fixture name. 511 :raises pytest.FixtureLookupError: 512 If the given fixture could not be found. 513 """ 514 # Note that in addition to the use case described in the docstring, 515 # getfixturevalue() is also called by pytest itself during item and fixture 516 # setup to evaluate the fixtures that are requested statically 517 # (using function parameters, autouse, etc). 518 519 fixturedef = self._get_active_fixturedef(argname) 520 assert fixturedef.cached_result is not None, ( 521 f'The fixture value for "{argname}" is not available. ' 522 "This can happen when the fixture has already been torn down." 523 ) 524 return fixturedef.cached_result[0]
Dynamically run a named fixture function.
Declaring fixtures via function argument is recommended where possible. But if you can only decide whether to use another fixture at test setup time, you may use this function to retrieve it inside a fixture or test function body.
This method can be used during the test setup phase or the test run phase, but during the test teardown phase a fixture's value may not be available.
Parameters
- argname: The fixture name.
Raises
- pytest.FixtureLookupError: If the given fixture could not be found.
11def freeze_includes() -> List[str]: 12 """Return a list of module names used by pytest that should be 13 included by cx_freeze.""" 14 import _pytest 15 16 result = list(_iter_all_modules(_pytest)) 17 return result
Return a list of module names used by pytest that should be included by cx_freeze.
1495class Function(PyobjMixin, nodes.Item): 1496 """Item responsible for setting up and executing a Python test function. 1497 1498 :param name: 1499 The full function name, including any decorations like those 1500 added by parametrization (``my_func[my_param]``). 1501 :param parent: 1502 The parent Node. 1503 :param config: 1504 The pytest Config object. 1505 :param callspec: 1506 If given, this function has been parametrized and the callspec contains 1507 meta information about the parametrization. 1508 :param callobj: 1509 If given, the object which will be called when the Function is invoked, 1510 otherwise the callobj will be obtained from ``parent`` using ``originalname``. 1511 :param keywords: 1512 Keywords bound to the function object for "-k" matching. 1513 :param session: 1514 The pytest Session object. 1515 :param fixtureinfo: 1516 Fixture information already resolved at this fixture node.. 1517 :param originalname: 1518 The attribute name to use for accessing the underlying function object. 1519 Defaults to ``name``. Set this if name is different from the original name, 1520 for example when it contains decorations like those added by parametrization 1521 (``my_func[my_param]``). 1522 """ 1523 1524 # Disable since functions handle it themselves. 1525 _ALLOW_MARKERS = False 1526 1527 def __init__( 1528 self, 1529 name: str, 1530 parent, 1531 config: Optional[Config] = None, 1532 callspec: Optional[CallSpec2] = None, 1533 callobj=NOTSET, 1534 keywords: Optional[Mapping[str, Any]] = None, 1535 session: Optional[Session] = None, 1536 fixtureinfo: Optional[FuncFixtureInfo] = None, 1537 originalname: Optional[str] = None, 1538 ) -> None: 1539 super().__init__(name, parent, config=config, session=session) 1540 1541 if callobj is not NOTSET: 1542 self._obj = callobj 1543 self._instance = getattr(callobj, "__self__", None) 1544 1545 #: Original function name, without any decorations (for example 1546 #: parametrization adds a ``"[...]"`` suffix to function names), used to access 1547 #: the underlying function object from ``parent`` (in case ``callobj`` is not given 1548 #: explicitly). 1549 #: 1550 #: .. versionadded:: 3.0 1551 self.originalname = originalname or name 1552 1553 # Note: when FunctionDefinition is introduced, we should change ``originalname`` 1554 # to a readonly property that returns FunctionDefinition.name. 1555 1556 self.own_markers.extend(get_unpacked_marks(self.obj)) 1557 if callspec: 1558 self.callspec = callspec 1559 self.own_markers.extend(callspec.marks) 1560 1561 # todo: this is a hell of a hack 1562 # https://github.com/pytest-dev/pytest/issues/4569 1563 # Note: the order of the updates is important here; indicates what 1564 # takes priority (ctor argument over function attributes over markers). 1565 # Take own_markers only; NodeKeywords handles parent traversal on its own. 1566 self.keywords.update((mark.name, mark) for mark in self.own_markers) 1567 self.keywords.update(self.obj.__dict__) 1568 if keywords: 1569 self.keywords.update(keywords) 1570 1571 if fixtureinfo is None: 1572 fm = self.session._fixturemanager 1573 fixtureinfo = fm.getfixtureinfo(self, self.obj, self.cls) 1574 self._fixtureinfo: FuncFixtureInfo = fixtureinfo 1575 self.fixturenames = fixtureinfo.names_closure 1576 self._initrequest() 1577 1578 # todo: determine sound type limitations 1579 @classmethod 1580 def from_parent(cls, parent, **kw) -> "Self": 1581 """The public constructor.""" 1582 return super().from_parent(parent=parent, **kw) 1583 1584 def _initrequest(self) -> None: 1585 self.funcargs: Dict[str, object] = {} 1586 self._request = fixtures.TopRequest(self, _ispytest=True) 1587 1588 @property 1589 def function(self): 1590 """Underlying python 'function' object.""" 1591 return getimfunc(self.obj) 1592 1593 @property 1594 def instance(self): 1595 try: 1596 return self._instance 1597 except AttributeError: 1598 if isinstance(self.parent, Class): 1599 # Each Function gets a fresh class instance. 1600 self._instance = self._getinstance() 1601 else: 1602 self._instance = None 1603 return self._instance 1604 1605 def _getinstance(self): 1606 if isinstance(self.parent, Class): 1607 # Each Function gets a fresh class instance. 1608 return self.parent.newinstance() 1609 else: 1610 return None 1611 1612 def _getobj(self): 1613 instance = self.instance 1614 if instance is not None: 1615 parent_obj = instance 1616 else: 1617 assert self.parent is not None 1618 parent_obj = self.parent.obj # type: ignore[attr-defined] 1619 return getattr(parent_obj, self.originalname) 1620 1621 @property 1622 def _pyfuncitem(self): 1623 """(compatonly) for code expecting pytest-2.2 style request objects.""" 1624 return self 1625 1626 def runtest(self) -> None: 1627 """Execute the underlying test function.""" 1628 self.ihook.pytest_pyfunc_call(pyfuncitem=self) 1629 1630 def setup(self) -> None: 1631 self._request._fillfixtures() 1632 1633 def _traceback_filter(self, excinfo: ExceptionInfo[BaseException]) -> Traceback: 1634 if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False): 1635 code = _pytest._code.Code.from_function(get_real_func(self.obj)) 1636 path, firstlineno = code.path, code.firstlineno 1637 traceback = excinfo.traceback 1638 ntraceback = traceback.cut(path=path, firstlineno=firstlineno) 1639 if ntraceback == traceback: 1640 ntraceback = ntraceback.cut(path=path) 1641 if ntraceback == traceback: 1642 ntraceback = ntraceback.filter(filter_traceback) 1643 if not ntraceback: 1644 ntraceback = traceback 1645 ntraceback = ntraceback.filter(excinfo) 1646 1647 # issue364: mark all but first and last frames to 1648 # only show a single-line message for each frame. 1649 if self.config.getoption("tbstyle", "auto") == "auto": 1650 if len(ntraceback) > 2: 1651 ntraceback = Traceback( 1652 ( 1653 ntraceback[0], 1654 *(t.with_repr_style("short") for t in ntraceback[1:-1]), 1655 ntraceback[-1], 1656 ) 1657 ) 1658 1659 return ntraceback 1660 return excinfo.traceback 1661 1662 # TODO: Type ignored -- breaks Liskov Substitution. 1663 def repr_failure( # type: ignore[override] 1664 self, 1665 excinfo: ExceptionInfo[BaseException], 1666 ) -> Union[str, TerminalRepr]: 1667 style = self.config.getoption("tbstyle", "auto") 1668 if style == "auto": 1669 style = "long" 1670 return self._repr_failure_py(excinfo, style=style)
Item responsible for setting up and executing a Python test function.
Parameters
- name:
The full function name, including any decorations like those
added by parametrization (
my_func[my_param]
). - parent: The parent Node.
- config: The pytest Config object.
- callspec: If given, this function has been parametrized and the callspec contains meta information about the parametrization.
- callobj:
If given, the object which will be called when the Function is invoked,
otherwise the callobj will be obtained from
parent
usingoriginalname
. - keywords: Keywords bound to the function object for "-k" matching.
- session: The pytest Session object.
- fixtureinfo: Fixture information already resolved at this fixture node..
- originalname:
The attribute name to use for accessing the underlying function object.
Defaults to
name
. Set this if name is different from the original name, for example when it contains decorations like those added by parametrization (my_func[my_param]
).
1527 def __init__( 1528 self, 1529 name: str, 1530 parent, 1531 config: Optional[Config] = None, 1532 callspec: Optional[CallSpec2] = None, 1533 callobj=NOTSET, 1534 keywords: Optional[Mapping[str, Any]] = None, 1535 session: Optional[Session] = None, 1536 fixtureinfo: Optional[FuncFixtureInfo] = None, 1537 originalname: Optional[str] = None, 1538 ) -> None: 1539 super().__init__(name, parent, config=config, session=session) 1540 1541 if callobj is not NOTSET: 1542 self._obj = callobj 1543 self._instance = getattr(callobj, "__self__", None) 1544 1545 #: Original function name, without any decorations (for example 1546 #: parametrization adds a ``"[...]"`` suffix to function names), used to access 1547 #: the underlying function object from ``parent`` (in case ``callobj`` is not given 1548 #: explicitly). 1549 #: 1550 #: .. versionadded:: 3.0 1551 self.originalname = originalname or name 1552 1553 # Note: when FunctionDefinition is introduced, we should change ``originalname`` 1554 # to a readonly property that returns FunctionDefinition.name. 1555 1556 self.own_markers.extend(get_unpacked_marks(self.obj)) 1557 if callspec: 1558 self.callspec = callspec 1559 self.own_markers.extend(callspec.marks) 1560 1561 # todo: this is a hell of a hack 1562 # https://github.com/pytest-dev/pytest/issues/4569 1563 # Note: the order of the updates is important here; indicates what 1564 # takes priority (ctor argument over function attributes over markers). 1565 # Take own_markers only; NodeKeywords handles parent traversal on its own. 1566 self.keywords.update((mark.name, mark) for mark in self.own_markers) 1567 self.keywords.update(self.obj.__dict__) 1568 if keywords: 1569 self.keywords.update(keywords) 1570 1571 if fixtureinfo is None: 1572 fm = self.session._fixturemanager 1573 fixtureinfo = fm.getfixtureinfo(self, self.obj, self.cls) 1574 self._fixtureinfo: FuncFixtureInfo = fixtureinfo 1575 self.fixturenames = fixtureinfo.names_closure 1576 self._initrequest()
1579 @classmethod 1580 def from_parent(cls, parent, **kw) -> "Self": 1581 """The public constructor.""" 1582 return super().from_parent(parent=parent, **kw)
The public constructor.
1588 @property 1589 def function(self): 1590 """Underlying python 'function' object.""" 1591 return getimfunc(self.obj)
Underlying python 'function' object.
1593 @property 1594 def instance(self): 1595 try: 1596 return self._instance 1597 except AttributeError: 1598 if isinstance(self.parent, Class): 1599 # Each Function gets a fresh class instance. 1600 self._instance = self._getinstance() 1601 else: 1602 self._instance = None 1603 return self._instance
Python instance object the function is bound to.
Returns None if not a test method, e.g. for a standalone test function, a class or a module.
1626 def runtest(self) -> None: 1627 """Execute the underlying test function.""" 1628 self.ihook.pytest_pyfunc_call(pyfuncitem=self)
Execute the underlying test function.
1663 def repr_failure( # type: ignore[override] 1664 self, 1665 excinfo: ExceptionInfo[BaseException], 1666 ) -> Union[str, TerminalRepr]: 1667 style = self.config.getoption("tbstyle", "auto") 1668 if style == "auto": 1669 style = "long" 1670 return self._repr_failure_py(excinfo, style=style)
Return a representation of a collection or test failure.
seealso :ref:non-python tests
.
Parameters
- excinfo: Exception information for the failure.
Inherited Members
- _pytest.python.PyobjMixin
- module
- cls
- obj
- getmodpath
- reportinfo
- _pytest.nodes.Node
- fspath
- name
- parent
- path
- keywords
- own_markers
- extra_keyword_matches
- stash
- ihook
- warn
- nodeid
- teardown
- iter_parents
- listchain
- add_marker
- iter_markers
- iter_markers_with_node
- get_closest_marker
- listextrakeywords
- listnames
- addfinalizer
- getparent
- config
- session
253@final 254class HookRecorder: 255 """Record all hooks called in a plugin manager. 256 257 Hook recorders are created by :class:`Pytester`. 258 259 This wraps all the hook calls in the plugin manager, recording each call 260 before propagating the normal calls. 261 """ 262 263 def __init__( 264 self, pluginmanager: PytestPluginManager, *, _ispytest: bool = False 265 ) -> None: 266 check_ispytest(_ispytest) 267 268 self._pluginmanager = pluginmanager 269 self.calls: List[RecordedHookCall] = [] 270 self.ret: Optional[Union[int, ExitCode]] = None 271 272 def before(hook_name: str, hook_impls, kwargs) -> None: 273 self.calls.append(RecordedHookCall(hook_name, kwargs)) 274 275 def after(outcome, hook_name: str, hook_impls, kwargs) -> None: 276 pass 277 278 self._undo_wrapping = pluginmanager.add_hookcall_monitoring(before, after) 279 280 def finish_recording(self) -> None: 281 self._undo_wrapping() 282 283 def getcalls(self, names: Union[str, Iterable[str]]) -> List[RecordedHookCall]: 284 """Get all recorded calls to hooks with the given names (or name).""" 285 if isinstance(names, str): 286 names = names.split() 287 return [call for call in self.calls if call._name in names] 288 289 def assert_contains(self, entries: Sequence[Tuple[str, str]]) -> None: 290 __tracebackhide__ = True 291 i = 0 292 entries = list(entries) 293 backlocals = sys._getframe(1).f_locals 294 while entries: 295 name, check = entries.pop(0) 296 for ind, call in enumerate(self.calls[i:]): 297 if call._name == name: 298 print("NAMEMATCH", name, call) 299 if eval(check, backlocals, call.__dict__): 300 print("CHECKERMATCH", repr(check), "->", call) 301 else: 302 print("NOCHECKERMATCH", repr(check), "-", call) 303 continue 304 i += ind + 1 305 break 306 print("NONAMEMATCH", name, "with", call) 307 else: 308 fail(f"could not find {name!r} check {check!r}") 309 310 def popcall(self, name: str) -> RecordedHookCall: 311 __tracebackhide__ = True 312 for i, call in enumerate(self.calls): 313 if call._name == name: 314 del self.calls[i] 315 return call 316 lines = [f"could not find call {name!r}, in:"] 317 lines.extend([" %s" % x for x in self.calls]) 318 fail("\n".join(lines)) 319 320 def getcall(self, name: str) -> RecordedHookCall: 321 values = self.getcalls(name) 322 assert len(values) == 1, (name, values) 323 return values[0] 324 325 # functionality for test reports 326 327 @overload 328 def getreports( 329 self, 330 names: "Literal['pytest_collectreport']", 331 ) -> Sequence[CollectReport]: ... 332 333 @overload 334 def getreports( 335 self, 336 names: "Literal['pytest_runtest_logreport']", 337 ) -> Sequence[TestReport]: ... 338 339 @overload 340 def getreports( 341 self, 342 names: Union[str, Iterable[str]] = ( 343 "pytest_collectreport", 344 "pytest_runtest_logreport", 345 ), 346 ) -> Sequence[Union[CollectReport, TestReport]]: ... 347 348 def getreports( 349 self, 350 names: Union[str, Iterable[str]] = ( 351 "pytest_collectreport", 352 "pytest_runtest_logreport", 353 ), 354 ) -> Sequence[Union[CollectReport, TestReport]]: 355 return [x.report for x in self.getcalls(names)] 356 357 def matchreport( 358 self, 359 inamepart: str = "", 360 names: Union[str, Iterable[str]] = ( 361 "pytest_runtest_logreport", 362 "pytest_collectreport", 363 ), 364 when: Optional[str] = None, 365 ) -> Union[CollectReport, TestReport]: 366 """Return a testreport whose dotted import path matches.""" 367 values = [] 368 for rep in self.getreports(names=names): 369 if not when and rep.when != "call" and rep.passed: 370 # setup/teardown passing reports - let's ignore those 371 continue 372 if when and rep.when != when: 373 continue 374 if not inamepart or inamepart in rep.nodeid.split("::"): 375 values.append(rep) 376 if not values: 377 raise ValueError( 378 f"could not find test report matching {inamepart!r}: " 379 "no test reports at all!" 380 ) 381 if len(values) > 1: 382 raise ValueError( 383 f"found 2 or more testreports matching {inamepart!r}: {values}" 384 ) 385 return values[0] 386 387 @overload 388 def getfailures( 389 self, 390 names: "Literal['pytest_collectreport']", 391 ) -> Sequence[CollectReport]: ... 392 393 @overload 394 def getfailures( 395 self, 396 names: "Literal['pytest_runtest_logreport']", 397 ) -> Sequence[TestReport]: ... 398 399 @overload 400 def getfailures( 401 self, 402 names: Union[str, Iterable[str]] = ( 403 "pytest_collectreport", 404 "pytest_runtest_logreport", 405 ), 406 ) -> Sequence[Union[CollectReport, TestReport]]: ... 407 408 def getfailures( 409 self, 410 names: Union[str, Iterable[str]] = ( 411 "pytest_collectreport", 412 "pytest_runtest_logreport", 413 ), 414 ) -> Sequence[Union[CollectReport, TestReport]]: 415 return [rep for rep in self.getreports(names) if rep.failed] 416 417 def getfailedcollections(self) -> Sequence[CollectReport]: 418 return self.getfailures("pytest_collectreport") 419 420 def listoutcomes( 421 self, 422 ) -> Tuple[ 423 Sequence[TestReport], 424 Sequence[Union[CollectReport, TestReport]], 425 Sequence[Union[CollectReport, TestReport]], 426 ]: 427 passed = [] 428 skipped = [] 429 failed = [] 430 for rep in self.getreports( 431 ("pytest_collectreport", "pytest_runtest_logreport") 432 ): 433 if rep.passed: 434 if rep.when == "call": 435 assert isinstance(rep, TestReport) 436 passed.append(rep) 437 elif rep.skipped: 438 skipped.append(rep) 439 else: 440 assert rep.failed, f"Unexpected outcome: {rep!r}" 441 failed.append(rep) 442 return passed, skipped, failed 443 444 def countoutcomes(self) -> List[int]: 445 return [len(x) for x in self.listoutcomes()] 446 447 def assertoutcome(self, passed: int = 0, skipped: int = 0, failed: int = 0) -> None: 448 __tracebackhide__ = True 449 from _pytest.pytester_assertions import assertoutcome 450 451 outcomes = self.listoutcomes() 452 assertoutcome( 453 outcomes, 454 passed=passed, 455 skipped=skipped, 456 failed=failed, 457 ) 458 459 def clear(self) -> None: 460 self.calls[:] = []
Record all hooks called in a plugin manager.
Hook recorders are created by Pytester
.
This wraps all the hook calls in the plugin manager, recording each call before propagating the normal calls.
263 def __init__( 264 self, pluginmanager: PytestPluginManager, *, _ispytest: bool = False 265 ) -> None: 266 check_ispytest(_ispytest) 267 268 self._pluginmanager = pluginmanager 269 self.calls: List[RecordedHookCall] = [] 270 self.ret: Optional[Union[int, ExitCode]] = None 271 272 def before(hook_name: str, hook_impls, kwargs) -> None: 273 self.calls.append(RecordedHookCall(hook_name, kwargs)) 274 275 def after(outcome, hook_name: str, hook_impls, kwargs) -> None: 276 pass 277 278 self._undo_wrapping = pluginmanager.add_hookcall_monitoring(before, after)
283 def getcalls(self, names: Union[str, Iterable[str]]) -> List[RecordedHookCall]: 284 """Get all recorded calls to hooks with the given names (or name).""" 285 if isinstance(names, str): 286 names = names.split() 287 return [call for call in self.calls if call._name in names]
Get all recorded calls to hooks with the given names (or name).
289 def assert_contains(self, entries: Sequence[Tuple[str, str]]) -> None: 290 __tracebackhide__ = True 291 i = 0 292 entries = list(entries) 293 backlocals = sys._getframe(1).f_locals 294 while entries: 295 name, check = entries.pop(0) 296 for ind, call in enumerate(self.calls[i:]): 297 if call._name == name: 298 print("NAMEMATCH", name, call) 299 if eval(check, backlocals, call.__dict__): 300 print("CHECKERMATCH", repr(check), "->", call) 301 else: 302 print("NOCHECKERMATCH", repr(check), "-", call) 303 continue 304 i += ind + 1 305 break 306 print("NONAMEMATCH", name, "with", call) 307 else: 308 fail(f"could not find {name!r} check {check!r}")
310 def popcall(self, name: str) -> RecordedHookCall: 311 __tracebackhide__ = True 312 for i, call in enumerate(self.calls): 313 if call._name == name: 314 del self.calls[i] 315 return call 316 lines = [f"could not find call {name!r}, in:"] 317 lines.extend([" %s" % x for x in self.calls]) 318 fail("\n".join(lines))
357 def matchreport( 358 self, 359 inamepart: str = "", 360 names: Union[str, Iterable[str]] = ( 361 "pytest_runtest_logreport", 362 "pytest_collectreport", 363 ), 364 when: Optional[str] = None, 365 ) -> Union[CollectReport, TestReport]: 366 """Return a testreport whose dotted import path matches.""" 367 values = [] 368 for rep in self.getreports(names=names): 369 if not when and rep.when != "call" and rep.passed: 370 # setup/teardown passing reports - let's ignore those 371 continue 372 if when and rep.when != when: 373 continue 374 if not inamepart or inamepart in rep.nodeid.split("::"): 375 values.append(rep) 376 if not values: 377 raise ValueError( 378 f"could not find test report matching {inamepart!r}: " 379 "no test reports at all!" 380 ) 381 if len(values) > 1: 382 raise ValueError( 383 f"found 2 or more testreports matching {inamepart!r}: {values}" 384 ) 385 return values[0]
Return a testreport whose dotted import path matches.
420 def listoutcomes( 421 self, 422 ) -> Tuple[ 423 Sequence[TestReport], 424 Sequence[Union[CollectReport, TestReport]], 425 Sequence[Union[CollectReport, TestReport]], 426 ]: 427 passed = [] 428 skipped = [] 429 failed = [] 430 for rep in self.getreports( 431 ("pytest_collectreport", "pytest_runtest_logreport") 432 ): 433 if rep.passed: 434 if rep.when == "call": 435 assert isinstance(rep, TestReport) 436 passed.append(rep) 437 elif rep.skipped: 438 skipped.append(rep) 439 else: 440 assert rep.failed, f"Unexpected outcome: {rep!r}" 441 failed.append(rep) 442 return passed, skipped, failed
447 def assertoutcome(self, passed: int = 0, skipped: int = 0, failed: int = 0) -> None: 448 __tracebackhide__ = True 449 from _pytest.pytester_assertions import assertoutcome 450 451 outcomes = self.listoutcomes() 452 assertoutcome( 453 outcomes, 454 passed=passed, 455 skipped=skipped, 456 failed=failed, 457 )
197def importorskip( 198 modname: str, 199 minversion: Optional[str] = None, 200 reason: Optional[str] = None, 201 *, 202 exc_type: Optional[Type[ImportError]] = None, 203) -> Any: 204 """Import and return the requested module ``modname``, or skip the 205 current test if the module cannot be imported. 206 207 :param modname: 208 The name of the module to import. 209 :param minversion: 210 If given, the imported module's ``__version__`` attribute must be at 211 least this minimal version, otherwise the test is still skipped. 212 :param reason: 213 If given, this reason is shown as the message when the module cannot 214 be imported. 215 :param exc_type: 216 The exception that should be captured in order to skip modules. 217 Must be :py:class:`ImportError` or a subclass. 218 219 If the module can be imported but raises :class:`ImportError`, pytest will 220 issue a warning to the user, as often users expect the module not to be 221 found (which would raise :class:`ModuleNotFoundError` instead). 222 223 This warning can be suppressed by passing ``exc_type=ImportError`` explicitly. 224 225 See :ref:`import-or-skip-import-error` for details. 226 227 228 :returns: 229 The imported module. This should be assigned to its canonical name. 230 231 Example:: 232 233 docutils = pytest.importorskip("docutils") 234 235 .. versionadded:: 8.2 236 237 The ``exc_type`` parameter. 238 """ 239 import warnings 240 241 __tracebackhide__ = True 242 compile(modname, "", "eval") # to catch syntaxerrors 243 244 # Until pytest 9.1, we will warn the user if we catch ImportError (instead of ModuleNotFoundError), 245 # as this might be hiding an installation/environment problem, which is not usually what is intended 246 # when using importorskip() (#11523). 247 # In 9.1, to keep the function signature compatible, we just change the code below to: 248 # 1. Use `exc_type = ModuleNotFoundError` if `exc_type` is not given. 249 # 2. Remove `warn_on_import` and the warning handling. 250 if exc_type is None: 251 exc_type = ImportError 252 warn_on_import_error = True 253 else: 254 warn_on_import_error = False 255 256 skipped: Optional[Skipped] = None 257 warning: Optional[Warning] = None 258 259 with warnings.catch_warnings(): 260 # Make sure to ignore ImportWarnings that might happen because 261 # of existing directories with the same name we're trying to 262 # import but without a __init__.py file. 263 warnings.simplefilter("ignore") 264 265 try: 266 __import__(modname) 267 except exc_type as exc: 268 # Do not raise or issue warnings inside the catch_warnings() block. 269 if reason is None: 270 reason = f"could not import {modname!r}: {exc}" 271 skipped = Skipped(reason, allow_module_level=True) 272 273 if warn_on_import_error and not isinstance(exc, ModuleNotFoundError): 274 lines = [ 275 "", 276 f"Module '{modname}' was found, but when imported by pytest it raised:", 277 f" {exc!r}", 278 "In pytest 9.1 this warning will become an error by default.", 279 "You can fix the underlying problem, or alternatively overwrite this behavior and silence this " 280 "warning by passing exc_type=ImportError explicitly.", 281 "See https://docs.pytest.org/en/stable/deprecations.html#pytest-importorskip-default-behavior-regarding-importerror", 282 ] 283 warning = PytestDeprecationWarning("\n".join(lines)) 284 285 if warning: 286 warnings.warn(warning, stacklevel=2) 287 if skipped: 288 raise skipped 289 290 mod = sys.modules[modname] 291 if minversion is None: 292 return mod 293 verattr = getattr(mod, "__version__", None) 294 if minversion is not None: 295 # Imported lazily to improve start-up time. 296 from packaging.version import Version 297 298 if verattr is None or Version(verattr) < Version(minversion): 299 raise Skipped( 300 f"module {modname!r} has __version__ {verattr!r}, required is: {minversion!r}", 301 allow_module_level=True, 302 ) 303 return mod
Import and return the requested module modname
, or skip the
current test if the module cannot be imported.
Parameters
- modname: The name of the module to import.
- minversion:
If given, the imported module's
__version__
attribute must be at least this minimal version, otherwise the test is still skipped. - reason: If given, this reason is shown as the message when the module cannot be imported.
exc_type: The exception that should be captured in order to skip modules. Must be
ImportError
or a subclass.If the module can be imported but raises
ImportError
, pytest will issue a warning to the user, as often users expect the module not to be found (which would raiseModuleNotFoundError
instead).This warning can be suppressed by passing
exc_type=ImportError
explicitly.See :ref:
import-or-skip-import-error
for details.
:returns: The imported module. This should be assigned to its canonical name.
Example::
docutils = pytest.importorskip("docutils")
New in version 8.2:
The exc_type
parameter.
655class Item(Node, abc.ABC): 656 """Base class of all test invocation items. 657 658 Note that for a single function there might be multiple test invocation items. 659 """ 660 661 nextitem = None 662 663 def __init__( 664 self, 665 name, 666 parent=None, 667 config: Optional[Config] = None, 668 session: Optional["Session"] = None, 669 nodeid: Optional[str] = None, 670 **kw, 671 ) -> None: 672 # The first two arguments are intentionally passed positionally, 673 # to keep plugins who define a node type which inherits from 674 # (pytest.Item, pytest.File) working (see issue #8435). 675 # They can be made kwargs when the deprecation above is done. 676 super().__init__( 677 name, 678 parent, 679 config=config, 680 session=session, 681 nodeid=nodeid, 682 **kw, 683 ) 684 self._report_sections: List[Tuple[str, str, str]] = [] 685 686 #: A list of tuples (name, value) that holds user defined properties 687 #: for this test. 688 self.user_properties: List[Tuple[str, object]] = [] 689 690 self._check_item_and_collector_diamond_inheritance() 691 692 def _check_item_and_collector_diamond_inheritance(self) -> None: 693 """ 694 Check if the current type inherits from both File and Collector 695 at the same time, emitting a warning accordingly (#8447). 696 """ 697 cls = type(self) 698 699 # We inject an attribute in the type to avoid issuing this warning 700 # for the same class more than once, which is not helpful. 701 # It is a hack, but was deemed acceptable in order to avoid 702 # flooding the user in the common case. 703 attr_name = "_pytest_diamond_inheritance_warning_shown" 704 if getattr(cls, attr_name, False): 705 return 706 setattr(cls, attr_name, True) 707 708 problems = ", ".join( 709 base.__name__ for base in cls.__bases__ if issubclass(base, Collector) 710 ) 711 if problems: 712 warnings.warn( 713 f"{cls.__name__} is an Item subclass and should not be a collector, " 714 f"however its bases {problems} are collectors.\n" 715 "Please split the Collectors and the Item into separate node types.\n" 716 "Pytest Doc example: https://docs.pytest.org/en/latest/example/nonpython.html\n" 717 "example pull request on a plugin: https://github.com/asmeurer/pytest-flakes/pull/40/", 718 PytestWarning, 719 ) 720 721 @abc.abstractmethod 722 def runtest(self) -> None: 723 """Run the test case for this item. 724 725 Must be implemented by subclasses. 726 727 .. seealso:: :ref:`non-python tests` 728 """ 729 raise NotImplementedError("runtest must be implemented by Item subclass") 730 731 def add_report_section(self, when: str, key: str, content: str) -> None: 732 """Add a new report section, similar to what's done internally to add 733 stdout and stderr captured output:: 734 735 item.add_report_section("call", "stdout", "report section contents") 736 737 :param str when: 738 One of the possible capture states, ``"setup"``, ``"call"``, ``"teardown"``. 739 :param str key: 740 Name of the section, can be customized at will. Pytest uses ``"stdout"`` and 741 ``"stderr"`` internally. 742 :param str content: 743 The full contents as a string. 744 """ 745 if content: 746 self._report_sections.append((when, key, content)) 747 748 def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]: 749 """Get location information for this item for test reports. 750 751 Returns a tuple with three elements: 752 753 - The path of the test (default ``self.path``) 754 - The 0-based line number of the test (default ``None``) 755 - A name of the test to be shown (default ``""``) 756 757 .. seealso:: :ref:`non-python tests` 758 """ 759 return self.path, None, "" 760 761 @cached_property 762 def location(self) -> Tuple[str, Optional[int], str]: 763 """ 764 Returns a tuple of ``(relfspath, lineno, testname)`` for this item 765 where ``relfspath`` is file path relative to ``config.rootpath`` 766 and lineno is a 0-based line number. 767 """ 768 location = self.reportinfo() 769 path = absolutepath(location[0]) 770 relfspath = self.session._node_location_to_relpath(path) 771 assert type(location[2]) is str 772 return (relfspath, location[1], location[2])
Base class of all test invocation items.
Note that for a single function there might be multiple test invocation items.
721 @abc.abstractmethod 722 def runtest(self) -> None: 723 """Run the test case for this item. 724 725 Must be implemented by subclasses. 726 727 .. seealso:: :ref:`non-python tests` 728 """ 729 raise NotImplementedError("runtest must be implemented by Item subclass")
Run the test case for this item.
Must be implemented by subclasses.
seealso :ref:non-python tests
.
731 def add_report_section(self, when: str, key: str, content: str) -> None: 732 """Add a new report section, similar to what's done internally to add 733 stdout and stderr captured output:: 734 735 item.add_report_section("call", "stdout", "report section contents") 736 737 :param str when: 738 One of the possible capture states, ``"setup"``, ``"call"``, ``"teardown"``. 739 :param str key: 740 Name of the section, can be customized at will. Pytest uses ``"stdout"`` and 741 ``"stderr"`` internally. 742 :param str content: 743 The full contents as a string. 744 """ 745 if content: 746 self._report_sections.append((when, key, content))
Add a new report section, similar to what's done internally to add stdout and stderr captured output::
item.add_report_section("call", "stdout", "report section contents")
Parameters
- str when:
One of the possible capture states,
"setup"
,"call"
,"teardown"
. - str key:
Name of the section, can be customized at will. Pytest uses
"stdout"
and"stderr"
internally. - str content: The full contents as a string.
748 def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]: 749 """Get location information for this item for test reports. 750 751 Returns a tuple with three elements: 752 753 - The path of the test (default ``self.path``) 754 - The 0-based line number of the test (default ``None``) 755 - A name of the test to be shown (default ``""``) 756 757 .. seealso:: :ref:`non-python tests` 758 """ 759 return self.path, None, ""
Get location information for this item for test reports.
Returns a tuple with three elements:
- The path of the test (default
self.path
) - The 0-based line number of the test (default
None
) - A name of the test to be shown (default
""
)
seealso :ref:non-python tests
.
761 @cached_property 762 def location(self) -> Tuple[str, Optional[int], str]: 763 """ 764 Returns a tuple of ``(relfspath, lineno, testname)`` for this item 765 where ``relfspath`` is file path relative to ``config.rootpath`` 766 and lineno is a 0-based line number. 767 """ 768 location = self.reportinfo() 769 path = absolutepath(location[0]) 770 relfspath = self.session._node_location_to_relpath(path) 771 assert type(location[2]) is str 772 return (relfspath, location[1], location[2])
Returns a tuple of (relfspath, lineno, testname)
for this item
where relfspath
is file path relative to config.rootpath
and lineno is a 0-based line number.
Inherited Members
- _pytest.nodes.Node
- fspath
- name
- parent
- path
- keywords
- own_markers
- extra_keyword_matches
- stash
- from_parent
- ihook
- warn
- nodeid
- setup
- teardown
- iter_parents
- listchain
- add_marker
- iter_markers
- iter_markers_with_node
- get_closest_marker
- listextrakeywords
- listnames
- addfinalizer
- getparent
- repr_failure
- config
- session
1547class LineMatcher: 1548 """Flexible matching of text. 1549 1550 This is a convenience class to test large texts like the output of 1551 commands. 1552 1553 The constructor takes a list of lines without their trailing newlines, i.e. 1554 ``text.splitlines()``. 1555 """ 1556 1557 def __init__(self, lines: List[str]) -> None: 1558 self.lines = lines 1559 self._log_output: List[str] = [] 1560 1561 def __str__(self) -> str: 1562 """Return the entire original text. 1563 1564 .. versionadded:: 6.2 1565 You can use :meth:`str` in older versions. 1566 """ 1567 return "\n".join(self.lines) 1568 1569 def _getlines(self, lines2: Union[str, Sequence[str], Source]) -> Sequence[str]: 1570 if isinstance(lines2, str): 1571 lines2 = Source(lines2) 1572 if isinstance(lines2, Source): 1573 lines2 = lines2.strip().lines 1574 return lines2 1575 1576 def fnmatch_lines_random(self, lines2: Sequence[str]) -> None: 1577 """Check lines exist in the output in any order (using :func:`python:fnmatch.fnmatch`).""" 1578 __tracebackhide__ = True 1579 self._match_lines_random(lines2, fnmatch) 1580 1581 def re_match_lines_random(self, lines2: Sequence[str]) -> None: 1582 """Check lines exist in the output in any order (using :func:`python:re.match`).""" 1583 __tracebackhide__ = True 1584 self._match_lines_random(lines2, lambda name, pat: bool(re.match(pat, name))) 1585 1586 def _match_lines_random( 1587 self, lines2: Sequence[str], match_func: Callable[[str, str], bool] 1588 ) -> None: 1589 __tracebackhide__ = True 1590 lines2 = self._getlines(lines2) 1591 for line in lines2: 1592 for x in self.lines: 1593 if line == x or match_func(x, line): 1594 self._log("matched: ", repr(line)) 1595 break 1596 else: 1597 msg = "line %r not found in output" % line 1598 self._log(msg) 1599 self._fail(msg) 1600 1601 def get_lines_after(self, fnline: str) -> Sequence[str]: 1602 """Return all lines following the given line in the text. 1603 1604 The given line can contain glob wildcards. 1605 """ 1606 for i, line in enumerate(self.lines): 1607 if fnline == line or fnmatch(line, fnline): 1608 return self.lines[i + 1 :] 1609 raise ValueError("line %r not found in output" % fnline) 1610 1611 def _log(self, *args) -> None: 1612 self._log_output.append(" ".join(str(x) for x in args)) 1613 1614 @property 1615 def _log_text(self) -> str: 1616 return "\n".join(self._log_output) 1617 1618 def fnmatch_lines( 1619 self, lines2: Sequence[str], *, consecutive: bool = False 1620 ) -> None: 1621 """Check lines exist in the output (using :func:`python:fnmatch.fnmatch`). 1622 1623 The argument is a list of lines which have to match and can use glob 1624 wildcards. If they do not match a pytest.fail() is called. The 1625 matches and non-matches are also shown as part of the error message. 1626 1627 :param lines2: String patterns to match. 1628 :param consecutive: Match lines consecutively? 1629 """ 1630 __tracebackhide__ = True 1631 self._match_lines(lines2, fnmatch, "fnmatch", consecutive=consecutive) 1632 1633 def re_match_lines( 1634 self, lines2: Sequence[str], *, consecutive: bool = False 1635 ) -> None: 1636 """Check lines exist in the output (using :func:`python:re.match`). 1637 1638 The argument is a list of lines which have to match using ``re.match``. 1639 If they do not match a pytest.fail() is called. 1640 1641 The matches and non-matches are also shown as part of the error message. 1642 1643 :param lines2: string patterns to match. 1644 :param consecutive: match lines consecutively? 1645 """ 1646 __tracebackhide__ = True 1647 self._match_lines( 1648 lines2, 1649 lambda name, pat: bool(re.match(pat, name)), 1650 "re.match", 1651 consecutive=consecutive, 1652 ) 1653 1654 def _match_lines( 1655 self, 1656 lines2: Sequence[str], 1657 match_func: Callable[[str, str], bool], 1658 match_nickname: str, 1659 *, 1660 consecutive: bool = False, 1661 ) -> None: 1662 """Underlying implementation of ``fnmatch_lines`` and ``re_match_lines``. 1663 1664 :param Sequence[str] lines2: 1665 List of string patterns to match. The actual format depends on 1666 ``match_func``. 1667 :param match_func: 1668 A callable ``match_func(line, pattern)`` where line is the 1669 captured line from stdout/stderr and pattern is the matching 1670 pattern. 1671 :param str match_nickname: 1672 The nickname for the match function that will be logged to stdout 1673 when a match occurs. 1674 :param consecutive: 1675 Match lines consecutively? 1676 """ 1677 if not isinstance(lines2, collections.abc.Sequence): 1678 raise TypeError(f"invalid type for lines2: {type(lines2).__name__}") 1679 lines2 = self._getlines(lines2) 1680 lines1 = self.lines[:] 1681 extralines = [] 1682 __tracebackhide__ = True 1683 wnick = len(match_nickname) + 1 1684 started = False 1685 for line in lines2: 1686 nomatchprinted = False 1687 while lines1: 1688 nextline = lines1.pop(0) 1689 if line == nextline: 1690 self._log("exact match:", repr(line)) 1691 started = True 1692 break 1693 elif match_func(nextline, line): 1694 self._log("%s:" % match_nickname, repr(line)) 1695 self._log( 1696 "{:>{width}}".format("with:", width=wnick), repr(nextline) 1697 ) 1698 started = True 1699 break 1700 else: 1701 if consecutive and started: 1702 msg = f"no consecutive match: {line!r}" 1703 self._log(msg) 1704 self._log( 1705 "{:>{width}}".format("with:", width=wnick), repr(nextline) 1706 ) 1707 self._fail(msg) 1708 if not nomatchprinted: 1709 self._log( 1710 "{:>{width}}".format("nomatch:", width=wnick), repr(line) 1711 ) 1712 nomatchprinted = True 1713 self._log("{:>{width}}".format("and:", width=wnick), repr(nextline)) 1714 extralines.append(nextline) 1715 else: 1716 msg = f"remains unmatched: {line!r}" 1717 self._log(msg) 1718 self._fail(msg) 1719 self._log_output = [] 1720 1721 def no_fnmatch_line(self, pat: str) -> None: 1722 """Ensure captured lines do not match the given pattern, using ``fnmatch.fnmatch``. 1723 1724 :param str pat: The pattern to match lines. 1725 """ 1726 __tracebackhide__ = True 1727 self._no_match_line(pat, fnmatch, "fnmatch") 1728 1729 def no_re_match_line(self, pat: str) -> None: 1730 """Ensure captured lines do not match the given pattern, using ``re.match``. 1731 1732 :param str pat: The regular expression to match lines. 1733 """ 1734 __tracebackhide__ = True 1735 self._no_match_line( 1736 pat, lambda name, pat: bool(re.match(pat, name)), "re.match" 1737 ) 1738 1739 def _no_match_line( 1740 self, pat: str, match_func: Callable[[str, str], bool], match_nickname: str 1741 ) -> None: 1742 """Ensure captured lines does not have a the given pattern, using ``fnmatch.fnmatch``. 1743 1744 :param str pat: The pattern to match lines. 1745 """ 1746 __tracebackhide__ = True 1747 nomatch_printed = False 1748 wnick = len(match_nickname) + 1 1749 for line in self.lines: 1750 if match_func(line, pat): 1751 msg = f"{match_nickname}: {pat!r}" 1752 self._log(msg) 1753 self._log("{:>{width}}".format("with:", width=wnick), repr(line)) 1754 self._fail(msg) 1755 else: 1756 if not nomatch_printed: 1757 self._log("{:>{width}}".format("nomatch:", width=wnick), repr(pat)) 1758 nomatch_printed = True 1759 self._log("{:>{width}}".format("and:", width=wnick), repr(line)) 1760 self._log_output = [] 1761 1762 def _fail(self, msg: str) -> None: 1763 __tracebackhide__ = True 1764 log_text = self._log_text 1765 self._log_output = [] 1766 fail(log_text) 1767 1768 def str(self) -> str: 1769 """Return the entire original text.""" 1770 return str(self)
Flexible matching of text.
This is a convenience class to test large texts like the output of commands.
The constructor takes a list of lines without their trailing newlines, i.e.
text.splitlines()
.
1576 def fnmatch_lines_random(self, lines2: Sequence[str]) -> None: 1577 """Check lines exist in the output in any order (using :func:`python:fnmatch.fnmatch`).""" 1578 __tracebackhide__ = True 1579 self._match_lines_random(lines2, fnmatch)
Check lines exist in the output in any order (using python:fnmatch.fnmatch()
).
1581 def re_match_lines_random(self, lines2: Sequence[str]) -> None: 1582 """Check lines exist in the output in any order (using :func:`python:re.match`).""" 1583 __tracebackhide__ = True 1584 self._match_lines_random(lines2, lambda name, pat: bool(re.match(pat, name)))
Check lines exist in the output in any order (using python:re.match()
).
1601 def get_lines_after(self, fnline: str) -> Sequence[str]: 1602 """Return all lines following the given line in the text. 1603 1604 The given line can contain glob wildcards. 1605 """ 1606 for i, line in enumerate(self.lines): 1607 if fnline == line or fnmatch(line, fnline): 1608 return self.lines[i + 1 :] 1609 raise ValueError("line %r not found in output" % fnline)
Return all lines following the given line in the text.
The given line can contain glob wildcards.
1618 def fnmatch_lines( 1619 self, lines2: Sequence[str], *, consecutive: bool = False 1620 ) -> None: 1621 """Check lines exist in the output (using :func:`python:fnmatch.fnmatch`). 1622 1623 The argument is a list of lines which have to match and can use glob 1624 wildcards. If they do not match a pytest.fail() is called. The 1625 matches and non-matches are also shown as part of the error message. 1626 1627 :param lines2: String patterns to match. 1628 :param consecutive: Match lines consecutively? 1629 """ 1630 __tracebackhide__ = True 1631 self._match_lines(lines2, fnmatch, "fnmatch", consecutive=consecutive)
Check lines exist in the output (using python:fnmatch.fnmatch()
).
The argument is a list of lines which have to match and can use glob wildcards. If they do not match a pytest.fail() is called. The matches and non-matches are also shown as part of the error message.
Parameters
- lines2: String patterns to match.
- consecutive: Match lines consecutively?
1633 def re_match_lines( 1634 self, lines2: Sequence[str], *, consecutive: bool = False 1635 ) -> None: 1636 """Check lines exist in the output (using :func:`python:re.match`). 1637 1638 The argument is a list of lines which have to match using ``re.match``. 1639 If they do not match a pytest.fail() is called. 1640 1641 The matches and non-matches are also shown as part of the error message. 1642 1643 :param lines2: string patterns to match. 1644 :param consecutive: match lines consecutively? 1645 """ 1646 __tracebackhide__ = True 1647 self._match_lines( 1648 lines2, 1649 lambda name, pat: bool(re.match(pat, name)), 1650 "re.match", 1651 consecutive=consecutive, 1652 )
Check lines exist in the output (using python:re.match()
).
The argument is a list of lines which have to match using re.match
.
If they do not match a pytest.fail() is called.
The matches and non-matches are also shown as part of the error message.
Parameters
- lines2: string patterns to match.
- consecutive: match lines consecutively?
1721 def no_fnmatch_line(self, pat: str) -> None: 1722 """Ensure captured lines do not match the given pattern, using ``fnmatch.fnmatch``. 1723 1724 :param str pat: The pattern to match lines. 1725 """ 1726 __tracebackhide__ = True 1727 self._no_match_line(pat, fnmatch, "fnmatch")
Ensure captured lines do not match the given pattern, using fnmatch.fnmatch
.
Parameters
- str pat: The pattern to match lines.
1729 def no_re_match_line(self, pat: str) -> None: 1730 """Ensure captured lines do not match the given pattern, using ``re.match``. 1731 1732 :param str pat: The regular expression to match lines. 1733 """ 1734 __tracebackhide__ = True 1735 self._no_match_line( 1736 pat, lambda name, pat: bool(re.match(pat, name)), "re.match" 1737 )
Ensure captured lines do not match the given pattern, using re.match
.
Parameters
- str pat: The regular expression to match lines.
408@final 409class LogCaptureFixture: 410 """Provides access and control of log capturing.""" 411 412 def __init__(self, item: nodes.Node, *, _ispytest: bool = False) -> None: 413 check_ispytest(_ispytest) 414 self._item = item 415 self._initial_handler_level: Optional[int] = None 416 # Dict of log name -> log level. 417 self._initial_logger_levels: Dict[Optional[str], int] = {} 418 self._initial_disabled_logging_level: Optional[int] = None 419 420 def _finalize(self) -> None: 421 """Finalize the fixture. 422 423 This restores the log levels and the disabled logging levels changed by :meth:`set_level`. 424 """ 425 # Restore log levels. 426 if self._initial_handler_level is not None: 427 self.handler.setLevel(self._initial_handler_level) 428 for logger_name, level in self._initial_logger_levels.items(): 429 logger = logging.getLogger(logger_name) 430 logger.setLevel(level) 431 # Disable logging at the original disabled logging level. 432 if self._initial_disabled_logging_level is not None: 433 logging.disable(self._initial_disabled_logging_level) 434 self._initial_disabled_logging_level = None 435 436 @property 437 def handler(self) -> LogCaptureHandler: 438 """Get the logging handler used by the fixture.""" 439 return self._item.stash[caplog_handler_key] 440 441 def get_records( 442 self, when: Literal["setup", "call", "teardown"] 443 ) -> List[logging.LogRecord]: 444 """Get the logging records for one of the possible test phases. 445 446 :param when: 447 Which test phase to obtain the records from. 448 Valid values are: "setup", "call" and "teardown". 449 450 :returns: The list of captured records at the given stage. 451 452 .. versionadded:: 3.4 453 """ 454 return self._item.stash[caplog_records_key].get(when, []) 455 456 @property 457 def text(self) -> str: 458 """The formatted log text.""" 459 return _remove_ansi_escape_sequences(self.handler.stream.getvalue()) 460 461 @property 462 def records(self) -> List[logging.LogRecord]: 463 """The list of log records.""" 464 return self.handler.records 465 466 @property 467 def record_tuples(self) -> List[Tuple[str, int, str]]: 468 """A list of a stripped down version of log records intended 469 for use in assertion comparison. 470 471 The format of the tuple is: 472 473 (logger_name, log_level, message) 474 """ 475 return [(r.name, r.levelno, r.getMessage()) for r in self.records] 476 477 @property 478 def messages(self) -> List[str]: 479 """A list of format-interpolated log messages. 480 481 Unlike 'records', which contains the format string and parameters for 482 interpolation, log messages in this list are all interpolated. 483 484 Unlike 'text', which contains the output from the handler, log 485 messages in this list are unadorned with levels, timestamps, etc, 486 making exact comparisons more reliable. 487 488 Note that traceback or stack info (from :func:`logging.exception` or 489 the `exc_info` or `stack_info` arguments to the logging functions) is 490 not included, as this is added by the formatter in the handler. 491 492 .. versionadded:: 3.7 493 """ 494 return [r.getMessage() for r in self.records] 495 496 def clear(self) -> None: 497 """Reset the list of log records and the captured log text.""" 498 self.handler.clear() 499 500 def _force_enable_logging( 501 self, level: Union[int, str], logger_obj: logging.Logger 502 ) -> int: 503 """Enable the desired logging level if the global level was disabled via ``logging.disabled``. 504 505 Only enables logging levels greater than or equal to the requested ``level``. 506 507 Does nothing if the desired ``level`` wasn't disabled. 508 509 :param level: 510 The logger level caplog should capture. 511 All logging is enabled if a non-standard logging level string is supplied. 512 Valid level strings are in :data:`logging._nameToLevel`. 513 :param logger_obj: The logger object to check. 514 515 :return: The original disabled logging level. 516 """ 517 original_disable_level: int = logger_obj.manager.disable 518 519 if isinstance(level, str): 520 # Try to translate the level string to an int for `logging.disable()` 521 level = logging.getLevelName(level) 522 523 if not isinstance(level, int): 524 # The level provided was not valid, so just un-disable all logging. 525 logging.disable(logging.NOTSET) 526 elif not logger_obj.isEnabledFor(level): 527 # Each level is `10` away from other levels. 528 # https://docs.python.org/3/library/logging.html#logging-levels 529 disable_level = max(level - 10, logging.NOTSET) 530 logging.disable(disable_level) 531 532 return original_disable_level 533 534 def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None: 535 """Set the threshold level of a logger for the duration of a test. 536 537 Logging messages which are less severe than this level will not be captured. 538 539 .. versionchanged:: 3.4 540 The levels of the loggers changed by this function will be 541 restored to their initial values at the end of the test. 542 543 Will enable the requested logging level if it was disabled via :func:`logging.disable`. 544 545 :param level: The level. 546 :param logger: The logger to update. If not given, the root logger. 547 """ 548 logger_obj = logging.getLogger(logger) 549 # Save the original log-level to restore it during teardown. 550 self._initial_logger_levels.setdefault(logger, logger_obj.level) 551 logger_obj.setLevel(level) 552 if self._initial_handler_level is None: 553 self._initial_handler_level = self.handler.level 554 self.handler.setLevel(level) 555 initial_disabled_logging_level = self._force_enable_logging(level, logger_obj) 556 if self._initial_disabled_logging_level is None: 557 self._initial_disabled_logging_level = initial_disabled_logging_level 558 559 @contextmanager 560 def at_level( 561 self, level: Union[int, str], logger: Optional[str] = None 562 ) -> Generator[None, None, None]: 563 """Context manager that sets the level for capturing of logs. After 564 the end of the 'with' statement the level is restored to its original 565 value. 566 567 Will enable the requested logging level if it was disabled via :func:`logging.disable`. 568 569 :param level: The level. 570 :param logger: The logger to update. If not given, the root logger. 571 """ 572 logger_obj = logging.getLogger(logger) 573 orig_level = logger_obj.level 574 logger_obj.setLevel(level) 575 handler_orig_level = self.handler.level 576 self.handler.setLevel(level) 577 original_disable_level = self._force_enable_logging(level, logger_obj) 578 try: 579 yield 580 finally: 581 logger_obj.setLevel(orig_level) 582 self.handler.setLevel(handler_orig_level) 583 logging.disable(original_disable_level) 584 585 @contextmanager 586 def filtering(self, filter_: logging.Filter) -> Generator[None, None, None]: 587 """Context manager that temporarily adds the given filter to the caplog's 588 :meth:`handler` for the 'with' statement block, and removes that filter at the 589 end of the block. 590 591 :param filter_: A custom :class:`logging.Filter` object. 592 593 .. versionadded:: 7.5 594 """ 595 self.handler.addFilter(filter_) 596 try: 597 yield 598 finally: 599 self.handler.removeFilter(filter_)
Provides access and control of log capturing.
412 def __init__(self, item: nodes.Node, *, _ispytest: bool = False) -> None: 413 check_ispytest(_ispytest) 414 self._item = item 415 self._initial_handler_level: Optional[int] = None 416 # Dict of log name -> log level. 417 self._initial_logger_levels: Dict[Optional[str], int] = {} 418 self._initial_disabled_logging_level: Optional[int] = None
436 @property 437 def handler(self) -> LogCaptureHandler: 438 """Get the logging handler used by the fixture.""" 439 return self._item.stash[caplog_handler_key]
Get the logging handler used by the fixture.
441 def get_records( 442 self, when: Literal["setup", "call", "teardown"] 443 ) -> List[logging.LogRecord]: 444 """Get the logging records for one of the possible test phases. 445 446 :param when: 447 Which test phase to obtain the records from. 448 Valid values are: "setup", "call" and "teardown". 449 450 :returns: The list of captured records at the given stage. 451 452 .. versionadded:: 3.4 453 """ 454 return self._item.stash[caplog_records_key].get(when, [])
Get the logging records for one of the possible test phases.
Parameters
- when: Which test phase to obtain the records from. Valid values are: "setup", "call" and "teardown".
:returns: The list of captured records at the given stage.
New in version 3.4.
456 @property 457 def text(self) -> str: 458 """The formatted log text.""" 459 return _remove_ansi_escape_sequences(self.handler.stream.getvalue())
The formatted log text.
461 @property 462 def records(self) -> List[logging.LogRecord]: 463 """The list of log records.""" 464 return self.handler.records
The list of log records.
466 @property 467 def record_tuples(self) -> List[Tuple[str, int, str]]: 468 """A list of a stripped down version of log records intended 469 for use in assertion comparison. 470 471 The format of the tuple is: 472 473 (logger_name, log_level, message) 474 """ 475 return [(r.name, r.levelno, r.getMessage()) for r in self.records]
A list of a stripped down version of log records intended for use in assertion comparison.
The format of the tuple is:
(logger_name, log_level, message)
477 @property 478 def messages(self) -> List[str]: 479 """A list of format-interpolated log messages. 480 481 Unlike 'records', which contains the format string and parameters for 482 interpolation, log messages in this list are all interpolated. 483 484 Unlike 'text', which contains the output from the handler, log 485 messages in this list are unadorned with levels, timestamps, etc, 486 making exact comparisons more reliable. 487 488 Note that traceback or stack info (from :func:`logging.exception` or 489 the `exc_info` or `stack_info` arguments to the logging functions) is 490 not included, as this is added by the formatter in the handler. 491 492 .. versionadded:: 3.7 493 """ 494 return [r.getMessage() for r in self.records]
A list of format-interpolated log messages.
Unlike 'records', which contains the format string and parameters for interpolation, log messages in this list are all interpolated.
Unlike 'text', which contains the output from the handler, log messages in this list are unadorned with levels, timestamps, etc, making exact comparisons more reliable.
Note that traceback or stack info (from logging.exception()
or
the exc_info
or stack_info
arguments to the logging functions) is
not included, as this is added by the formatter in the handler.
New in version 3.7.
496 def clear(self) -> None: 497 """Reset the list of log records and the captured log text.""" 498 self.handler.clear()
Reset the list of log records and the captured log text.
534 def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None: 535 """Set the threshold level of a logger for the duration of a test. 536 537 Logging messages which are less severe than this level will not be captured. 538 539 .. versionchanged:: 3.4 540 The levels of the loggers changed by this function will be 541 restored to their initial values at the end of the test. 542 543 Will enable the requested logging level if it was disabled via :func:`logging.disable`. 544 545 :param level: The level. 546 :param logger: The logger to update. If not given, the root logger. 547 """ 548 logger_obj = logging.getLogger(logger) 549 # Save the original log-level to restore it during teardown. 550 self._initial_logger_levels.setdefault(logger, logger_obj.level) 551 logger_obj.setLevel(level) 552 if self._initial_handler_level is None: 553 self._initial_handler_level = self.handler.level 554 self.handler.setLevel(level) 555 initial_disabled_logging_level = self._force_enable_logging(level, logger_obj) 556 if self._initial_disabled_logging_level is None: 557 self._initial_disabled_logging_level = initial_disabled_logging_level
Set the threshold level of a logger for the duration of a test.
Logging messages which are less severe than this level will not be captured.
Changed in version 3.4: The levels of the loggers changed by this function will be restored to their initial values at the end of the test.
Will enable the requested logging level if it was disabled via logging.disable()
.
Parameters
- level: The level.
- logger: The logger to update. If not given, the root logger.
559 @contextmanager 560 def at_level( 561 self, level: Union[int, str], logger: Optional[str] = None 562 ) -> Generator[None, None, None]: 563 """Context manager that sets the level for capturing of logs. After 564 the end of the 'with' statement the level is restored to its original 565 value. 566 567 Will enable the requested logging level if it was disabled via :func:`logging.disable`. 568 569 :param level: The level. 570 :param logger: The logger to update. If not given, the root logger. 571 """ 572 logger_obj = logging.getLogger(logger) 573 orig_level = logger_obj.level 574 logger_obj.setLevel(level) 575 handler_orig_level = self.handler.level 576 self.handler.setLevel(level) 577 original_disable_level = self._force_enable_logging(level, logger_obj) 578 try: 579 yield 580 finally: 581 logger_obj.setLevel(orig_level) 582 self.handler.setLevel(handler_orig_level) 583 logging.disable(original_disable_level)
Context manager that sets the level for capturing of logs. After the end of the 'with' statement the level is restored to its original value.
Will enable the requested logging level if it was disabled via logging.disable()
.
Parameters
- level: The level.
- logger: The logger to update. If not given, the root logger.
585 @contextmanager 586 def filtering(self, filter_: logging.Filter) -> Generator[None, None, None]: 587 """Context manager that temporarily adds the given filter to the caplog's 588 :meth:`handler` for the 'with' statement block, and removes that filter at the 589 end of the block. 590 591 :param filter_: A custom :class:`logging.Filter` object. 592 593 .. versionadded:: 7.5 594 """ 595 self.handler.addFilter(filter_) 596 try: 597 yield 598 finally: 599 self.handler.removeFilter(filter_)
Context manager that temporarily adds the given filter to the caplog's
handler()
for the 'with' statement block, and removes that filter at the
end of the block.
Parameters
- filter_: A custom
logging.Filter
object.
New in version 7.5.
143def main( 144 args: Optional[Union[List[str], "os.PathLike[str]"]] = None, 145 plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None, 146) -> Union[int, ExitCode]: 147 """Perform an in-process test run. 148 149 :param args: 150 List of command line arguments. If `None` or not given, defaults to reading 151 arguments directly from the process command line (:data:`sys.argv`). 152 :param plugins: List of plugin objects to be auto-registered during initialization. 153 154 :returns: An exit code. 155 """ 156 old_pytest_version = os.environ.get("PYTEST_VERSION") 157 try: 158 os.environ["PYTEST_VERSION"] = __version__ 159 try: 160 config = _prepareconfig(args, plugins) 161 except ConftestImportFailure as e: 162 exc_info = ExceptionInfo.from_exception(e.cause) 163 tw = TerminalWriter(sys.stderr) 164 tw.line(f"ImportError while loading conftest '{e.path}'.", red=True) 165 exc_info.traceback = exc_info.traceback.filter( 166 filter_traceback_for_conftest_import_failure 167 ) 168 exc_repr = ( 169 exc_info.getrepr(style="short", chain=False) 170 if exc_info.traceback 171 else exc_info.exconly() 172 ) 173 formatted_tb = str(exc_repr) 174 for line in formatted_tb.splitlines(): 175 tw.line(line.rstrip(), red=True) 176 return ExitCode.USAGE_ERROR 177 else: 178 try: 179 ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main( 180 config=config 181 ) 182 try: 183 return ExitCode(ret) 184 except ValueError: 185 return ret 186 finally: 187 config._ensure_unconfigure() 188 except UsageError as e: 189 tw = TerminalWriter(sys.stderr) 190 for msg in e.args: 191 tw.line(f"ERROR: {msg}\n", red=True) 192 return ExitCode.USAGE_ERROR 193 finally: 194 if old_pytest_version is None: 195 os.environ.pop("PYTEST_VERSION", None) 196 else: 197 os.environ["PYTEST_VERSION"] = old_pytest_version
Perform an in-process test run.
Parameters
- args:
List of command line arguments. If
None
or not given, defaults to reading arguments directly from the process command line (sys.argv
). - plugins: List of plugin objects to be auto-registered during initialization.
:returns: An exit code.
195@final 196@dataclasses.dataclass(frozen=True) 197class Mark: 198 """A pytest mark.""" 199 200 #: Name of the mark. 201 name: str 202 #: Positional arguments of the mark decorator. 203 args: Tuple[Any, ...] 204 #: Keyword arguments of the mark decorator. 205 kwargs: Mapping[str, Any] 206 207 #: Source Mark for ids with parametrize Marks. 208 _param_ids_from: Optional["Mark"] = dataclasses.field(default=None, repr=False) 209 #: Resolved/generated ids with parametrize Marks. 210 _param_ids_generated: Optional[Sequence[str]] = dataclasses.field( 211 default=None, repr=False 212 ) 213 214 def __init__( 215 self, 216 name: str, 217 args: Tuple[Any, ...], 218 kwargs: Mapping[str, Any], 219 param_ids_from: Optional["Mark"] = None, 220 param_ids_generated: Optional[Sequence[str]] = None, 221 *, 222 _ispytest: bool = False, 223 ) -> None: 224 """:meta private:""" 225 check_ispytest(_ispytest) 226 # Weirdness to bypass frozen=True. 227 object.__setattr__(self, "name", name) 228 object.__setattr__(self, "args", args) 229 object.__setattr__(self, "kwargs", kwargs) 230 object.__setattr__(self, "_param_ids_from", param_ids_from) 231 object.__setattr__(self, "_param_ids_generated", param_ids_generated) 232 233 def _has_param_ids(self) -> bool: 234 return "ids" in self.kwargs or len(self.args) >= 4 235 236 def combined_with(self, other: "Mark") -> "Mark": 237 """Return a new Mark which is a combination of this 238 Mark and another Mark. 239 240 Combines by appending args and merging kwargs. 241 242 :param Mark other: The mark to combine with. 243 :rtype: Mark 244 """ 245 assert self.name == other.name 246 247 # Remember source of ids with parametrize Marks. 248 param_ids_from: Optional[Mark] = None 249 if self.name == "parametrize": 250 if other._has_param_ids(): 251 param_ids_from = other 252 elif self._has_param_ids(): 253 param_ids_from = self 254 255 return Mark( 256 self.name, 257 self.args + other.args, 258 dict(self.kwargs, **other.kwargs), 259 param_ids_from=param_ids_from, 260 _ispytest=True, 261 )
A pytest mark.
214 def __init__( 215 self, 216 name: str, 217 args: Tuple[Any, ...], 218 kwargs: Mapping[str, Any], 219 param_ids_from: Optional["Mark"] = None, 220 param_ids_generated: Optional[Sequence[str]] = None, 221 *, 222 _ispytest: bool = False, 223 ) -> None: 224 """:meta private:""" 225 check_ispytest(_ispytest) 226 # Weirdness to bypass frozen=True. 227 object.__setattr__(self, "name", name) 228 object.__setattr__(self, "args", args) 229 object.__setattr__(self, "kwargs", kwargs) 230 object.__setattr__(self, "_param_ids_from", param_ids_from) 231 object.__setattr__(self, "_param_ids_generated", param_ids_generated)
:meta private:
236 def combined_with(self, other: "Mark") -> "Mark": 237 """Return a new Mark which is a combination of this 238 Mark and another Mark. 239 240 Combines by appending args and merging kwargs. 241 242 :param Mark other: The mark to combine with. 243 :rtype: Mark 244 """ 245 assert self.name == other.name 246 247 # Remember source of ids with parametrize Marks. 248 param_ids_from: Optional[Mark] = None 249 if self.name == "parametrize": 250 if other._has_param_ids(): 251 param_ids_from = other 252 elif self._has_param_ids(): 253 param_ids_from = self 254 255 return Mark( 256 self.name, 257 self.args + other.args, 258 dict(self.kwargs, **other.kwargs), 259 param_ids_from=param_ids_from, 260 _ispytest=True, 261 )
Return a new Mark which is a combination of this Mark and another Mark.
Combines by appending args and merging kwargs.
Parameters
- Mark other: The mark to combine with.
270@dataclasses.dataclass 271class MarkDecorator: 272 """A decorator for applying a mark on test functions and classes. 273 274 ``MarkDecorators`` are created with ``pytest.mark``:: 275 276 mark1 = pytest.mark.NAME # Simple MarkDecorator 277 mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator 278 279 and can then be applied as decorators to test functions:: 280 281 @mark2 282 def test_function(): 283 pass 284 285 When a ``MarkDecorator`` is called, it does the following: 286 287 1. If called with a single class as its only positional argument and no 288 additional keyword arguments, it attaches the mark to the class so it 289 gets applied automatically to all test cases found in that class. 290 291 2. If called with a single function as its only positional argument and 292 no additional keyword arguments, it attaches the mark to the function, 293 containing all the arguments already stored internally in the 294 ``MarkDecorator``. 295 296 3. When called in any other case, it returns a new ``MarkDecorator`` 297 instance with the original ``MarkDecorator``'s content updated with 298 the arguments passed to this call. 299 300 Note: The rules above prevent a ``MarkDecorator`` from storing only a 301 single function or class reference as its positional argument with no 302 additional keyword or positional arguments. You can work around this by 303 using `with_args()`. 304 """ 305 306 mark: Mark 307 308 def __init__(self, mark: Mark, *, _ispytest: bool = False) -> None: 309 """:meta private:""" 310 check_ispytest(_ispytest) 311 self.mark = mark 312 313 @property 314 def name(self) -> str: 315 """Alias for mark.name.""" 316 return self.mark.name 317 318 @property 319 def args(self) -> Tuple[Any, ...]: 320 """Alias for mark.args.""" 321 return self.mark.args 322 323 @property 324 def kwargs(self) -> Mapping[str, Any]: 325 """Alias for mark.kwargs.""" 326 return self.mark.kwargs 327 328 @property 329 def markname(self) -> str: 330 """:meta private:""" 331 return self.name # for backward-compat (2.4.1 had this attr) 332 333 def with_args(self, *args: object, **kwargs: object) -> "MarkDecorator": 334 """Return a MarkDecorator with extra arguments added. 335 336 Unlike calling the MarkDecorator, with_args() can be used even 337 if the sole argument is a callable/class. 338 """ 339 mark = Mark(self.name, args, kwargs, _ispytest=True) 340 return MarkDecorator(self.mark.combined_with(mark), _ispytest=True) 341 342 # Type ignored because the overloads overlap with an incompatible 343 # return type. Not much we can do about that. Thankfully mypy picks 344 # the first match so it works out even if we break the rules. 345 @overload 346 def __call__(self, arg: Markable) -> Markable: # type: ignore[overload-overlap] 347 pass 348 349 @overload 350 def __call__(self, *args: object, **kwargs: object) -> "MarkDecorator": 351 pass 352 353 def __call__(self, *args: object, **kwargs: object): 354 """Call the MarkDecorator.""" 355 if args and not kwargs: 356 func = args[0] 357 is_class = inspect.isclass(func) 358 if len(args) == 1 and (istestfunc(func) or is_class): 359 store_mark(func, self.mark, stacklevel=3) 360 return func 361 return self.with_args(*args, **kwargs)
A decorator for applying a mark on test functions and classes.
MarkDecorators
are created with pytest.mark
::
mark1 = pytest.mark.NAME # Simple MarkDecorator
mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator
and can then be applied as decorators to test functions::
@mark2
def test_function():
pass
When a MarkDecorator
is called, it does the following:
If called with a single class as its only positional argument and no additional keyword arguments, it attaches the mark to the class so it gets applied automatically to all test cases found in that class.
If called with a single function as its only positional argument and no additional keyword arguments, it attaches the mark to the function, containing all the arguments already stored internally in the
MarkDecorator
.When called in any other case, it returns a new
MarkDecorator
instance with the originalMarkDecorator
's content updated with the arguments passed to this call.
Note: The rules above prevent a MarkDecorator
from storing only a
single function or class reference as its positional argument with no
additional keyword or positional arguments. You can work around this by
using with_args()
.
308 def __init__(self, mark: Mark, *, _ispytest: bool = False) -> None: 309 """:meta private:""" 310 check_ispytest(_ispytest) 311 self.mark = mark
:meta private:
318 @property 319 def args(self) -> Tuple[Any, ...]: 320 """Alias for mark.args.""" 321 return self.mark.args
Alias for mark.args.
323 @property 324 def kwargs(self) -> Mapping[str, Any]: 325 """Alias for mark.kwargs.""" 326 return self.mark.kwargs
Alias for mark.kwargs.
328 @property 329 def markname(self) -> str: 330 """:meta private:""" 331 return self.name # for backward-compat (2.4.1 had this attr)
:meta private:
333 def with_args(self, *args: object, **kwargs: object) -> "MarkDecorator": 334 """Return a MarkDecorator with extra arguments added. 335 336 Unlike calling the MarkDecorator, with_args() can be used even 337 if the sole argument is a callable/class. 338 """ 339 mark = Mark(self.name, args, kwargs, _ispytest=True) 340 return MarkDecorator(self.mark.combined_with(mark), _ispytest=True)
Return a MarkDecorator with extra arguments added.
Unlike calling the MarkDecorator, with_args() can be used even if the sole argument is a callable/class.
493@final 494class MarkGenerator: 495 """Factory for :class:`MarkDecorator` objects - exposed as 496 a ``pytest.mark`` singleton instance. 497 498 Example:: 499 500 import pytest 501 502 503 @pytest.mark.slowtest 504 def test_function(): 505 pass 506 507 applies a 'slowtest' :class:`Mark` on ``test_function``. 508 """ 509 510 # See TYPE_CHECKING above. 511 if TYPE_CHECKING: 512 skip: _SkipMarkDecorator 513 skipif: _SkipifMarkDecorator 514 xfail: _XfailMarkDecorator 515 parametrize: _ParametrizeMarkDecorator 516 usefixtures: _UsefixturesMarkDecorator 517 filterwarnings: _FilterwarningsMarkDecorator 518 519 def __init__(self, *, _ispytest: bool = False) -> None: 520 check_ispytest(_ispytest) 521 self._config: Optional[Config] = None 522 self._markers: Set[str] = set() 523 524 def __getattr__(self, name: str) -> MarkDecorator: 525 """Generate a new :class:`MarkDecorator` with the given name.""" 526 if name[0] == "_": 527 raise AttributeError("Marker name must NOT start with underscore") 528 529 if self._config is not None: 530 # We store a set of markers as a performance optimisation - if a mark 531 # name is in the set we definitely know it, but a mark may be known and 532 # not in the set. We therefore start by updating the set! 533 if name not in self._markers: 534 for line in self._config.getini("markers"): 535 # example lines: "skipif(condition): skip the given test if..." 536 # or "hypothesis: tests which use Hypothesis", so to get the 537 # marker name we split on both `:` and `(`. 538 marker = line.split(":")[0].split("(")[0].strip() 539 self._markers.add(marker) 540 541 # If the name is not in the set of known marks after updating, 542 # then it really is time to issue a warning or an error. 543 if name not in self._markers: 544 if self._config.option.strict_markers or self._config.option.strict: 545 fail( 546 f"{name!r} not found in `markers` configuration option", 547 pytrace=False, 548 ) 549 550 # Raise a specific error for common misspellings of "parametrize". 551 if name in ["parameterize", "parametrise", "parameterise"]: 552 __tracebackhide__ = True 553 fail(f"Unknown '{name}' mark, did you mean 'parametrize'?") 554 555 warnings.warn( 556 "Unknown pytest.mark.%s - is this a typo? You can register " 557 "custom marks to avoid this warning - for details, see " 558 "https://docs.pytest.org/en/stable/how-to/mark.html" % name, 559 PytestUnknownMarkWarning, 560 2, 561 ) 562 563 return MarkDecorator(Mark(name, (), {}, _ispytest=True), _ispytest=True)
Factory for MarkDecorator
objects - exposed as
a pytest.mark
singleton instance.
Example::
import pytest
@pytest.mark.slowtest
def test_function():
pass
applies a 'slowtest' Mark
on test_function
.
1087@final 1088class Metafunc: 1089 """Objects passed to the :hook:`pytest_generate_tests` hook. 1090 1091 They help to inspect a test function and to generate tests according to 1092 test configuration or values specified in the class or module where a 1093 test function is defined. 1094 """ 1095 1096 def __init__( 1097 self, 1098 definition: "FunctionDefinition", 1099 fixtureinfo: fixtures.FuncFixtureInfo, 1100 config: Config, 1101 cls=None, 1102 module=None, 1103 *, 1104 _ispytest: bool = False, 1105 ) -> None: 1106 check_ispytest(_ispytest) 1107 1108 #: Access to the underlying :class:`_pytest.python.FunctionDefinition`. 1109 self.definition = definition 1110 1111 #: Access to the :class:`pytest.Config` object for the test session. 1112 self.config = config 1113 1114 #: The module object where the test function is defined in. 1115 self.module = module 1116 1117 #: Underlying Python test function. 1118 self.function = definition.obj 1119 1120 #: Set of fixture names required by the test function. 1121 self.fixturenames = fixtureinfo.names_closure 1122 1123 #: Class object where the test function is defined in or ``None``. 1124 self.cls = cls 1125 1126 self._arg2fixturedefs = fixtureinfo.name2fixturedefs 1127 1128 # Result of parametrize(). 1129 self._calls: List[CallSpec2] = [] 1130 1131 def parametrize( 1132 self, 1133 argnames: Union[str, Sequence[str]], 1134 argvalues: Iterable[Union[ParameterSet, Sequence[object], object]], 1135 indirect: Union[bool, Sequence[str]] = False, 1136 ids: Optional[ 1137 Union[Iterable[Optional[object]], Callable[[Any], Optional[object]]] 1138 ] = None, 1139 scope: Optional[_ScopeName] = None, 1140 *, 1141 _param_mark: Optional[Mark] = None, 1142 ) -> None: 1143 """Add new invocations to the underlying test function using the list 1144 of argvalues for the given argnames. Parametrization is performed 1145 during the collection phase. If you need to setup expensive resources 1146 see about setting indirect to do it rather than at test setup time. 1147 1148 Can be called multiple times per test function (but only on different 1149 argument names), in which case each call parametrizes all previous 1150 parametrizations, e.g. 1151 1152 :: 1153 1154 unparametrized: t 1155 parametrize ["x", "y"]: t[x], t[y] 1156 parametrize [1, 2]: t[x-1], t[x-2], t[y-1], t[y-2] 1157 1158 :param argnames: 1159 A comma-separated string denoting one or more argument names, or 1160 a list/tuple of argument strings. 1161 1162 :param argvalues: 1163 The list of argvalues determines how often a test is invoked with 1164 different argument values. 1165 1166 If only one argname was specified argvalues is a list of values. 1167 If N argnames were specified, argvalues must be a list of 1168 N-tuples, where each tuple-element specifies a value for its 1169 respective argname. 1170 1171 :param indirect: 1172 A list of arguments' names (subset of argnames) or a boolean. 1173 If True the list contains all names from the argnames. Each 1174 argvalue corresponding to an argname in this list will 1175 be passed as request.param to its respective argname fixture 1176 function so that it can perform more expensive setups during the 1177 setup phase of a test rather than at collection time. 1178 1179 :param ids: 1180 Sequence of (or generator for) ids for ``argvalues``, 1181 or a callable to return part of the id for each argvalue. 1182 1183 With sequences (and generators like ``itertools.count()``) the 1184 returned ids should be of type ``string``, ``int``, ``float``, 1185 ``bool``, or ``None``. 1186 They are mapped to the corresponding index in ``argvalues``. 1187 ``None`` means to use the auto-generated id. 1188 1189 If it is a callable it will be called for each entry in 1190 ``argvalues``, and the return value is used as part of the 1191 auto-generated id for the whole set (where parts are joined with 1192 dashes ("-")). 1193 This is useful to provide more specific ids for certain items, e.g. 1194 dates. Returning ``None`` will use an auto-generated id. 1195 1196 If no ids are provided they will be generated automatically from 1197 the argvalues. 1198 1199 :param scope: 1200 If specified it denotes the scope of the parameters. 1201 The scope is used for grouping tests by parameter instances. 1202 It will also override any fixture-function defined scope, allowing 1203 to set a dynamic scope using test context or configuration. 1204 """ 1205 argnames, parametersets = ParameterSet._for_parametrize( 1206 argnames, 1207 argvalues, 1208 self.function, 1209 self.config, 1210 nodeid=self.definition.nodeid, 1211 ) 1212 del argvalues 1213 1214 if "request" in argnames: 1215 fail( 1216 "'request' is a reserved name and cannot be used in @pytest.mark.parametrize", 1217 pytrace=False, 1218 ) 1219 1220 if scope is not None: 1221 scope_ = Scope.from_user( 1222 scope, descr=f"parametrize() call in {self.function.__name__}" 1223 ) 1224 else: 1225 scope_ = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) 1226 1227 self._validate_if_using_arg_names(argnames, indirect) 1228 1229 # Use any already (possibly) generated ids with parametrize Marks. 1230 if _param_mark and _param_mark._param_ids_from: 1231 generated_ids = _param_mark._param_ids_from._param_ids_generated 1232 if generated_ids is not None: 1233 ids = generated_ids 1234 1235 ids = self._resolve_parameter_set_ids( 1236 argnames, ids, parametersets, nodeid=self.definition.nodeid 1237 ) 1238 1239 # Store used (possibly generated) ids with parametrize Marks. 1240 if _param_mark and _param_mark._param_ids_from and generated_ids is None: 1241 object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids) 1242 1243 # Add funcargs as fixturedefs to fixtureinfo.arg2fixturedefs by registering 1244 # artificial "pseudo" FixtureDef's so that later at test execution time we can 1245 # rely on a proper FixtureDef to exist for fixture setup. 1246 node = None 1247 # If we have a scope that is higher than function, we need 1248 # to make sure we only ever create an according fixturedef on 1249 # a per-scope basis. We thus store and cache the fixturedef on the 1250 # node related to the scope. 1251 if scope_ is not Scope.Function: 1252 collector = self.definition.parent 1253 assert collector is not None 1254 node = get_scope_node(collector, scope_) 1255 if node is None: 1256 # If used class scope and there is no class, use module-level 1257 # collector (for now). 1258 if scope_ is Scope.Class: 1259 assert isinstance(collector, Module) 1260 node = collector 1261 # If used package scope and there is no package, use session 1262 # (for now). 1263 elif scope_ is Scope.Package: 1264 node = collector.session 1265 else: 1266 assert False, f"Unhandled missing scope: {scope}" 1267 if node is None: 1268 name2pseudofixturedef = None 1269 else: 1270 default: Dict[str, FixtureDef[Any]] = {} 1271 name2pseudofixturedef = node.stash.setdefault( 1272 name2pseudofixturedef_key, default 1273 ) 1274 arg_directness = self._resolve_args_directness(argnames, indirect) 1275 for argname in argnames: 1276 if arg_directness[argname] == "indirect": 1277 continue 1278 if name2pseudofixturedef is not None and argname in name2pseudofixturedef: 1279 fixturedef = name2pseudofixturedef[argname] 1280 else: 1281 fixturedef = FixtureDef( 1282 config=self.config, 1283 baseid="", 1284 argname=argname, 1285 func=get_direct_param_fixture_func, 1286 scope=scope_, 1287 params=None, 1288 ids=None, 1289 _ispytest=True, 1290 ) 1291 if name2pseudofixturedef is not None: 1292 name2pseudofixturedef[argname] = fixturedef 1293 self._arg2fixturedefs[argname] = [fixturedef] 1294 1295 # Create the new calls: if we are parametrize() multiple times (by applying the decorator 1296 # more than once) then we accumulate those calls generating the cartesian product 1297 # of all calls. 1298 newcalls = [] 1299 for callspec in self._calls or [CallSpec2()]: 1300 for param_index, (param_id, param_set) in enumerate( 1301 zip(ids, parametersets) 1302 ): 1303 newcallspec = callspec.setmulti( 1304 argnames=argnames, 1305 valset=param_set.values, 1306 id=param_id, 1307 marks=param_set.marks, 1308 scope=scope_, 1309 param_index=param_index, 1310 ) 1311 newcalls.append(newcallspec) 1312 self._calls = newcalls 1313 1314 def _resolve_parameter_set_ids( 1315 self, 1316 argnames: Sequence[str], 1317 ids: Optional[ 1318 Union[Iterable[Optional[object]], Callable[[Any], Optional[object]]] 1319 ], 1320 parametersets: Sequence[ParameterSet], 1321 nodeid: str, 1322 ) -> List[str]: 1323 """Resolve the actual ids for the given parameter sets. 1324 1325 :param argnames: 1326 Argument names passed to ``parametrize()``. 1327 :param ids: 1328 The `ids` parameter of the ``parametrize()`` call (see docs). 1329 :param parametersets: 1330 The parameter sets, each containing a set of values corresponding 1331 to ``argnames``. 1332 :param nodeid str: 1333 The nodeid of the definition item that generated this 1334 parametrization. 1335 :returns: 1336 List with ids for each parameter set given. 1337 """ 1338 if ids is None: 1339 idfn = None 1340 ids_ = None 1341 elif callable(ids): 1342 idfn = ids 1343 ids_ = None 1344 else: 1345 idfn = None 1346 ids_ = self._validate_ids(ids, parametersets, self.function.__name__) 1347 id_maker = IdMaker( 1348 argnames, 1349 parametersets, 1350 idfn, 1351 ids_, 1352 self.config, 1353 nodeid=nodeid, 1354 func_name=self.function.__name__, 1355 ) 1356 return id_maker.make_unique_parameterset_ids() 1357 1358 def _validate_ids( 1359 self, 1360 ids: Iterable[Optional[object]], 1361 parametersets: Sequence[ParameterSet], 1362 func_name: str, 1363 ) -> List[Optional[object]]: 1364 try: 1365 num_ids = len(ids) # type: ignore[arg-type] 1366 except TypeError: 1367 try: 1368 iter(ids) 1369 except TypeError as e: 1370 raise TypeError("ids must be a callable or an iterable") from e 1371 num_ids = len(parametersets) 1372 1373 # num_ids == 0 is a special case: https://github.com/pytest-dev/pytest/issues/1849 1374 if num_ids != len(parametersets) and num_ids != 0: 1375 msg = "In {}: {} parameter sets specified, with different number of ids: {}" 1376 fail(msg.format(func_name, len(parametersets), num_ids), pytrace=False) 1377 1378 return list(itertools.islice(ids, num_ids)) 1379 1380 def _resolve_args_directness( 1381 self, 1382 argnames: Sequence[str], 1383 indirect: Union[bool, Sequence[str]], 1384 ) -> Dict[str, Literal["indirect", "direct"]]: 1385 """Resolve if each parametrized argument must be considered an indirect 1386 parameter to a fixture of the same name, or a direct parameter to the 1387 parametrized function, based on the ``indirect`` parameter of the 1388 parametrized() call. 1389 1390 :param argnames: 1391 List of argument names passed to ``parametrize()``. 1392 :param indirect: 1393 Same as the ``indirect`` parameter of ``parametrize()``. 1394 :returns 1395 A dict mapping each arg name to either "indirect" or "direct". 1396 """ 1397 arg_directness: Dict[str, Literal["indirect", "direct"]] 1398 if isinstance(indirect, bool): 1399 arg_directness = dict.fromkeys( 1400 argnames, "indirect" if indirect else "direct" 1401 ) 1402 elif isinstance(indirect, Sequence): 1403 arg_directness = dict.fromkeys(argnames, "direct") 1404 for arg in indirect: 1405 if arg not in argnames: 1406 fail( 1407 f"In {self.function.__name__}: indirect fixture '{arg}' doesn't exist", 1408 pytrace=False, 1409 ) 1410 arg_directness[arg] = "indirect" 1411 else: 1412 fail( 1413 f"In {self.function.__name__}: expected Sequence or boolean" 1414 f" for indirect, got {type(indirect).__name__}", 1415 pytrace=False, 1416 ) 1417 return arg_directness 1418 1419 def _validate_if_using_arg_names( 1420 self, 1421 argnames: Sequence[str], 1422 indirect: Union[bool, Sequence[str]], 1423 ) -> None: 1424 """Check if all argnames are being used, by default values, or directly/indirectly. 1425 1426 :param List[str] argnames: List of argument names passed to ``parametrize()``. 1427 :param indirect: Same as the ``indirect`` parameter of ``parametrize()``. 1428 :raises ValueError: If validation fails. 1429 """ 1430 default_arg_names = set(get_default_arg_names(self.function)) 1431 func_name = self.function.__name__ 1432 for arg in argnames: 1433 if arg not in self.fixturenames: 1434 if arg in default_arg_names: 1435 fail( 1436 f"In {func_name}: function already takes an argument '{arg}' with a default value", 1437 pytrace=False, 1438 ) 1439 else: 1440 if isinstance(indirect, Sequence): 1441 name = "fixture" if arg in indirect else "argument" 1442 else: 1443 name = "fixture" if indirect else "argument" 1444 fail( 1445 f"In {func_name}: function uses no {name} '{arg}'", 1446 pytrace=False, 1447 )
Objects passed to the :hook:pytest_generate_tests
hook.
They help to inspect a test function and to generate tests according to test configuration or values specified in the class or module where a test function is defined.
1096 def __init__( 1097 self, 1098 definition: "FunctionDefinition", 1099 fixtureinfo: fixtures.FuncFixtureInfo, 1100 config: Config, 1101 cls=None, 1102 module=None, 1103 *, 1104 _ispytest: bool = False, 1105 ) -> None: 1106 check_ispytest(_ispytest) 1107 1108 #: Access to the underlying :class:`_pytest.python.FunctionDefinition`. 1109 self.definition = definition 1110 1111 #: Access to the :class:`pytest.Config` object for the test session. 1112 self.config = config 1113 1114 #: The module object where the test function is defined in. 1115 self.module = module 1116 1117 #: Underlying Python test function. 1118 self.function = definition.obj 1119 1120 #: Set of fixture names required by the test function. 1121 self.fixturenames = fixtureinfo.names_closure 1122 1123 #: Class object where the test function is defined in or ``None``. 1124 self.cls = cls 1125 1126 self._arg2fixturedefs = fixtureinfo.name2fixturedefs 1127 1128 # Result of parametrize(). 1129 self._calls: List[CallSpec2] = []
1131 def parametrize( 1132 self, 1133 argnames: Union[str, Sequence[str]], 1134 argvalues: Iterable[Union[ParameterSet, Sequence[object], object]], 1135 indirect: Union[bool, Sequence[str]] = False, 1136 ids: Optional[ 1137 Union[Iterable[Optional[object]], Callable[[Any], Optional[object]]] 1138 ] = None, 1139 scope: Optional[_ScopeName] = None, 1140 *, 1141 _param_mark: Optional[Mark] = None, 1142 ) -> None: 1143 """Add new invocations to the underlying test function using the list 1144 of argvalues for the given argnames. Parametrization is performed 1145 during the collection phase. If you need to setup expensive resources 1146 see about setting indirect to do it rather than at test setup time. 1147 1148 Can be called multiple times per test function (but only on different 1149 argument names), in which case each call parametrizes all previous 1150 parametrizations, e.g. 1151 1152 :: 1153 1154 unparametrized: t 1155 parametrize ["x", "y"]: t[x], t[y] 1156 parametrize [1, 2]: t[x-1], t[x-2], t[y-1], t[y-2] 1157 1158 :param argnames: 1159 A comma-separated string denoting one or more argument names, or 1160 a list/tuple of argument strings. 1161 1162 :param argvalues: 1163 The list of argvalues determines how often a test is invoked with 1164 different argument values. 1165 1166 If only one argname was specified argvalues is a list of values. 1167 If N argnames were specified, argvalues must be a list of 1168 N-tuples, where each tuple-element specifies a value for its 1169 respective argname. 1170 1171 :param indirect: 1172 A list of arguments' names (subset of argnames) or a boolean. 1173 If True the list contains all names from the argnames. Each 1174 argvalue corresponding to an argname in this list will 1175 be passed as request.param to its respective argname fixture 1176 function so that it can perform more expensive setups during the 1177 setup phase of a test rather than at collection time. 1178 1179 :param ids: 1180 Sequence of (or generator for) ids for ``argvalues``, 1181 or a callable to return part of the id for each argvalue. 1182 1183 With sequences (and generators like ``itertools.count()``) the 1184 returned ids should be of type ``string``, ``int``, ``float``, 1185 ``bool``, or ``None``. 1186 They are mapped to the corresponding index in ``argvalues``. 1187 ``None`` means to use the auto-generated id. 1188 1189 If it is a callable it will be called for each entry in 1190 ``argvalues``, and the return value is used as part of the 1191 auto-generated id for the whole set (where parts are joined with 1192 dashes ("-")). 1193 This is useful to provide more specific ids for certain items, e.g. 1194 dates. Returning ``None`` will use an auto-generated id. 1195 1196 If no ids are provided they will be generated automatically from 1197 the argvalues. 1198 1199 :param scope: 1200 If specified it denotes the scope of the parameters. 1201 The scope is used for grouping tests by parameter instances. 1202 It will also override any fixture-function defined scope, allowing 1203 to set a dynamic scope using test context or configuration. 1204 """ 1205 argnames, parametersets = ParameterSet._for_parametrize( 1206 argnames, 1207 argvalues, 1208 self.function, 1209 self.config, 1210 nodeid=self.definition.nodeid, 1211 ) 1212 del argvalues 1213 1214 if "request" in argnames: 1215 fail( 1216 "'request' is a reserved name and cannot be used in @pytest.mark.parametrize", 1217 pytrace=False, 1218 ) 1219 1220 if scope is not None: 1221 scope_ = Scope.from_user( 1222 scope, descr=f"parametrize() call in {self.function.__name__}" 1223 ) 1224 else: 1225 scope_ = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) 1226 1227 self._validate_if_using_arg_names(argnames, indirect) 1228 1229 # Use any already (possibly) generated ids with parametrize Marks. 1230 if _param_mark and _param_mark._param_ids_from: 1231 generated_ids = _param_mark._param_ids_from._param_ids_generated 1232 if generated_ids is not None: 1233 ids = generated_ids 1234 1235 ids = self._resolve_parameter_set_ids( 1236 argnames, ids, parametersets, nodeid=self.definition.nodeid 1237 ) 1238 1239 # Store used (possibly generated) ids with parametrize Marks. 1240 if _param_mark and _param_mark._param_ids_from and generated_ids is None: 1241 object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids) 1242 1243 # Add funcargs as fixturedefs to fixtureinfo.arg2fixturedefs by registering 1244 # artificial "pseudo" FixtureDef's so that later at test execution time we can 1245 # rely on a proper FixtureDef to exist for fixture setup. 1246 node = None 1247 # If we have a scope that is higher than function, we need 1248 # to make sure we only ever create an according fixturedef on 1249 # a per-scope basis. We thus store and cache the fixturedef on the 1250 # node related to the scope. 1251 if scope_ is not Scope.Function: 1252 collector = self.definition.parent 1253 assert collector is not None 1254 node = get_scope_node(collector, scope_) 1255 if node is None: 1256 # If used class scope and there is no class, use module-level 1257 # collector (for now). 1258 if scope_ is Scope.Class: 1259 assert isinstance(collector, Module) 1260 node = collector 1261 # If used package scope and there is no package, use session 1262 # (for now). 1263 elif scope_ is Scope.Package: 1264 node = collector.session 1265 else: 1266 assert False, f"Unhandled missing scope: {scope}" 1267 if node is None: 1268 name2pseudofixturedef = None 1269 else: 1270 default: Dict[str, FixtureDef[Any]] = {} 1271 name2pseudofixturedef = node.stash.setdefault( 1272 name2pseudofixturedef_key, default 1273 ) 1274 arg_directness = self._resolve_args_directness(argnames, indirect) 1275 for argname in argnames: 1276 if arg_directness[argname] == "indirect": 1277 continue 1278 if name2pseudofixturedef is not None and argname in name2pseudofixturedef: 1279 fixturedef = name2pseudofixturedef[argname] 1280 else: 1281 fixturedef = FixtureDef( 1282 config=self.config, 1283 baseid="", 1284 argname=argname, 1285 func=get_direct_param_fixture_func, 1286 scope=scope_, 1287 params=None, 1288 ids=None, 1289 _ispytest=True, 1290 ) 1291 if name2pseudofixturedef is not None: 1292 name2pseudofixturedef[argname] = fixturedef 1293 self._arg2fixturedefs[argname] = [fixturedef] 1294 1295 # Create the new calls: if we are parametrize() multiple times (by applying the decorator 1296 # more than once) then we accumulate those calls generating the cartesian product 1297 # of all calls. 1298 newcalls = [] 1299 for callspec in self._calls or [CallSpec2()]: 1300 for param_index, (param_id, param_set) in enumerate( 1301 zip(ids, parametersets) 1302 ): 1303 newcallspec = callspec.setmulti( 1304 argnames=argnames, 1305 valset=param_set.values, 1306 id=param_id, 1307 marks=param_set.marks, 1308 scope=scope_, 1309 param_index=param_index, 1310 ) 1311 newcalls.append(newcallspec) 1312 self._calls = newcalls
Add new invocations to the underlying test function using the list of argvalues for the given argnames. Parametrization is performed during the collection phase. If you need to setup expensive resources see about setting indirect to do it rather than at test setup time.
Can be called multiple times per test function (but only on different argument names), in which case each call parametrizes all previous parametrizations, e.g.
::
unparametrized: t
parametrize ["x", "y"]: t[x], t[y]
parametrize [1, 2]: t[x-1], t[x-2], t[y-1], t[y-2]
Parameters
argnames: A comma-separated string denoting one or more argument names, or a list/tuple of argument strings.
argvalues: The list of argvalues determines how often a test is invoked with different argument values.
If only one argname was specified argvalues is a list of values. If N argnames were specified, argvalues must be a list of N-tuples, where each tuple-element specifies a value for its respective argname.
indirect: A list of arguments' names (subset of argnames) or a boolean. If True the list contains all names from the argnames. Each argvalue corresponding to an argname in this list will be passed as request.param to its respective argname fixture function so that it can perform more expensive setups during the setup phase of a test rather than at collection time.
ids: Sequence of (or generator for) ids for
argvalues
, or a callable to return part of the id for each argvalue.With sequences (and generators like
itertools.count()
) the returned ids should be of typestring
,int
,float
,bool
, orNone
. They are mapped to the corresponding index inargvalues
.None
means to use the auto-generated id.If it is a callable it will be called for each entry in
argvalues
, and the return value is used as part of the auto-generated id for the whole set (where parts are joined with dashes ("-")). This is useful to provide more specific ids for certain items, e.g. dates. ReturningNone
will use an auto-generated id.If no ids are provided they will be generated automatically from the argvalues.
scope: If specified it denotes the scope of the parameters. The scope is used for grouping tests by parameter instances. It will also override any fixture-function defined scope, allowing to set a dynamic scope using test context or configuration.
537class Module(nodes.File, PyCollector): 538 """Collector for test classes and functions in a Python module.""" 539 540 def _getobj(self): 541 return importtestmodule(self.path, self.config) 542 543 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: 544 self._register_setup_module_fixture() 545 self._register_setup_function_fixture() 546 self.session._fixturemanager.parsefactories(self) 547 return super().collect() 548 549 def _register_setup_module_fixture(self) -> None: 550 """Register an autouse, module-scoped fixture for the collected module object 551 that invokes setUpModule/tearDownModule if either or both are available. 552 553 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with 554 other fixtures (#517). 555 """ 556 setup_module = _get_first_non_fixture_func( 557 self.obj, ("setUpModule", "setup_module") 558 ) 559 teardown_module = _get_first_non_fixture_func( 560 self.obj, ("tearDownModule", "teardown_module") 561 ) 562 563 if setup_module is None and teardown_module is None: 564 return 565 566 def xunit_setup_module_fixture(request) -> Generator[None, None, None]: 567 module = request.module 568 if setup_module is not None: 569 _call_with_optional_argument(setup_module, module) 570 yield 571 if teardown_module is not None: 572 _call_with_optional_argument(teardown_module, module) 573 574 self.session._fixturemanager._register_fixture( 575 # Use a unique name to speed up lookup. 576 name=f"_xunit_setup_module_fixture_{self.obj.__name__}", 577 func=xunit_setup_module_fixture, 578 nodeid=self.nodeid, 579 scope="module", 580 autouse=True, 581 ) 582 583 def _register_setup_function_fixture(self) -> None: 584 """Register an autouse, function-scoped fixture for the collected module object 585 that invokes setup_function/teardown_function if either or both are available. 586 587 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with 588 other fixtures (#517). 589 """ 590 setup_function = _get_first_non_fixture_func(self.obj, ("setup_function",)) 591 teardown_function = _get_first_non_fixture_func( 592 self.obj, ("teardown_function",) 593 ) 594 if setup_function is None and teardown_function is None: 595 return 596 597 def xunit_setup_function_fixture(request) -> Generator[None, None, None]: 598 if request.instance is not None: 599 # in this case we are bound to an instance, so we need to let 600 # setup_method handle this 601 yield 602 return 603 function = request.function 604 if setup_function is not None: 605 _call_with_optional_argument(setup_function, function) 606 yield 607 if teardown_function is not None: 608 _call_with_optional_argument(teardown_function, function) 609 610 self.session._fixturemanager._register_fixture( 611 # Use a unique name to speed up lookup. 612 name=f"_xunit_setup_function_fixture_{self.obj.__name__}", 613 func=xunit_setup_function_fixture, 614 nodeid=self.nodeid, 615 scope="function", 616 autouse=True, 617 )
Collector for test classes and functions in a Python module.
543 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: 544 self._register_setup_module_fixture() 545 self._register_setup_function_fixture() 546 self.session._fixturemanager.parsefactories(self) 547 return super().collect()
Collect children (items and collectors) for this collector.
Inherited Members
- _pytest.nodes.FSCollector
- FSCollector
- path
- from_parent
- _pytest.python.PyCollector
- funcnamefilter
- isnosetest
- classnamefilter
- istestfunction
- istestclass
- _pytest.python.PyobjMixin
- module
- cls
- instance
- obj
- getmodpath
- reportinfo
- _pytest.nodes.Node
- fspath
- name
- parent
- keywords
- own_markers
- extra_keyword_matches
- stash
- ihook
- warn
- nodeid
- setup
- teardown
- iter_parents
- listchain
- add_marker
- iter_markers
- iter_markers_with_node
- get_closest_marker
- listextrakeywords
- listnames
- addfinalizer
- getparent
- config
- session
119@final 120class MonkeyPatch: 121 """Helper to conveniently monkeypatch attributes/items/environment 122 variables/syspath. 123 124 Returned by the :fixture:`monkeypatch` fixture. 125 126 .. versionchanged:: 6.2 127 Can now also be used directly as `pytest.MonkeyPatch()`, for when 128 the fixture is not available. In this case, use 129 :meth:`with MonkeyPatch.context() as mp: <context>` or remember to call 130 :meth:`undo` explicitly. 131 """ 132 133 def __init__(self) -> None: 134 self._setattr: List[Tuple[object, str, object]] = [] 135 self._setitem: List[Tuple[Mapping[Any, Any], object, object]] = [] 136 self._cwd: Optional[str] = None 137 self._savesyspath: Optional[List[str]] = None 138 139 @classmethod 140 @contextmanager 141 def context(cls) -> Generator["MonkeyPatch", None, None]: 142 """Context manager that returns a new :class:`MonkeyPatch` object 143 which undoes any patching done inside the ``with`` block upon exit. 144 145 Example: 146 .. code-block:: python 147 148 import functools 149 150 151 def test_partial(monkeypatch): 152 with monkeypatch.context() as m: 153 m.setattr(functools, "partial", 3) 154 155 Useful in situations where it is desired to undo some patches before the test ends, 156 such as mocking ``stdlib`` functions that might break pytest itself if mocked (for examples 157 of this see :issue:`3290`). 158 """ 159 m = cls() 160 try: 161 yield m 162 finally: 163 m.undo() 164 165 @overload 166 def setattr( 167 self, 168 target: str, 169 name: object, 170 value: Notset = ..., 171 raising: bool = ..., 172 ) -> None: ... 173 174 @overload 175 def setattr( 176 self, 177 target: object, 178 name: str, 179 value: object, 180 raising: bool = ..., 181 ) -> None: ... 182 183 def setattr( 184 self, 185 target: Union[str, object], 186 name: Union[object, str], 187 value: object = notset, 188 raising: bool = True, 189 ) -> None: 190 """ 191 Set attribute value on target, memorizing the old value. 192 193 For example: 194 195 .. code-block:: python 196 197 import os 198 199 monkeypatch.setattr(os, "getcwd", lambda: "/") 200 201 The code above replaces the :func:`os.getcwd` function by a ``lambda`` which 202 always returns ``"/"``. 203 204 For convenience, you can specify a string as ``target`` which 205 will be interpreted as a dotted import path, with the last part 206 being the attribute name: 207 208 .. code-block:: python 209 210 monkeypatch.setattr("os.getcwd", lambda: "/") 211 212 Raises :class:`AttributeError` if the attribute does not exist, unless 213 ``raising`` is set to False. 214 215 **Where to patch** 216 217 ``monkeypatch.setattr`` works by (temporarily) changing the object that a name points to with another one. 218 There can be many names pointing to any individual object, so for patching to work you must ensure 219 that you patch the name used by the system under test. 220 221 See the section :ref:`Where to patch <python:where-to-patch>` in the :mod:`unittest.mock` 222 docs for a complete explanation, which is meant for :func:`unittest.mock.patch` but 223 applies to ``monkeypatch.setattr`` as well. 224 """ 225 __tracebackhide__ = True 226 import inspect 227 228 if isinstance(value, Notset): 229 if not isinstance(target, str): 230 raise TypeError( 231 "use setattr(target, name, value) or " 232 "setattr(target, value) with target being a dotted " 233 "import string" 234 ) 235 value = name 236 name, target = derive_importpath(target, raising) 237 else: 238 if not isinstance(name, str): 239 raise TypeError( 240 "use setattr(target, name, value) with name being a string or " 241 "setattr(target, value) with target being a dotted " 242 "import string" 243 ) 244 245 oldval = getattr(target, name, notset) 246 if raising and oldval is notset: 247 raise AttributeError(f"{target!r} has no attribute {name!r}") 248 249 # avoid class descriptors like staticmethod/classmethod 250 if inspect.isclass(target): 251 oldval = target.__dict__.get(name, notset) 252 self._setattr.append((target, name, oldval)) 253 setattr(target, name, value) 254 255 def delattr( 256 self, 257 target: Union[object, str], 258 name: Union[str, Notset] = notset, 259 raising: bool = True, 260 ) -> None: 261 """Delete attribute ``name`` from ``target``. 262 263 If no ``name`` is specified and ``target`` is a string 264 it will be interpreted as a dotted import path with the 265 last part being the attribute name. 266 267 Raises AttributeError it the attribute does not exist, unless 268 ``raising`` is set to False. 269 """ 270 __tracebackhide__ = True 271 import inspect 272 273 if isinstance(name, Notset): 274 if not isinstance(target, str): 275 raise TypeError( 276 "use delattr(target, name) or " 277 "delattr(target) with target being a dotted " 278 "import string" 279 ) 280 name, target = derive_importpath(target, raising) 281 282 if not hasattr(target, name): 283 if raising: 284 raise AttributeError(name) 285 else: 286 oldval = getattr(target, name, notset) 287 # Avoid class descriptors like staticmethod/classmethod. 288 if inspect.isclass(target): 289 oldval = target.__dict__.get(name, notset) 290 self._setattr.append((target, name, oldval)) 291 delattr(target, name) 292 293 def setitem(self, dic: Mapping[K, V], name: K, value: V) -> None: 294 """Set dictionary entry ``name`` to value.""" 295 self._setitem.append((dic, name, dic.get(name, notset))) 296 # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict 297 dic[name] = value # type: ignore[index] 298 299 def delitem(self, dic: Mapping[K, V], name: K, raising: bool = True) -> None: 300 """Delete ``name`` from dict. 301 302 Raises ``KeyError`` if it doesn't exist, unless ``raising`` is set to 303 False. 304 """ 305 if name not in dic: 306 if raising: 307 raise KeyError(name) 308 else: 309 self._setitem.append((dic, name, dic.get(name, notset))) 310 # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict 311 del dic[name] # type: ignore[attr-defined] 312 313 def setenv(self, name: str, value: str, prepend: Optional[str] = None) -> None: 314 """Set environment variable ``name`` to ``value``. 315 316 If ``prepend`` is a character, read the current environment variable 317 value and prepend the ``value`` adjoined with the ``prepend`` 318 character. 319 """ 320 if not isinstance(value, str): 321 warnings.warn( # type: ignore[unreachable] 322 PytestWarning( 323 f"Value of environment variable {name} type should be str, but got " 324 f"{value!r} (type: {type(value).__name__}); converted to str implicitly" 325 ), 326 stacklevel=2, 327 ) 328 value = str(value) 329 if prepend and name in os.environ: 330 value = value + prepend + os.environ[name] 331 self.setitem(os.environ, name, value) 332 333 def delenv(self, name: str, raising: bool = True) -> None: 334 """Delete ``name`` from the environment. 335 336 Raises ``KeyError`` if it does not exist, unless ``raising`` is set to 337 False. 338 """ 339 environ: MutableMapping[str, str] = os.environ 340 self.delitem(environ, name, raising=raising) 341 342 def syspath_prepend(self, path) -> None: 343 """Prepend ``path`` to ``sys.path`` list of import locations.""" 344 if self._savesyspath is None: 345 self._savesyspath = sys.path[:] 346 sys.path.insert(0, str(path)) 347 348 # https://github.com/pypa/setuptools/blob/d8b901bc/docs/pkg_resources.txt#L162-L171 349 # this is only needed when pkg_resources was already loaded by the namespace package 350 if "pkg_resources" in sys.modules: 351 from pkg_resources import fixup_namespace_packages 352 353 fixup_namespace_packages(str(path)) 354 355 # A call to syspathinsert() usually means that the caller wants to 356 # import some dynamically created files, thus with python3 we 357 # invalidate its import caches. 358 # This is especially important when any namespace package is in use, 359 # since then the mtime based FileFinder cache (that gets created in 360 # this case already) gets not invalidated when writing the new files 361 # quickly afterwards. 362 from importlib import invalidate_caches 363 364 invalidate_caches() 365 366 def chdir(self, path: Union[str, "os.PathLike[str]"]) -> None: 367 """Change the current working directory to the specified path. 368 369 :param path: 370 The path to change into. 371 """ 372 if self._cwd is None: 373 self._cwd = os.getcwd() 374 os.chdir(path) 375 376 def undo(self) -> None: 377 """Undo previous changes. 378 379 This call consumes the undo stack. Calling it a second time has no 380 effect unless you do more monkeypatching after the undo call. 381 382 There is generally no need to call `undo()`, since it is 383 called automatically during tear-down. 384 385 .. note:: 386 The same `monkeypatch` fixture is used across a 387 single test function invocation. If `monkeypatch` is used both by 388 the test function itself and one of the test fixtures, 389 calling `undo()` will undo all of the changes made in 390 both functions. 391 392 Prefer to use :meth:`context() <pytest.MonkeyPatch.context>` instead. 393 """ 394 for obj, name, value in reversed(self._setattr): 395 if value is not notset: 396 setattr(obj, name, value) 397 else: 398 delattr(obj, name) 399 self._setattr[:] = [] 400 for dictionary, key, value in reversed(self._setitem): 401 if value is notset: 402 try: 403 # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict 404 del dictionary[key] # type: ignore[attr-defined] 405 except KeyError: 406 pass # Was already deleted, so we have the desired state. 407 else: 408 # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict 409 dictionary[key] = value # type: ignore[index] 410 self._setitem[:] = [] 411 if self._savesyspath is not None: 412 sys.path[:] = self._savesyspath 413 self._savesyspath = None 414 415 if self._cwd is not None: 416 os.chdir(self._cwd) 417 self._cwd = None
Helper to conveniently monkeypatch attributes/items/environment variables/syspath.
Returned by the :fixture:monkeypatch
fixture.
Changed in version 6.2:
Can now also be used directly as pytest.MonkeyPatch()
, for when
the fixture is not available. In this case, use
with MonkeyPatch.context() as mp: <context>()
or remember to call
undo()
explicitly.
139 @classmethod 140 @contextmanager 141 def context(cls) -> Generator["MonkeyPatch", None, None]: 142 """Context manager that returns a new :class:`MonkeyPatch` object 143 which undoes any patching done inside the ``with`` block upon exit. 144 145 Example: 146 .. code-block:: python 147 148 import functools 149 150 151 def test_partial(monkeypatch): 152 with monkeypatch.context() as m: 153 m.setattr(functools, "partial", 3) 154 155 Useful in situations where it is desired to undo some patches before the test ends, 156 such as mocking ``stdlib`` functions that might break pytest itself if mocked (for examples 157 of this see :issue:`3290`). 158 """ 159 m = cls() 160 try: 161 yield m 162 finally: 163 m.undo()
Context manager that returns a new MonkeyPatch
object
which undoes any patching done inside the with
block upon exit.
Example:
import functools
def test_partial(monkeypatch):
with monkeypatch.context() as m:
m.setattr(functools, "partial", 3)
Useful in situations where it is desired to undo some patches before the test ends,
such as mocking stdlib
functions that might break pytest itself if mocked (for examples
of this see :issue:3290
).
183 def setattr( 184 self, 185 target: Union[str, object], 186 name: Union[object, str], 187 value: object = notset, 188 raising: bool = True, 189 ) -> None: 190 """ 191 Set attribute value on target, memorizing the old value. 192 193 For example: 194 195 .. code-block:: python 196 197 import os 198 199 monkeypatch.setattr(os, "getcwd", lambda: "/") 200 201 The code above replaces the :func:`os.getcwd` function by a ``lambda`` which 202 always returns ``"/"``. 203 204 For convenience, you can specify a string as ``target`` which 205 will be interpreted as a dotted import path, with the last part 206 being the attribute name: 207 208 .. code-block:: python 209 210 monkeypatch.setattr("os.getcwd", lambda: "/") 211 212 Raises :class:`AttributeError` if the attribute does not exist, unless 213 ``raising`` is set to False. 214 215 **Where to patch** 216 217 ``monkeypatch.setattr`` works by (temporarily) changing the object that a name points to with another one. 218 There can be many names pointing to any individual object, so for patching to work you must ensure 219 that you patch the name used by the system under test. 220 221 See the section :ref:`Where to patch <python:where-to-patch>` in the :mod:`unittest.mock` 222 docs for a complete explanation, which is meant for :func:`unittest.mock.patch` but 223 applies to ``monkeypatch.setattr`` as well. 224 """ 225 __tracebackhide__ = True 226 import inspect 227 228 if isinstance(value, Notset): 229 if not isinstance(target, str): 230 raise TypeError( 231 "use setattr(target, name, value) or " 232 "setattr(target, value) with target being a dotted " 233 "import string" 234 ) 235 value = name 236 name, target = derive_importpath(target, raising) 237 else: 238 if not isinstance(name, str): 239 raise TypeError( 240 "use setattr(target, name, value) with name being a string or " 241 "setattr(target, value) with target being a dotted " 242 "import string" 243 ) 244 245 oldval = getattr(target, name, notset) 246 if raising and oldval is notset: 247 raise AttributeError(f"{target!r} has no attribute {name!r}") 248 249 # avoid class descriptors like staticmethod/classmethod 250 if inspect.isclass(target): 251 oldval = target.__dict__.get(name, notset) 252 self._setattr.append((target, name, oldval)) 253 setattr(target, name, value)
Set attribute value on target, memorizing the old value.
For example:
import os
monkeypatch.setattr(os, "getcwd", lambda: "/")
The code above replaces the os.getcwd()
function by a lambda
which
always returns "/"
.
For convenience, you can specify a string as target
which
will be interpreted as a dotted import path, with the last part
being the attribute name:
monkeypatch.setattr("os.getcwd", lambda: "/")
Raises AttributeError
if the attribute does not exist, unless
raising
is set to False.
Where to patch
monkeypatch.setattr
works by (temporarily) changing the object that a name points to with another one.
There can be many names pointing to any individual object, so for patching to work you must ensure
that you patch the name used by the system under test.
See the section :ref:Where to patch <python:where-to-patch>
in the unittest.mock
docs for a complete explanation, which is meant for unittest.mock.patch()
but
applies to monkeypatch.setattr
as well.
255 def delattr( 256 self, 257 target: Union[object, str], 258 name: Union[str, Notset] = notset, 259 raising: bool = True, 260 ) -> None: 261 """Delete attribute ``name`` from ``target``. 262 263 If no ``name`` is specified and ``target`` is a string 264 it will be interpreted as a dotted import path with the 265 last part being the attribute name. 266 267 Raises AttributeError it the attribute does not exist, unless 268 ``raising`` is set to False. 269 """ 270 __tracebackhide__ = True 271 import inspect 272 273 if isinstance(name, Notset): 274 if not isinstance(target, str): 275 raise TypeError( 276 "use delattr(target, name) or " 277 "delattr(target) with target being a dotted " 278 "import string" 279 ) 280 name, target = derive_importpath(target, raising) 281 282 if not hasattr(target, name): 283 if raising: 284 raise AttributeError(name) 285 else: 286 oldval = getattr(target, name, notset) 287 # Avoid class descriptors like staticmethod/classmethod. 288 if inspect.isclass(target): 289 oldval = target.__dict__.get(name, notset) 290 self._setattr.append((target, name, oldval)) 291 delattr(target, name)
Delete attribute name
from target
.
If no name
is specified and target
is a string
it will be interpreted as a dotted import path with the
last part being the attribute name.
Raises AttributeError it the attribute does not exist, unless
raising
is set to False.
293 def setitem(self, dic: Mapping[K, V], name: K, value: V) -> None: 294 """Set dictionary entry ``name`` to value.""" 295 self._setitem.append((dic, name, dic.get(name, notset))) 296 # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict 297 dic[name] = value # type: ignore[index]
Set dictionary entry name
to value.
299 def delitem(self, dic: Mapping[K, V], name: K, raising: bool = True) -> None: 300 """Delete ``name`` from dict. 301 302 Raises ``KeyError`` if it doesn't exist, unless ``raising`` is set to 303 False. 304 """ 305 if name not in dic: 306 if raising: 307 raise KeyError(name) 308 else: 309 self._setitem.append((dic, name, dic.get(name, notset))) 310 # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict 311 del dic[name] # type: ignore[attr-defined]
Delete name
from dict.
Raises KeyError
if it doesn't exist, unless raising
is set to
False.
313 def setenv(self, name: str, value: str, prepend: Optional[str] = None) -> None: 314 """Set environment variable ``name`` to ``value``. 315 316 If ``prepend`` is a character, read the current environment variable 317 value and prepend the ``value`` adjoined with the ``prepend`` 318 character. 319 """ 320 if not isinstance(value, str): 321 warnings.warn( # type: ignore[unreachable] 322 PytestWarning( 323 f"Value of environment variable {name} type should be str, but got " 324 f"{value!r} (type: {type(value).__name__}); converted to str implicitly" 325 ), 326 stacklevel=2, 327 ) 328 value = str(value) 329 if prepend and name in os.environ: 330 value = value + prepend + os.environ[name] 331 self.setitem(os.environ, name, value)
Set environment variable name
to value
.
If prepend
is a character, read the current environment variable
value and prepend the value
adjoined with the prepend
character.
333 def delenv(self, name: str, raising: bool = True) -> None: 334 """Delete ``name`` from the environment. 335 336 Raises ``KeyError`` if it does not exist, unless ``raising`` is set to 337 False. 338 """ 339 environ: MutableMapping[str, str] = os.environ 340 self.delitem(environ, name, raising=raising)
Delete name
from the environment.
Raises KeyError
if it does not exist, unless raising
is set to
False.
342 def syspath_prepend(self, path) -> None: 343 """Prepend ``path`` to ``sys.path`` list of import locations.""" 344 if self._savesyspath is None: 345 self._savesyspath = sys.path[:] 346 sys.path.insert(0, str(path)) 347 348 # https://github.com/pypa/setuptools/blob/d8b901bc/docs/pkg_resources.txt#L162-L171 349 # this is only needed when pkg_resources was already loaded by the namespace package 350 if "pkg_resources" in sys.modules: 351 from pkg_resources import fixup_namespace_packages 352 353 fixup_namespace_packages(str(path)) 354 355 # A call to syspathinsert() usually means that the caller wants to 356 # import some dynamically created files, thus with python3 we 357 # invalidate its import caches. 358 # This is especially important when any namespace package is in use, 359 # since then the mtime based FileFinder cache (that gets created in 360 # this case already) gets not invalidated when writing the new files 361 # quickly afterwards. 362 from importlib import invalidate_caches 363 364 invalidate_caches()
Prepend path
to sys.path
list of import locations.
366 def chdir(self, path: Union[str, "os.PathLike[str]"]) -> None: 367 """Change the current working directory to the specified path. 368 369 :param path: 370 The path to change into. 371 """ 372 if self._cwd is None: 373 self._cwd = os.getcwd() 374 os.chdir(path)
Change the current working directory to the specified path.
Parameters
- path: The path to change into.
376 def undo(self) -> None: 377 """Undo previous changes. 378 379 This call consumes the undo stack. Calling it a second time has no 380 effect unless you do more monkeypatching after the undo call. 381 382 There is generally no need to call `undo()`, since it is 383 called automatically during tear-down. 384 385 .. note:: 386 The same `monkeypatch` fixture is used across a 387 single test function invocation. If `monkeypatch` is used both by 388 the test function itself and one of the test fixtures, 389 calling `undo()` will undo all of the changes made in 390 both functions. 391 392 Prefer to use :meth:`context() <pytest.MonkeyPatch.context>` instead. 393 """ 394 for obj, name, value in reversed(self._setattr): 395 if value is not notset: 396 setattr(obj, name, value) 397 else: 398 delattr(obj, name) 399 self._setattr[:] = [] 400 for dictionary, key, value in reversed(self._setitem): 401 if value is notset: 402 try: 403 # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict 404 del dictionary[key] # type: ignore[attr-defined] 405 except KeyError: 406 pass # Was already deleted, so we have the desired state. 407 else: 408 # Not all Mapping types support indexing, but MutableMapping doesn't support TypedDict 409 dictionary[key] = value # type: ignore[index] 410 self._setitem[:] = [] 411 if self._savesyspath is not None: 412 sys.path[:] = self._savesyspath 413 self._savesyspath = None 414 415 if self._cwd is not None: 416 os.chdir(self._cwd) 417 self._cwd = None
Undo previous changes.
This call consumes the undo stack. Calling it a second time has no effect unless you do more monkeypatching after the undo call.
There is generally no need to call undo()
, since it is
called automatically during tear-down.
The same monkeypatch
fixture is used across a
single test function invocation. If monkeypatch
is used both by
the test function itself and one of the test fixtures,
calling undo()
will undo all of the changes made in
both functions.
Prefer to use context() <pytest.MonkeyPatch.context>()
instead.
352class OptionGroup: 353 """A group of options shown in its own section.""" 354 355 def __init__( 356 self, 357 name: str, 358 description: str = "", 359 parser: Optional[Parser] = None, 360 *, 361 _ispytest: bool = False, 362 ) -> None: 363 check_ispytest(_ispytest) 364 self.name = name 365 self.description = description 366 self.options: List[Argument] = [] 367 self.parser = parser 368 369 def addoption(self, *opts: str, **attrs: Any) -> None: 370 """Add an option to this group. 371 372 If a shortened version of a long option is specified, it will 373 be suppressed in the help. ``addoption('--twowords', '--two-words')`` 374 results in help showing ``--two-words`` only, but ``--twowords`` gets 375 accepted **and** the automatic destination is in ``args.twowords``. 376 377 :param opts: 378 Option names, can be short or long options. 379 :param attrs: 380 Same attributes as the argparse library's :meth:`add_argument() 381 <argparse.ArgumentParser.add_argument>` function accepts. 382 """ 383 conflict = set(opts).intersection( 384 name for opt in self.options for name in opt.names() 385 ) 386 if conflict: 387 raise ValueError("option names %s already added" % conflict) 388 option = Argument(*opts, **attrs) 389 self._addoption_instance(option, shortupper=False) 390 391 def _addoption(self, *opts: str, **attrs: Any) -> None: 392 option = Argument(*opts, **attrs) 393 self._addoption_instance(option, shortupper=True) 394 395 def _addoption_instance(self, option: "Argument", shortupper: bool = False) -> None: 396 if not shortupper: 397 for opt in option._short_opts: 398 if opt[0] == "-" and opt[1].islower(): 399 raise ValueError("lowercase shortoptions reserved") 400 if self.parser: 401 self.parser.processoption(option) 402 self.options.append(option)
A group of options shown in its own section.
355 def __init__( 356 self, 357 name: str, 358 description: str = "", 359 parser: Optional[Parser] = None, 360 *, 361 _ispytest: bool = False, 362 ) -> None: 363 check_ispytest(_ispytest) 364 self.name = name 365 self.description = description 366 self.options: List[Argument] = [] 367 self.parser = parser
369 def addoption(self, *opts: str, **attrs: Any) -> None: 370 """Add an option to this group. 371 372 If a shortened version of a long option is specified, it will 373 be suppressed in the help. ``addoption('--twowords', '--two-words')`` 374 results in help showing ``--two-words`` only, but ``--twowords`` gets 375 accepted **and** the automatic destination is in ``args.twowords``. 376 377 :param opts: 378 Option names, can be short or long options. 379 :param attrs: 380 Same attributes as the argparse library's :meth:`add_argument() 381 <argparse.ArgumentParser.add_argument>` function accepts. 382 """ 383 conflict = set(opts).intersection( 384 name for opt in self.options for name in opt.names() 385 ) 386 if conflict: 387 raise ValueError("option names %s already added" % conflict) 388 option = Argument(*opts, **attrs) 389 self._addoption_instance(option, shortupper=False)
Add an option to this group.
If a shortened version of a long option is specified, it will
be suppressed in the help. addoption('--twowords', '--two-words')
results in help showing --two-words
only, but --twowords
gets
accepted and the automatic destination is in args.twowords
.
Parameters
- opts: Option names, can be short or long options.
- attrs:
Same attributes as the argparse library's
add_argument() <argparse.ArgumentParser.add_argument>()
function accepts.
620class Package(nodes.Directory): 621 """Collector for files and directories in a Python packages -- directories 622 with an `__init__.py` file. 623 624 .. note:: 625 626 Directories without an `__init__.py` file are instead collected by 627 :class:`~pytest.Dir` by default. Both are :class:`~pytest.Directory` 628 collectors. 629 630 .. versionchanged:: 8.0 631 632 Now inherits from :class:`~pytest.Directory`. 633 """ 634 635 def __init__( 636 self, 637 fspath: Optional[LEGACY_PATH], 638 parent: nodes.Collector, 639 # NOTE: following args are unused: 640 config=None, 641 session=None, 642 nodeid=None, 643 path: Optional[Path] = None, 644 ) -> None: 645 # NOTE: Could be just the following, but kept as-is for compat. 646 # super().__init__(self, fspath, parent=parent) 647 session = parent.session 648 super().__init__( 649 fspath=fspath, 650 path=path, 651 parent=parent, 652 config=config, 653 session=session, 654 nodeid=nodeid, 655 ) 656 657 def setup(self) -> None: 658 init_mod = importtestmodule(self.path / "__init__.py", self.config) 659 660 # Not using fixtures to call setup_module here because autouse fixtures 661 # from packages are not called automatically (#4085). 662 setup_module = _get_first_non_fixture_func( 663 init_mod, ("setUpModule", "setup_module") 664 ) 665 if setup_module is not None: 666 _call_with_optional_argument(setup_module, init_mod) 667 668 teardown_module = _get_first_non_fixture_func( 669 init_mod, ("tearDownModule", "teardown_module") 670 ) 671 if teardown_module is not None: 672 func = partial(_call_with_optional_argument, teardown_module, init_mod) 673 self.addfinalizer(func) 674 675 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: 676 # Always collect __init__.py first. 677 def sort_key(entry: "os.DirEntry[str]") -> object: 678 return (entry.name != "__init__.py", entry.name) 679 680 config = self.config 681 col: Optional[nodes.Collector] 682 cols: Sequence[nodes.Collector] 683 ihook = self.ihook 684 for direntry in scandir(self.path, sort_key): 685 if direntry.is_dir(): 686 path = Path(direntry.path) 687 if not self.session.isinitpath(path, with_parents=True): 688 if ihook.pytest_ignore_collect(collection_path=path, config=config): 689 continue 690 col = ihook.pytest_collect_directory(path=path, parent=self) 691 if col is not None: 692 yield col 693 694 elif direntry.is_file(): 695 path = Path(direntry.path) 696 if not self.session.isinitpath(path): 697 if ihook.pytest_ignore_collect(collection_path=path, config=config): 698 continue 699 cols = ihook.pytest_collect_file(file_path=path, parent=self) 700 yield from cols
Collector for files and directories in a Python packages -- directories
with an __init__.py
file.
Directories without an __init__.py
file are instead collected by
~pytest.Dir
by default. Both are ~pytest.Directory
collectors.
Changed in version 8.0:
Now inherits from ~pytest.Directory
.
635 def __init__( 636 self, 637 fspath: Optional[LEGACY_PATH], 638 parent: nodes.Collector, 639 # NOTE: following args are unused: 640 config=None, 641 session=None, 642 nodeid=None, 643 path: Optional[Path] = None, 644 ) -> None: 645 # NOTE: Could be just the following, but kept as-is for compat. 646 # super().__init__(self, fspath, parent=parent) 647 session = parent.session 648 super().__init__( 649 fspath=fspath, 650 path=path, 651 parent=parent, 652 config=config, 653 session=session, 654 nodeid=nodeid, 655 )
657 def setup(self) -> None: 658 init_mod = importtestmodule(self.path / "__init__.py", self.config) 659 660 # Not using fixtures to call setup_module here because autouse fixtures 661 # from packages are not called automatically (#4085). 662 setup_module = _get_first_non_fixture_func( 663 init_mod, ("setUpModule", "setup_module") 664 ) 665 if setup_module is not None: 666 _call_with_optional_argument(setup_module, init_mod) 667 668 teardown_module = _get_first_non_fixture_func( 669 init_mod, ("tearDownModule", "teardown_module") 670 ) 671 if teardown_module is not None: 672 func = partial(_call_with_optional_argument, teardown_module, init_mod) 673 self.addfinalizer(func)
675 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: 676 # Always collect __init__.py first. 677 def sort_key(entry: "os.DirEntry[str]") -> object: 678 return (entry.name != "__init__.py", entry.name) 679 680 config = self.config 681 col: Optional[nodes.Collector] 682 cols: Sequence[nodes.Collector] 683 ihook = self.ihook 684 for direntry in scandir(self.path, sort_key): 685 if direntry.is_dir(): 686 path = Path(direntry.path) 687 if not self.session.isinitpath(path, with_parents=True): 688 if ihook.pytest_ignore_collect(collection_path=path, config=config): 689 continue 690 col = ihook.pytest_collect_directory(path=path, parent=self) 691 if col is not None: 692 yield col 693 694 elif direntry.is_file(): 695 path = Path(direntry.path) 696 if not self.session.isinitpath(path): 697 if ihook.pytest_ignore_collect(collection_path=path, config=config): 698 continue 699 cols = ihook.pytest_collect_file(file_path=path, parent=self) 700 yield from cols
Collect children (items and collectors) for this collector.
Inherited Members
- _pytest.nodes.FSCollector
- path
- from_parent
- _pytest.nodes.Node
- fspath
- name
- parent
- keywords
- own_markers
- extra_keyword_matches
- stash
- ihook
- warn
- nodeid
- teardown
- iter_parents
- listchain
- add_marker
- iter_markers
- iter_markers_with_node
- get_closest_marker
- listextrakeywords
- listnames
- addfinalizer
- getparent
- config
- session
46def param( 47 *values: object, 48 marks: Union[MarkDecorator, Collection[Union[MarkDecorator, Mark]]] = (), 49 id: Optional[str] = None, 50) -> ParameterSet: 51 """Specify a parameter in `pytest.mark.parametrize`_ calls or 52 :ref:`parametrized fixtures <fixture-parametrize-marks>`. 53 54 .. code-block:: python 55 56 @pytest.mark.parametrize( 57 "test_input,expected", 58 [ 59 ("3+5", 8), 60 pytest.param("6*9", 42, marks=pytest.mark.xfail), 61 ], 62 ) 63 def test_eval(test_input, expected): 64 assert eval(test_input) == expected 65 66 :param values: Variable args of the values of the parameter set, in order. 67 :param marks: A single mark or a list of marks to be applied to this parameter set. 68 :param id: The id to attribute to this parameter set. 69 """ 70 return ParameterSet.param(*values, marks=marks, id=id)
Specify a parameter in pytest.mark.parametrize
_ calls or
:ref:parametrized fixtures <fixture-parametrize-marks>
.
@pytest.mark.parametrize(
"test_input,expected",
[
("3+5", 8),
pytest.param("6*9", 42, marks=pytest.mark.xfail),
],
)
def test_eval(test_input, expected):
assert eval(test_input) == expected
Parameters
- values: Variable args of the values of the parameter set, in order.
- marks: A single mark or a list of marks to be applied to this parameter set.
- id: The id to attribute to this parameter set.
37@final 38class Parser: 39 """Parser for command line arguments and ini-file values. 40 41 :ivar extra_info: Dict of generic param -> value to display in case 42 there's an error processing the command line arguments. 43 """ 44 45 prog: Optional[str] = None 46 47 def __init__( 48 self, 49 usage: Optional[str] = None, 50 processopt: Optional[Callable[["Argument"], None]] = None, 51 *, 52 _ispytest: bool = False, 53 ) -> None: 54 check_ispytest(_ispytest) 55 self._anonymous = OptionGroup("Custom options", parser=self, _ispytest=True) 56 self._groups: List[OptionGroup] = [] 57 self._processopt = processopt 58 self._usage = usage 59 self._inidict: Dict[str, Tuple[str, Optional[str], Any]] = {} 60 self._ininames: List[str] = [] 61 self.extra_info: Dict[str, Any] = {} 62 63 def processoption(self, option: "Argument") -> None: 64 if self._processopt: 65 if option.dest: 66 self._processopt(option) 67 68 def getgroup( 69 self, name: str, description: str = "", after: Optional[str] = None 70 ) -> "OptionGroup": 71 """Get (or create) a named option Group. 72 73 :param name: Name of the option group. 74 :param description: Long description for --help output. 75 :param after: Name of another group, used for ordering --help output. 76 :returns: The option group. 77 78 The returned group object has an ``addoption`` method with the same 79 signature as :func:`parser.addoption <pytest.Parser.addoption>` but 80 will be shown in the respective group in the output of 81 ``pytest --help``. 82 """ 83 for group in self._groups: 84 if group.name == name: 85 return group 86 group = OptionGroup(name, description, parser=self, _ispytest=True) 87 i = 0 88 for i, grp in enumerate(self._groups): 89 if grp.name == after: 90 break 91 self._groups.insert(i + 1, group) 92 return group 93 94 def addoption(self, *opts: str, **attrs: Any) -> None: 95 """Register a command line option. 96 97 :param opts: 98 Option names, can be short or long options. 99 :param attrs: 100 Same attributes as the argparse library's :meth:`add_argument() 101 <argparse.ArgumentParser.add_argument>` function accepts. 102 103 After command line parsing, options are available on the pytest config 104 object via ``config.option.NAME`` where ``NAME`` is usually set 105 by passing a ``dest`` attribute, for example 106 ``addoption("--long", dest="NAME", ...)``. 107 """ 108 self._anonymous.addoption(*opts, **attrs) 109 110 def parse( 111 self, 112 args: Sequence[Union[str, "os.PathLike[str]"]], 113 namespace: Optional[argparse.Namespace] = None, 114 ) -> argparse.Namespace: 115 from _pytest._argcomplete import try_argcomplete 116 117 self.optparser = self._getparser() 118 try_argcomplete(self.optparser) 119 strargs = [os.fspath(x) for x in args] 120 return self.optparser.parse_args(strargs, namespace=namespace) 121 122 def _getparser(self) -> "MyOptionParser": 123 from _pytest._argcomplete import filescompleter 124 125 optparser = MyOptionParser(self, self.extra_info, prog=self.prog) 126 groups = [*self._groups, self._anonymous] 127 for group in groups: 128 if group.options: 129 desc = group.description or group.name 130 arggroup = optparser.add_argument_group(desc) 131 for option in group.options: 132 n = option.names() 133 a = option.attrs() 134 arggroup.add_argument(*n, **a) 135 file_or_dir_arg = optparser.add_argument(FILE_OR_DIR, nargs="*") 136 # bash like autocompletion for dirs (appending '/') 137 # Type ignored because typeshed doesn't know about argcomplete. 138 file_or_dir_arg.completer = filescompleter # type: ignore 139 return optparser 140 141 def parse_setoption( 142 self, 143 args: Sequence[Union[str, "os.PathLike[str]"]], 144 option: argparse.Namespace, 145 namespace: Optional[argparse.Namespace] = None, 146 ) -> List[str]: 147 parsedoption = self.parse(args, namespace=namespace) 148 for name, value in parsedoption.__dict__.items(): 149 setattr(option, name, value) 150 return cast(List[str], getattr(parsedoption, FILE_OR_DIR)) 151 152 def parse_known_args( 153 self, 154 args: Sequence[Union[str, "os.PathLike[str]"]], 155 namespace: Optional[argparse.Namespace] = None, 156 ) -> argparse.Namespace: 157 """Parse the known arguments at this point. 158 159 :returns: An argparse namespace object. 160 """ 161 return self.parse_known_and_unknown_args(args, namespace=namespace)[0] 162 163 def parse_known_and_unknown_args( 164 self, 165 args: Sequence[Union[str, "os.PathLike[str]"]], 166 namespace: Optional[argparse.Namespace] = None, 167 ) -> Tuple[argparse.Namespace, List[str]]: 168 """Parse the known arguments at this point, and also return the 169 remaining unknown arguments. 170 171 :returns: 172 A tuple containing an argparse namespace object for the known 173 arguments, and a list of the unknown arguments. 174 """ 175 optparser = self._getparser() 176 strargs = [os.fspath(x) for x in args] 177 return optparser.parse_known_args(strargs, namespace=namespace) 178 179 def addini( 180 self, 181 name: str, 182 help: str, 183 type: Optional[ 184 Literal["string", "paths", "pathlist", "args", "linelist", "bool"] 185 ] = None, 186 default: Any = NOT_SET, 187 ) -> None: 188 """Register an ini-file option. 189 190 :param name: 191 Name of the ini-variable. 192 :param type: 193 Type of the variable. Can be: 194 195 * ``string``: a string 196 * ``bool``: a boolean 197 * ``args``: a list of strings, separated as in a shell 198 * ``linelist``: a list of strings, separated by line breaks 199 * ``paths``: a list of :class:`pathlib.Path`, separated as in a shell 200 * ``pathlist``: a list of ``py.path``, separated as in a shell 201 202 For ``paths`` and ``pathlist`` types, they are considered relative to the ini-file. 203 In case the execution is happening without an ini-file defined, 204 they will be considered relative to the current working directory (for example with ``--override-ini``). 205 206 .. versionadded:: 7.0 207 The ``paths`` variable type. 208 209 .. versionadded:: 8.1 210 Use the current working directory to resolve ``paths`` and ``pathlist`` in the absence of an ini-file. 211 212 Defaults to ``string`` if ``None`` or not passed. 213 :param default: 214 Default value if no ini-file option exists but is queried. 215 216 The value of ini-variables can be retrieved via a call to 217 :py:func:`config.getini(name) <pytest.Config.getini>`. 218 """ 219 assert type in (None, "string", "paths", "pathlist", "args", "linelist", "bool") 220 if default is NOT_SET: 221 default = get_ini_default_for_type(type) 222 223 self._inidict[name] = (help, type, default) 224 self._ininames.append(name)
Parser for command line arguments and ini-file values.
:ivar extra_info: Dict of generic param -> value to display in case there's an error processing the command line arguments.
47 def __init__( 48 self, 49 usage: Optional[str] = None, 50 processopt: Optional[Callable[["Argument"], None]] = None, 51 *, 52 _ispytest: bool = False, 53 ) -> None: 54 check_ispytest(_ispytest) 55 self._anonymous = OptionGroup("Custom options", parser=self, _ispytest=True) 56 self._groups: List[OptionGroup] = [] 57 self._processopt = processopt 58 self._usage = usage 59 self._inidict: Dict[str, Tuple[str, Optional[str], Any]] = {} 60 self._ininames: List[str] = [] 61 self.extra_info: Dict[str, Any] = {}
68 def getgroup( 69 self, name: str, description: str = "", after: Optional[str] = None 70 ) -> "OptionGroup": 71 """Get (or create) a named option Group. 72 73 :param name: Name of the option group. 74 :param description: Long description for --help output. 75 :param after: Name of another group, used for ordering --help output. 76 :returns: The option group. 77 78 The returned group object has an ``addoption`` method with the same 79 signature as :func:`parser.addoption <pytest.Parser.addoption>` but 80 will be shown in the respective group in the output of 81 ``pytest --help``. 82 """ 83 for group in self._groups: 84 if group.name == name: 85 return group 86 group = OptionGroup(name, description, parser=self, _ispytest=True) 87 i = 0 88 for i, grp in enumerate(self._groups): 89 if grp.name == after: 90 break 91 self._groups.insert(i + 1, group) 92 return group
Get (or create) a named option Group.
Parameters
- name: Name of the option group.
- description: Long description for --help output.
- after: Name of another group, used for ordering --help output. :returns: The option group.
The returned group object has an addoption
method with the same
signature as parser.addoption <pytest.Parser.addoption>()
but
will be shown in the respective group in the output of
pytest --help
.
94 def addoption(self, *opts: str, **attrs: Any) -> None: 95 """Register a command line option. 96 97 :param opts: 98 Option names, can be short or long options. 99 :param attrs: 100 Same attributes as the argparse library's :meth:`add_argument() 101 <argparse.ArgumentParser.add_argument>` function accepts. 102 103 After command line parsing, options are available on the pytest config 104 object via ``config.option.NAME`` where ``NAME`` is usually set 105 by passing a ``dest`` attribute, for example 106 ``addoption("--long", dest="NAME", ...)``. 107 """ 108 self._anonymous.addoption(*opts, **attrs)
Register a command line option.
Parameters
- opts: Option names, can be short or long options.
- attrs:
Same attributes as the argparse library's
add_argument() <argparse.ArgumentParser.add_argument>()
function accepts.
After command line parsing, options are available on the pytest config
object via config.option.NAME
where NAME
is usually set
by passing a dest
attribute, for example
addoption("--long", dest="NAME", ...)
.
110 def parse( 111 self, 112 args: Sequence[Union[str, "os.PathLike[str]"]], 113 namespace: Optional[argparse.Namespace] = None, 114 ) -> argparse.Namespace: 115 from _pytest._argcomplete import try_argcomplete 116 117 self.optparser = self._getparser() 118 try_argcomplete(self.optparser) 119 strargs = [os.fspath(x) for x in args] 120 return self.optparser.parse_args(strargs, namespace=namespace)
141 def parse_setoption( 142 self, 143 args: Sequence[Union[str, "os.PathLike[str]"]], 144 option: argparse.Namespace, 145 namespace: Optional[argparse.Namespace] = None, 146 ) -> List[str]: 147 parsedoption = self.parse(args, namespace=namespace) 148 for name, value in parsedoption.__dict__.items(): 149 setattr(option, name, value) 150 return cast(List[str], getattr(parsedoption, FILE_OR_DIR))
152 def parse_known_args( 153 self, 154 args: Sequence[Union[str, "os.PathLike[str]"]], 155 namespace: Optional[argparse.Namespace] = None, 156 ) -> argparse.Namespace: 157 """Parse the known arguments at this point. 158 159 :returns: An argparse namespace object. 160 """ 161 return self.parse_known_and_unknown_args(args, namespace=namespace)[0]
Parse the known arguments at this point.
:returns: An argparse namespace object.
163 def parse_known_and_unknown_args( 164 self, 165 args: Sequence[Union[str, "os.PathLike[str]"]], 166 namespace: Optional[argparse.Namespace] = None, 167 ) -> Tuple[argparse.Namespace, List[str]]: 168 """Parse the known arguments at this point, and also return the 169 remaining unknown arguments. 170 171 :returns: 172 A tuple containing an argparse namespace object for the known 173 arguments, and a list of the unknown arguments. 174 """ 175 optparser = self._getparser() 176 strargs = [os.fspath(x) for x in args] 177 return optparser.parse_known_args(strargs, namespace=namespace)
Parse the known arguments at this point, and also return the remaining unknown arguments.
:returns: A tuple containing an argparse namespace object for the known arguments, and a list of the unknown arguments.
179 def addini( 180 self, 181 name: str, 182 help: str, 183 type: Optional[ 184 Literal["string", "paths", "pathlist", "args", "linelist", "bool"] 185 ] = None, 186 default: Any = NOT_SET, 187 ) -> None: 188 """Register an ini-file option. 189 190 :param name: 191 Name of the ini-variable. 192 :param type: 193 Type of the variable. Can be: 194 195 * ``string``: a string 196 * ``bool``: a boolean 197 * ``args``: a list of strings, separated as in a shell 198 * ``linelist``: a list of strings, separated by line breaks 199 * ``paths``: a list of :class:`pathlib.Path`, separated as in a shell 200 * ``pathlist``: a list of ``py.path``, separated as in a shell 201 202 For ``paths`` and ``pathlist`` types, they are considered relative to the ini-file. 203 In case the execution is happening without an ini-file defined, 204 they will be considered relative to the current working directory (for example with ``--override-ini``). 205 206 .. versionadded:: 7.0 207 The ``paths`` variable type. 208 209 .. versionadded:: 8.1 210 Use the current working directory to resolve ``paths`` and ``pathlist`` in the absence of an ini-file. 211 212 Defaults to ``string`` if ``None`` or not passed. 213 :param default: 214 Default value if no ini-file option exists but is queried. 215 216 The value of ini-variables can be retrieved via a call to 217 :py:func:`config.getini(name) <pytest.Config.getini>`. 218 """ 219 assert type in (None, "string", "paths", "pathlist", "args", "linelist", "bool") 220 if default is NOT_SET: 221 default = get_ini_default_for_type(type) 222 223 self._inidict[name] = (help, type, default) 224 self._ininames.append(name)
Register an ini-file option.
Parameters
- name: Name of the ini-variable.
type: Type of the variable. Can be:
* ``string``: a string * ``bool``: a boolean * ``args``: a list of strings, separated as in a shell * ``linelist``: a list of strings, separated by line breaks * ``paths``: a list of `pathlib.Path`, separated as in a shell * ``pathlist``: a list of ``py.path``, separated as in a shell
For
paths
andpathlist
types, they are considered relative to the ini-file. In case the execution is happening without an ini-file defined, they will be considered relative to the current working directory (for example with--override-ini
).New in version 7.0: The
paths
variable type.New in version 8.1: Use the current working directory to resolve
paths
andpathlist
in the absence of an ini-file.Defaults to
string
ifNone
or not passed.- default: Default value if no ini-file option exists but is queried.
The value of ini-variables can be retrieved via a call to
config.getini(name) <pytest.Config.getini>()
.
Warning emitted by the pytest assert rewrite module.
Inherited Members
- builtins.UserWarning
- UserWarning
- builtins.BaseException
- with_traceback
- add_note
- args
Warning emitted by the cache plugin in various situations.
Inherited Members
- builtins.UserWarning
- UserWarning
- builtins.BaseException
- with_traceback
- add_note
- args
Warning emitted when pytest is not able to collect a file or symbol in a module.
Inherited Members
- builtins.UserWarning
- UserWarning
- builtins.BaseException
- with_traceback
- add_note
- args
Warning emitted for configuration issues.
Inherited Members
- builtins.UserWarning
- UserWarning
- builtins.BaseException
- with_traceback
- add_note
- args
Warning class for features that will be removed in a future version.
Inherited Members
- builtins.UserWarning
- UserWarning
- builtins.BaseException
- with_traceback
- add_note
- args
Warning category used to denote experiments in pytest.
Use sparingly as the API might change or even be removed completely in a future version.
Inherited Members
- builtins.UserWarning
- UserWarning
- builtins.BaseException
- with_traceback
- add_note
- args
Warning class for features that will be removed in pytest 9.
Inherited Members
- builtins.UserWarning
- UserWarning
- builtins.BaseException
- with_traceback
- add_note
- args
Warning emitted when a test function is returning value other than None.
Inherited Members
- builtins.UserWarning
- UserWarning
- builtins.BaseException
- with_traceback
- add_note
- args
650@final 651class Pytester: 652 """ 653 Facilities to write tests/configuration files, execute pytest in isolation, and match 654 against expected output, perfect for black-box testing of pytest plugins. 655 656 It attempts to isolate the test run from external factors as much as possible, modifying 657 the current working directory to :attr:`path` and environment variables during initialization. 658 """ 659 660 __test__ = False 661 662 CLOSE_STDIN: "Final" = NOTSET 663 664 class TimeoutExpired(Exception): 665 pass 666 667 def __init__( 668 self, 669 request: FixtureRequest, 670 tmp_path_factory: TempPathFactory, 671 monkeypatch: MonkeyPatch, 672 *, 673 _ispytest: bool = False, 674 ) -> None: 675 check_ispytest(_ispytest) 676 self._request = request 677 self._mod_collections: WeakKeyDictionary[ 678 Collector, List[Union[Item, Collector]] 679 ] = WeakKeyDictionary() 680 if request.function: 681 name: str = request.function.__name__ 682 else: 683 name = request.node.name 684 self._name = name 685 self._path: Path = tmp_path_factory.mktemp(name, numbered=True) 686 #: A list of plugins to use with :py:meth:`parseconfig` and 687 #: :py:meth:`runpytest`. Initially this is an empty list but plugins can 688 #: be added to the list. The type of items to add to the list depends on 689 #: the method using them so refer to them for details. 690 self.plugins: List[Union[str, _PluggyPlugin]] = [] 691 self._sys_path_snapshot = SysPathsSnapshot() 692 self._sys_modules_snapshot = self.__take_sys_modules_snapshot() 693 self._request.addfinalizer(self._finalize) 694 self._method = self._request.config.getoption("--runpytest") 695 self._test_tmproot = tmp_path_factory.mktemp(f"tmp-{name}", numbered=True) 696 697 self._monkeypatch = mp = monkeypatch 698 self.chdir() 699 mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self._test_tmproot)) 700 # Ensure no unexpected caching via tox. 701 mp.delenv("TOX_ENV_DIR", raising=False) 702 # Discard outer pytest options. 703 mp.delenv("PYTEST_ADDOPTS", raising=False) 704 # Ensure no user config is used. 705 tmphome = str(self.path) 706 mp.setenv("HOME", tmphome) 707 mp.setenv("USERPROFILE", tmphome) 708 # Do not use colors for inner runs by default. 709 mp.setenv("PY_COLORS", "0") 710 711 @property 712 def path(self) -> Path: 713 """Temporary directory path used to create files/run tests from, etc.""" 714 return self._path 715 716 def __repr__(self) -> str: 717 return f"<Pytester {self.path!r}>" 718 719 def _finalize(self) -> None: 720 """ 721 Clean up global state artifacts. 722 723 Some methods modify the global interpreter state and this tries to 724 clean this up. It does not remove the temporary directory however so 725 it can be looked at after the test run has finished. 726 """ 727 self._sys_modules_snapshot.restore() 728 self._sys_path_snapshot.restore() 729 730 def __take_sys_modules_snapshot(self) -> SysModulesSnapshot: 731 # Some zope modules used by twisted-related tests keep internal state 732 # and can't be deleted; we had some trouble in the past with 733 # `zope.interface` for example. 734 # 735 # Preserve readline due to https://bugs.python.org/issue41033. 736 # pexpect issues a SIGWINCH. 737 def preserve_module(name): 738 return name.startswith(("zope", "readline")) 739 740 return SysModulesSnapshot(preserve=preserve_module) 741 742 def make_hook_recorder(self, pluginmanager: PytestPluginManager) -> HookRecorder: 743 """Create a new :class:`HookRecorder` for a :class:`PytestPluginManager`.""" 744 pluginmanager.reprec = reprec = HookRecorder(pluginmanager, _ispytest=True) # type: ignore[attr-defined] 745 self._request.addfinalizer(reprec.finish_recording) 746 return reprec 747 748 def chdir(self) -> None: 749 """Cd into the temporary directory. 750 751 This is done automatically upon instantiation. 752 """ 753 self._monkeypatch.chdir(self.path) 754 755 def _makefile( 756 self, 757 ext: str, 758 lines: Sequence[Union[Any, bytes]], 759 files: Dict[str, str], 760 encoding: str = "utf-8", 761 ) -> Path: 762 items = list(files.items()) 763 764 if ext and not ext.startswith("."): 765 raise ValueError( 766 f"pytester.makefile expects a file extension, try .{ext} instead of {ext}" 767 ) 768 769 def to_text(s: Union[Any, bytes]) -> str: 770 return s.decode(encoding) if isinstance(s, bytes) else str(s) 771 772 if lines: 773 source = "\n".join(to_text(x) for x in lines) 774 basename = self._name 775 items.insert(0, (basename, source)) 776 777 ret = None 778 for basename, value in items: 779 p = self.path.joinpath(basename).with_suffix(ext) 780 p.parent.mkdir(parents=True, exist_ok=True) 781 source_ = Source(value) 782 source = "\n".join(to_text(line) for line in source_.lines) 783 p.write_text(source.strip(), encoding=encoding) 784 if ret is None: 785 ret = p 786 assert ret is not None 787 return ret 788 789 def makefile(self, ext: str, *args: str, **kwargs: str) -> Path: 790 r"""Create new text file(s) in the test directory. 791 792 :param ext: 793 The extension the file(s) should use, including the dot, e.g. `.py`. 794 :param args: 795 All args are treated as strings and joined using newlines. 796 The result is written as contents to the file. The name of the 797 file is based on the test function requesting this fixture. 798 :param kwargs: 799 Each keyword is the name of a file, while the value of it will 800 be written as contents of the file. 801 :returns: 802 The first created file. 803 804 Examples: 805 .. code-block:: python 806 807 pytester.makefile(".txt", "line1", "line2") 808 809 pytester.makefile(".ini", pytest="[pytest]\naddopts=-rs\n") 810 811 To create binary files, use :meth:`pathlib.Path.write_bytes` directly: 812 813 .. code-block:: python 814 815 filename = pytester.path.joinpath("foo.bin") 816 filename.write_bytes(b"...") 817 """ 818 return self._makefile(ext, args, kwargs) 819 820 def makeconftest(self, source: str) -> Path: 821 """Write a conftest.py file. 822 823 :param source: The contents. 824 :returns: The conftest.py file. 825 """ 826 return self.makepyfile(conftest=source) 827 828 def makeini(self, source: str) -> Path: 829 """Write a tox.ini file. 830 831 :param source: The contents. 832 :returns: The tox.ini file. 833 """ 834 return self.makefile(".ini", tox=source) 835 836 def getinicfg(self, source: str) -> SectionWrapper: 837 """Return the pytest section from the tox.ini config file.""" 838 p = self.makeini(source) 839 return IniConfig(str(p))["pytest"] 840 841 def makepyprojecttoml(self, source: str) -> Path: 842 """Write a pyproject.toml file. 843 844 :param source: The contents. 845 :returns: The pyproject.ini file. 846 847 .. versionadded:: 6.0 848 """ 849 return self.makefile(".toml", pyproject=source) 850 851 def makepyfile(self, *args, **kwargs) -> Path: 852 r"""Shortcut for .makefile() with a .py extension. 853 854 Defaults to the test name with a '.py' extension, e.g test_foobar.py, overwriting 855 existing files. 856 857 Examples: 858 .. code-block:: python 859 860 def test_something(pytester): 861 # Initial file is created test_something.py. 862 pytester.makepyfile("foobar") 863 # To create multiple files, pass kwargs accordingly. 864 pytester.makepyfile(custom="foobar") 865 # At this point, both 'test_something.py' & 'custom.py' exist in the test directory. 866 867 """ 868 return self._makefile(".py", args, kwargs) 869 870 def maketxtfile(self, *args, **kwargs) -> Path: 871 r"""Shortcut for .makefile() with a .txt extension. 872 873 Defaults to the test name with a '.txt' extension, e.g test_foobar.txt, overwriting 874 existing files. 875 876 Examples: 877 .. code-block:: python 878 879 def test_something(pytester): 880 # Initial file is created test_something.txt. 881 pytester.maketxtfile("foobar") 882 # To create multiple files, pass kwargs accordingly. 883 pytester.maketxtfile(custom="foobar") 884 # At this point, both 'test_something.txt' & 'custom.txt' exist in the test directory. 885 886 """ 887 return self._makefile(".txt", args, kwargs) 888 889 def syspathinsert( 890 self, path: Optional[Union[str, "os.PathLike[str]"]] = None 891 ) -> None: 892 """Prepend a directory to sys.path, defaults to :attr:`path`. 893 894 This is undone automatically when this object dies at the end of each 895 test. 896 897 :param path: 898 The path. 899 """ 900 if path is None: 901 path = self.path 902 903 self._monkeypatch.syspath_prepend(str(path)) 904 905 def mkdir(self, name: Union[str, "os.PathLike[str]"]) -> Path: 906 """Create a new (sub)directory. 907 908 :param name: 909 The name of the directory, relative to the pytester path. 910 :returns: 911 The created directory. 912 """ 913 p = self.path / name 914 p.mkdir() 915 return p 916 917 def mkpydir(self, name: Union[str, "os.PathLike[str]"]) -> Path: 918 """Create a new python package. 919 920 This creates a (sub)directory with an empty ``__init__.py`` file so it 921 gets recognised as a Python package. 922 """ 923 p = self.path / name 924 p.mkdir() 925 p.joinpath("__init__.py").touch() 926 return p 927 928 def copy_example(self, name: Optional[str] = None) -> Path: 929 """Copy file from project's directory into the testdir. 930 931 :param name: 932 The name of the file to copy. 933 :return: 934 Path to the copied directory (inside ``self.path``). 935 """ 936 example_dir_ = self._request.config.getini("pytester_example_dir") 937 if example_dir_ is None: 938 raise ValueError("pytester_example_dir is unset, can't copy examples") 939 example_dir: Path = self._request.config.rootpath / example_dir_ 940 941 for extra_element in self._request.node.iter_markers("pytester_example_path"): 942 assert extra_element.args 943 example_dir = example_dir.joinpath(*extra_element.args) 944 945 if name is None: 946 func_name = self._name 947 maybe_dir = example_dir / func_name 948 maybe_file = example_dir / (func_name + ".py") 949 950 if maybe_dir.is_dir(): 951 example_path = maybe_dir 952 elif maybe_file.is_file(): 953 example_path = maybe_file 954 else: 955 raise LookupError( 956 f"{func_name} can't be found as module or package in {example_dir}" 957 ) 958 else: 959 example_path = example_dir.joinpath(name) 960 961 if example_path.is_dir() and not example_path.joinpath("__init__.py").is_file(): 962 shutil.copytree(example_path, self.path, symlinks=True, dirs_exist_ok=True) 963 return self.path 964 elif example_path.is_file(): 965 result = self.path.joinpath(example_path.name) 966 shutil.copy(example_path, result) 967 return result 968 else: 969 raise LookupError( 970 f'example "{example_path}" is not found as a file or directory' 971 ) 972 973 def getnode( 974 self, config: Config, arg: Union[str, "os.PathLike[str]"] 975 ) -> Union[Collector, Item]: 976 """Get the collection node of a file. 977 978 :param config: 979 A pytest config. 980 See :py:meth:`parseconfig` and :py:meth:`parseconfigure` for creating it. 981 :param arg: 982 Path to the file. 983 :returns: 984 The node. 985 """ 986 session = Session.from_config(config) 987 assert "::" not in str(arg) 988 p = Path(os.path.abspath(arg)) 989 config.hook.pytest_sessionstart(session=session) 990 res = session.perform_collect([str(p)], genitems=False)[0] 991 config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) 992 return res 993 994 def getpathnode( 995 self, path: Union[str, "os.PathLike[str]"] 996 ) -> Union[Collector, Item]: 997 """Return the collection node of a file. 998 999 This is like :py:meth:`getnode` but uses :py:meth:`parseconfigure` to 1000 create the (configured) pytest Config instance. 1001 1002 :param path: 1003 Path to the file. 1004 :returns: 1005 The node. 1006 """ 1007 path = Path(path) 1008 config = self.parseconfigure(path) 1009 session = Session.from_config(config) 1010 x = bestrelpath(session.path, path) 1011 config.hook.pytest_sessionstart(session=session) 1012 res = session.perform_collect([x], genitems=False)[0] 1013 config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) 1014 return res 1015 1016 def genitems(self, colitems: Sequence[Union[Item, Collector]]) -> List[Item]: 1017 """Generate all test items from a collection node. 1018 1019 This recurses into the collection node and returns a list of all the 1020 test items contained within. 1021 1022 :param colitems: 1023 The collection nodes. 1024 :returns: 1025 The collected items. 1026 """ 1027 session = colitems[0].session 1028 result: List[Item] = [] 1029 for colitem in colitems: 1030 result.extend(session.genitems(colitem)) 1031 return result 1032 1033 def runitem(self, source: str) -> Any: 1034 """Run the "test_func" Item. 1035 1036 The calling test instance (class containing the test method) must 1037 provide a ``.getrunner()`` method which should return a runner which 1038 can run the test protocol for a single item, e.g. 1039 ``_pytest.runner.runtestprotocol``. 1040 """ 1041 # used from runner functional tests 1042 item = self.getitem(source) 1043 # the test class where we are called from wants to provide the runner 1044 testclassinstance = self._request.instance 1045 runner = testclassinstance.getrunner() 1046 return runner(item) 1047 1048 def inline_runsource(self, source: str, *cmdlineargs) -> HookRecorder: 1049 """Run a test module in process using ``pytest.main()``. 1050 1051 This run writes "source" into a temporary file and runs 1052 ``pytest.main()`` on it, returning a :py:class:`HookRecorder` instance 1053 for the result. 1054 1055 :param source: The source code of the test module. 1056 :param cmdlineargs: Any extra command line arguments to use. 1057 """ 1058 p = self.makepyfile(source) 1059 values = [*list(cmdlineargs), p] 1060 return self.inline_run(*values) 1061 1062 def inline_genitems(self, *args) -> Tuple[List[Item], HookRecorder]: 1063 """Run ``pytest.main(['--collect-only'])`` in-process. 1064 1065 Runs the :py:func:`pytest.main` function to run all of pytest inside 1066 the test process itself like :py:meth:`inline_run`, but returns a 1067 tuple of the collected items and a :py:class:`HookRecorder` instance. 1068 """ 1069 rec = self.inline_run("--collect-only", *args) 1070 items = [x.item for x in rec.getcalls("pytest_itemcollected")] 1071 return items, rec 1072 1073 def inline_run( 1074 self, 1075 *args: Union[str, "os.PathLike[str]"], 1076 plugins=(), 1077 no_reraise_ctrlc: bool = False, 1078 ) -> HookRecorder: 1079 """Run ``pytest.main()`` in-process, returning a HookRecorder. 1080 1081 Runs the :py:func:`pytest.main` function to run all of pytest inside 1082 the test process itself. This means it can return a 1083 :py:class:`HookRecorder` instance which gives more detailed results 1084 from that run than can be done by matching stdout/stderr from 1085 :py:meth:`runpytest`. 1086 1087 :param args: 1088 Command line arguments to pass to :py:func:`pytest.main`. 1089 :param plugins: 1090 Extra plugin instances the ``pytest.main()`` instance should use. 1091 :param no_reraise_ctrlc: 1092 Typically we reraise keyboard interrupts from the child run. If 1093 True, the KeyboardInterrupt exception is captured. 1094 """ 1095 # (maybe a cpython bug?) the importlib cache sometimes isn't updated 1096 # properly between file creation and inline_run (especially if imports 1097 # are interspersed with file creation) 1098 importlib.invalidate_caches() 1099 1100 plugins = list(plugins) 1101 finalizers = [] 1102 try: 1103 # Any sys.module or sys.path changes done while running pytest 1104 # inline should be reverted after the test run completes to avoid 1105 # clashing with later inline tests run within the same pytest test, 1106 # e.g. just because they use matching test module names. 1107 finalizers.append(self.__take_sys_modules_snapshot().restore) 1108 finalizers.append(SysPathsSnapshot().restore) 1109 1110 # Important note: 1111 # - our tests should not leave any other references/registrations 1112 # laying around other than possibly loaded test modules 1113 # referenced from sys.modules, as nothing will clean those up 1114 # automatically 1115 1116 rec = [] 1117 1118 class Collect: 1119 def pytest_configure(x, config: Config) -> None: 1120 rec.append(self.make_hook_recorder(config.pluginmanager)) 1121 1122 plugins.append(Collect()) 1123 ret = main([str(x) for x in args], plugins=plugins) 1124 if len(rec) == 1: 1125 reprec = rec.pop() 1126 else: 1127 1128 class reprec: # type: ignore 1129 pass 1130 1131 reprec.ret = ret 1132 1133 # Typically we reraise keyboard interrupts from the child run 1134 # because it's our user requesting interruption of the testing. 1135 if ret == ExitCode.INTERRUPTED and not no_reraise_ctrlc: 1136 calls = reprec.getcalls("pytest_keyboard_interrupt") 1137 if calls and calls[-1].excinfo.type == KeyboardInterrupt: 1138 raise KeyboardInterrupt() 1139 return reprec 1140 finally: 1141 for finalizer in finalizers: 1142 finalizer() 1143 1144 def runpytest_inprocess( 1145 self, *args: Union[str, "os.PathLike[str]"], **kwargs: Any 1146 ) -> RunResult: 1147 """Return result of running pytest in-process, providing a similar 1148 interface to what self.runpytest() provides.""" 1149 syspathinsert = kwargs.pop("syspathinsert", False) 1150 1151 if syspathinsert: 1152 self.syspathinsert() 1153 now = timing.time() 1154 capture = _get_multicapture("sys") 1155 capture.start_capturing() 1156 try: 1157 try: 1158 reprec = self.inline_run(*args, **kwargs) 1159 except SystemExit as e: 1160 ret = e.args[0] 1161 try: 1162 ret = ExitCode(e.args[0]) 1163 except ValueError: 1164 pass 1165 1166 class reprec: # type: ignore 1167 ret = ret 1168 1169 except Exception: 1170 traceback.print_exc() 1171 1172 class reprec: # type: ignore 1173 ret = ExitCode(3) 1174 1175 finally: 1176 out, err = capture.readouterr() 1177 capture.stop_capturing() 1178 sys.stdout.write(out) 1179 sys.stderr.write(err) 1180 1181 assert reprec.ret is not None 1182 res = RunResult( 1183 reprec.ret, out.splitlines(), err.splitlines(), timing.time() - now 1184 ) 1185 res.reprec = reprec # type: ignore 1186 return res 1187 1188 def runpytest( 1189 self, *args: Union[str, "os.PathLike[str]"], **kwargs: Any 1190 ) -> RunResult: 1191 """Run pytest inline or in a subprocess, depending on the command line 1192 option "--runpytest" and return a :py:class:`~pytest.RunResult`.""" 1193 new_args = self._ensure_basetemp(args) 1194 if self._method == "inprocess": 1195 return self.runpytest_inprocess(*new_args, **kwargs) 1196 elif self._method == "subprocess": 1197 return self.runpytest_subprocess(*new_args, **kwargs) 1198 raise RuntimeError(f"Unrecognized runpytest option: {self._method}") 1199 1200 def _ensure_basetemp( 1201 self, args: Sequence[Union[str, "os.PathLike[str]"]] 1202 ) -> List[Union[str, "os.PathLike[str]"]]: 1203 new_args = list(args) 1204 for x in new_args: 1205 if str(x).startswith("--basetemp"): 1206 break 1207 else: 1208 new_args.append("--basetemp=%s" % self.path.parent.joinpath("basetemp")) 1209 return new_args 1210 1211 def parseconfig(self, *args: Union[str, "os.PathLike[str]"]) -> Config: 1212 """Return a new pytest :class:`pytest.Config` instance from given 1213 commandline args. 1214 1215 This invokes the pytest bootstrapping code in _pytest.config to create a 1216 new :py:class:`pytest.PytestPluginManager` and call the 1217 :hook:`pytest_cmdline_parse` hook to create a new :class:`pytest.Config` 1218 instance. 1219 1220 If :attr:`plugins` has been populated they should be plugin modules 1221 to be registered with the plugin manager. 1222 """ 1223 import _pytest.config 1224 1225 new_args = self._ensure_basetemp(args) 1226 new_args = [str(x) for x in new_args] 1227 1228 config = _pytest.config._prepareconfig(new_args, self.plugins) # type: ignore[arg-type] 1229 # we don't know what the test will do with this half-setup config 1230 # object and thus we make sure it gets unconfigured properly in any 1231 # case (otherwise capturing could still be active, for example) 1232 self._request.addfinalizer(config._ensure_unconfigure) 1233 return config 1234 1235 def parseconfigure(self, *args: Union[str, "os.PathLike[str]"]) -> Config: 1236 """Return a new pytest configured Config instance. 1237 1238 Returns a new :py:class:`pytest.Config` instance like 1239 :py:meth:`parseconfig`, but also calls the :hook:`pytest_configure` 1240 hook. 1241 """ 1242 config = self.parseconfig(*args) 1243 config._do_configure() 1244 return config 1245 1246 def getitem( 1247 self, source: Union[str, "os.PathLike[str]"], funcname: str = "test_func" 1248 ) -> Item: 1249 """Return the test item for a test function. 1250 1251 Writes the source to a python file and runs pytest's collection on 1252 the resulting module, returning the test item for the requested 1253 function name. 1254 1255 :param source: 1256 The module source. 1257 :param funcname: 1258 The name of the test function for which to return a test item. 1259 :returns: 1260 The test item. 1261 """ 1262 items = self.getitems(source) 1263 for item in items: 1264 if item.name == funcname: 1265 return item 1266 assert 0, f"{funcname!r} item not found in module:\n{source}\nitems: {items}" 1267 1268 def getitems(self, source: Union[str, "os.PathLike[str]"]) -> List[Item]: 1269 """Return all test items collected from the module. 1270 1271 Writes the source to a Python file and runs pytest's collection on 1272 the resulting module, returning all test items contained within. 1273 """ 1274 modcol = self.getmodulecol(source) 1275 return self.genitems([modcol]) 1276 1277 def getmodulecol( 1278 self, 1279 source: Union[str, "os.PathLike[str]"], 1280 configargs=(), 1281 *, 1282 withinit: bool = False, 1283 ): 1284 """Return the module collection node for ``source``. 1285 1286 Writes ``source`` to a file using :py:meth:`makepyfile` and then 1287 runs the pytest collection on it, returning the collection node for the 1288 test module. 1289 1290 :param source: 1291 The source code of the module to collect. 1292 1293 :param configargs: 1294 Any extra arguments to pass to :py:meth:`parseconfigure`. 1295 1296 :param withinit: 1297 Whether to also write an ``__init__.py`` file to the same 1298 directory to ensure it is a package. 1299 """ 1300 if isinstance(source, os.PathLike): 1301 path = self.path.joinpath(source) 1302 assert not withinit, "not supported for paths" 1303 else: 1304 kw = {self._name: str(source)} 1305 path = self.makepyfile(**kw) 1306 if withinit: 1307 self.makepyfile(__init__="#") 1308 self.config = config = self.parseconfigure(path, *configargs) 1309 return self.getnode(config, path) 1310 1311 def collect_by_name( 1312 self, modcol: Collector, name: str 1313 ) -> Optional[Union[Item, Collector]]: 1314 """Return the collection node for name from the module collection. 1315 1316 Searches a module collection node for a collection node matching the 1317 given name. 1318 1319 :param modcol: A module collection node; see :py:meth:`getmodulecol`. 1320 :param name: The name of the node to return. 1321 """ 1322 if modcol not in self._mod_collections: 1323 self._mod_collections[modcol] = list(modcol.collect()) 1324 for colitem in self._mod_collections[modcol]: 1325 if colitem.name == name: 1326 return colitem 1327 return None 1328 1329 def popen( 1330 self, 1331 cmdargs: Sequence[Union[str, "os.PathLike[str]"]], 1332 stdout: Union[int, TextIO] = subprocess.PIPE, 1333 stderr: Union[int, TextIO] = subprocess.PIPE, 1334 stdin: Union[NotSetType, bytes, IO[Any], int] = CLOSE_STDIN, 1335 **kw, 1336 ): 1337 """Invoke :py:class:`subprocess.Popen`. 1338 1339 Calls :py:class:`subprocess.Popen` making sure the current working 1340 directory is in ``PYTHONPATH``. 1341 1342 You probably want to use :py:meth:`run` instead. 1343 """ 1344 env = os.environ.copy() 1345 env["PYTHONPATH"] = os.pathsep.join( 1346 filter(None, [os.getcwd(), env.get("PYTHONPATH", "")]) 1347 ) 1348 kw["env"] = env 1349 1350 if stdin is self.CLOSE_STDIN: 1351 kw["stdin"] = subprocess.PIPE 1352 elif isinstance(stdin, bytes): 1353 kw["stdin"] = subprocess.PIPE 1354 else: 1355 kw["stdin"] = stdin 1356 1357 popen = subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) 1358 if stdin is self.CLOSE_STDIN: 1359 assert popen.stdin is not None 1360 popen.stdin.close() 1361 elif isinstance(stdin, bytes): 1362 assert popen.stdin is not None 1363 popen.stdin.write(stdin) 1364 1365 return popen 1366 1367 def run( 1368 self, 1369 *cmdargs: Union[str, "os.PathLike[str]"], 1370 timeout: Optional[float] = None, 1371 stdin: Union[NotSetType, bytes, IO[Any], int] = CLOSE_STDIN, 1372 ) -> RunResult: 1373 """Run a command with arguments. 1374 1375 Run a process using :py:class:`subprocess.Popen` saving the stdout and 1376 stderr. 1377 1378 :param cmdargs: 1379 The sequence of arguments to pass to :py:class:`subprocess.Popen`, 1380 with path-like objects being converted to :py:class:`str` 1381 automatically. 1382 :param timeout: 1383 The period in seconds after which to timeout and raise 1384 :py:class:`Pytester.TimeoutExpired`. 1385 :param stdin: 1386 Optional standard input. 1387 1388 - If it is ``CLOSE_STDIN`` (Default), then this method calls 1389 :py:class:`subprocess.Popen` with ``stdin=subprocess.PIPE``, and 1390 the standard input is closed immediately after the new command is 1391 started. 1392 1393 - If it is of type :py:class:`bytes`, these bytes are sent to the 1394 standard input of the command. 1395 1396 - Otherwise, it is passed through to :py:class:`subprocess.Popen`. 1397 For further information in this case, consult the document of the 1398 ``stdin`` parameter in :py:class:`subprocess.Popen`. 1399 :returns: 1400 The result. 1401 """ 1402 __tracebackhide__ = True 1403 1404 cmdargs = tuple(os.fspath(arg) for arg in cmdargs) 1405 p1 = self.path.joinpath("stdout") 1406 p2 = self.path.joinpath("stderr") 1407 print("running:", *cmdargs) 1408 print(" in:", Path.cwd()) 1409 1410 with p1.open("w", encoding="utf8") as f1, p2.open("w", encoding="utf8") as f2: 1411 now = timing.time() 1412 popen = self.popen( 1413 cmdargs, 1414 stdin=stdin, 1415 stdout=f1, 1416 stderr=f2, 1417 close_fds=(sys.platform != "win32"), 1418 ) 1419 if popen.stdin is not None: 1420 popen.stdin.close() 1421 1422 def handle_timeout() -> None: 1423 __tracebackhide__ = True 1424 1425 timeout_message = f"{timeout} second timeout expired running: {cmdargs}" 1426 1427 popen.kill() 1428 popen.wait() 1429 raise self.TimeoutExpired(timeout_message) 1430 1431 if timeout is None: 1432 ret = popen.wait() 1433 else: 1434 try: 1435 ret = popen.wait(timeout) 1436 except subprocess.TimeoutExpired: 1437 handle_timeout() 1438 1439 with p1.open(encoding="utf8") as f1, p2.open(encoding="utf8") as f2: 1440 out = f1.read().splitlines() 1441 err = f2.read().splitlines() 1442 1443 self._dump_lines(out, sys.stdout) 1444 self._dump_lines(err, sys.stderr) 1445 1446 with contextlib.suppress(ValueError): 1447 ret = ExitCode(ret) 1448 return RunResult(ret, out, err, timing.time() - now) 1449 1450 def _dump_lines(self, lines, fp): 1451 try: 1452 for line in lines: 1453 print(line, file=fp) 1454 except UnicodeEncodeError: 1455 print(f"couldn't print to {fp} because of encoding") 1456 1457 def _getpytestargs(self) -> Tuple[str, ...]: 1458 return sys.executable, "-mpytest" 1459 1460 def runpython(self, script: "os.PathLike[str]") -> RunResult: 1461 """Run a python script using sys.executable as interpreter.""" 1462 return self.run(sys.executable, script) 1463 1464 def runpython_c(self, command: str) -> RunResult: 1465 """Run ``python -c "command"``.""" 1466 return self.run(sys.executable, "-c", command) 1467 1468 def runpytest_subprocess( 1469 self, *args: Union[str, "os.PathLike[str]"], timeout: Optional[float] = None 1470 ) -> RunResult: 1471 """Run pytest as a subprocess with given arguments. 1472 1473 Any plugins added to the :py:attr:`plugins` list will be added using the 1474 ``-p`` command line option. Additionally ``--basetemp`` is used to put 1475 any temporary files and directories in a numbered directory prefixed 1476 with "runpytest-" to not conflict with the normal numbered pytest 1477 location for temporary files and directories. 1478 1479 :param args: 1480 The sequence of arguments to pass to the pytest subprocess. 1481 :param timeout: 1482 The period in seconds after which to timeout and raise 1483 :py:class:`Pytester.TimeoutExpired`. 1484 :returns: 1485 The result. 1486 """ 1487 __tracebackhide__ = True 1488 p = make_numbered_dir(root=self.path, prefix="runpytest-", mode=0o700) 1489 args = ("--basetemp=%s" % p, *args) 1490 plugins = [x for x in self.plugins if isinstance(x, str)] 1491 if plugins: 1492 args = ("-p", plugins[0], *args) 1493 args = self._getpytestargs() + args 1494 return self.run(*args, timeout=timeout) 1495 1496 def spawn_pytest( 1497 self, string: str, expect_timeout: float = 10.0 1498 ) -> "pexpect.spawn": 1499 """Run pytest using pexpect. 1500 1501 This makes sure to use the right pytest and sets up the temporary 1502 directory locations. 1503 1504 The pexpect child is returned. 1505 """ 1506 basetemp = self.path / "temp-pexpect" 1507 basetemp.mkdir(mode=0o700) 1508 invoke = " ".join(map(str, self._getpytestargs())) 1509 cmd = f"{invoke} --basetemp={basetemp} {string}" 1510 return self.spawn(cmd, expect_timeout=expect_timeout) 1511 1512 def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn": 1513 """Run a command using pexpect. 1514 1515 The pexpect child is returned. 1516 """ 1517 pexpect = importorskip("pexpect", "3.0") 1518 if hasattr(sys, "pypy_version_info") and "64" in platform.machine(): 1519 skip("pypy-64 bit not supported") 1520 if not hasattr(pexpect, "spawn"): 1521 skip("pexpect.spawn not available") 1522 logfile = self.path.joinpath("spawn.out").open("wb") 1523 1524 child = pexpect.spawn(cmd, logfile=logfile, timeout=expect_timeout) 1525 self._request.addfinalizer(logfile.close) 1526 return child
Facilities to write tests/configuration files, execute pytest in isolation, and match against expected output, perfect for black-box testing of pytest plugins.
It attempts to isolate the test run from external factors as much as possible, modifying
the current working directory to path
and environment variables during initialization.
667 def __init__( 668 self, 669 request: FixtureRequest, 670 tmp_path_factory: TempPathFactory, 671 monkeypatch: MonkeyPatch, 672 *, 673 _ispytest: bool = False, 674 ) -> None: 675 check_ispytest(_ispytest) 676 self._request = request 677 self._mod_collections: WeakKeyDictionary[ 678 Collector, List[Union[Item, Collector]] 679 ] = WeakKeyDictionary() 680 if request.function: 681 name: str = request.function.__name__ 682 else: 683 name = request.node.name 684 self._name = name 685 self._path: Path = tmp_path_factory.mktemp(name, numbered=True) 686 #: A list of plugins to use with :py:meth:`parseconfig` and 687 #: :py:meth:`runpytest`. Initially this is an empty list but plugins can 688 #: be added to the list. The type of items to add to the list depends on 689 #: the method using them so refer to them for details. 690 self.plugins: List[Union[str, _PluggyPlugin]] = [] 691 self._sys_path_snapshot = SysPathsSnapshot() 692 self._sys_modules_snapshot = self.__take_sys_modules_snapshot() 693 self._request.addfinalizer(self._finalize) 694 self._method = self._request.config.getoption("--runpytest") 695 self._test_tmproot = tmp_path_factory.mktemp(f"tmp-{name}", numbered=True) 696 697 self._monkeypatch = mp = monkeypatch 698 self.chdir() 699 mp.setenv("PYTEST_DEBUG_TEMPROOT", str(self._test_tmproot)) 700 # Ensure no unexpected caching via tox. 701 mp.delenv("TOX_ENV_DIR", raising=False) 702 # Discard outer pytest options. 703 mp.delenv("PYTEST_ADDOPTS", raising=False) 704 # Ensure no user config is used. 705 tmphome = str(self.path) 706 mp.setenv("HOME", tmphome) 707 mp.setenv("USERPROFILE", tmphome) 708 # Do not use colors for inner runs by default. 709 mp.setenv("PY_COLORS", "0")
711 @property 712 def path(self) -> Path: 713 """Temporary directory path used to create files/run tests from, etc.""" 714 return self._path
Temporary directory path used to create files/run tests from, etc.
742 def make_hook_recorder(self, pluginmanager: PytestPluginManager) -> HookRecorder: 743 """Create a new :class:`HookRecorder` for a :class:`PytestPluginManager`.""" 744 pluginmanager.reprec = reprec = HookRecorder(pluginmanager, _ispytest=True) # type: ignore[attr-defined] 745 self._request.addfinalizer(reprec.finish_recording) 746 return reprec
Create a new HookRecorder
for a PytestPluginManager
.
748 def chdir(self) -> None: 749 """Cd into the temporary directory. 750 751 This is done automatically upon instantiation. 752 """ 753 self._monkeypatch.chdir(self.path)
Cd into the temporary directory.
This is done automatically upon instantiation.
789 def makefile(self, ext: str, *args: str, **kwargs: str) -> Path: 790 r"""Create new text file(s) in the test directory. 791 792 :param ext: 793 The extension the file(s) should use, including the dot, e.g. `.py`. 794 :param args: 795 All args are treated as strings and joined using newlines. 796 The result is written as contents to the file. The name of the 797 file is based on the test function requesting this fixture. 798 :param kwargs: 799 Each keyword is the name of a file, while the value of it will 800 be written as contents of the file. 801 :returns: 802 The first created file. 803 804 Examples: 805 .. code-block:: python 806 807 pytester.makefile(".txt", "line1", "line2") 808 809 pytester.makefile(".ini", pytest="[pytest]\naddopts=-rs\n") 810 811 To create binary files, use :meth:`pathlib.Path.write_bytes` directly: 812 813 .. code-block:: python 814 815 filename = pytester.path.joinpath("foo.bin") 816 filename.write_bytes(b"...") 817 """ 818 return self._makefile(ext, args, kwargs)
Create new text file(s) in the test directory.
Parameters
- ext:
The extension the file(s) should use, including the dot, e.g.
.py
. - args: All args are treated as strings and joined using newlines. The result is written as contents to the file. The name of the file is based on the test function requesting this fixture.
- kwargs: Each keyword is the name of a file, while the value of it will be written as contents of the file. :returns: The first created file.
Examples:
pytester.makefile(".txt", "line1", "line2")
pytester.makefile(".ini", pytest="[pytest]\naddopts=-rs\n")
To create binary files, use pathlib.Path.write_bytes()
directly:
filename = pytester.path.joinpath("foo.bin")
filename.write_bytes(b"...")
820 def makeconftest(self, source: str) -> Path: 821 """Write a conftest.py file. 822 823 :param source: The contents. 824 :returns: The conftest.py file. 825 """ 826 return self.makepyfile(conftest=source)
Write a conftest.py file.
Parameters
- source: The contents. :returns: The conftest.py file.
828 def makeini(self, source: str) -> Path: 829 """Write a tox.ini file. 830 831 :param source: The contents. 832 :returns: The tox.ini file. 833 """ 834 return self.makefile(".ini", tox=source)
Write a tox.ini file.
Parameters
- source: The contents. :returns: The tox.ini file.
836 def getinicfg(self, source: str) -> SectionWrapper: 837 """Return the pytest section from the tox.ini config file.""" 838 p = self.makeini(source) 839 return IniConfig(str(p))["pytest"]
Return the pytest section from the tox.ini config file.
841 def makepyprojecttoml(self, source: str) -> Path: 842 """Write a pyproject.toml file. 843 844 :param source: The contents. 845 :returns: The pyproject.ini file. 846 847 .. versionadded:: 6.0 848 """ 849 return self.makefile(".toml", pyproject=source)
Write a pyproject.toml file.
Parameters
- source: The contents. :returns: The pyproject.ini file.
New in version 6.0.
851 def makepyfile(self, *args, **kwargs) -> Path: 852 r"""Shortcut for .makefile() with a .py extension. 853 854 Defaults to the test name with a '.py' extension, e.g test_foobar.py, overwriting 855 existing files. 856 857 Examples: 858 .. code-block:: python 859 860 def test_something(pytester): 861 # Initial file is created test_something.py. 862 pytester.makepyfile("foobar") 863 # To create multiple files, pass kwargs accordingly. 864 pytester.makepyfile(custom="foobar") 865 # At this point, both 'test_something.py' & 'custom.py' exist in the test directory. 866 867 """ 868 return self._makefile(".py", args, kwargs)
Shortcut for .makefile() with a .py extension.
Defaults to the test name with a '.py' extension, e.g test_foobar.py, overwriting existing files.
Examples:
def test_something(pytester):
# Initial file is created test_something.py.
pytester.makepyfile("foobar")
# To create multiple files, pass kwargs accordingly.
pytester.makepyfile(custom="foobar")
# At this point, both 'test_something.py' & 'custom.py' exist in the test directory.
870 def maketxtfile(self, *args, **kwargs) -> Path: 871 r"""Shortcut for .makefile() with a .txt extension. 872 873 Defaults to the test name with a '.txt' extension, e.g test_foobar.txt, overwriting 874 existing files. 875 876 Examples: 877 .. code-block:: python 878 879 def test_something(pytester): 880 # Initial file is created test_something.txt. 881 pytester.maketxtfile("foobar") 882 # To create multiple files, pass kwargs accordingly. 883 pytester.maketxtfile(custom="foobar") 884 # At this point, both 'test_something.txt' & 'custom.txt' exist in the test directory. 885 886 """ 887 return self._makefile(".txt", args, kwargs)
Shortcut for .makefile() with a .txt extension.
Defaults to the test name with a '.txt' extension, e.g test_foobar.txt, overwriting existing files.
Examples:
def test_something(pytester):
# Initial file is created test_something.txt.
pytester.maketxtfile("foobar")
# To create multiple files, pass kwargs accordingly.
pytester.maketxtfile(custom="foobar")
# At this point, both 'test_something.txt' & 'custom.txt' exist in the test directory.
889 def syspathinsert( 890 self, path: Optional[Union[str, "os.PathLike[str]"]] = None 891 ) -> None: 892 """Prepend a directory to sys.path, defaults to :attr:`path`. 893 894 This is undone automatically when this object dies at the end of each 895 test. 896 897 :param path: 898 The path. 899 """ 900 if path is None: 901 path = self.path 902 903 self._monkeypatch.syspath_prepend(str(path))
Prepend a directory to sys.path, defaults to path
.
This is undone automatically when this object dies at the end of each test.
Parameters
- path: The path.
905 def mkdir(self, name: Union[str, "os.PathLike[str]"]) -> Path: 906 """Create a new (sub)directory. 907 908 :param name: 909 The name of the directory, relative to the pytester path. 910 :returns: 911 The created directory. 912 """ 913 p = self.path / name 914 p.mkdir() 915 return p
Create a new (sub)directory.
Parameters
- name: The name of the directory, relative to the pytester path. :returns: The created directory.
917 def mkpydir(self, name: Union[str, "os.PathLike[str]"]) -> Path: 918 """Create a new python package. 919 920 This creates a (sub)directory with an empty ``__init__.py`` file so it 921 gets recognised as a Python package. 922 """ 923 p = self.path / name 924 p.mkdir() 925 p.joinpath("__init__.py").touch() 926 return p
Create a new python package.
This creates a (sub)directory with an empty __init__.py
file so it
gets recognised as a Python package.
928 def copy_example(self, name: Optional[str] = None) -> Path: 929 """Copy file from project's directory into the testdir. 930 931 :param name: 932 The name of the file to copy. 933 :return: 934 Path to the copied directory (inside ``self.path``). 935 """ 936 example_dir_ = self._request.config.getini("pytester_example_dir") 937 if example_dir_ is None: 938 raise ValueError("pytester_example_dir is unset, can't copy examples") 939 example_dir: Path = self._request.config.rootpath / example_dir_ 940 941 for extra_element in self._request.node.iter_markers("pytester_example_path"): 942 assert extra_element.args 943 example_dir = example_dir.joinpath(*extra_element.args) 944 945 if name is None: 946 func_name = self._name 947 maybe_dir = example_dir / func_name 948 maybe_file = example_dir / (func_name + ".py") 949 950 if maybe_dir.is_dir(): 951 example_path = maybe_dir 952 elif maybe_file.is_file(): 953 example_path = maybe_file 954 else: 955 raise LookupError( 956 f"{func_name} can't be found as module or package in {example_dir}" 957 ) 958 else: 959 example_path = example_dir.joinpath(name) 960 961 if example_path.is_dir() and not example_path.joinpath("__init__.py").is_file(): 962 shutil.copytree(example_path, self.path, symlinks=True, dirs_exist_ok=True) 963 return self.path 964 elif example_path.is_file(): 965 result = self.path.joinpath(example_path.name) 966 shutil.copy(example_path, result) 967 return result 968 else: 969 raise LookupError( 970 f'example "{example_path}" is not found as a file or directory' 971 )
Copy file from project's directory into the testdir.
Parameters
- name: The name of the file to copy.
Returns
Path to the copied directory (inside ``self.path``).
973 def getnode( 974 self, config: Config, arg: Union[str, "os.PathLike[str]"] 975 ) -> Union[Collector, Item]: 976 """Get the collection node of a file. 977 978 :param config: 979 A pytest config. 980 See :py:meth:`parseconfig` and :py:meth:`parseconfigure` for creating it. 981 :param arg: 982 Path to the file. 983 :returns: 984 The node. 985 """ 986 session = Session.from_config(config) 987 assert "::" not in str(arg) 988 p = Path(os.path.abspath(arg)) 989 config.hook.pytest_sessionstart(session=session) 990 res = session.perform_collect([str(p)], genitems=False)[0] 991 config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) 992 return res
Get the collection node of a file.
Parameters
- config:
A pytest config.
See
parseconfig()
andparseconfigure()
for creating it. - arg: Path to the file. :returns: The node.
994 def getpathnode( 995 self, path: Union[str, "os.PathLike[str]"] 996 ) -> Union[Collector, Item]: 997 """Return the collection node of a file. 998 999 This is like :py:meth:`getnode` but uses :py:meth:`parseconfigure` to 1000 create the (configured) pytest Config instance. 1001 1002 :param path: 1003 Path to the file. 1004 :returns: 1005 The node. 1006 """ 1007 path = Path(path) 1008 config = self.parseconfigure(path) 1009 session = Session.from_config(config) 1010 x = bestrelpath(session.path, path) 1011 config.hook.pytest_sessionstart(session=session) 1012 res = session.perform_collect([x], genitems=False)[0] 1013 config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) 1014 return res
Return the collection node of a file.
This is like getnode()
but uses parseconfigure()
to
create the (configured) pytest Config instance.
Parameters
- path: Path to the file. :returns: The node.
1016 def genitems(self, colitems: Sequence[Union[Item, Collector]]) -> List[Item]: 1017 """Generate all test items from a collection node. 1018 1019 This recurses into the collection node and returns a list of all the 1020 test items contained within. 1021 1022 :param colitems: 1023 The collection nodes. 1024 :returns: 1025 The collected items. 1026 """ 1027 session = colitems[0].session 1028 result: List[Item] = [] 1029 for colitem in colitems: 1030 result.extend(session.genitems(colitem)) 1031 return result
Generate all test items from a collection node.
This recurses into the collection node and returns a list of all the test items contained within.
Parameters
- colitems: The collection nodes. :returns: The collected items.
1033 def runitem(self, source: str) -> Any: 1034 """Run the "test_func" Item. 1035 1036 The calling test instance (class containing the test method) must 1037 provide a ``.getrunner()`` method which should return a runner which 1038 can run the test protocol for a single item, e.g. 1039 ``_pytest.runner.runtestprotocol``. 1040 """ 1041 # used from runner functional tests 1042 item = self.getitem(source) 1043 # the test class where we are called from wants to provide the runner 1044 testclassinstance = self._request.instance 1045 runner = testclassinstance.getrunner() 1046 return runner(item)
Run the "test_func" Item.
The calling test instance (class containing the test method) must
provide a .getrunner()
method which should return a runner which
can run the test protocol for a single item, e.g.
_pytest.runner.runtestprotocol
.
1048 def inline_runsource(self, source: str, *cmdlineargs) -> HookRecorder: 1049 """Run a test module in process using ``pytest.main()``. 1050 1051 This run writes "source" into a temporary file and runs 1052 ``pytest.main()`` on it, returning a :py:class:`HookRecorder` instance 1053 for the result. 1054 1055 :param source: The source code of the test module. 1056 :param cmdlineargs: Any extra command line arguments to use. 1057 """ 1058 p = self.makepyfile(source) 1059 values = [*list(cmdlineargs), p] 1060 return self.inline_run(*values)
Run a test module in process using pytest.main()
.
This run writes "source" into a temporary file and runs
pytest.main()
on it, returning a HookRecorder
instance
for the result.
Parameters
- source: The source code of the test module.
- cmdlineargs: Any extra command line arguments to use.
1062 def inline_genitems(self, *args) -> Tuple[List[Item], HookRecorder]: 1063 """Run ``pytest.main(['--collect-only'])`` in-process. 1064 1065 Runs the :py:func:`pytest.main` function to run all of pytest inside 1066 the test process itself like :py:meth:`inline_run`, but returns a 1067 tuple of the collected items and a :py:class:`HookRecorder` instance. 1068 """ 1069 rec = self.inline_run("--collect-only", *args) 1070 items = [x.item for x in rec.getcalls("pytest_itemcollected")] 1071 return items, rec
Run pytest.main(['--collect-only'])
in-process.
Runs the pytest.main()
function to run all of pytest inside
the test process itself like inline_run()
, but returns a
tuple of the collected items and a HookRecorder
instance.
1073 def inline_run( 1074 self, 1075 *args: Union[str, "os.PathLike[str]"], 1076 plugins=(), 1077 no_reraise_ctrlc: bool = False, 1078 ) -> HookRecorder: 1079 """Run ``pytest.main()`` in-process, returning a HookRecorder. 1080 1081 Runs the :py:func:`pytest.main` function to run all of pytest inside 1082 the test process itself. This means it can return a 1083 :py:class:`HookRecorder` instance which gives more detailed results 1084 from that run than can be done by matching stdout/stderr from 1085 :py:meth:`runpytest`. 1086 1087 :param args: 1088 Command line arguments to pass to :py:func:`pytest.main`. 1089 :param plugins: 1090 Extra plugin instances the ``pytest.main()`` instance should use. 1091 :param no_reraise_ctrlc: 1092 Typically we reraise keyboard interrupts from the child run. If 1093 True, the KeyboardInterrupt exception is captured. 1094 """ 1095 # (maybe a cpython bug?) the importlib cache sometimes isn't updated 1096 # properly between file creation and inline_run (especially if imports 1097 # are interspersed with file creation) 1098 importlib.invalidate_caches() 1099 1100 plugins = list(plugins) 1101 finalizers = [] 1102 try: 1103 # Any sys.module or sys.path changes done while running pytest 1104 # inline should be reverted after the test run completes to avoid 1105 # clashing with later inline tests run within the same pytest test, 1106 # e.g. just because they use matching test module names. 1107 finalizers.append(self.__take_sys_modules_snapshot().restore) 1108 finalizers.append(SysPathsSnapshot().restore) 1109 1110 # Important note: 1111 # - our tests should not leave any other references/registrations 1112 # laying around other than possibly loaded test modules 1113 # referenced from sys.modules, as nothing will clean those up 1114 # automatically 1115 1116 rec = [] 1117 1118 class Collect: 1119 def pytest_configure(x, config: Config) -> None: 1120 rec.append(self.make_hook_recorder(config.pluginmanager)) 1121 1122 plugins.append(Collect()) 1123 ret = main([str(x) for x in args], plugins=plugins) 1124 if len(rec) == 1: 1125 reprec = rec.pop() 1126 else: 1127 1128 class reprec: # type: ignore 1129 pass 1130 1131 reprec.ret = ret 1132 1133 # Typically we reraise keyboard interrupts from the child run 1134 # because it's our user requesting interruption of the testing. 1135 if ret == ExitCode.INTERRUPTED and not no_reraise_ctrlc: 1136 calls = reprec.getcalls("pytest_keyboard_interrupt") 1137 if calls and calls[-1].excinfo.type == KeyboardInterrupt: 1138 raise KeyboardInterrupt() 1139 return reprec 1140 finally: 1141 for finalizer in finalizers: 1142 finalizer()
Run pytest.main()
in-process, returning a HookRecorder.
Runs the pytest.main()
function to run all of pytest inside
the test process itself. This means it can return a
HookRecorder
instance which gives more detailed results
from that run than can be done by matching stdout/stderr from
runpytest()
.
Parameters
- args:
Command line arguments to pass to
pytest.main()
. - plugins:
Extra plugin instances the
pytest.main()
instance should use. - no_reraise_ctrlc: Typically we reraise keyboard interrupts from the child run. If True, the KeyboardInterrupt exception is captured.
1144 def runpytest_inprocess( 1145 self, *args: Union[str, "os.PathLike[str]"], **kwargs: Any 1146 ) -> RunResult: 1147 """Return result of running pytest in-process, providing a similar 1148 interface to what self.runpytest() provides.""" 1149 syspathinsert = kwargs.pop("syspathinsert", False) 1150 1151 if syspathinsert: 1152 self.syspathinsert() 1153 now = timing.time() 1154 capture = _get_multicapture("sys") 1155 capture.start_capturing() 1156 try: 1157 try: 1158 reprec = self.inline_run(*args, **kwargs) 1159 except SystemExit as e: 1160 ret = e.args[0] 1161 try: 1162 ret = ExitCode(e.args[0]) 1163 except ValueError: 1164 pass 1165 1166 class reprec: # type: ignore 1167 ret = ret 1168 1169 except Exception: 1170 traceback.print_exc() 1171 1172 class reprec: # type: ignore 1173 ret = ExitCode(3) 1174 1175 finally: 1176 out, err = capture.readouterr() 1177 capture.stop_capturing() 1178 sys.stdout.write(out) 1179 sys.stderr.write(err) 1180 1181 assert reprec.ret is not None 1182 res = RunResult( 1183 reprec.ret, out.splitlines(), err.splitlines(), timing.time() - now 1184 ) 1185 res.reprec = reprec # type: ignore 1186 return res
Return result of running pytest in-process, providing a similar interface to what self.runpytest() provides.
1188 def runpytest( 1189 self, *args: Union[str, "os.PathLike[str]"], **kwargs: Any 1190 ) -> RunResult: 1191 """Run pytest inline or in a subprocess, depending on the command line 1192 option "--runpytest" and return a :py:class:`~pytest.RunResult`.""" 1193 new_args = self._ensure_basetemp(args) 1194 if self._method == "inprocess": 1195 return self.runpytest_inprocess(*new_args, **kwargs) 1196 elif self._method == "subprocess": 1197 return self.runpytest_subprocess(*new_args, **kwargs) 1198 raise RuntimeError(f"Unrecognized runpytest option: {self._method}")
Run pytest inline or in a subprocess, depending on the command line
option "--runpytest" and return a ~pytest.RunResult
.
1211 def parseconfig(self, *args: Union[str, "os.PathLike[str]"]) -> Config: 1212 """Return a new pytest :class:`pytest.Config` instance from given 1213 commandline args. 1214 1215 This invokes the pytest bootstrapping code in _pytest.config to create a 1216 new :py:class:`pytest.PytestPluginManager` and call the 1217 :hook:`pytest_cmdline_parse` hook to create a new :class:`pytest.Config` 1218 instance. 1219 1220 If :attr:`plugins` has been populated they should be plugin modules 1221 to be registered with the plugin manager. 1222 """ 1223 import _pytest.config 1224 1225 new_args = self._ensure_basetemp(args) 1226 new_args = [str(x) for x in new_args] 1227 1228 config = _pytest.config._prepareconfig(new_args, self.plugins) # type: ignore[arg-type] 1229 # we don't know what the test will do with this half-setup config 1230 # object and thus we make sure it gets unconfigured properly in any 1231 # case (otherwise capturing could still be active, for example) 1232 self._request.addfinalizer(config._ensure_unconfigure) 1233 return config
Return a new pytest pytest.Config
instance from given
commandline args.
This invokes the pytest bootstrapping code in _pytest.config to create a
new pytest.PytestPluginManager
and call the
:hook:pytest_cmdline_parse
hook to create a new pytest.Config
instance.
If plugins
has been populated they should be plugin modules
to be registered with the plugin manager.
1235 def parseconfigure(self, *args: Union[str, "os.PathLike[str]"]) -> Config: 1236 """Return a new pytest configured Config instance. 1237 1238 Returns a new :py:class:`pytest.Config` instance like 1239 :py:meth:`parseconfig`, but also calls the :hook:`pytest_configure` 1240 hook. 1241 """ 1242 config = self.parseconfig(*args) 1243 config._do_configure() 1244 return config
Return a new pytest configured Config instance.
Returns a new pytest.Config
instance like
parseconfig()
, but also calls the :hook:pytest_configure
hook.
1246 def getitem( 1247 self, source: Union[str, "os.PathLike[str]"], funcname: str = "test_func" 1248 ) -> Item: 1249 """Return the test item for a test function. 1250 1251 Writes the source to a python file and runs pytest's collection on 1252 the resulting module, returning the test item for the requested 1253 function name. 1254 1255 :param source: 1256 The module source. 1257 :param funcname: 1258 The name of the test function for which to return a test item. 1259 :returns: 1260 The test item. 1261 """ 1262 items = self.getitems(source) 1263 for item in items: 1264 if item.name == funcname: 1265 return item 1266 assert 0, f"{funcname!r} item not found in module:\n{source}\nitems: {items}"
Return the test item for a test function.
Writes the source to a python file and runs pytest's collection on the resulting module, returning the test item for the requested function name.
Parameters
- source: The module source.
- funcname: The name of the test function for which to return a test item. :returns: The test item.
1268 def getitems(self, source: Union[str, "os.PathLike[str]"]) -> List[Item]: 1269 """Return all test items collected from the module. 1270 1271 Writes the source to a Python file and runs pytest's collection on 1272 the resulting module, returning all test items contained within. 1273 """ 1274 modcol = self.getmodulecol(source) 1275 return self.genitems([modcol])
Return all test items collected from the module.
Writes the source to a Python file and runs pytest's collection on the resulting module, returning all test items contained within.
1277 def getmodulecol( 1278 self, 1279 source: Union[str, "os.PathLike[str]"], 1280 configargs=(), 1281 *, 1282 withinit: bool = False, 1283 ): 1284 """Return the module collection node for ``source``. 1285 1286 Writes ``source`` to a file using :py:meth:`makepyfile` and then 1287 runs the pytest collection on it, returning the collection node for the 1288 test module. 1289 1290 :param source: 1291 The source code of the module to collect. 1292 1293 :param configargs: 1294 Any extra arguments to pass to :py:meth:`parseconfigure`. 1295 1296 :param withinit: 1297 Whether to also write an ``__init__.py`` file to the same 1298 directory to ensure it is a package. 1299 """ 1300 if isinstance(source, os.PathLike): 1301 path = self.path.joinpath(source) 1302 assert not withinit, "not supported for paths" 1303 else: 1304 kw = {self._name: str(source)} 1305 path = self.makepyfile(**kw) 1306 if withinit: 1307 self.makepyfile(__init__="#") 1308 self.config = config = self.parseconfigure(path, *configargs) 1309 return self.getnode(config, path)
Return the module collection node for source
.
Writes source
to a file using makepyfile()
and then
runs the pytest collection on it, returning the collection node for the
test module.
Parameters
source: The source code of the module to collect.
configargs: Any extra arguments to pass to
parseconfigure()
.withinit: Whether to also write an
__init__.py
file to the same directory to ensure it is a package.
1311 def collect_by_name( 1312 self, modcol: Collector, name: str 1313 ) -> Optional[Union[Item, Collector]]: 1314 """Return the collection node for name from the module collection. 1315 1316 Searches a module collection node for a collection node matching the 1317 given name. 1318 1319 :param modcol: A module collection node; see :py:meth:`getmodulecol`. 1320 :param name: The name of the node to return. 1321 """ 1322 if modcol not in self._mod_collections: 1323 self._mod_collections[modcol] = list(modcol.collect()) 1324 for colitem in self._mod_collections[modcol]: 1325 if colitem.name == name: 1326 return colitem 1327 return None
Return the collection node for name from the module collection.
Searches a module collection node for a collection node matching the given name.
Parameters
- modcol: A module collection node; see
getmodulecol()
. - name: The name of the node to return.
1329 def popen( 1330 self, 1331 cmdargs: Sequence[Union[str, "os.PathLike[str]"]], 1332 stdout: Union[int, TextIO] = subprocess.PIPE, 1333 stderr: Union[int, TextIO] = subprocess.PIPE, 1334 stdin: Union[NotSetType, bytes, IO[Any], int] = CLOSE_STDIN, 1335 **kw, 1336 ): 1337 """Invoke :py:class:`subprocess.Popen`. 1338 1339 Calls :py:class:`subprocess.Popen` making sure the current working 1340 directory is in ``PYTHONPATH``. 1341 1342 You probably want to use :py:meth:`run` instead. 1343 """ 1344 env = os.environ.copy() 1345 env["PYTHONPATH"] = os.pathsep.join( 1346 filter(None, [os.getcwd(), env.get("PYTHONPATH", "")]) 1347 ) 1348 kw["env"] = env 1349 1350 if stdin is self.CLOSE_STDIN: 1351 kw["stdin"] = subprocess.PIPE 1352 elif isinstance(stdin, bytes): 1353 kw["stdin"] = subprocess.PIPE 1354 else: 1355 kw["stdin"] = stdin 1356 1357 popen = subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) 1358 if stdin is self.CLOSE_STDIN: 1359 assert popen.stdin is not None 1360 popen.stdin.close() 1361 elif isinstance(stdin, bytes): 1362 assert popen.stdin is not None 1363 popen.stdin.write(stdin) 1364 1365 return popen
Invoke subprocess.Popen
.
Calls subprocess.Popen
making sure the current working
directory is in PYTHONPATH
.
You probably want to use run()
instead.
1367 def run( 1368 self, 1369 *cmdargs: Union[str, "os.PathLike[str]"], 1370 timeout: Optional[float] = None, 1371 stdin: Union[NotSetType, bytes, IO[Any], int] = CLOSE_STDIN, 1372 ) -> RunResult: 1373 """Run a command with arguments. 1374 1375 Run a process using :py:class:`subprocess.Popen` saving the stdout and 1376 stderr. 1377 1378 :param cmdargs: 1379 The sequence of arguments to pass to :py:class:`subprocess.Popen`, 1380 with path-like objects being converted to :py:class:`str` 1381 automatically. 1382 :param timeout: 1383 The period in seconds after which to timeout and raise 1384 :py:class:`Pytester.TimeoutExpired`. 1385 :param stdin: 1386 Optional standard input. 1387 1388 - If it is ``CLOSE_STDIN`` (Default), then this method calls 1389 :py:class:`subprocess.Popen` with ``stdin=subprocess.PIPE``, and 1390 the standard input is closed immediately after the new command is 1391 started. 1392 1393 - If it is of type :py:class:`bytes`, these bytes are sent to the 1394 standard input of the command. 1395 1396 - Otherwise, it is passed through to :py:class:`subprocess.Popen`. 1397 For further information in this case, consult the document of the 1398 ``stdin`` parameter in :py:class:`subprocess.Popen`. 1399 :returns: 1400 The result. 1401 """ 1402 __tracebackhide__ = True 1403 1404 cmdargs = tuple(os.fspath(arg) for arg in cmdargs) 1405 p1 = self.path.joinpath("stdout") 1406 p2 = self.path.joinpath("stderr") 1407 print("running:", *cmdargs) 1408 print(" in:", Path.cwd()) 1409 1410 with p1.open("w", encoding="utf8") as f1, p2.open("w", encoding="utf8") as f2: 1411 now = timing.time() 1412 popen = self.popen( 1413 cmdargs, 1414 stdin=stdin, 1415 stdout=f1, 1416 stderr=f2, 1417 close_fds=(sys.platform != "win32"), 1418 ) 1419 if popen.stdin is not None: 1420 popen.stdin.close() 1421 1422 def handle_timeout() -> None: 1423 __tracebackhide__ = True 1424 1425 timeout_message = f"{timeout} second timeout expired running: {cmdargs}" 1426 1427 popen.kill() 1428 popen.wait() 1429 raise self.TimeoutExpired(timeout_message) 1430 1431 if timeout is None: 1432 ret = popen.wait() 1433 else: 1434 try: 1435 ret = popen.wait(timeout) 1436 except subprocess.TimeoutExpired: 1437 handle_timeout() 1438 1439 with p1.open(encoding="utf8") as f1, p2.open(encoding="utf8") as f2: 1440 out = f1.read().splitlines() 1441 err = f2.read().splitlines() 1442 1443 self._dump_lines(out, sys.stdout) 1444 self._dump_lines(err, sys.stderr) 1445 1446 with contextlib.suppress(ValueError): 1447 ret = ExitCode(ret) 1448 return RunResult(ret, out, err, timing.time() - now)
Run a command with arguments.
Run a process using subprocess.Popen
saving the stdout and
stderr.
Parameters
- cmdargs:
The sequence of arguments to pass to
subprocess.Popen
, with path-like objects being converted tostr
automatically. - timeout:
The period in seconds after which to timeout and raise
Pytester.TimeoutExpired
. stdin: Optional standard input.
If it is
CLOSE_STDIN
(Default), then this method callssubprocess.Popen
withstdin=subprocess.PIPE
, and the standard input is closed immediately after the new command is started.If it is of type
bytes
, these bytes are sent to the standard input of the command.Otherwise, it is passed through to
subprocess.Popen
. For further information in this case, consult the document of thestdin
parameter insubprocess.Popen
. :returns: The result.
1460 def runpython(self, script: "os.PathLike[str]") -> RunResult: 1461 """Run a python script using sys.executable as interpreter.""" 1462 return self.run(sys.executable, script)
Run a python script using sys.executable as interpreter.
1464 def runpython_c(self, command: str) -> RunResult: 1465 """Run ``python -c "command"``.""" 1466 return self.run(sys.executable, "-c", command)
Run python -c "command"
.
1468 def runpytest_subprocess( 1469 self, *args: Union[str, "os.PathLike[str]"], timeout: Optional[float] = None 1470 ) -> RunResult: 1471 """Run pytest as a subprocess with given arguments. 1472 1473 Any plugins added to the :py:attr:`plugins` list will be added using the 1474 ``-p`` command line option. Additionally ``--basetemp`` is used to put 1475 any temporary files and directories in a numbered directory prefixed 1476 with "runpytest-" to not conflict with the normal numbered pytest 1477 location for temporary files and directories. 1478 1479 :param args: 1480 The sequence of arguments to pass to the pytest subprocess. 1481 :param timeout: 1482 The period in seconds after which to timeout and raise 1483 :py:class:`Pytester.TimeoutExpired`. 1484 :returns: 1485 The result. 1486 """ 1487 __tracebackhide__ = True 1488 p = make_numbered_dir(root=self.path, prefix="runpytest-", mode=0o700) 1489 args = ("--basetemp=%s" % p, *args) 1490 plugins = [x for x in self.plugins if isinstance(x, str)] 1491 if plugins: 1492 args = ("-p", plugins[0], *args) 1493 args = self._getpytestargs() + args 1494 return self.run(*args, timeout=timeout)
Run pytest as a subprocess with given arguments.
Any plugins added to the plugins
list will be added using the
-p
command line option. Additionally --basetemp
is used to put
any temporary files and directories in a numbered directory prefixed
with "runpytest-" to not conflict with the normal numbered pytest
location for temporary files and directories.
Parameters
- args: The sequence of arguments to pass to the pytest subprocess.
- timeout:
The period in seconds after which to timeout and raise
Pytester.TimeoutExpired
. :returns: The result.
1496 def spawn_pytest( 1497 self, string: str, expect_timeout: float = 10.0 1498 ) -> "pexpect.spawn": 1499 """Run pytest using pexpect. 1500 1501 This makes sure to use the right pytest and sets up the temporary 1502 directory locations. 1503 1504 The pexpect child is returned. 1505 """ 1506 basetemp = self.path / "temp-pexpect" 1507 basetemp.mkdir(mode=0o700) 1508 invoke = " ".join(map(str, self._getpytestargs())) 1509 cmd = f"{invoke} --basetemp={basetemp} {string}" 1510 return self.spawn(cmd, expect_timeout=expect_timeout)
Run pytest using pexpect.
This makes sure to use the right pytest and sets up the temporary directory locations.
The pexpect child is returned.
1512 def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn": 1513 """Run a command using pexpect. 1514 1515 The pexpect child is returned. 1516 """ 1517 pexpect = importorskip("pexpect", "3.0") 1518 if hasattr(sys, "pypy_version_info") and "64" in platform.machine(): 1519 skip("pypy-64 bit not supported") 1520 if not hasattr(pexpect, "spawn"): 1521 skip("pexpect.spawn not available") 1522 logfile = self.path.joinpath("spawn.out").open("wb") 1523 1524 child = pexpect.spawn(cmd, logfile=logfile, timeout=expect_timeout) 1525 self._request.addfinalizer(logfile.close) 1526 return child
Run a command using pexpect.
The pexpect child is returned.
Common base class for all non-exit exceptions.
Inherited Members
- builtins.Exception
- Exception
- builtins.BaseException
- with_traceback
- add_note
- args
396@final 397class PytestPluginManager(PluginManager): 398 """A :py:class:`pluggy.PluginManager <pluggy.PluginManager>` with 399 additional pytest-specific functionality: 400 401 * Loading plugins from the command line, ``PYTEST_PLUGINS`` env variable and 402 ``pytest_plugins`` global variables found in plugins being loaded. 403 * ``conftest.py`` loading during start-up. 404 """ 405 406 def __init__(self) -> None: 407 import _pytest.assertion 408 409 super().__init__("pytest") 410 411 # -- State related to local conftest plugins. 412 # All loaded conftest modules. 413 self._conftest_plugins: Set[types.ModuleType] = set() 414 # All conftest modules applicable for a directory. 415 # This includes the directory's own conftest modules as well 416 # as those of its parent directories. 417 self._dirpath2confmods: Dict[Path, List[types.ModuleType]] = {} 418 # Cutoff directory above which conftests are no longer discovered. 419 self._confcutdir: Optional[Path] = None 420 # If set, conftest loading is skipped. 421 self._noconftest = False 422 423 # _getconftestmodules()'s call to _get_directory() causes a stat 424 # storm when it's called potentially thousands of times in a test 425 # session (#9478), often with the same path, so cache it. 426 self._get_directory = lru_cache(256)(_get_directory) 427 428 # plugins that were explicitly skipped with pytest.skip 429 # list of (module name, skip reason) 430 # previously we would issue a warning when a plugin was skipped, but 431 # since we refactored warnings as first citizens of Config, they are 432 # just stored here to be used later. 433 self.skipped_plugins: List[Tuple[str, str]] = [] 434 435 self.add_hookspecs(_pytest.hookspec) 436 self.register(self) 437 if os.environ.get("PYTEST_DEBUG"): 438 err: IO[str] = sys.stderr 439 encoding: str = getattr(err, "encoding", "utf8") 440 try: 441 err = open( 442 os.dup(err.fileno()), 443 mode=err.mode, 444 buffering=1, 445 encoding=encoding, 446 ) 447 except Exception: 448 pass 449 self.trace.root.setwriter(err.write) 450 self.enable_tracing() 451 452 # Config._consider_importhook will set a real object if required. 453 self.rewrite_hook = _pytest.assertion.DummyRewriteHook() 454 # Used to know when we are importing conftests after the pytest_configure stage. 455 self._configured = False 456 457 def parse_hookimpl_opts( 458 self, plugin: _PluggyPlugin, name: str 459 ) -> Optional[HookimplOpts]: 460 """:meta private:""" 461 # pytest hooks are always prefixed with "pytest_", 462 # so we avoid accessing possibly non-readable attributes 463 # (see issue #1073). 464 if not name.startswith("pytest_"): 465 return None 466 # Ignore names which can not be hooks. 467 if name == "pytest_plugins": 468 return None 469 470 opts = super().parse_hookimpl_opts(plugin, name) 471 if opts is not None: 472 return opts 473 474 method = getattr(plugin, name) 475 # Consider only actual functions for hooks (#3775). 476 if not inspect.isroutine(method): 477 return None 478 # Collect unmarked hooks as long as they have the `pytest_' prefix. 479 return _get_legacy_hook_marks( # type: ignore[return-value] 480 method, "impl", ("tryfirst", "trylast", "optionalhook", "hookwrapper") 481 ) 482 483 def parse_hookspec_opts(self, module_or_class, name: str) -> Optional[HookspecOpts]: 484 """:meta private:""" 485 opts = super().parse_hookspec_opts(module_or_class, name) 486 if opts is None: 487 method = getattr(module_or_class, name) 488 if name.startswith("pytest_"): 489 opts = _get_legacy_hook_marks( # type: ignore[assignment] 490 method, 491 "spec", 492 ("firstresult", "historic"), 493 ) 494 return opts 495 496 def register( 497 self, plugin: _PluggyPlugin, name: Optional[str] = None 498 ) -> Optional[str]: 499 if name in _pytest.deprecated.DEPRECATED_EXTERNAL_PLUGINS: 500 warnings.warn( 501 PytestConfigWarning( 502 "{} plugin has been merged into the core, " 503 "please remove it from your requirements.".format( 504 name.replace("_", "-") 505 ) 506 ) 507 ) 508 return None 509 plugin_name = super().register(plugin, name) 510 if plugin_name is not None: 511 self.hook.pytest_plugin_registered.call_historic( 512 kwargs=dict( 513 plugin=plugin, 514 plugin_name=plugin_name, 515 manager=self, 516 ) 517 ) 518 519 if isinstance(plugin, types.ModuleType): 520 self.consider_module(plugin) 521 return plugin_name 522 523 def getplugin(self, name: str): 524 # Support deprecated naming because plugins (xdist e.g.) use it. 525 plugin: Optional[_PluggyPlugin] = self.get_plugin(name) 526 return plugin 527 528 def hasplugin(self, name: str) -> bool: 529 """Return whether a plugin with the given name is registered.""" 530 return bool(self.get_plugin(name)) 531 532 def pytest_configure(self, config: "Config") -> None: 533 """:meta private:""" 534 # XXX now that the pluginmanager exposes hookimpl(tryfirst...) 535 # we should remove tryfirst/trylast as markers. 536 config.addinivalue_line( 537 "markers", 538 "tryfirst: mark a hook implementation function such that the " 539 "plugin machinery will try to call it first/as early as possible. " 540 "DEPRECATED, use @pytest.hookimpl(tryfirst=True) instead.", 541 ) 542 config.addinivalue_line( 543 "markers", 544 "trylast: mark a hook implementation function such that the " 545 "plugin machinery will try to call it last/as late as possible. " 546 "DEPRECATED, use @pytest.hookimpl(trylast=True) instead.", 547 ) 548 self._configured = True 549 550 # 551 # Internal API for local conftest plugin handling. 552 # 553 def _set_initial_conftests( 554 self, 555 args: Sequence[Union[str, Path]], 556 pyargs: bool, 557 noconftest: bool, 558 rootpath: Path, 559 confcutdir: Optional[Path], 560 invocation_dir: Path, 561 importmode: Union[ImportMode, str], 562 *, 563 consider_namespace_packages: bool, 564 ) -> None: 565 """Load initial conftest files given a preparsed "namespace". 566 567 As conftest files may add their own command line options which have 568 arguments ('--my-opt somepath') we might get some false positives. 569 All builtin and 3rd party plugins will have been loaded, however, so 570 common options will not confuse our logic here. 571 """ 572 self._confcutdir = ( 573 absolutepath(invocation_dir / confcutdir) if confcutdir else None 574 ) 575 self._noconftest = noconftest 576 self._using_pyargs = pyargs 577 foundanchor = False 578 for intitial_path in args: 579 path = str(intitial_path) 580 # remove node-id syntax 581 i = path.find("::") 582 if i != -1: 583 path = path[:i] 584 anchor = absolutepath(invocation_dir / path) 585 586 # Ensure we do not break if what appears to be an anchor 587 # is in fact a very long option (#10169, #11394). 588 if safe_exists(anchor): 589 self._try_load_conftest( 590 anchor, 591 importmode, 592 rootpath, 593 consider_namespace_packages=consider_namespace_packages, 594 ) 595 foundanchor = True 596 if not foundanchor: 597 self._try_load_conftest( 598 invocation_dir, 599 importmode, 600 rootpath, 601 consider_namespace_packages=consider_namespace_packages, 602 ) 603 604 def _is_in_confcutdir(self, path: Path) -> bool: 605 """Whether to consider the given path to load conftests from.""" 606 if self._confcutdir is None: 607 return True 608 # The semantics here are literally: 609 # Do not load a conftest if it is found upwards from confcut dir. 610 # But this is *not* the same as: 611 # Load only conftests from confcutdir or below. 612 # At first glance they might seem the same thing, however we do support use cases where 613 # we want to load conftests that are not found in confcutdir or below, but are found 614 # in completely different directory hierarchies like packages installed 615 # in out-of-source trees. 616 # (see #9767 for a regression where the logic was inverted). 617 return path not in self._confcutdir.parents 618 619 def _try_load_conftest( 620 self, 621 anchor: Path, 622 importmode: Union[str, ImportMode], 623 rootpath: Path, 624 *, 625 consider_namespace_packages: bool, 626 ) -> None: 627 self._loadconftestmodules( 628 anchor, 629 importmode, 630 rootpath, 631 consider_namespace_packages=consider_namespace_packages, 632 ) 633 # let's also consider test* subdirs 634 if anchor.is_dir(): 635 for x in anchor.glob("test*"): 636 if x.is_dir(): 637 self._loadconftestmodules( 638 x, 639 importmode, 640 rootpath, 641 consider_namespace_packages=consider_namespace_packages, 642 ) 643 644 def _loadconftestmodules( 645 self, 646 path: Path, 647 importmode: Union[str, ImportMode], 648 rootpath: Path, 649 *, 650 consider_namespace_packages: bool, 651 ) -> None: 652 if self._noconftest: 653 return 654 655 directory = self._get_directory(path) 656 657 # Optimization: avoid repeated searches in the same directory. 658 # Assumes always called with same importmode and rootpath. 659 if directory in self._dirpath2confmods: 660 return 661 662 clist = [] 663 for parent in reversed((directory, *directory.parents)): 664 if self._is_in_confcutdir(parent): 665 conftestpath = parent / "conftest.py" 666 if conftestpath.is_file(): 667 mod = self._importconftest( 668 conftestpath, 669 importmode, 670 rootpath, 671 consider_namespace_packages=consider_namespace_packages, 672 ) 673 clist.append(mod) 674 self._dirpath2confmods[directory] = clist 675 676 def _getconftestmodules(self, path: Path) -> Sequence[types.ModuleType]: 677 directory = self._get_directory(path) 678 return self._dirpath2confmods.get(directory, ()) 679 680 def _rget_with_confmod( 681 self, 682 name: str, 683 path: Path, 684 ) -> Tuple[types.ModuleType, Any]: 685 modules = self._getconftestmodules(path) 686 for mod in reversed(modules): 687 try: 688 return mod, getattr(mod, name) 689 except AttributeError: 690 continue 691 raise KeyError(name) 692 693 def _importconftest( 694 self, 695 conftestpath: Path, 696 importmode: Union[str, ImportMode], 697 rootpath: Path, 698 *, 699 consider_namespace_packages: bool, 700 ) -> types.ModuleType: 701 conftestpath_plugin_name = str(conftestpath) 702 existing = self.get_plugin(conftestpath_plugin_name) 703 if existing is not None: 704 return cast(types.ModuleType, existing) 705 706 # conftest.py files there are not in a Python package all have module 707 # name "conftest", and thus conflict with each other. Clear the existing 708 # before loading the new one, otherwise the existing one will be 709 # returned from the module cache. 710 pkgpath = resolve_package_path(conftestpath) 711 if pkgpath is None: 712 try: 713 del sys.modules[conftestpath.stem] 714 except KeyError: 715 pass 716 717 try: 718 mod = import_path( 719 conftestpath, 720 mode=importmode, 721 root=rootpath, 722 consider_namespace_packages=consider_namespace_packages, 723 ) 724 except Exception as e: 725 assert e.__traceback__ is not None 726 raise ConftestImportFailure(conftestpath, cause=e) from e 727 728 self._check_non_top_pytest_plugins(mod, conftestpath) 729 730 self._conftest_plugins.add(mod) 731 dirpath = conftestpath.parent 732 if dirpath in self._dirpath2confmods: 733 for path, mods in self._dirpath2confmods.items(): 734 if dirpath in path.parents or path == dirpath: 735 if mod in mods: 736 raise AssertionError( 737 f"While trying to load conftest path {conftestpath!s}, " 738 f"found that the module {mod} is already loaded with path {mod.__file__}. " 739 "This is not supposed to happen. Please report this issue to pytest." 740 ) 741 mods.append(mod) 742 self.trace(f"loading conftestmodule {mod!r}") 743 self.consider_conftest(mod, registration_name=conftestpath_plugin_name) 744 return mod 745 746 def _check_non_top_pytest_plugins( 747 self, 748 mod: types.ModuleType, 749 conftestpath: Path, 750 ) -> None: 751 if ( 752 hasattr(mod, "pytest_plugins") 753 and self._configured 754 and not self._using_pyargs 755 ): 756 msg = ( 757 "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported:\n" 758 "It affects the entire test suite instead of just below the conftest as expected.\n" 759 " {}\n" 760 "Please move it to a top level conftest file at the rootdir:\n" 761 " {}\n" 762 "For more information, visit:\n" 763 " https://docs.pytest.org/en/stable/deprecations.html#pytest-plugins-in-non-top-level-conftest-files" 764 ) 765 fail(msg.format(conftestpath, self._confcutdir), pytrace=False) 766 767 # 768 # API for bootstrapping plugin loading 769 # 770 # 771 772 def consider_preparse( 773 self, args: Sequence[str], *, exclude_only: bool = False 774 ) -> None: 775 """:meta private:""" 776 i = 0 777 n = len(args) 778 while i < n: 779 opt = args[i] 780 i += 1 781 if isinstance(opt, str): 782 if opt == "-p": 783 try: 784 parg = args[i] 785 except IndexError: 786 return 787 i += 1 788 elif opt.startswith("-p"): 789 parg = opt[2:] 790 else: 791 continue 792 parg = parg.strip() 793 if exclude_only and not parg.startswith("no:"): 794 continue 795 self.consider_pluginarg(parg) 796 797 def consider_pluginarg(self, arg: str) -> None: 798 """:meta private:""" 799 if arg.startswith("no:"): 800 name = arg[3:] 801 if name in essential_plugins: 802 raise UsageError("plugin %s cannot be disabled" % name) 803 804 # PR #4304: remove stepwise if cacheprovider is blocked. 805 if name == "cacheprovider": 806 self.set_blocked("stepwise") 807 self.set_blocked("pytest_stepwise") 808 809 self.set_blocked(name) 810 if not name.startswith("pytest_"): 811 self.set_blocked("pytest_" + name) 812 else: 813 name = arg 814 # Unblock the plugin. 815 self.unblock(name) 816 if not name.startswith("pytest_"): 817 self.unblock("pytest_" + name) 818 self.import_plugin(arg, consider_entry_points=True) 819 820 def consider_conftest( 821 self, conftestmodule: types.ModuleType, registration_name: str 822 ) -> None: 823 """:meta private:""" 824 self.register(conftestmodule, name=registration_name) 825 826 def consider_env(self) -> None: 827 """:meta private:""" 828 self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS")) 829 830 def consider_module(self, mod: types.ModuleType) -> None: 831 """:meta private:""" 832 self._import_plugin_specs(getattr(mod, "pytest_plugins", [])) 833 834 def _import_plugin_specs( 835 self, spec: Union[None, types.ModuleType, str, Sequence[str]] 836 ) -> None: 837 plugins = _get_plugin_specs_as_list(spec) 838 for import_spec in plugins: 839 self.import_plugin(import_spec) 840 841 def import_plugin(self, modname: str, consider_entry_points: bool = False) -> None: 842 """Import a plugin with ``modname``. 843 844 If ``consider_entry_points`` is True, entry point names are also 845 considered to find a plugin. 846 """ 847 # Most often modname refers to builtin modules, e.g. "pytester", 848 # "terminal" or "capture". Those plugins are registered under their 849 # basename for historic purposes but must be imported with the 850 # _pytest prefix. 851 assert isinstance(modname, str), ( 852 "module name as text required, got %r" % modname 853 ) 854 if self.is_blocked(modname) or self.get_plugin(modname) is not None: 855 return 856 857 importspec = "_pytest." + modname if modname in builtin_plugins else modname 858 self.rewrite_hook.mark_rewrite(importspec) 859 860 if consider_entry_points: 861 loaded = self.load_setuptools_entrypoints("pytest11", name=modname) 862 if loaded: 863 return 864 865 try: 866 __import__(importspec) 867 except ImportError as e: 868 raise ImportError( 869 f'Error importing plugin "{modname}": {e.args[0]}' 870 ).with_traceback(e.__traceback__) from e 871 872 except Skipped as e: 873 self.skipped_plugins.append((modname, e.msg or "")) 874 else: 875 mod = sys.modules[importspec] 876 self.register(mod, modname)
A pluggy.PluginManager <pluggy.PluginManager>
with
additional pytest-specific functionality:
- Loading plugins from the command line,
PYTEST_PLUGINS
env variable andpytest_plugins
global variables found in plugins being loaded. conftest.py
loading during start-up.
457 def parse_hookimpl_opts( 458 self, plugin: _PluggyPlugin, name: str 459 ) -> Optional[HookimplOpts]: 460 """:meta private:""" 461 # pytest hooks are always prefixed with "pytest_", 462 # so we avoid accessing possibly non-readable attributes 463 # (see issue #1073). 464 if not name.startswith("pytest_"): 465 return None 466 # Ignore names which can not be hooks. 467 if name == "pytest_plugins": 468 return None 469 470 opts = super().parse_hookimpl_opts(plugin, name) 471 if opts is not None: 472 return opts 473 474 method = getattr(plugin, name) 475 # Consider only actual functions for hooks (#3775). 476 if not inspect.isroutine(method): 477 return None 478 # Collect unmarked hooks as long as they have the `pytest_' prefix. 479 return _get_legacy_hook_marks( # type: ignore[return-value] 480 method, "impl", ("tryfirst", "trylast", "optionalhook", "hookwrapper") 481 )
:meta private:
483 def parse_hookspec_opts(self, module_or_class, name: str) -> Optional[HookspecOpts]: 484 """:meta private:""" 485 opts = super().parse_hookspec_opts(module_or_class, name) 486 if opts is None: 487 method = getattr(module_or_class, name) 488 if name.startswith("pytest_"): 489 opts = _get_legacy_hook_marks( # type: ignore[assignment] 490 method, 491 "spec", 492 ("firstresult", "historic"), 493 ) 494 return opts
:meta private:
496 def register( 497 self, plugin: _PluggyPlugin, name: Optional[str] = None 498 ) -> Optional[str]: 499 if name in _pytest.deprecated.DEPRECATED_EXTERNAL_PLUGINS: 500 warnings.warn( 501 PytestConfigWarning( 502 "{} plugin has been merged into the core, " 503 "please remove it from your requirements.".format( 504 name.replace("_", "-") 505 ) 506 ) 507 ) 508 return None 509 plugin_name = super().register(plugin, name) 510 if plugin_name is not None: 511 self.hook.pytest_plugin_registered.call_historic( 512 kwargs=dict( 513 plugin=plugin, 514 plugin_name=plugin_name, 515 manager=self, 516 ) 517 ) 518 519 if isinstance(plugin, types.ModuleType): 520 self.consider_module(plugin) 521 return plugin_name
Register a plugin and return its name.
Parameters
- name:
The name under which to register the plugin. If not specified, a
name is generated using
get_canonical_name()
.
:returns:
The plugin name. If the name is blocked from registering, returns
None
.
If the plugin is already registered, raises a ValueError
.
528 def hasplugin(self, name: str) -> bool: 529 """Return whether a plugin with the given name is registered.""" 530 return bool(self.get_plugin(name))
Return whether a plugin with the given name is registered.
532 def pytest_configure(self, config: "Config") -> None: 533 """:meta private:""" 534 # XXX now that the pluginmanager exposes hookimpl(tryfirst...) 535 # we should remove tryfirst/trylast as markers. 536 config.addinivalue_line( 537 "markers", 538 "tryfirst: mark a hook implementation function such that the " 539 "plugin machinery will try to call it first/as early as possible. " 540 "DEPRECATED, use @pytest.hookimpl(tryfirst=True) instead.", 541 ) 542 config.addinivalue_line( 543 "markers", 544 "trylast: mark a hook implementation function such that the " 545 "plugin machinery will try to call it last/as late as possible. " 546 "DEPRECATED, use @pytest.hookimpl(trylast=True) instead.", 547 ) 548 self._configured = True
:meta private:
772 def consider_preparse( 773 self, args: Sequence[str], *, exclude_only: bool = False 774 ) -> None: 775 """:meta private:""" 776 i = 0 777 n = len(args) 778 while i < n: 779 opt = args[i] 780 i += 1 781 if isinstance(opt, str): 782 if opt == "-p": 783 try: 784 parg = args[i] 785 except IndexError: 786 return 787 i += 1 788 elif opt.startswith("-p"): 789 parg = opt[2:] 790 else: 791 continue 792 parg = parg.strip() 793 if exclude_only and not parg.startswith("no:"): 794 continue 795 self.consider_pluginarg(parg)
:meta private:
797 def consider_pluginarg(self, arg: str) -> None: 798 """:meta private:""" 799 if arg.startswith("no:"): 800 name = arg[3:] 801 if name in essential_plugins: 802 raise UsageError("plugin %s cannot be disabled" % name) 803 804 # PR #4304: remove stepwise if cacheprovider is blocked. 805 if name == "cacheprovider": 806 self.set_blocked("stepwise") 807 self.set_blocked("pytest_stepwise") 808 809 self.set_blocked(name) 810 if not name.startswith("pytest_"): 811 self.set_blocked("pytest_" + name) 812 else: 813 name = arg 814 # Unblock the plugin. 815 self.unblock(name) 816 if not name.startswith("pytest_"): 817 self.unblock("pytest_" + name) 818 self.import_plugin(arg, consider_entry_points=True)
:meta private:
820 def consider_conftest( 821 self, conftestmodule: types.ModuleType, registration_name: str 822 ) -> None: 823 """:meta private:""" 824 self.register(conftestmodule, name=registration_name)
:meta private:
826 def consider_env(self) -> None: 827 """:meta private:""" 828 self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
:meta private:
830 def consider_module(self, mod: types.ModuleType) -> None: 831 """:meta private:""" 832 self._import_plugin_specs(getattr(mod, "pytest_plugins", []))
:meta private:
841 def import_plugin(self, modname: str, consider_entry_points: bool = False) -> None: 842 """Import a plugin with ``modname``. 843 844 If ``consider_entry_points`` is True, entry point names are also 845 considered to find a plugin. 846 """ 847 # Most often modname refers to builtin modules, e.g. "pytester", 848 # "terminal" or "capture". Those plugins are registered under their 849 # basename for historic purposes but must be imported with the 850 # _pytest prefix. 851 assert isinstance(modname, str), ( 852 "module name as text required, got %r" % modname 853 ) 854 if self.is_blocked(modname) or self.get_plugin(modname) is not None: 855 return 856 857 importspec = "_pytest." + modname if modname in builtin_plugins else modname 858 self.rewrite_hook.mark_rewrite(importspec) 859 860 if consider_entry_points: 861 loaded = self.load_setuptools_entrypoints("pytest11", name=modname) 862 if loaded: 863 return 864 865 try: 866 __import__(importspec) 867 except ImportError as e: 868 raise ImportError( 869 f'Error importing plugin "{modname}": {e.args[0]}' 870 ).with_traceback(e.__traceback__) from e 871 872 except Skipped as e: 873 self.skipped_plugins.append((modname, e.msg or "")) 874 else: 875 mod = sys.modules[importspec] 876 self.register(mod, modname)
Import a plugin with modname
.
If consider_entry_points
is True, entry point names are also
considered to find a plugin.
Inherited Members
- pluggy._manager.PluginManager
- project_name
- hook
- trace
- unregister
- set_blocked
- is_blocked
- unblock
- add_hookspecs
- get_plugins
- is_registered
- get_canonical_name
- get_plugin
- has_plugin
- get_name
- check_pending
- load_setuptools_entrypoints
- list_plugin_distinfo
- list_name_plugin
- get_hookcallers
- add_hookcall_monitoring
- enable_tracing
- subset_hook_caller
Warning emitted for an unhandled coroutine.
A coroutine was encountered when collecting test functions, but was not handled by any async-aware plugin. Coroutine test functions are not natively supported.
Inherited Members
- builtins.UserWarning
- UserWarning
- builtins.BaseException
- with_traceback
- add_note
- args
An unhandled exception occurred in a ~threading.Thread
.
Such exceptions don't propagate normally.
Inherited Members
- builtins.UserWarning
- UserWarning
- builtins.BaseException
- with_traceback
- add_note
- args
Warning emitted on use of unknown markers.
See :ref:mark
for details.
Inherited Members
- builtins.UserWarning
- UserWarning
- builtins.BaseException
- with_traceback
- add_note
- args
An unraisable exception was reported.
Unraisable exceptions are exceptions raised in __del__ <object.__del__>()
implementations and similar situations when the exception cannot be raised
as normal.
Inherited Members
- builtins.UserWarning
- UserWarning
- builtins.BaseException
- with_traceback
- add_note
- args
Base class for all warnings emitted by pytest.
Inherited Members
- builtins.UserWarning
- UserWarning
- builtins.BaseException
- with_traceback
- add_note
- args
789def raises( 790 expected_exception: Union[Type[E], Tuple[Type[E], ...]], *args: Any, **kwargs: Any 791) -> Union["RaisesContext[E]", _pytest._code.ExceptionInfo[E]]: 792 r"""Assert that a code block/function call raises an exception type, or one of its subclasses. 793 794 :param expected_exception: 795 The expected exception type, or a tuple if one of multiple possible 796 exception types are expected. Note that subclasses of the passed exceptions 797 will also match. 798 799 :kwparam str | re.Pattern[str] | None match: 800 If specified, a string containing a regular expression, 801 or a regular expression object, that is tested against the string 802 representation of the exception and its :pep:`678` `__notes__` 803 using :func:`re.search`. 804 805 To match a literal string that may contain :ref:`special characters 806 <re-syntax>`, the pattern can first be escaped with :func:`re.escape`. 807 808 (This is only used when ``pytest.raises`` is used as a context manager, 809 and passed through to the function otherwise. 810 When using ``pytest.raises`` as a function, you can use: 811 ``pytest.raises(Exc, func, match="passed on").match("my pattern")``.) 812 813 Use ``pytest.raises`` as a context manager, which will capture the exception of the given 814 type, or any of its subclasses:: 815 816 >>> import pytest 817 >>> with pytest.raises(ZeroDivisionError): 818 ... 1/0 819 820 If the code block does not raise the expected exception (:class:`ZeroDivisionError` in the example 821 above), or no exception at all, the check will fail instead. 822 823 You can also use the keyword argument ``match`` to assert that the 824 exception matches a text or regex:: 825 826 >>> with pytest.raises(ValueError, match='must be 0 or None'): 827 ... raise ValueError("value must be 0 or None") 828 829 >>> with pytest.raises(ValueError, match=r'must be \d+$'): 830 ... raise ValueError("value must be 42") 831 832 The ``match`` argument searches the formatted exception string, which includes any 833 `PEP-678 <https://peps.python.org/pep-0678/>`__ ``__notes__``: 834 835 >>> with pytest.raises(ValueError, match=r"had a note added"): # doctest: +SKIP 836 ... e = ValueError("value must be 42") 837 ... e.add_note("had a note added") 838 ... raise e 839 840 The context manager produces an :class:`ExceptionInfo` object which can be used to inspect the 841 details of the captured exception:: 842 843 >>> with pytest.raises(ValueError) as exc_info: 844 ... raise ValueError("value must be 42") 845 >>> assert exc_info.type is ValueError 846 >>> assert exc_info.value.args[0] == "value must be 42" 847 848 .. warning:: 849 850 Given that ``pytest.raises`` matches subclasses, be wary of using it to match :class:`Exception` like this:: 851 852 with pytest.raises(Exception): # Careful, this will catch ANY exception raised. 853 some_function() 854 855 Because :class:`Exception` is the base class of almost all exceptions, it is easy for this to hide 856 real bugs, where the user wrote this expecting a specific exception, but some other exception is being 857 raised due to a bug introduced during a refactoring. 858 859 Avoid using ``pytest.raises`` to catch :class:`Exception` unless certain that you really want to catch 860 **any** exception raised. 861 862 .. note:: 863 864 When using ``pytest.raises`` as a context manager, it's worthwhile to 865 note that normal context manager rules apply and that the exception 866 raised *must* be the final line in the scope of the context manager. 867 Lines of code after that, within the scope of the context manager will 868 not be executed. For example:: 869 870 >>> value = 15 871 >>> with pytest.raises(ValueError) as exc_info: 872 ... if value > 10: 873 ... raise ValueError("value must be <= 10") 874 ... assert exc_info.type is ValueError # This will not execute. 875 876 Instead, the following approach must be taken (note the difference in 877 scope):: 878 879 >>> with pytest.raises(ValueError) as exc_info: 880 ... if value > 10: 881 ... raise ValueError("value must be <= 10") 882 ... 883 >>> assert exc_info.type is ValueError 884 885 **Using with** ``pytest.mark.parametrize`` 886 887 When using :ref:`pytest.mark.parametrize ref` 888 it is possible to parametrize tests such that 889 some runs raise an exception and others do not. 890 891 See :ref:`parametrizing_conditional_raising` for an example. 892 893 .. seealso:: 894 895 :ref:`assertraises` for more examples and detailed discussion. 896 897 **Legacy form** 898 899 It is possible to specify a callable by passing a to-be-called lambda:: 900 901 >>> raises(ZeroDivisionError, lambda: 1/0) 902 <ExceptionInfo ...> 903 904 or you can specify an arbitrary callable with arguments:: 905 906 >>> def f(x): return 1/x 907 ... 908 >>> raises(ZeroDivisionError, f, 0) 909 <ExceptionInfo ...> 910 >>> raises(ZeroDivisionError, f, x=0) 911 <ExceptionInfo ...> 912 913 The form above is fully supported but discouraged for new code because the 914 context manager form is regarded as more readable and less error-prone. 915 916 .. note:: 917 Similar to caught exception objects in Python, explicitly clearing 918 local references to returned ``ExceptionInfo`` objects can 919 help the Python interpreter speed up its garbage collection. 920 921 Clearing those references breaks a reference cycle 922 (``ExceptionInfo`` --> caught exception --> frame stack raising 923 the exception --> current frame stack --> local variables --> 924 ``ExceptionInfo``) which makes Python keep all objects referenced 925 from that cycle (including all local variables in the current 926 frame) alive until the next cyclic garbage collection run. 927 More detailed information can be found in the official Python 928 documentation for :ref:`the try statement <python:try>`. 929 """ 930 __tracebackhide__ = True 931 932 if not expected_exception: 933 raise ValueError( 934 f"Expected an exception type or a tuple of exception types, but got `{expected_exception!r}`. " 935 f"Raising exceptions is already understood as failing the test, so you don't need " 936 f"any special code to say 'this should never raise an exception'." 937 ) 938 if isinstance(expected_exception, type): 939 expected_exceptions: Tuple[Type[E], ...] = (expected_exception,) 940 else: 941 expected_exceptions = expected_exception 942 for exc in expected_exceptions: 943 if not isinstance(exc, type) or not issubclass(exc, BaseException): 944 msg = "expected exception must be a BaseException type, not {}" # type: ignore[unreachable] 945 not_a = exc.__name__ if isinstance(exc, type) else type(exc).__name__ 946 raise TypeError(msg.format(not_a)) 947 948 message = f"DID NOT RAISE {expected_exception}" 949 950 if not args: 951 match: Optional[Union[str, Pattern[str]]] = kwargs.pop("match", None) 952 if kwargs: 953 msg = "Unexpected keyword arguments passed to pytest.raises: " 954 msg += ", ".join(sorted(kwargs)) 955 msg += "\nUse context-manager form instead?" 956 raise TypeError(msg) 957 return RaisesContext(expected_exception, message, match) 958 else: 959 func = args[0] 960 if not callable(func): 961 raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") 962 try: 963 func(*args[1:], **kwargs) 964 except expected_exception as e: 965 return _pytest._code.ExceptionInfo.from_exception(e) 966 fail(message)
Assert that a code block/function call raises an exception type, or one of its subclasses.
Parameters
- expected_exception: The expected exception type, or a tuple if one of multiple possible exception types are expected. Note that subclasses of the passed exceptions will also match.
:kwparam str | re.Pattern[str] | None match:
If specified, a string containing a regular expression,
or a regular expression object, that is tested against the string
representation of the exception and its :pep:678
__notes__
using re.search()
.
To match a literal string that may contain :ref:`special characters
<re-syntax>`, the pattern can first be escaped with `re.escape()`.
(This is only used when ``pytest.raises`` is used as a context manager,
and passed through to the function otherwise.
When using ``pytest.raises`` as a function, you can use:
``pytest.raises(Exc, func, match="passed on").match("my pattern")``.)
Use pytest.raises
as a context manager, which will capture the exception of the given
type, or any of its subclasses::
>>> import pytest
>>> with pytest.raises(ZeroDivisionError):
... 1/0
If the code block does not raise the expected exception (ZeroDivisionError
in the example
above), or no exception at all, the check will fail instead.
You can also use the keyword argument match
to assert that the
exception matches a text or regex::
>>> with pytest.raises(ValueError, match='must be 0 or None'):
... raise ValueError("value must be 0 or None")
>>> with pytest.raises(ValueError, match=r'must be \d+$'):
... raise ValueError("value must be 42")
The match
argument searches the formatted exception string, which includes any
PEP-678 _ __notes__
:
>>> with pytest.raises(ValueError, match=r"had a note added"): # doctest: +SKIP
... e = ValueError("value must be 42")
... e.add_note("had a note added")
... raise e
The context manager produces an ExceptionInfo
object which can be used to inspect the
details of the captured exception::
>>> with pytest.raises(ValueError) as exc_info:
... raise ValueError("value must be 42")
>>> assert exc_info.type is ValueError
>>> assert exc_info.value.args[0] == "value must be 42"
Given that pytest.raises
matches subclasses, be wary of using it to match Exception
like this::
with pytest.raises(Exception): # Careful, this will catch ANY exception raised.
some_function()
Because Exception
is the base class of almost all exceptions, it is easy for this to hide
real bugs, where the user wrote this expecting a specific exception, but some other exception is being
raised due to a bug introduced during a refactoring.
Avoid using pytest.raises
to catch Exception
unless certain that you really want to catch
any exception raised.
When using pytest.raises
as a context manager, it's worthwhile to
note that normal context manager rules apply and that the exception
raised must be the final line in the scope of the context manager.
Lines of code after that, within the scope of the context manager will
not be executed. For example::
>>> value = 15
>>> with pytest.raises(ValueError) as exc_info:
... if value > 10:
... raise ValueError("value must be <= 10")
... assert exc_info.type is ValueError # This will not execute.
Instead, the following approach must be taken (note the difference in scope)::
>>> with pytest.raises(ValueError) as exc_info:
... if value > 10:
... raise ValueError("value must be <= 10")
...
>>> assert exc_info.type is ValueError
Using with pytest.mark.parametrize
When using :ref:pytest.mark.parametrize ref
it is possible to parametrize tests such that
some runs raise an exception and others do not.
See :ref:parametrizing_conditional_raising
for an example.
seealso:
:ref:assertraises
for more examples and detailed discussion.
Legacy form
It is possible to specify a callable by passing a to-be-called lambda::
>>> raises(ZeroDivisionError, lambda: 1/0)
<ExceptionInfo ...>
or you can specify an arbitrary callable with arguments::
>>> def f(x): return 1/x
...
>>> raises(ZeroDivisionError, f, 0)
<ExceptionInfo ...>
>>> raises(ZeroDivisionError, f, x=0)
<ExceptionInfo ...>
The form above is fully supported but discouraged for new code because the context manager form is regarded as more readable and less error-prone.
Similar to caught exception objects in Python, explicitly clearing
local references to returned ExceptionInfo
objects can
help the Python interpreter speed up its garbage collection.
Clearing those references breaks a reference cycle
(ExceptionInfo
--> caught exception --> frame stack raising
the exception --> current frame stack --> local variables -->
ExceptionInfo
) which makes Python keep all objects referenced
from that cycle (including all local variables in the current
frame) alive until the next cyclic garbage collection run.
More detailed information can be found in the official Python
documentation for :ref:the try statement <python:try>
.
225@final 226class RecordedHookCall: 227 """A recorded call to a hook. 228 229 The arguments to the hook call are set as attributes. 230 For example: 231 232 .. code-block:: python 233 234 calls = hook_recorder.getcalls("pytest_runtest_setup") 235 # Suppose pytest_runtest_setup was called once with `item=an_item`. 236 assert calls[0].item is an_item 237 """ 238 239 def __init__(self, name: str, kwargs) -> None: 240 self.__dict__.update(kwargs) 241 self._name = name 242 243 def __repr__(self) -> str: 244 d = self.__dict__.copy() 245 del d["_name"] 246 return f"<RecordedHookCall {self._name!r}(**{d!r})>" 247 248 if TYPE_CHECKING: 249 # The class has undetermined attributes, this tells mypy about it. 250 def __getattr__(self, key: str): ...
A recorded call to a hook.
The arguments to the hook call are set as attributes. For example:
calls = hook_recorder.getcalls("pytest_runtest_setup")
# Suppose pytest_runtest_setup was called once with `item=an_item`.
assert calls[0].item is an_item
59def register_assert_rewrite(*names: str) -> None: 60 """Register one or more module names to be rewritten on import. 61 62 This function will make sure that this module or all modules inside 63 the package will get their assert statements rewritten. 64 Thus you should make sure to call this before the module is 65 actually imported, usually in your __init__.py if you are a plugin 66 using a package. 67 68 :param names: The module names to register. 69 """ 70 for name in names: 71 if not isinstance(name, str): 72 msg = "expected module names as *args, got {0} instead" # type: ignore[unreachable] 73 raise TypeError(msg.format(repr(names))) 74 for hook in sys.meta_path: 75 if isinstance(hook, rewrite.AssertionRewritingHook): 76 importhook = hook 77 break 78 else: 79 # TODO(typing): Add a protocol for mark_rewrite() and use it 80 # for importhook and for PytestPluginManager.rewrite_hook. 81 importhook = DummyRewriteHook() # type: ignore 82 importhook.mark_rewrite(*names)
Register one or more module names to be rewritten on import.
This function will make sure that this module or all modules inside the package will get their assert statements rewritten. Thus you should make sure to call this before the module is actually imported, usually in your __init__.py if you are a plugin using a package.
Parameters
- names: The module names to register.
521@final 522class RunResult: 523 """The result of running a command from :class:`~pytest.Pytester`.""" 524 525 def __init__( 526 self, 527 ret: Union[int, ExitCode], 528 outlines: List[str], 529 errlines: List[str], 530 duration: float, 531 ) -> None: 532 try: 533 self.ret: Union[int, ExitCode] = ExitCode(ret) 534 """The return value.""" 535 except ValueError: 536 self.ret = ret 537 self.outlines = outlines 538 """List of lines captured from stdout.""" 539 self.errlines = errlines 540 """List of lines captured from stderr.""" 541 self.stdout = LineMatcher(outlines) 542 """:class:`~pytest.LineMatcher` of stdout. 543 544 Use e.g. :func:`str(stdout) <pytest.LineMatcher.__str__()>` to reconstruct stdout, or the commonly used 545 :func:`stdout.fnmatch_lines() <pytest.LineMatcher.fnmatch_lines()>` method. 546 """ 547 self.stderr = LineMatcher(errlines) 548 """:class:`~pytest.LineMatcher` of stderr.""" 549 self.duration = duration 550 """Duration in seconds.""" 551 552 def __repr__(self) -> str: 553 return ( 554 "<RunResult ret=%s len(stdout.lines)=%d len(stderr.lines)=%d duration=%.2fs>" 555 % (self.ret, len(self.stdout.lines), len(self.stderr.lines), self.duration) 556 ) 557 558 def parseoutcomes(self) -> Dict[str, int]: 559 """Return a dictionary of outcome noun -> count from parsing the terminal 560 output that the test process produced. 561 562 The returned nouns will always be in plural form:: 563 564 ======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ==== 565 566 Will return ``{"failed": 1, "passed": 1, "warnings": 1, "errors": 1}``. 567 """ 568 return self.parse_summary_nouns(self.outlines) 569 570 @classmethod 571 def parse_summary_nouns(cls, lines) -> Dict[str, int]: 572 """Extract the nouns from a pytest terminal summary line. 573 574 It always returns the plural noun for consistency:: 575 576 ======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ==== 577 578 Will return ``{"failed": 1, "passed": 1, "warnings": 1, "errors": 1}``. 579 """ 580 for line in reversed(lines): 581 if rex_session_duration.search(line): 582 outcomes = rex_outcome.findall(line) 583 ret = {noun: int(count) for (count, noun) in outcomes} 584 break 585 else: 586 raise ValueError("Pytest terminal summary report not found") 587 588 to_plural = { 589 "warning": "warnings", 590 "error": "errors", 591 } 592 return {to_plural.get(k, k): v for k, v in ret.items()} 593 594 def assert_outcomes( 595 self, 596 passed: int = 0, 597 skipped: int = 0, 598 failed: int = 0, 599 errors: int = 0, 600 xpassed: int = 0, 601 xfailed: int = 0, 602 warnings: Optional[int] = None, 603 deselected: Optional[int] = None, 604 ) -> None: 605 """ 606 Assert that the specified outcomes appear with the respective 607 numbers (0 means it didn't occur) in the text output from a test run. 608 609 ``warnings`` and ``deselected`` are only checked if not None. 610 """ 611 __tracebackhide__ = True 612 from _pytest.pytester_assertions import assert_outcomes 613 614 outcomes = self.parseoutcomes() 615 assert_outcomes( 616 outcomes, 617 passed=passed, 618 skipped=skipped, 619 failed=failed, 620 errors=errors, 621 xpassed=xpassed, 622 xfailed=xfailed, 623 warnings=warnings, 624 deselected=deselected, 625 )
The result of running a command from ~pytest.Pytester
.
525 def __init__( 526 self, 527 ret: Union[int, ExitCode], 528 outlines: List[str], 529 errlines: List[str], 530 duration: float, 531 ) -> None: 532 try: 533 self.ret: Union[int, ExitCode] = ExitCode(ret) 534 """The return value.""" 535 except ValueError: 536 self.ret = ret 537 self.outlines = outlines 538 """List of lines captured from stdout.""" 539 self.errlines = errlines 540 """List of lines captured from stderr.""" 541 self.stdout = LineMatcher(outlines) 542 """:class:`~pytest.LineMatcher` of stdout. 543 544 Use e.g. :func:`str(stdout) <pytest.LineMatcher.__str__()>` to reconstruct stdout, or the commonly used 545 :func:`stdout.fnmatch_lines() <pytest.LineMatcher.fnmatch_lines()>` method. 546 """ 547 self.stderr = LineMatcher(errlines) 548 """:class:`~pytest.LineMatcher` of stderr.""" 549 self.duration = duration 550 """Duration in seconds."""
~pytest.LineMatcher
of stdout.
Use e.g. str(stdout) <pytest.LineMatcher.__str__()>()
to reconstruct stdout, or the commonly used
stdout.fnmatch_lines() <pytest.LineMatcher.fnmatch_lines()>()
method.
558 def parseoutcomes(self) -> Dict[str, int]: 559 """Return a dictionary of outcome noun -> count from parsing the terminal 560 output that the test process produced. 561 562 The returned nouns will always be in plural form:: 563 564 ======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ==== 565 566 Will return ``{"failed": 1, "passed": 1, "warnings": 1, "errors": 1}``. 567 """ 568 return self.parse_summary_nouns(self.outlines)
Return a dictionary of outcome noun -> count from parsing the terminal output that the test process produced.
The returned nouns will always be in plural form::
======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ====
Will return {"failed": 1, "passed": 1, "warnings": 1, "errors": 1}
.
570 @classmethod 571 def parse_summary_nouns(cls, lines) -> Dict[str, int]: 572 """Extract the nouns from a pytest terminal summary line. 573 574 It always returns the plural noun for consistency:: 575 576 ======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ==== 577 578 Will return ``{"failed": 1, "passed": 1, "warnings": 1, "errors": 1}``. 579 """ 580 for line in reversed(lines): 581 if rex_session_duration.search(line): 582 outcomes = rex_outcome.findall(line) 583 ret = {noun: int(count) for (count, noun) in outcomes} 584 break 585 else: 586 raise ValueError("Pytest terminal summary report not found") 587 588 to_plural = { 589 "warning": "warnings", 590 "error": "errors", 591 } 592 return {to_plural.get(k, k): v for k, v in ret.items()}
Extract the nouns from a pytest terminal summary line.
It always returns the plural noun for consistency::
======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ====
Will return {"failed": 1, "passed": 1, "warnings": 1, "errors": 1}
.
594 def assert_outcomes( 595 self, 596 passed: int = 0, 597 skipped: int = 0, 598 failed: int = 0, 599 errors: int = 0, 600 xpassed: int = 0, 601 xfailed: int = 0, 602 warnings: Optional[int] = None, 603 deselected: Optional[int] = None, 604 ) -> None: 605 """ 606 Assert that the specified outcomes appear with the respective 607 numbers (0 means it didn't occur) in the text output from a test run. 608 609 ``warnings`` and ``deselected`` are only checked if not None. 610 """ 611 __tracebackhide__ = True 612 from _pytest.pytester_assertions import assert_outcomes 613 614 outcomes = self.parseoutcomes() 615 assert_outcomes( 616 outcomes, 617 passed=passed, 618 skipped=skipped, 619 failed=failed, 620 errors=errors, 621 xpassed=xpassed, 622 xfailed=xfailed, 623 warnings=warnings, 624 deselected=deselected, 625 )
Assert that the specified outcomes appear with the respective numbers (0 means it didn't occur) in the text output from a test run.
warnings
and deselected
are only checked if not None.
543@final 544class Session(nodes.Collector): 545 """The root of the collection tree. 546 547 ``Session`` collects the initial paths given as arguments to pytest. 548 """ 549 550 Interrupted = Interrupted 551 Failed = Failed 552 # Set on the session by runner.pytest_sessionstart. 553 _setupstate: SetupState 554 # Set on the session by fixtures.pytest_sessionstart. 555 _fixturemanager: FixtureManager 556 exitstatus: Union[int, ExitCode] 557 558 def __init__(self, config: Config) -> None: 559 super().__init__( 560 name="", 561 path=config.rootpath, 562 fspath=None, 563 parent=None, 564 config=config, 565 session=self, 566 nodeid="", 567 ) 568 self.testsfailed = 0 569 self.testscollected = 0 570 self._shouldstop: Union[bool, str] = False 571 self._shouldfail: Union[bool, str] = False 572 self.trace = config.trace.root.get("collection") 573 self._initialpaths: FrozenSet[Path] = frozenset() 574 self._initialpaths_with_parents: FrozenSet[Path] = frozenset() 575 self._notfound: List[Tuple[str, Sequence[nodes.Collector]]] = [] 576 self._initial_parts: List[CollectionArgument] = [] 577 self._collection_cache: Dict[nodes.Collector, CollectReport] = {} 578 self.items: List[nodes.Item] = [] 579 580 self._bestrelpathcache: Dict[Path, str] = _bestrelpath_cache(config.rootpath) 581 582 self.config.pluginmanager.register(self, name="session") 583 584 @classmethod 585 def from_config(cls, config: Config) -> "Session": 586 session: Session = cls._create(config=config) 587 return session 588 589 def __repr__(self) -> str: 590 return "<%s %s exitstatus=%r testsfailed=%d testscollected=%d>" % ( 591 self.__class__.__name__, 592 self.name, 593 getattr(self, "exitstatus", "<UNSET>"), 594 self.testsfailed, 595 self.testscollected, 596 ) 597 598 @property 599 def shouldstop(self) -> Union[bool, str]: 600 return self._shouldstop 601 602 @shouldstop.setter 603 def shouldstop(self, value: Union[bool, str]) -> None: 604 # The runner checks shouldfail and assumes that if it is set we are 605 # definitely stopping, so prevent unsetting it. 606 if value is False and self._shouldstop: 607 warnings.warn( 608 PytestWarning( 609 "session.shouldstop cannot be unset after it has been set; ignoring." 610 ), 611 stacklevel=2, 612 ) 613 return 614 self._shouldstop = value 615 616 @property 617 def shouldfail(self) -> Union[bool, str]: 618 return self._shouldfail 619 620 @shouldfail.setter 621 def shouldfail(self, value: Union[bool, str]) -> None: 622 # The runner checks shouldfail and assumes that if it is set we are 623 # definitely stopping, so prevent unsetting it. 624 if value is False and self._shouldfail: 625 warnings.warn( 626 PytestWarning( 627 "session.shouldfail cannot be unset after it has been set; ignoring." 628 ), 629 stacklevel=2, 630 ) 631 return 632 self._shouldfail = value 633 634 @property 635 def startpath(self) -> Path: 636 """The path from which pytest was invoked. 637 638 .. versionadded:: 7.0.0 639 """ 640 return self.config.invocation_params.dir 641 642 def _node_location_to_relpath(self, node_path: Path) -> str: 643 # bestrelpath is a quite slow function. 644 return self._bestrelpathcache[node_path] 645 646 @hookimpl(tryfirst=True) 647 def pytest_collectstart(self) -> None: 648 if self.shouldfail: 649 raise self.Failed(self.shouldfail) 650 if self.shouldstop: 651 raise self.Interrupted(self.shouldstop) 652 653 @hookimpl(tryfirst=True) 654 def pytest_runtest_logreport( 655 self, report: Union[TestReport, CollectReport] 656 ) -> None: 657 if report.failed and not hasattr(report, "wasxfail"): 658 self.testsfailed += 1 659 maxfail = self.config.getvalue("maxfail") 660 if maxfail and self.testsfailed >= maxfail: 661 self.shouldfail = "stopping after %d failures" % (self.testsfailed) 662 663 pytest_collectreport = pytest_runtest_logreport 664 665 def isinitpath( 666 self, 667 path: Union[str, "os.PathLike[str]"], 668 *, 669 with_parents: bool = False, 670 ) -> bool: 671 """Is path an initial path? 672 673 An initial path is a path explicitly given to pytest on the command 674 line. 675 676 :param with_parents: 677 If set, also return True if the path is a parent of an initial path. 678 679 .. versionchanged:: 8.0 680 Added the ``with_parents`` parameter. 681 """ 682 # Optimization: Path(Path(...)) is much slower than isinstance. 683 path_ = path if isinstance(path, Path) else Path(path) 684 if with_parents: 685 return path_ in self._initialpaths_with_parents 686 else: 687 return path_ in self._initialpaths 688 689 def gethookproxy(self, fspath: "os.PathLike[str]") -> pluggy.HookRelay: 690 # Optimization: Path(Path(...)) is much slower than isinstance. 691 path = fspath if isinstance(fspath, Path) else Path(fspath) 692 pm = self.config.pluginmanager 693 # Check if we have the common case of running 694 # hooks with all conftest.py files. 695 my_conftestmodules = pm._getconftestmodules(path) 696 remove_mods = pm._conftest_plugins.difference(my_conftestmodules) 697 proxy: pluggy.HookRelay 698 if remove_mods: 699 # One or more conftests are not in use at this path. 700 proxy = PathAwareHookProxy(FSHookProxy(pm, remove_mods)) # type: ignore[arg-type,assignment] 701 else: 702 # All plugins are active for this fspath. 703 proxy = self.config.hook 704 return proxy 705 706 def _collect_path( 707 self, 708 path: Path, 709 path_cache: Dict[Path, Sequence[nodes.Collector]], 710 ) -> Sequence[nodes.Collector]: 711 """Create a Collector for the given path. 712 713 `path_cache` makes it so the same Collectors are returned for the same 714 path. 715 """ 716 if path in path_cache: 717 return path_cache[path] 718 719 if path.is_dir(): 720 ihook = self.gethookproxy(path.parent) 721 col: Optional[nodes.Collector] = ihook.pytest_collect_directory( 722 path=path, parent=self 723 ) 724 cols: Sequence[nodes.Collector] = (col,) if col is not None else () 725 726 elif path.is_file(): 727 ihook = self.gethookproxy(path) 728 cols = ihook.pytest_collect_file(file_path=path, parent=self) 729 730 else: 731 # Broken symlink or invalid/missing file. 732 cols = () 733 734 path_cache[path] = cols 735 return cols 736 737 @overload 738 def perform_collect( 739 self, args: Optional[Sequence[str]] = ..., genitems: "Literal[True]" = ... 740 ) -> Sequence[nodes.Item]: ... 741 742 @overload 743 def perform_collect( 744 self, args: Optional[Sequence[str]] = ..., genitems: bool = ... 745 ) -> Sequence[Union[nodes.Item, nodes.Collector]]: ... 746 747 def perform_collect( 748 self, args: Optional[Sequence[str]] = None, genitems: bool = True 749 ) -> Sequence[Union[nodes.Item, nodes.Collector]]: 750 """Perform the collection phase for this session. 751 752 This is called by the default :hook:`pytest_collection` hook 753 implementation; see the documentation of this hook for more details. 754 For testing purposes, it may also be called directly on a fresh 755 ``Session``. 756 757 This function normally recursively expands any collectors collected 758 from the session to their items, and only items are returned. For 759 testing purposes, this may be suppressed by passing ``genitems=False``, 760 in which case the return value contains these collectors unexpanded, 761 and ``session.items`` is empty. 762 """ 763 if args is None: 764 args = self.config.args 765 766 self.trace("perform_collect", self, args) 767 self.trace.root.indent += 1 768 769 hook = self.config.hook 770 771 self._notfound = [] 772 self._initial_parts = [] 773 self._collection_cache = {} 774 self.items = [] 775 items: Sequence[Union[nodes.Item, nodes.Collector]] = self.items 776 try: 777 initialpaths: List[Path] = [] 778 initialpaths_with_parents: List[Path] = [] 779 for arg in args: 780 collection_argument = resolve_collection_argument( 781 self.config.invocation_params.dir, 782 arg, 783 as_pypath=self.config.option.pyargs, 784 ) 785 self._initial_parts.append(collection_argument) 786 initialpaths.append(collection_argument.path) 787 initialpaths_with_parents.append(collection_argument.path) 788 initialpaths_with_parents.extend(collection_argument.path.parents) 789 self._initialpaths = frozenset(initialpaths) 790 self._initialpaths_with_parents = frozenset(initialpaths_with_parents) 791 792 rep = collect_one_node(self) 793 self.ihook.pytest_collectreport(report=rep) 794 self.trace.root.indent -= 1 795 if self._notfound: 796 errors = [] 797 for arg, collectors in self._notfound: 798 if collectors: 799 errors.append( 800 f"not found: {arg}\n(no match in any of {collectors!r})" 801 ) 802 else: 803 errors.append(f"found no collectors for {arg}") 804 805 raise UsageError(*errors) 806 807 if not genitems: 808 items = rep.result 809 else: 810 if rep.passed: 811 for node in rep.result: 812 self.items.extend(self.genitems(node)) 813 814 self.config.pluginmanager.check_pending() 815 hook.pytest_collection_modifyitems( 816 session=self, config=self.config, items=items 817 ) 818 finally: 819 self._notfound = [] 820 self._initial_parts = [] 821 self._collection_cache = {} 822 hook.pytest_collection_finish(session=self) 823 824 if genitems: 825 self.testscollected = len(items) 826 827 return items 828 829 def _collect_one_node( 830 self, 831 node: nodes.Collector, 832 handle_dupes: bool = True, 833 ) -> Tuple[CollectReport, bool]: 834 if node in self._collection_cache and handle_dupes: 835 rep = self._collection_cache[node] 836 return rep, True 837 else: 838 rep = collect_one_node(node) 839 self._collection_cache[node] = rep 840 return rep, False 841 842 def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: 843 # This is a cache for the root directories of the initial paths. 844 # We can't use collection_cache for Session because of its special 845 # role as the bootstrapping collector. 846 path_cache: Dict[Path, Sequence[nodes.Collector]] = {} 847 848 pm = self.config.pluginmanager 849 850 for collection_argument in self._initial_parts: 851 self.trace("processing argument", collection_argument) 852 self.trace.root.indent += 1 853 854 argpath = collection_argument.path 855 names = collection_argument.parts 856 module_name = collection_argument.module_name 857 858 # resolve_collection_argument() ensures this. 859 if argpath.is_dir(): 860 assert not names, f"invalid arg {(argpath, names)!r}" 861 862 paths = [argpath] 863 # Add relevant parents of the path, from the root, e.g. 864 # /a/b/c.py -> [/, /a, /a/b, /a/b/c.py] 865 if module_name is None: 866 # Paths outside of the confcutdir should not be considered. 867 for path in argpath.parents: 868 if not pm._is_in_confcutdir(path): 869 break 870 paths.insert(0, path) 871 else: 872 # For --pyargs arguments, only consider paths matching the module 873 # name. Paths beyond the package hierarchy are not included. 874 module_name_parts = module_name.split(".") 875 for i, path in enumerate(argpath.parents, 2): 876 if i > len(module_name_parts) or path.stem != module_name_parts[-i]: 877 break 878 paths.insert(0, path) 879 880 # Start going over the parts from the root, collecting each level 881 # and discarding all nodes which don't match the level's part. 882 any_matched_in_initial_part = False 883 notfound_collectors = [] 884 work: List[ 885 Tuple[Union[nodes.Collector, nodes.Item], List[Union[Path, str]]] 886 ] = [(self, [*paths, *names])] 887 while work: 888 matchnode, matchparts = work.pop() 889 890 # Pop'd all of the parts, this is a match. 891 if not matchparts: 892 yield matchnode 893 any_matched_in_initial_part = True 894 continue 895 896 # Should have been matched by now, discard. 897 if not isinstance(matchnode, nodes.Collector): 898 continue 899 900 # Collect this level of matching. 901 # Collecting Session (self) is done directly to avoid endless 902 # recursion to this function. 903 subnodes: Sequence[Union[nodes.Collector, nodes.Item]] 904 if isinstance(matchnode, Session): 905 assert isinstance(matchparts[0], Path) 906 subnodes = matchnode._collect_path(matchparts[0], path_cache) 907 else: 908 # For backward compat, files given directly multiple 909 # times on the command line should not be deduplicated. 910 handle_dupes = not ( 911 len(matchparts) == 1 912 and isinstance(matchparts[0], Path) 913 and matchparts[0].is_file() 914 ) 915 rep, duplicate = self._collect_one_node(matchnode, handle_dupes) 916 if not duplicate and not rep.passed: 917 # Report collection failures here to avoid failing to 918 # run some test specified in the command line because 919 # the module could not be imported (#134). 920 matchnode.ihook.pytest_collectreport(report=rep) 921 if not rep.passed: 922 continue 923 subnodes = rep.result 924 925 # Prune this level. 926 any_matched_in_collector = False 927 for node in reversed(subnodes): 928 # Path part e.g. `/a/b/` in `/a/b/test_file.py::TestIt::test_it`. 929 if isinstance(matchparts[0], Path): 930 is_match = node.path == matchparts[0] 931 if sys.platform == "win32" and not is_match: 932 # In case the file paths do not match, fallback to samefile() to 933 # account for short-paths on Windows (#11895). 934 same_file = os.path.samefile(node.path, matchparts[0]) 935 # We don't want to match links to the current node, 936 # otherwise we would match the same file more than once (#12039). 937 is_match = same_file and ( 938 os.path.islink(node.path) 939 == os.path.islink(matchparts[0]) 940 ) 941 942 # Name part e.g. `TestIt` in `/a/b/test_file.py::TestIt::test_it`. 943 else: 944 # TODO: Remove parametrized workaround once collection structure contains 945 # parametrization. 946 is_match = ( 947 node.name == matchparts[0] 948 or node.name.split("[")[0] == matchparts[0] 949 ) 950 if is_match: 951 work.append((node, matchparts[1:])) 952 any_matched_in_collector = True 953 954 if not any_matched_in_collector: 955 notfound_collectors.append(matchnode) 956 957 if not any_matched_in_initial_part: 958 report_arg = "::".join((str(argpath), *names)) 959 self._notfound.append((report_arg, notfound_collectors)) 960 961 self.trace.root.indent -= 1 962 963 def genitems( 964 self, node: Union[nodes.Item, nodes.Collector] 965 ) -> Iterator[nodes.Item]: 966 self.trace("genitems", node) 967 if isinstance(node, nodes.Item): 968 node.ihook.pytest_itemcollected(item=node) 969 yield node 970 else: 971 assert isinstance(node, nodes.Collector) 972 keepduplicates = self.config.getoption("keepduplicates") 973 # For backward compat, dedup only applies to files. 974 handle_dupes = not (keepduplicates and isinstance(node, nodes.File)) 975 rep, duplicate = self._collect_one_node(node, handle_dupes) 976 if duplicate and not keepduplicates: 977 return 978 if rep.passed: 979 for subnode in rep.result: 980 yield from self.genitems(subnode) 981 if not duplicate: 982 node.ihook.pytest_collectreport(report=rep)
The root of the collection tree.
Session
collects the initial paths given as arguments to pytest.
558 def __init__(self, config: Config) -> None: 559 super().__init__( 560 name="", 561 path=config.rootpath, 562 fspath=None, 563 parent=None, 564 config=config, 565 session=self, 566 nodeid="", 567 ) 568 self.testsfailed = 0 569 self.testscollected = 0 570 self._shouldstop: Union[bool, str] = False 571 self._shouldfail: Union[bool, str] = False 572 self.trace = config.trace.root.get("collection") 573 self._initialpaths: FrozenSet[Path] = frozenset() 574 self._initialpaths_with_parents: FrozenSet[Path] = frozenset() 575 self._notfound: List[Tuple[str, Sequence[nodes.Collector]]] = [] 576 self._initial_parts: List[CollectionArgument] = [] 577 self._collection_cache: Dict[nodes.Collector, CollectReport] = {} 578 self.items: List[nodes.Item] = [] 579 580 self._bestrelpathcache: Dict[Path, str] = _bestrelpath_cache(config.rootpath) 581 582 self.config.pluginmanager.register(self, name="session")
634 @property 635 def startpath(self) -> Path: 636 """The path from which pytest was invoked. 637 638 .. versionadded:: 7.0.0 639 """ 640 return self.config.invocation_params.dir
The path from which pytest was invoked.
New in version 7.0.0.
653 @hookimpl(tryfirst=True) 654 def pytest_runtest_logreport( 655 self, report: Union[TestReport, CollectReport] 656 ) -> None: 657 if report.failed and not hasattr(report, "wasxfail"): 658 self.testsfailed += 1 659 maxfail = self.config.getvalue("maxfail") 660 if maxfail and self.testsfailed >= maxfail: 661 self.shouldfail = "stopping after %d failures" % (self.testsfailed)
653 @hookimpl(tryfirst=True) 654 def pytest_runtest_logreport( 655 self, report: Union[TestReport, CollectReport] 656 ) -> None: 657 if report.failed and not hasattr(report, "wasxfail"): 658 self.testsfailed += 1 659 maxfail = self.config.getvalue("maxfail") 660 if maxfail and self.testsfailed >= maxfail: 661 self.shouldfail = "stopping after %d failures" % (self.testsfailed)
665 def isinitpath( 666 self, 667 path: Union[str, "os.PathLike[str]"], 668 *, 669 with_parents: bool = False, 670 ) -> bool: 671 """Is path an initial path? 672 673 An initial path is a path explicitly given to pytest on the command 674 line. 675 676 :param with_parents: 677 If set, also return True if the path is a parent of an initial path. 678 679 .. versionchanged:: 8.0 680 Added the ``with_parents`` parameter. 681 """ 682 # Optimization: Path(Path(...)) is much slower than isinstance. 683 path_ = path if isinstance(path, Path) else Path(path) 684 if with_parents: 685 return path_ in self._initialpaths_with_parents 686 else: 687 return path_ in self._initialpaths
Is path an initial path?
An initial path is a path explicitly given to pytest on the command line.
Parameters
- with_parents: If set, also return True if the path is a parent of an initial path.
Changed in version 8.0:
Added the with_parents
parameter.
689 def gethookproxy(self, fspath: "os.PathLike[str]") -> pluggy.HookRelay: 690 # Optimization: Path(Path(...)) is much slower than isinstance. 691 path = fspath if isinstance(fspath, Path) else Path(fspath) 692 pm = self.config.pluginmanager 693 # Check if we have the common case of running 694 # hooks with all conftest.py files. 695 my_conftestmodules = pm._getconftestmodules(path) 696 remove_mods = pm._conftest_plugins.difference(my_conftestmodules) 697 proxy: pluggy.HookRelay 698 if remove_mods: 699 # One or more conftests are not in use at this path. 700 proxy = PathAwareHookProxy(FSHookProxy(pm, remove_mods)) # type: ignore[arg-type,assignment] 701 else: 702 # All plugins are active for this fspath. 703 proxy = self.config.hook 704 return proxy
747 def perform_collect( 748 self, args: Optional[Sequence[str]] = None, genitems: bool = True 749 ) -> Sequence[Union[nodes.Item, nodes.Collector]]: 750 """Perform the collection phase for this session. 751 752 This is called by the default :hook:`pytest_collection` hook 753 implementation; see the documentation of this hook for more details. 754 For testing purposes, it may also be called directly on a fresh 755 ``Session``. 756 757 This function normally recursively expands any collectors collected 758 from the session to their items, and only items are returned. For 759 testing purposes, this may be suppressed by passing ``genitems=False``, 760 in which case the return value contains these collectors unexpanded, 761 and ``session.items`` is empty. 762 """ 763 if args is None: 764 args = self.config.args 765 766 self.trace("perform_collect", self, args) 767 self.trace.root.indent += 1 768 769 hook = self.config.hook 770 771 self._notfound = [] 772 self._initial_parts = [] 773 self._collection_cache = {} 774 self.items = [] 775 items: Sequence[Union[nodes.Item, nodes.Collector]] = self.items 776 try: 777 initialpaths: List[Path] = [] 778 initialpaths_with_parents: List[Path] = [] 779 for arg in args: 780 collection_argument = resolve_collection_argument( 781 self.config.invocation_params.dir, 782 arg, 783 as_pypath=self.config.option.pyargs, 784 ) 785 self._initial_parts.append(collection_argument) 786 initialpaths.append(collection_argument.path) 787 initialpaths_with_parents.append(collection_argument.path) 788 initialpaths_with_parents.extend(collection_argument.path.parents) 789 self._initialpaths = frozenset(initialpaths) 790 self._initialpaths_with_parents = frozenset(initialpaths_with_parents) 791 792 rep = collect_one_node(self) 793 self.ihook.pytest_collectreport(report=rep) 794 self.trace.root.indent -= 1 795 if self._notfound: 796 errors = [] 797 for arg, collectors in self._notfound: 798 if collectors: 799 errors.append( 800 f"not found: {arg}\n(no match in any of {collectors!r})" 801 ) 802 else: 803 errors.append(f"found no collectors for {arg}") 804 805 raise UsageError(*errors) 806 807 if not genitems: 808 items = rep.result 809 else: 810 if rep.passed: 811 for node in rep.result: 812 self.items.extend(self.genitems(node)) 813 814 self.config.pluginmanager.check_pending() 815 hook.pytest_collection_modifyitems( 816 session=self, config=self.config, items=items 817 ) 818 finally: 819 self._notfound = [] 820 self._initial_parts = [] 821 self._collection_cache = {} 822 hook.pytest_collection_finish(session=self) 823 824 if genitems: 825 self.testscollected = len(items) 826 827 return items
Perform the collection phase for this session.
This is called by the default :hook:pytest_collection
hook
implementation; see the documentation of this hook for more details.
For testing purposes, it may also be called directly on a fresh
Session
.
This function normally recursively expands any collectors collected
from the session to their items, and only items are returned. For
testing purposes, this may be suppressed by passing genitems=False
,
in which case the return value contains these collectors unexpanded,
and session.items
is empty.
842 def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: 843 # This is a cache for the root directories of the initial paths. 844 # We can't use collection_cache for Session because of its special 845 # role as the bootstrapping collector. 846 path_cache: Dict[Path, Sequence[nodes.Collector]] = {} 847 848 pm = self.config.pluginmanager 849 850 for collection_argument in self._initial_parts: 851 self.trace("processing argument", collection_argument) 852 self.trace.root.indent += 1 853 854 argpath = collection_argument.path 855 names = collection_argument.parts 856 module_name = collection_argument.module_name 857 858 # resolve_collection_argument() ensures this. 859 if argpath.is_dir(): 860 assert not names, f"invalid arg {(argpath, names)!r}" 861 862 paths = [argpath] 863 # Add relevant parents of the path, from the root, e.g. 864 # /a/b/c.py -> [/, /a, /a/b, /a/b/c.py] 865 if module_name is None: 866 # Paths outside of the confcutdir should not be considered. 867 for path in argpath.parents: 868 if not pm._is_in_confcutdir(path): 869 break 870 paths.insert(0, path) 871 else: 872 # For --pyargs arguments, only consider paths matching the module 873 # name. Paths beyond the package hierarchy are not included. 874 module_name_parts = module_name.split(".") 875 for i, path in enumerate(argpath.parents, 2): 876 if i > len(module_name_parts) or path.stem != module_name_parts[-i]: 877 break 878 paths.insert(0, path) 879 880 # Start going over the parts from the root, collecting each level 881 # and discarding all nodes which don't match the level's part. 882 any_matched_in_initial_part = False 883 notfound_collectors = [] 884 work: List[ 885 Tuple[Union[nodes.Collector, nodes.Item], List[Union[Path, str]]] 886 ] = [(self, [*paths, *names])] 887 while work: 888 matchnode, matchparts = work.pop() 889 890 # Pop'd all of the parts, this is a match. 891 if not matchparts: 892 yield matchnode 893 any_matched_in_initial_part = True 894 continue 895 896 # Should have been matched by now, discard. 897 if not isinstance(matchnode, nodes.Collector): 898 continue 899 900 # Collect this level of matching. 901 # Collecting Session (self) is done directly to avoid endless 902 # recursion to this function. 903 subnodes: Sequence[Union[nodes.Collector, nodes.Item]] 904 if isinstance(matchnode, Session): 905 assert isinstance(matchparts[0], Path) 906 subnodes = matchnode._collect_path(matchparts[0], path_cache) 907 else: 908 # For backward compat, files given directly multiple 909 # times on the command line should not be deduplicated. 910 handle_dupes = not ( 911 len(matchparts) == 1 912 and isinstance(matchparts[0], Path) 913 and matchparts[0].is_file() 914 ) 915 rep, duplicate = self._collect_one_node(matchnode, handle_dupes) 916 if not duplicate and not rep.passed: 917 # Report collection failures here to avoid failing to 918 # run some test specified in the command line because 919 # the module could not be imported (#134). 920 matchnode.ihook.pytest_collectreport(report=rep) 921 if not rep.passed: 922 continue 923 subnodes = rep.result 924 925 # Prune this level. 926 any_matched_in_collector = False 927 for node in reversed(subnodes): 928 # Path part e.g. `/a/b/` in `/a/b/test_file.py::TestIt::test_it`. 929 if isinstance(matchparts[0], Path): 930 is_match = node.path == matchparts[0] 931 if sys.platform == "win32" and not is_match: 932 # In case the file paths do not match, fallback to samefile() to 933 # account for short-paths on Windows (#11895). 934 same_file = os.path.samefile(node.path, matchparts[0]) 935 # We don't want to match links to the current node, 936 # otherwise we would match the same file more than once (#12039). 937 is_match = same_file and ( 938 os.path.islink(node.path) 939 == os.path.islink(matchparts[0]) 940 ) 941 942 # Name part e.g. `TestIt` in `/a/b/test_file.py::TestIt::test_it`. 943 else: 944 # TODO: Remove parametrized workaround once collection structure contains 945 # parametrization. 946 is_match = ( 947 node.name == matchparts[0] 948 or node.name.split("[")[0] == matchparts[0] 949 ) 950 if is_match: 951 work.append((node, matchparts[1:])) 952 any_matched_in_collector = True 953 954 if not any_matched_in_collector: 955 notfound_collectors.append(matchnode) 956 957 if not any_matched_in_initial_part: 958 report_arg = "::".join((str(argpath), *names)) 959 self._notfound.append((report_arg, notfound_collectors)) 960 961 self.trace.root.indent -= 1
Collect children (items and collectors) for this collector.
963 def genitems( 964 self, node: Union[nodes.Item, nodes.Collector] 965 ) -> Iterator[nodes.Item]: 966 self.trace("genitems", node) 967 if isinstance(node, nodes.Item): 968 node.ihook.pytest_itemcollected(item=node) 969 yield node 970 else: 971 assert isinstance(node, nodes.Collector) 972 keepduplicates = self.config.getoption("keepduplicates") 973 # For backward compat, dedup only applies to files. 974 handle_dupes = not (keepduplicates and isinstance(node, nodes.File)) 975 rep, duplicate = self._collect_one_node(node, handle_dupes) 976 if duplicate and not keepduplicates: 977 return 978 if rep.passed: 979 for subnode in rep.result: 980 yield from self.genitems(subnode) 981 if not duplicate: 982 node.ihook.pytest_collectreport(report=rep)
Inherited Members
- _pytest.nodes.Node
- fspath
- name
- parent
- path
- keywords
- own_markers
- extra_keyword_matches
- stash
- from_parent
- ihook
- warn
- nodeid
- setup
- teardown
- iter_parents
- listchain
- add_marker
- iter_markers
- iter_markers_with_node
- get_closest_marker
- listextrakeywords
- listnames
- addfinalizer
- getparent
- config
- session
Signals that the test run was interrupted.
Inherited Members
- builtins.KeyboardInterrupt
- KeyboardInterrupt
- builtins.BaseException
- with_traceback
- add_note
- args
Signals a stop as failed test run.
Inherited Members
- builtins.Exception
- Exception
- builtins.BaseException
- with_traceback
- add_note
- args
279 @classmethod 280 def set_trace(cls, *args, **kwargs) -> None: 281 """Invoke debugging via ``Pdb.set_trace``, dropping any IO capturing.""" 282 frame = sys._getframe().f_back 283 _pdb = cls._init_pdb("set_trace", *args, **kwargs) 284 _pdb.set_trace(frame)
Invoke debugging via Pdb.set_trace
, dropping any IO capturing.
123@_with_exception(Skipped) 124def skip( 125 reason: str = "", 126 *, 127 allow_module_level: bool = False, 128) -> NoReturn: 129 """Skip an executing test with the given message. 130 131 This function should be called only during testing (setup, call or teardown) or 132 during collection by using the ``allow_module_level`` flag. This function can 133 be called in doctests as well. 134 135 :param reason: 136 The message to show the user as reason for the skip. 137 138 :param allow_module_level: 139 Allows this function to be called at module level. 140 Raising the skip exception at module level will stop 141 the execution of the module and prevent the collection of all tests in the module, 142 even those defined before the `skip` call. 143 144 Defaults to False. 145 146 .. note:: 147 It is better to use the :ref:`pytest.mark.skipif ref` marker when 148 possible to declare a test to be skipped under certain conditions 149 like mismatching platforms or dependencies. 150 Similarly, use the ``# doctest: +SKIP`` directive (see :py:data:`doctest.SKIP`) 151 to skip a doctest statically. 152 """ 153 __tracebackhide__ = True 154 raise Skipped(msg=reason, allow_module_level=allow_module_level)
Skip an executing test with the given message.
This function should be called only during testing (setup, call or teardown) or
during collection by using the allow_module_level
flag. This function can
be called in doctests as well.
Parameters
reason: The message to show the user as reason for the skip.
allow_module_level: Allows this function to be called at module level. Raising the skip exception at module level will stop the execution of the module and prevent the collection of all tests in the module, even those defined before the
skip
call.Defaults to False.
It is better to use the :ref:pytest.mark.skipif ref
marker when
possible to declare a test to be skipped under certain conditions
like mismatching platforms or dependencies.
Similarly, use the # doctest: +SKIP
directive (see doctest.SKIP
)
to skip a doctest statically.
30class Stash: 31 r"""``Stash`` is a type-safe heterogeneous mutable mapping that 32 allows keys and value types to be defined separately from 33 where it (the ``Stash``) is created. 34 35 Usually you will be given an object which has a ``Stash``, for example 36 :class:`~pytest.Config` or a :class:`~_pytest.nodes.Node`: 37 38 .. code-block:: python 39 40 stash: Stash = some_object.stash 41 42 If a module or plugin wants to store data in this ``Stash``, it creates 43 :class:`StashKey`\s for its keys (at the module level): 44 45 .. code-block:: python 46 47 # At the top-level of the module 48 some_str_key = StashKey[str]() 49 some_bool_key = StashKey[bool]() 50 51 To store information: 52 53 .. code-block:: python 54 55 # Value type must match the key. 56 stash[some_str_key] = "value" 57 stash[some_bool_key] = True 58 59 To retrieve the information: 60 61 .. code-block:: python 62 63 # The static type of some_str is str. 64 some_str = stash[some_str_key] 65 # The static type of some_bool is bool. 66 some_bool = stash[some_bool_key] 67 68 .. versionadded:: 7.0 69 """ 70 71 __slots__ = ("_storage",) 72 73 def __init__(self) -> None: 74 self._storage: Dict[StashKey[Any], object] = {} 75 76 def __setitem__(self, key: StashKey[T], value: T) -> None: 77 """Set a value for key.""" 78 self._storage[key] = value 79 80 def __getitem__(self, key: StashKey[T]) -> T: 81 """Get the value for key. 82 83 Raises ``KeyError`` if the key wasn't set before. 84 """ 85 return cast(T, self._storage[key]) 86 87 def get(self, key: StashKey[T], default: D) -> Union[T, D]: 88 """Get the value for key, or return default if the key wasn't set 89 before.""" 90 try: 91 return self[key] 92 except KeyError: 93 return default 94 95 def setdefault(self, key: StashKey[T], default: T) -> T: 96 """Return the value of key if already set, otherwise set the value 97 of key to default and return default.""" 98 try: 99 return self[key] 100 except KeyError: 101 self[key] = default 102 return default 103 104 def __delitem__(self, key: StashKey[T]) -> None: 105 """Delete the value for key. 106 107 Raises ``KeyError`` if the key wasn't set before. 108 """ 109 del self._storage[key] 110 111 def __contains__(self, key: StashKey[T]) -> bool: 112 """Return whether key was set.""" 113 return key in self._storage 114 115 def __len__(self) -> int: 116 """Return how many items exist in the stash.""" 117 return len(self._storage)
Stash
is a type-safe heterogeneous mutable mapping that
allows keys and value types to be defined separately from
where it (the Stash
) is created.
Usually you will be given an object which has a Stash
, for example
~pytest.Config
or a ~_pytest.nodes.Node
:
stash: Stash = some_object.stash
If a module or plugin wants to store data in this Stash
, it creates
StashKey
\s for its keys (at the module level):
# At the top-level of the module
some_str_key = StashKey[str]()
some_bool_key = StashKey[bool]()
To store information:
# Value type must match the key.
stash[some_str_key] = "value"
stash[some_bool_key] = True
To retrieve the information:
# The static type of some_str is str.
some_str = stash[some_str_key]
# The static type of some_bool is bool.
some_bool = stash[some_bool_key]
New in version 7.0.
87 def get(self, key: StashKey[T], default: D) -> Union[T, D]: 88 """Get the value for key, or return default if the key wasn't set 89 before.""" 90 try: 91 return self[key] 92 except KeyError: 93 return default
Get the value for key, or return default if the key wasn't set before.
95 def setdefault(self, key: StashKey[T], default: T) -> T: 96 """Return the value of key if already set, otherwise set the value 97 of key to default and return default.""" 98 try: 99 return self[key] 100 except KeyError: 101 self[key] = default 102 return default
Return the value of key if already set, otherwise set the value of key to default and return default.
17class StashKey(Generic[T]): 18 """``StashKey`` is an object used as a key to a :class:`Stash`. 19 20 A ``StashKey`` is associated with the type ``T`` of the value of the key. 21 22 A ``StashKey`` is unique and cannot conflict with another key. 23 24 .. versionadded:: 7.0 25 """ 26 27 __slots__ = ()
272@final 273@dataclasses.dataclass 274class TempdirFactory: 275 """Backward compatibility wrapper that implements ``py.path.local`` 276 for :class:`TempPathFactory`. 277 278 .. note:: 279 These days, it is preferred to use ``tmp_path_factory``. 280 281 :ref:`About the tmpdir and tmpdir_factory fixtures<tmpdir and tmpdir_factory>`. 282 283 """ 284 285 _tmppath_factory: TempPathFactory 286 287 def __init__( 288 self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False 289 ) -> None: 290 check_ispytest(_ispytest) 291 self._tmppath_factory = tmppath_factory 292 293 def mktemp(self, basename: str, numbered: bool = True) -> LEGACY_PATH: 294 """Same as :meth:`TempPathFactory.mktemp`, but returns a ``py.path.local`` object.""" 295 return legacy_path(self._tmppath_factory.mktemp(basename, numbered).resolve()) 296 297 def getbasetemp(self) -> LEGACY_PATH: 298 """Same as :meth:`TempPathFactory.getbasetemp`, but returns a ``py.path.local`` object.""" 299 return legacy_path(self._tmppath_factory.getbasetemp().resolve())
Backward compatibility wrapper that implements py.path.local
for TempPathFactory
.
These days, it is preferred to use tmp_path_factory
.
:ref:About the tmpdir and tmpdir_factory fixtures<tmpdir and tmpdir_factory>
.
293 def mktemp(self, basename: str, numbered: bool = True) -> LEGACY_PATH: 294 """Same as :meth:`TempPathFactory.mktemp`, but returns a ``py.path.local`` object.""" 295 return legacy_path(self._tmppath_factory.mktemp(basename, numbered).resolve())
Same as TempPathFactory.mktemp()
, but returns a py.path.local
object.
297 def getbasetemp(self) -> LEGACY_PATH: 298 """Same as :meth:`TempPathFactory.getbasetemp`, but returns a ``py.path.local`` object.""" 299 return legacy_path(self._tmppath_factory.getbasetemp().resolve())
Same as TempPathFactory.getbasetemp()
, but returns a py.path.local
object.
42@final 43@dataclasses.dataclass 44class TempPathFactory: 45 """Factory for temporary directories under the common base temp directory. 46 47 The base directory can be configured using the ``--basetemp`` option. 48 """ 49 50 _given_basetemp: Optional[Path] 51 # pluggy TagTracerSub, not currently exposed, so Any. 52 _trace: Any 53 _basetemp: Optional[Path] 54 _retention_count: int 55 _retention_policy: RetentionType 56 57 def __init__( 58 self, 59 given_basetemp: Optional[Path], 60 retention_count: int, 61 retention_policy: RetentionType, 62 trace, 63 basetemp: Optional[Path] = None, 64 *, 65 _ispytest: bool = False, 66 ) -> None: 67 check_ispytest(_ispytest) 68 if given_basetemp is None: 69 self._given_basetemp = None 70 else: 71 # Use os.path.abspath() to get absolute path instead of resolve() as it 72 # does not work the same in all platforms (see #4427). 73 # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012). 74 self._given_basetemp = Path(os.path.abspath(str(given_basetemp))) 75 self._trace = trace 76 self._retention_count = retention_count 77 self._retention_policy = retention_policy 78 self._basetemp = basetemp 79 80 @classmethod 81 def from_config( 82 cls, 83 config: Config, 84 *, 85 _ispytest: bool = False, 86 ) -> "TempPathFactory": 87 """Create a factory according to pytest configuration. 88 89 :meta private: 90 """ 91 check_ispytest(_ispytest) 92 count = int(config.getini("tmp_path_retention_count")) 93 if count < 0: 94 raise ValueError( 95 f"tmp_path_retention_count must be >= 0. Current input: {count}." 96 ) 97 98 policy = config.getini("tmp_path_retention_policy") 99 if policy not in ("all", "failed", "none"): 100 raise ValueError( 101 f"tmp_path_retention_policy must be either all, failed, none. Current input: {policy}." 102 ) 103 104 return cls( 105 given_basetemp=config.option.basetemp, 106 trace=config.trace.get("tmpdir"), 107 retention_count=count, 108 retention_policy=policy, 109 _ispytest=True, 110 ) 111 112 def _ensure_relative_to_basetemp(self, basename: str) -> str: 113 basename = os.path.normpath(basename) 114 if (self.getbasetemp() / basename).resolve().parent != self.getbasetemp(): 115 raise ValueError(f"{basename} is not a normalized and relative path") 116 return basename 117 118 def mktemp(self, basename: str, numbered: bool = True) -> Path: 119 """Create a new temporary directory managed by the factory. 120 121 :param basename: 122 Directory base name, must be a relative path. 123 124 :param numbered: 125 If ``True``, ensure the directory is unique by adding a numbered 126 suffix greater than any existing one: ``basename="foo-"`` and ``numbered=True`` 127 means that this function will create directories named ``"foo-0"``, 128 ``"foo-1"``, ``"foo-2"`` and so on. 129 130 :returns: 131 The path to the new directory. 132 """ 133 basename = self._ensure_relative_to_basetemp(basename) 134 if not numbered: 135 p = self.getbasetemp().joinpath(basename) 136 p.mkdir(mode=0o700) 137 else: 138 p = make_numbered_dir(root=self.getbasetemp(), prefix=basename, mode=0o700) 139 self._trace("mktemp", p) 140 return p 141 142 def getbasetemp(self) -> Path: 143 """Return the base temporary directory, creating it if needed. 144 145 :returns: 146 The base temporary directory. 147 """ 148 if self._basetemp is not None: 149 return self._basetemp 150 151 if self._given_basetemp is not None: 152 basetemp = self._given_basetemp 153 if basetemp.exists(): 154 rm_rf(basetemp) 155 basetemp.mkdir(mode=0o700) 156 basetemp = basetemp.resolve() 157 else: 158 from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") 159 temproot = Path(from_env or tempfile.gettempdir()).resolve() 160 user = get_user() or "unknown" 161 # use a sub-directory in the temproot to speed-up 162 # make_numbered_dir() call 163 rootdir = temproot.joinpath(f"pytest-of-{user}") 164 try: 165 rootdir.mkdir(mode=0o700, exist_ok=True) 166 except OSError: 167 # getuser() likely returned illegal characters for the platform, use unknown back off mechanism 168 rootdir = temproot.joinpath("pytest-of-unknown") 169 rootdir.mkdir(mode=0o700, exist_ok=True) 170 # Because we use exist_ok=True with a predictable name, make sure 171 # we are the owners, to prevent any funny business (on unix, where 172 # temproot is usually shared). 173 # Also, to keep things private, fixup any world-readable temp 174 # rootdir's permissions. Historically 0o755 was used, so we can't 175 # just error out on this, at least for a while. 176 uid = get_user_id() 177 if uid is not None: 178 rootdir_stat = rootdir.stat() 179 if rootdir_stat.st_uid != uid: 180 raise OSError( 181 f"The temporary directory {rootdir} is not owned by the current user. " 182 "Fix this and try again." 183 ) 184 if (rootdir_stat.st_mode & 0o077) != 0: 185 os.chmod(rootdir, rootdir_stat.st_mode & ~0o077) 186 keep = self._retention_count 187 if self._retention_policy == "none": 188 keep = 0 189 basetemp = make_numbered_dir_with_cleanup( 190 prefix="pytest-", 191 root=rootdir, 192 keep=keep, 193 lock_timeout=LOCK_TIMEOUT, 194 mode=0o700, 195 ) 196 assert basetemp is not None, basetemp 197 self._basetemp = basetemp 198 self._trace("new basetemp", basetemp) 199 return basetemp
Factory for temporary directories under the common base temp directory.
The base directory can be configured using the --basetemp
option.
57 def __init__( 58 self, 59 given_basetemp: Optional[Path], 60 retention_count: int, 61 retention_policy: RetentionType, 62 trace, 63 basetemp: Optional[Path] = None, 64 *, 65 _ispytest: bool = False, 66 ) -> None: 67 check_ispytest(_ispytest) 68 if given_basetemp is None: 69 self._given_basetemp = None 70 else: 71 # Use os.path.abspath() to get absolute path instead of resolve() as it 72 # does not work the same in all platforms (see #4427). 73 # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012). 74 self._given_basetemp = Path(os.path.abspath(str(given_basetemp))) 75 self._trace = trace 76 self._retention_count = retention_count 77 self._retention_policy = retention_policy 78 self._basetemp = basetemp
80 @classmethod 81 def from_config( 82 cls, 83 config: Config, 84 *, 85 _ispytest: bool = False, 86 ) -> "TempPathFactory": 87 """Create a factory according to pytest configuration. 88 89 :meta private: 90 """ 91 check_ispytest(_ispytest) 92 count = int(config.getini("tmp_path_retention_count")) 93 if count < 0: 94 raise ValueError( 95 f"tmp_path_retention_count must be >= 0. Current input: {count}." 96 ) 97 98 policy = config.getini("tmp_path_retention_policy") 99 if policy not in ("all", "failed", "none"): 100 raise ValueError( 101 f"tmp_path_retention_policy must be either all, failed, none. Current input: {policy}." 102 ) 103 104 return cls( 105 given_basetemp=config.option.basetemp, 106 trace=config.trace.get("tmpdir"), 107 retention_count=count, 108 retention_policy=policy, 109 _ispytest=True, 110 )
Create a factory according to pytest configuration.
:meta private:
118 def mktemp(self, basename: str, numbered: bool = True) -> Path: 119 """Create a new temporary directory managed by the factory. 120 121 :param basename: 122 Directory base name, must be a relative path. 123 124 :param numbered: 125 If ``True``, ensure the directory is unique by adding a numbered 126 suffix greater than any existing one: ``basename="foo-"`` and ``numbered=True`` 127 means that this function will create directories named ``"foo-0"``, 128 ``"foo-1"``, ``"foo-2"`` and so on. 129 130 :returns: 131 The path to the new directory. 132 """ 133 basename = self._ensure_relative_to_basetemp(basename) 134 if not numbered: 135 p = self.getbasetemp().joinpath(basename) 136 p.mkdir(mode=0o700) 137 else: 138 p = make_numbered_dir(root=self.getbasetemp(), prefix=basename, mode=0o700) 139 self._trace("mktemp", p) 140 return p
Create a new temporary directory managed by the factory.
Parameters
basename: Directory base name, must be a relative path.
numbered: If
True
, ensure the directory is unique by adding a numbered suffix greater than any existing one:basename="foo-"
andnumbered=True
means that this function will create directories named"foo-0"
,"foo-1"
,"foo-2"
and so on.
:returns: The path to the new directory.
142 def getbasetemp(self) -> Path: 143 """Return the base temporary directory, creating it if needed. 144 145 :returns: 146 The base temporary directory. 147 """ 148 if self._basetemp is not None: 149 return self._basetemp 150 151 if self._given_basetemp is not None: 152 basetemp = self._given_basetemp 153 if basetemp.exists(): 154 rm_rf(basetemp) 155 basetemp.mkdir(mode=0o700) 156 basetemp = basetemp.resolve() 157 else: 158 from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") 159 temproot = Path(from_env or tempfile.gettempdir()).resolve() 160 user = get_user() or "unknown" 161 # use a sub-directory in the temproot to speed-up 162 # make_numbered_dir() call 163 rootdir = temproot.joinpath(f"pytest-of-{user}") 164 try: 165 rootdir.mkdir(mode=0o700, exist_ok=True) 166 except OSError: 167 # getuser() likely returned illegal characters for the platform, use unknown back off mechanism 168 rootdir = temproot.joinpath("pytest-of-unknown") 169 rootdir.mkdir(mode=0o700, exist_ok=True) 170 # Because we use exist_ok=True with a predictable name, make sure 171 # we are the owners, to prevent any funny business (on unix, where 172 # temproot is usually shared). 173 # Also, to keep things private, fixup any world-readable temp 174 # rootdir's permissions. Historically 0o755 was used, so we can't 175 # just error out on this, at least for a while. 176 uid = get_user_id() 177 if uid is not None: 178 rootdir_stat = rootdir.stat() 179 if rootdir_stat.st_uid != uid: 180 raise OSError( 181 f"The temporary directory {rootdir} is not owned by the current user. " 182 "Fix this and try again." 183 ) 184 if (rootdir_stat.st_mode & 0o077) != 0: 185 os.chmod(rootdir, rootdir_stat.st_mode & ~0o077) 186 keep = self._retention_count 187 if self._retention_policy == "none": 188 keep = 0 189 basetemp = make_numbered_dir_with_cleanup( 190 prefix="pytest-", 191 root=rootdir, 192 keep=keep, 193 lock_timeout=LOCK_TIMEOUT, 194 mode=0o700, 195 ) 196 assert basetemp is not None, basetemp 197 self._basetemp = basetemp 198 self._trace("new basetemp", basetemp) 199 return basetemp
Return the base temporary directory, creating it if needed.
:returns: The base temporary directory.
43@final 44class Testdir: 45 """ 46 Similar to :class:`Pytester`, but this class works with legacy legacy_path objects instead. 47 48 All methods just forward to an internal :class:`Pytester` instance, converting results 49 to `legacy_path` objects as necessary. 50 """ 51 52 __test__ = False 53 54 CLOSE_STDIN: "Final" = Pytester.CLOSE_STDIN 55 TimeoutExpired: "Final" = Pytester.TimeoutExpired 56 57 def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None: 58 check_ispytest(_ispytest) 59 self._pytester = pytester 60 61 @property 62 def tmpdir(self) -> LEGACY_PATH: 63 """Temporary directory where tests are executed.""" 64 return legacy_path(self._pytester.path) 65 66 @property 67 def test_tmproot(self) -> LEGACY_PATH: 68 return legacy_path(self._pytester._test_tmproot) 69 70 @property 71 def request(self): 72 return self._pytester._request 73 74 @property 75 def plugins(self): 76 return self._pytester.plugins 77 78 @plugins.setter 79 def plugins(self, plugins): 80 self._pytester.plugins = plugins 81 82 @property 83 def monkeypatch(self) -> MonkeyPatch: 84 return self._pytester._monkeypatch 85 86 def make_hook_recorder(self, pluginmanager) -> HookRecorder: 87 """See :meth:`Pytester.make_hook_recorder`.""" 88 return self._pytester.make_hook_recorder(pluginmanager) 89 90 def chdir(self) -> None: 91 """See :meth:`Pytester.chdir`.""" 92 return self._pytester.chdir() 93 94 def finalize(self) -> None: 95 return self._pytester._finalize() 96 97 def makefile(self, ext, *args, **kwargs) -> LEGACY_PATH: 98 """See :meth:`Pytester.makefile`.""" 99 if ext and not ext.startswith("."): 100 # pytester.makefile is going to throw a ValueError in a way that 101 # testdir.makefile did not, because 102 # pathlib.Path is stricter suffixes than py.path 103 # This ext arguments is likely user error, but since testdir has 104 # allowed this, we will prepend "." as a workaround to avoid breaking 105 # testdir usage that worked before 106 ext = "." + ext 107 return legacy_path(self._pytester.makefile(ext, *args, **kwargs)) 108 109 def makeconftest(self, source) -> LEGACY_PATH: 110 """See :meth:`Pytester.makeconftest`.""" 111 return legacy_path(self._pytester.makeconftest(source)) 112 113 def makeini(self, source) -> LEGACY_PATH: 114 """See :meth:`Pytester.makeini`.""" 115 return legacy_path(self._pytester.makeini(source)) 116 117 def getinicfg(self, source: str) -> SectionWrapper: 118 """See :meth:`Pytester.getinicfg`.""" 119 return self._pytester.getinicfg(source) 120 121 def makepyprojecttoml(self, source) -> LEGACY_PATH: 122 """See :meth:`Pytester.makepyprojecttoml`.""" 123 return legacy_path(self._pytester.makepyprojecttoml(source)) 124 125 def makepyfile(self, *args, **kwargs) -> LEGACY_PATH: 126 """See :meth:`Pytester.makepyfile`.""" 127 return legacy_path(self._pytester.makepyfile(*args, **kwargs)) 128 129 def maketxtfile(self, *args, **kwargs) -> LEGACY_PATH: 130 """See :meth:`Pytester.maketxtfile`.""" 131 return legacy_path(self._pytester.maketxtfile(*args, **kwargs)) 132 133 def syspathinsert(self, path=None) -> None: 134 """See :meth:`Pytester.syspathinsert`.""" 135 return self._pytester.syspathinsert(path) 136 137 def mkdir(self, name) -> LEGACY_PATH: 138 """See :meth:`Pytester.mkdir`.""" 139 return legacy_path(self._pytester.mkdir(name)) 140 141 def mkpydir(self, name) -> LEGACY_PATH: 142 """See :meth:`Pytester.mkpydir`.""" 143 return legacy_path(self._pytester.mkpydir(name)) 144 145 def copy_example(self, name=None) -> LEGACY_PATH: 146 """See :meth:`Pytester.copy_example`.""" 147 return legacy_path(self._pytester.copy_example(name)) 148 149 def getnode(self, config: Config, arg) -> Optional[Union[Item, Collector]]: 150 """See :meth:`Pytester.getnode`.""" 151 return self._pytester.getnode(config, arg) 152 153 def getpathnode(self, path): 154 """See :meth:`Pytester.getpathnode`.""" 155 return self._pytester.getpathnode(path) 156 157 def genitems(self, colitems: List[Union[Item, Collector]]) -> List[Item]: 158 """See :meth:`Pytester.genitems`.""" 159 return self._pytester.genitems(colitems) 160 161 def runitem(self, source): 162 """See :meth:`Pytester.runitem`.""" 163 return self._pytester.runitem(source) 164 165 def inline_runsource(self, source, *cmdlineargs): 166 """See :meth:`Pytester.inline_runsource`.""" 167 return self._pytester.inline_runsource(source, *cmdlineargs) 168 169 def inline_genitems(self, *args): 170 """See :meth:`Pytester.inline_genitems`.""" 171 return self._pytester.inline_genitems(*args) 172 173 def inline_run(self, *args, plugins=(), no_reraise_ctrlc: bool = False): 174 """See :meth:`Pytester.inline_run`.""" 175 return self._pytester.inline_run( 176 *args, plugins=plugins, no_reraise_ctrlc=no_reraise_ctrlc 177 ) 178 179 def runpytest_inprocess(self, *args, **kwargs) -> RunResult: 180 """See :meth:`Pytester.runpytest_inprocess`.""" 181 return self._pytester.runpytest_inprocess(*args, **kwargs) 182 183 def runpytest(self, *args, **kwargs) -> RunResult: 184 """See :meth:`Pytester.runpytest`.""" 185 return self._pytester.runpytest(*args, **kwargs) 186 187 def parseconfig(self, *args) -> Config: 188 """See :meth:`Pytester.parseconfig`.""" 189 return self._pytester.parseconfig(*args) 190 191 def parseconfigure(self, *args) -> Config: 192 """See :meth:`Pytester.parseconfigure`.""" 193 return self._pytester.parseconfigure(*args) 194 195 def getitem(self, source, funcname="test_func"): 196 """See :meth:`Pytester.getitem`.""" 197 return self._pytester.getitem(source, funcname) 198 199 def getitems(self, source): 200 """See :meth:`Pytester.getitems`.""" 201 return self._pytester.getitems(source) 202 203 def getmodulecol(self, source, configargs=(), withinit=False): 204 """See :meth:`Pytester.getmodulecol`.""" 205 return self._pytester.getmodulecol( 206 source, configargs=configargs, withinit=withinit 207 ) 208 209 def collect_by_name( 210 self, modcol: Collector, name: str 211 ) -> Optional[Union[Item, Collector]]: 212 """See :meth:`Pytester.collect_by_name`.""" 213 return self._pytester.collect_by_name(modcol, name) 214 215 def popen( 216 self, 217 cmdargs, 218 stdout=subprocess.PIPE, 219 stderr=subprocess.PIPE, 220 stdin=CLOSE_STDIN, 221 **kw, 222 ): 223 """See :meth:`Pytester.popen`.""" 224 return self._pytester.popen(cmdargs, stdout, stderr, stdin, **kw) 225 226 def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN) -> RunResult: 227 """See :meth:`Pytester.run`.""" 228 return self._pytester.run(*cmdargs, timeout=timeout, stdin=stdin) 229 230 def runpython(self, script) -> RunResult: 231 """See :meth:`Pytester.runpython`.""" 232 return self._pytester.runpython(script) 233 234 def runpython_c(self, command): 235 """See :meth:`Pytester.runpython_c`.""" 236 return self._pytester.runpython_c(command) 237 238 def runpytest_subprocess(self, *args, timeout=None) -> RunResult: 239 """See :meth:`Pytester.runpytest_subprocess`.""" 240 return self._pytester.runpytest_subprocess(*args, timeout=timeout) 241 242 def spawn_pytest( 243 self, string: str, expect_timeout: float = 10.0 244 ) -> "pexpect.spawn": 245 """See :meth:`Pytester.spawn_pytest`.""" 246 return self._pytester.spawn_pytest(string, expect_timeout=expect_timeout) 247 248 def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn": 249 """See :meth:`Pytester.spawn`.""" 250 return self._pytester.spawn(cmd, expect_timeout=expect_timeout) 251 252 def __repr__(self) -> str: 253 return f"<Testdir {self.tmpdir!r}>" 254 255 def __str__(self) -> str: 256 return str(self.tmpdir)
Similar to Pytester
, but this class works with legacy legacy_path objects instead.
All methods just forward to an internal Pytester
instance, converting results
to legacy_path
objects as necessary.
61 @property 62 def tmpdir(self) -> LEGACY_PATH: 63 """Temporary directory where tests are executed.""" 64 return legacy_path(self._pytester.path)
Temporary directory where tests are executed.
97 def makefile(self, ext, *args, **kwargs) -> LEGACY_PATH: 98 """See :meth:`Pytester.makefile`.""" 99 if ext and not ext.startswith("."): 100 # pytester.makefile is going to throw a ValueError in a way that 101 # testdir.makefile did not, because 102 # pathlib.Path is stricter suffixes than py.path 103 # This ext arguments is likely user error, but since testdir has 104 # allowed this, we will prepend "." as a workaround to avoid breaking 105 # testdir usage that worked before 106 ext = "." + ext 107 return legacy_path(self._pytester.makefile(ext, *args, **kwargs))
See Pytester.makefile()
.
113 def makeini(self, source) -> LEGACY_PATH: 114 """See :meth:`Pytester.makeini`.""" 115 return legacy_path(self._pytester.makeini(source))
See Pytester.makeini()
.
117 def getinicfg(self, source: str) -> SectionWrapper: 118 """See :meth:`Pytester.getinicfg`.""" 119 return self._pytester.getinicfg(source)
See Pytester.getinicfg()
.
137 def mkdir(self, name) -> LEGACY_PATH: 138 """See :meth:`Pytester.mkdir`.""" 139 return legacy_path(self._pytester.mkdir(name))
See Pytester.mkdir()
.
141 def mkpydir(self, name) -> LEGACY_PATH: 142 """See :meth:`Pytester.mkpydir`.""" 143 return legacy_path(self._pytester.mkpydir(name))
See Pytester.mkpydir()
.
149 def getnode(self, config: Config, arg) -> Optional[Union[Item, Collector]]: 150 """See :meth:`Pytester.getnode`.""" 151 return self._pytester.getnode(config, arg)
See Pytester.getnode()
.
157 def genitems(self, colitems: List[Union[Item, Collector]]) -> List[Item]: 158 """See :meth:`Pytester.genitems`.""" 159 return self._pytester.genitems(colitems)
See Pytester.genitems()
.
161 def runitem(self, source): 162 """See :meth:`Pytester.runitem`.""" 163 return self._pytester.runitem(source)
See Pytester.runitem()
.
183 def runpytest(self, *args, **kwargs) -> RunResult: 184 """See :meth:`Pytester.runpytest`.""" 185 return self._pytester.runpytest(*args, **kwargs)
See Pytester.runpytest()
.
195 def getitem(self, source, funcname="test_func"): 196 """See :meth:`Pytester.getitem`.""" 197 return self._pytester.getitem(source, funcname)
See Pytester.getitem()
.
199 def getitems(self, source): 200 """See :meth:`Pytester.getitems`.""" 201 return self._pytester.getitems(source)
See Pytester.getitems()
.
215 def popen( 216 self, 217 cmdargs, 218 stdout=subprocess.PIPE, 219 stderr=subprocess.PIPE, 220 stdin=CLOSE_STDIN, 221 **kw, 222 ): 223 """See :meth:`Pytester.popen`.""" 224 return self._pytester.popen(cmdargs, stdout, stderr, stdin, **kw)
See Pytester.popen()
.
226 def run(self, *cmdargs, timeout=None, stdin=CLOSE_STDIN) -> RunResult: 227 """See :meth:`Pytester.run`.""" 228 return self._pytester.run(*cmdargs, timeout=timeout, stdin=stdin)
See Pytester.run()
.
230 def runpython(self, script) -> RunResult: 231 """See :meth:`Pytester.runpython`.""" 232 return self._pytester.runpython(script)
See Pytester.runpython()
.
248 def spawn(self, cmd: str, expect_timeout: float = 10.0) -> "pexpect.spawn": 249 """See :meth:`Pytester.spawn`.""" 250 return self._pytester.spawn(cmd, expect_timeout=expect_timeout)
See Pytester.spawn()
.
Common base class for all non-exit exceptions.
Inherited Members
- builtins.Exception
- Exception
- builtins.BaseException
- with_traceback
- add_note
- args
244@final 245class TestReport(BaseReport): 246 """Basic test report object (also used for setup and teardown calls if 247 they fail). 248 249 Reports can contain arbitrary extra attributes. 250 """ 251 252 __test__ = False 253 # Defined by skipping plugin. 254 # xfail reason if xfailed, otherwise not defined. Use hasattr to distinguish. 255 wasxfail: str 256 257 def __init__( 258 self, 259 nodeid: str, 260 location: Tuple[str, Optional[int], str], 261 keywords: Mapping[str, Any], 262 outcome: Literal["passed", "failed", "skipped"], 263 longrepr: Union[ 264 None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr 265 ], 266 when: Literal["setup", "call", "teardown"], 267 sections: Iterable[Tuple[str, str]] = (), 268 duration: float = 0, 269 start: float = 0, 270 stop: float = 0, 271 user_properties: Optional[Iterable[Tuple[str, object]]] = None, 272 **extra, 273 ) -> None: 274 #: Normalized collection nodeid. 275 self.nodeid = nodeid 276 277 #: A (filesystempath, lineno, domaininfo) tuple indicating the 278 #: actual location of a test item - it might be different from the 279 #: collected one e.g. if a method is inherited from a different module. 280 #: The filesystempath may be relative to ``config.rootdir``. 281 #: The line number is 0-based. 282 self.location: Tuple[str, Optional[int], str] = location 283 284 #: A name -> value dictionary containing all keywords and 285 #: markers associated with a test invocation. 286 self.keywords: Mapping[str, Any] = keywords 287 288 #: Test outcome, always one of "passed", "failed", "skipped". 289 self.outcome = outcome 290 291 #: None or a failure representation. 292 self.longrepr = longrepr 293 294 #: One of 'setup', 'call', 'teardown' to indicate runtest phase. 295 self.when = when 296 297 #: User properties is a list of tuples (name, value) that holds user 298 #: defined properties of the test. 299 self.user_properties = list(user_properties or []) 300 301 #: Tuples of str ``(heading, content)`` with extra information 302 #: for the test report. Used by pytest to add text captured 303 #: from ``stdout``, ``stderr``, and intercepted logging events. May 304 #: be used by other plugins to add arbitrary information to reports. 305 self.sections = list(sections) 306 307 #: Time it took to run just the test. 308 self.duration: float = duration 309 310 #: The system time when the call started, in seconds since the epoch. 311 self.start: float = start 312 #: The system time when the call ended, in seconds since the epoch. 313 self.stop: float = stop 314 315 self.__dict__.update(extra) 316 317 def __repr__(self) -> str: 318 return f"<{self.__class__.__name__} {self.nodeid!r} when={self.when!r} outcome={self.outcome!r}>" 319 320 @classmethod 321 def from_item_and_call(cls, item: Item, call: "CallInfo[None]") -> "TestReport": 322 """Create and fill a TestReport with standard item and call info. 323 324 :param item: The item. 325 :param call: The call info. 326 """ 327 when = call.when 328 # Remove "collect" from the Literal type -- only for collection calls. 329 assert when != "collect" 330 duration = call.duration 331 start = call.start 332 stop = call.stop 333 keywords = {x: 1 for x in item.keywords} 334 excinfo = call.excinfo 335 sections = [] 336 if not call.excinfo: 337 outcome: Literal["passed", "failed", "skipped"] = "passed" 338 longrepr: Union[ 339 None, 340 ExceptionInfo[BaseException], 341 Tuple[str, int, str], 342 str, 343 TerminalRepr, 344 ] = None 345 else: 346 if not isinstance(excinfo, ExceptionInfo): 347 outcome = "failed" 348 longrepr = excinfo 349 elif isinstance(excinfo.value, skip.Exception): 350 outcome = "skipped" 351 r = excinfo._getreprcrash() 352 assert ( 353 r is not None 354 ), "There should always be a traceback entry for skipping a test." 355 if excinfo.value._use_item_location: 356 path, line = item.reportinfo()[:2] 357 assert line is not None 358 longrepr = os.fspath(path), line + 1, r.message 359 else: 360 longrepr = (str(r.path), r.lineno, r.message) 361 else: 362 outcome = "failed" 363 if call.when == "call": 364 longrepr = item.repr_failure(excinfo) 365 else: # exception in setup or teardown 366 longrepr = item._repr_failure_py( 367 excinfo, style=item.config.getoption("tbstyle", "auto") 368 ) 369 for rwhen, key, content in item._report_sections: 370 sections.append((f"Captured {key} {rwhen}", content)) 371 return cls( 372 item.nodeid, 373 item.location, 374 keywords, 375 outcome, 376 longrepr, 377 when, 378 sections, 379 duration, 380 start, 381 stop, 382 user_properties=item.user_properties, 383 )
Basic test report object (also used for setup and teardown calls if they fail).
Reports can contain arbitrary extra attributes.
257 def __init__( 258 self, 259 nodeid: str, 260 location: Tuple[str, Optional[int], str], 261 keywords: Mapping[str, Any], 262 outcome: Literal["passed", "failed", "skipped"], 263 longrepr: Union[ 264 None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr 265 ], 266 when: Literal["setup", "call", "teardown"], 267 sections: Iterable[Tuple[str, str]] = (), 268 duration: float = 0, 269 start: float = 0, 270 stop: float = 0, 271 user_properties: Optional[Iterable[Tuple[str, object]]] = None, 272 **extra, 273 ) -> None: 274 #: Normalized collection nodeid. 275 self.nodeid = nodeid 276 277 #: A (filesystempath, lineno, domaininfo) tuple indicating the 278 #: actual location of a test item - it might be different from the 279 #: collected one e.g. if a method is inherited from a different module. 280 #: The filesystempath may be relative to ``config.rootdir``. 281 #: The line number is 0-based. 282 self.location: Tuple[str, Optional[int], str] = location 283 284 #: A name -> value dictionary containing all keywords and 285 #: markers associated with a test invocation. 286 self.keywords: Mapping[str, Any] = keywords 287 288 #: Test outcome, always one of "passed", "failed", "skipped". 289 self.outcome = outcome 290 291 #: None or a failure representation. 292 self.longrepr = longrepr 293 294 #: One of 'setup', 'call', 'teardown' to indicate runtest phase. 295 self.when = when 296 297 #: User properties is a list of tuples (name, value) that holds user 298 #: defined properties of the test. 299 self.user_properties = list(user_properties or []) 300 301 #: Tuples of str ``(heading, content)`` with extra information 302 #: for the test report. Used by pytest to add text captured 303 #: from ``stdout``, ``stderr``, and intercepted logging events. May 304 #: be used by other plugins to add arbitrary information to reports. 305 self.sections = list(sections) 306 307 #: Time it took to run just the test. 308 self.duration: float = duration 309 310 #: The system time when the call started, in seconds since the epoch. 311 self.start: float = start 312 #: The system time when the call ended, in seconds since the epoch. 313 self.stop: float = stop 314 315 self.__dict__.update(extra)
320 @classmethod 321 def from_item_and_call(cls, item: Item, call: "CallInfo[None]") -> "TestReport": 322 """Create and fill a TestReport with standard item and call info. 323 324 :param item: The item. 325 :param call: The call info. 326 """ 327 when = call.when 328 # Remove "collect" from the Literal type -- only for collection calls. 329 assert when != "collect" 330 duration = call.duration 331 start = call.start 332 stop = call.stop 333 keywords = {x: 1 for x in item.keywords} 334 excinfo = call.excinfo 335 sections = [] 336 if not call.excinfo: 337 outcome: Literal["passed", "failed", "skipped"] = "passed" 338 longrepr: Union[ 339 None, 340 ExceptionInfo[BaseException], 341 Tuple[str, int, str], 342 str, 343 TerminalRepr, 344 ] = None 345 else: 346 if not isinstance(excinfo, ExceptionInfo): 347 outcome = "failed" 348 longrepr = excinfo 349 elif isinstance(excinfo.value, skip.Exception): 350 outcome = "skipped" 351 r = excinfo._getreprcrash() 352 assert ( 353 r is not None 354 ), "There should always be a traceback entry for skipping a test." 355 if excinfo.value._use_item_location: 356 path, line = item.reportinfo()[:2] 357 assert line is not None 358 longrepr = os.fspath(path), line + 1, r.message 359 else: 360 longrepr = (str(r.path), r.lineno, r.message) 361 else: 362 outcome = "failed" 363 if call.when == "call": 364 longrepr = item.repr_failure(excinfo) 365 else: # exception in setup or teardown 366 longrepr = item._repr_failure_py( 367 excinfo, style=item.config.getoption("tbstyle", "auto") 368 ) 369 for rwhen, key, content in item._report_sections: 370 sections.append((f"Captured {key} {rwhen}", content)) 371 return cls( 372 item.nodeid, 373 item.location, 374 keywords, 375 outcome, 376 longrepr, 377 when, 378 sections, 379 duration, 380 start, 381 stop, 382 user_properties=item.user_properties, 383 )
Create and fill a TestReport with standard item and call info.
Parameters
- item: The item.
- call: The call info.
Inherited Members
- _pytest.reports.BaseReport
- toterminal
- get_sections
- longreprtext
- caplog
- capstdout
- capstderr
- passed
- failed
- skipped
- fspath
- count_towards_summary
- head_line
118class TestShortLogReport(NamedTuple): 119 """Used to store the test status result category, shortletter and verbose word. 120 For example ``"rerun", "R", ("RERUN", {"yellow": True})``. 121 122 :ivar category: 123 The class of result, for example ``“passed”``, ``“skipped”``, ``“error”``, or the empty string. 124 125 :ivar letter: 126 The short letter shown as testing progresses, for example ``"."``, ``"s"``, ``"E"``, or the empty string. 127 128 :ivar word: 129 Verbose word is shown as testing progresses in verbose mode, for example ``"PASSED"``, ``"SKIPPED"``, 130 ``"ERROR"``, or the empty string. 131 """ 132 133 category: str 134 letter: str 135 word: Union[str, Tuple[str, Mapping[str, bool]]]
Used to store the test status result category, shortletter and verbose word.
For example "rerun", "R", ("RERUN", {"yellow": True})
.
:ivar category:
The class of result, for example “passed”
, “skipped”
, “error”
, or the empty string.
:ivar letter:
The short letter shown as testing progresses, for example "."
, "s"
, "E"
, or the empty string.
:ivar word:
Verbose word is shown as testing progresses in verbose mode, for example "PASSED"
, "SKIPPED"
,
"ERROR"
, or the empty string.
Create new instance of TestShortLogReport(category, letter, word)
Inherited Members
- builtins.tuple
- index
- count
Error in pytest usage or invocation.
Inherited Members
- builtins.Exception
- Exception
- builtins.BaseException
- with_traceback
- add_note
- args
170class WarningsRecorder(warnings.catch_warnings): # type:ignore[type-arg] 171 """A context manager to record raised warnings. 172 173 Each recorded warning is an instance of :class:`warnings.WarningMessage`. 174 175 Adapted from `warnings.catch_warnings`. 176 177 .. note:: 178 ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated 179 differently; see :ref:`ensuring_function_triggers`. 180 181 """ 182 183 def __init__(self, *, _ispytest: bool = False) -> None: 184 check_ispytest(_ispytest) 185 super().__init__(record=True) 186 self._entered = False 187 self._list: List[warnings.WarningMessage] = [] 188 189 @property 190 def list(self) -> List["warnings.WarningMessage"]: 191 """The list of recorded warnings.""" 192 return self._list 193 194 def __getitem__(self, i: int) -> "warnings.WarningMessage": 195 """Get a recorded warning by index.""" 196 return self._list[i] 197 198 def __iter__(self) -> Iterator["warnings.WarningMessage"]: 199 """Iterate through the recorded warnings.""" 200 return iter(self._list) 201 202 def __len__(self) -> int: 203 """The number of recorded warnings.""" 204 return len(self._list) 205 206 def pop(self, cls: Type[Warning] = Warning) -> "warnings.WarningMessage": 207 """Pop the first recorded warning which is an instance of ``cls``, 208 but not an instance of a child class of any other match. 209 Raises ``AssertionError`` if there is no match. 210 """ 211 best_idx: Optional[int] = None 212 for i, w in enumerate(self._list): 213 if w.category == cls: 214 return self._list.pop(i) # exact match, stop looking 215 if issubclass(w.category, cls) and ( 216 best_idx is None 217 or not issubclass(w.category, self._list[best_idx].category) 218 ): 219 best_idx = i 220 if best_idx is not None: 221 return self._list.pop(best_idx) 222 __tracebackhide__ = True 223 raise AssertionError(f"{cls!r} not found in warning list") 224 225 def clear(self) -> None: 226 """Clear the list of recorded warnings.""" 227 self._list[:] = [] 228 229 # Type ignored because it doesn't exactly warnings.catch_warnings.__enter__ 230 # -- it returns a List but we only emulate one. 231 def __enter__(self) -> "WarningsRecorder": # type: ignore 232 if self._entered: 233 __tracebackhide__ = True 234 raise RuntimeError(f"Cannot enter {self!r} twice") 235 _list = super().__enter__() 236 # record=True means it's None. 237 assert _list is not None 238 self._list = _list 239 warnings.simplefilter("always") 240 return self 241 242 def __exit__( 243 self, 244 exc_type: Optional[Type[BaseException]], 245 exc_val: Optional[BaseException], 246 exc_tb: Optional[TracebackType], 247 ) -> None: 248 if not self._entered: 249 __tracebackhide__ = True 250 raise RuntimeError(f"Cannot exit {self!r} without entering first") 251 252 super().__exit__(exc_type, exc_val, exc_tb) 253 254 # Built-in catch_warnings does not reset entered state so we do it 255 # manually here for this context manager to become reusable. 256 self._entered = False
A context manager to record raised warnings.
Each recorded warning is an instance of warnings.WarningMessage
.
Adapted from warnings.catch_warnings
.
DeprecationWarning
and PendingDeprecationWarning
are treated
differently; see :ref:ensuring_function_triggers
.
183 def __init__(self, *, _ispytest: bool = False) -> None: 184 check_ispytest(_ispytest) 185 super().__init__(record=True) 186 self._entered = False 187 self._list: List[warnings.WarningMessage] = []
Specify whether to record warnings and if an alternative module should be used other than sys.modules['warnings'].
For compatibility with Python 3.0, please consider all arguments to be keyword-only.
189 @property 190 def list(self) -> List["warnings.WarningMessage"]: 191 """The list of recorded warnings.""" 192 return self._list
The list of recorded warnings.
206 def pop(self, cls: Type[Warning] = Warning) -> "warnings.WarningMessage": 207 """Pop the first recorded warning which is an instance of ``cls``, 208 but not an instance of a child class of any other match. 209 Raises ``AssertionError`` if there is no match. 210 """ 211 best_idx: Optional[int] = None 212 for i, w in enumerate(self._list): 213 if w.category == cls: 214 return self._list.pop(i) # exact match, stop looking 215 if issubclass(w.category, cls) and ( 216 best_idx is None 217 or not issubclass(w.category, self._list[best_idx].category) 218 ): 219 best_idx = i 220 if best_idx is not None: 221 return self._list.pop(best_idx) 222 __tracebackhide__ = True 223 raise AssertionError(f"{cls!r} not found in warning list")
Pop the first recorded warning which is an instance of cls
,
but not an instance of a child class of any other match.
Raises AssertionError
if there is no match.
106def warns( 107 expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = Warning, 108 *args: Any, 109 match: Optional[Union[str, Pattern[str]]] = None, 110 **kwargs: Any, 111) -> Union["WarningsChecker", Any]: 112 r"""Assert that code raises a particular class of warning. 113 114 Specifically, the parameter ``expected_warning`` can be a warning class or tuple 115 of warning classes, and the code inside the ``with`` block must issue at least one 116 warning of that class or classes. 117 118 This helper produces a list of :class:`warnings.WarningMessage` objects, one for 119 each warning emitted (regardless of whether it is an ``expected_warning`` or not). 120 Since pytest 8.0, unmatched warnings are also re-emitted when the context closes. 121 122 This function can be used as a context manager:: 123 124 >>> import pytest 125 >>> with pytest.warns(RuntimeWarning): 126 ... warnings.warn("my warning", RuntimeWarning) 127 128 In the context manager form you may use the keyword argument ``match`` to assert 129 that the warning matches a text or regex:: 130 131 >>> with pytest.warns(UserWarning, match='must be 0 or None'): 132 ... warnings.warn("value must be 0 or None", UserWarning) 133 134 >>> with pytest.warns(UserWarning, match=r'must be \d+$'): 135 ... warnings.warn("value must be 42", UserWarning) 136 137 >>> with pytest.warns(UserWarning): # catch re-emitted warning 138 ... with pytest.warns(UserWarning, match=r'must be \d+$'): 139 ... warnings.warn("this is not here", UserWarning) 140 Traceback (most recent call last): 141 ... 142 Failed: DID NOT WARN. No warnings of type ...UserWarning... were emitted... 143 144 **Using with** ``pytest.mark.parametrize`` 145 146 When using :ref:`pytest.mark.parametrize ref` it is possible to parametrize tests 147 such that some runs raise a warning and others do not. 148 149 This could be achieved in the same way as with exceptions, see 150 :ref:`parametrizing_conditional_raising` for an example. 151 152 """ 153 __tracebackhide__ = True 154 if not args: 155 if kwargs: 156 argnames = ", ".join(sorted(kwargs)) 157 raise TypeError( 158 f"Unexpected keyword arguments passed to pytest.warns: {argnames}" 159 "\nUse context-manager form instead?" 160 ) 161 return WarningsChecker(expected_warning, match_expr=match, _ispytest=True) 162 else: 163 func = args[0] 164 if not callable(func): 165 raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") 166 with WarningsChecker(expected_warning, _ispytest=True): 167 return func(*args[1:], **kwargs)
Assert that code raises a particular class of warning.
Specifically, the parameter expected_warning
can be a warning class or tuple
of warning classes, and the code inside the with
block must issue at least one
warning of that class or classes.
This helper produces a list of warnings.WarningMessage
objects, one for
each warning emitted (regardless of whether it is an expected_warning
or not).
Since pytest 8.0, unmatched warnings are also re-emitted when the context closes.
This function can be used as a context manager::
>>> import pytest
>>> with pytest.warns(RuntimeWarning):
... warnings.warn("my warning", RuntimeWarning)
In the context manager form you may use the keyword argument match
to assert
that the warning matches a text or regex::
>>> with pytest.warns(UserWarning, match='must be 0 or None'):
... warnings.warn("value must be 0 or None", UserWarning)
>>> with pytest.warns(UserWarning, match=r'must be \d+$'):
... warnings.warn("value must be 42", UserWarning)
>>> with pytest.warns(UserWarning): # catch re-emitted warning
... with pytest.warns(UserWarning, match=r'must be \d+$'):
... warnings.warn("this is not here", UserWarning)
Traceback (most recent call last):
...
Failed: DID NOT WARN. No warnings of type ...UserWarning... were emitted...
Using with pytest.mark.parametrize
When using :ref:pytest.mark.parametrize ref
it is possible to parametrize tests
such that some runs raise a warning and others do not.
This could be achieved in the same way as with exceptions, see
:ref:parametrizing_conditional_raising
for an example.
176@_with_exception(XFailed) 177def xfail(reason: str = "") -> NoReturn: 178 """Imperatively xfail an executing test or setup function with the given reason. 179 180 This function should be called only during testing (setup, call or teardown). 181 182 No other code is executed after using ``xfail()`` (it is implemented 183 internally by raising an exception). 184 185 :param reason: 186 The message to show the user as reason for the xfail. 187 188 .. note:: 189 It is better to use the :ref:`pytest.mark.xfail ref` marker when 190 possible to declare a test to be xfailed under certain conditions 191 like known bugs or missing features. 192 """ 193 __tracebackhide__ = True 194 raise XFailed(reason)
Imperatively xfail an executing test or setup function with the given reason.
This function should be called only during testing (setup, call or teardown).
No other code is executed after using xfail()
(it is implemented
internally by raising an exception).
Parameters
- reason: The message to show the user as reason for the xfail.
It is better to use the :ref:pytest.mark.xfail ref
marker when
possible to declare a test to be xfailed under certain conditions
like known bugs or missing features.
1309def yield_fixture( 1310 fixture_function=None, 1311 *args, 1312 scope="function", 1313 params=None, 1314 autouse=False, 1315 ids=None, 1316 name=None, 1317): 1318 """(Return a) decorator to mark a yield-fixture factory function. 1319 1320 .. deprecated:: 3.0 1321 Use :py:func:`pytest.fixture` directly instead. 1322 """ 1323 warnings.warn(YIELD_FIXTURE, stacklevel=2) 1324 return fixture( 1325 fixture_function, 1326 *args, 1327 scope=scope, 1328 params=params, 1329 autouse=autouse, 1330 ids=ids, 1331 name=name, 1332 )
(Return a) decorator to mark a yield-fixture factory function.
Deprecated since version 3.0:
Use pytest.fixture()
directly instead.