Skip to content

Update dependency pytest to v9.2.0 - #367

Open
renovate[bot] wants to merge 1 commit into
developfrom
renovate/pytest-9.x-lockfile
Open

renovate[bot] wants to merge 1 commit into
developfrom
renovate/pytest-9.x-lockfile

Conversation

@renovate

@renovate renovate Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
pytest (changelog) 9.0.2 → 9.2.0 age confidence

Release Notes

pytest-dev/pytest (pytest)

v9.2.0

Compare Source

pytest 9.2.0 (2026-10-27)

Removals and backward incompatible breaking changes

  • #​14716: -c now raises a usage error for a path that does not exist, is a directory, or is in a format pytest has no loader for. Such paths were previously either ignored or reported as an unhandled FileNotFoundError.

  • #​8593: The indirect, ids and scope arguments of Metafunc.parametrize() <pytest.Metafunc.parametrize> -- and therefore of @pytest.mark.parametrize <pytest.mark.parametrize ref> -- are now keyword-only.

    Passing them positionally was a common source of confusing errors, e.g. parametrize("arg1", "arg2", [(1, 2)]) made the argvalues land in indirect and failed with indirect fixture '(1, 2)' doesn't exist; such mistakes now fail with an understandable TypeError. The type annotations have always declared these arguments as keyword-only.

  • #​993: Plugin import failures at startup are now reported and classified instead of escaping as a raw traceback with the accidental exit code 1:

    • a plugin that cannot be found -- via -p, pytest_plugins or PYTEST_PLUGINS -- exits with pytest.ExitCode USAGE_ERROR (4), like a conftest.py that fails to import;
    • a plugin that is found but raises while importing -- including a broken pytest11 entry point or a missing plugin dependency -- exits with pytest.ExitCode INTERNAL_ERROR (3), with the traceback preserved.

    pytest.main now returns these codes instead of raising ImportError. Also fixed an IndexError from pytest's own internals when the plugin raised an exception with no arguments.

Deprecations (removal in next major release)

  • #​14742: The private _pytest.python.CallSpec2 name has been renamed to CallSpec.
    The old CallSpec2 alias remains available for now and will be removed in pytest 10 or 11.

  • #​14755: Schedule yield_fixture decorator for removal in pytest 10 or 11.

  • #​14774: Similarly to the deprecation of class-scoped fixtures defined as instance methods in pytest 9.1,
    all higher-scoped fixtures (module, package, and session) defined
    as instance methods are now deprecated.

    They suffer from the same issue the original deprecation only fixed for
    class-scoped fixtures, see 10819 and 14011.

  • #​14808: Passing a non-string value to a string-typed ini option now emits a pytest.PytestRemovedIn10Warning. This affects [tool.pytest.ini_options] in pyproject.toml. In pytest 10 this will raise a TypeError, matching the behavior of the corresponding TOML config path.

  • #​7323: Passing bytes to TerminalReporter.write_line() is now deprecated, pass a str instead.

New features

  • #​14743: Full diff: now contains a legend explaining the + & - symbols in diff output.
  • #​2781: When strict_markers is enabled, marker names used in -m expressions are now validated against registered markers.
  • #​6881: Added the parametrize_long_str_id_strategy configuration option to control how long str/bytes parameter values are handled in auto-generated test IDs. Explicit IDs set via ids=[...] or pytest.param(..., id=...) are not affected. Available strategies: short (default, values over 100 chars fall back to argname+index), sha256 (SHA-256 hex digest), legacy (keep full value), and disallow (error requesting explicit IDs).

Improvements in existing functionality

  • #​10745: A RecursionError whose recursion origin cannot be located now shows only the
    first and last 10 stack frames, matching what pytest already did when locating
    the origin failed outright. Previously the full traceback -- often around a
    thousand near-identical frames -- was rendered, which was both slow and
    unreadable. Pass --full-trace to see every frame.

  • #​10745: The line numbers of a module's statements are now computed once per traceback
    instead of once per traceback entry, which noticeably speeds up rendering
    failures raised from large test modules.

  • #​11418: The cached rewritten modules in __pycache__ are now invalidated by a hash of
    the source instead of its modification time, using the checked-hash pyc format
    from 552.

    Timestamps make the cache useless wherever source files get fresh modification
    times, most notably in CI: a checkout gives every file a new mtime, so a
    restored cache is discarded in full and every test module is rewritten again.
    Hashing the source costs roughly 15 microseconds per file and makes the cache
    survive that.

    Existing timestamp based caches are ignored and rewritten once.

  • #​12624: The pytest_plugins variable and the PYTEST_PLUGINS environment variable now also accept entry point names of installed plugins (e.g. xdist), in addition to importable module names, matching the behavior of the -p command-line option.

    As part of this change, plugins specified by entry point name through these mechanisms are now also listed in the plugins: line of the terminal header (12615).

    Based on the original implementation by Marcel Telka in 12616.

  • #​12697: Warnings emitted while importing plugins through -p, PYTEST_PLUGINS, or entry point autoloading are now captured, filtered, and reported like other warnings.

  • #​12860: Assertion diffs for dict.items() and dict.keys() comparisons (>=, <=, >, <, ==) now show which items are missing, the same way set comparisons do.

  • #​13354: Node.get_closest_marker <_pytest.nodes.Node.get_closest_marker> now also accepts a ~pytest.MarkDecorator as its default, for example item.get_closest_marker("foo", pytest.mark.foo(1)), and returns the wrapped ~pytest.Mark.

    Previously the only way to build a default was to instantiate ~pytest.Mark directly, which is private and warns.

  • #​13369: Improved the error from pytest.RunResult.assert_outcomes when a plugin changes or removes pytest's standard terminal summary, and documented how to isolate output-changing plugins.

  • #​13431: Exposed pytest.ParameterSet as the type of the return value of pytest.param().
    This can be used in type annotations and isinstance checks.

  • #​13564: Issue a warning when fixtures are wrapped with a decorator that prevents
    correct discovery, including @classmethod above @pytest.fixture and
    @staticmethod above @pytest.fixture. Put @pytest.fixture above
    those decorators on test classes.

  • #​14164: Exposed pytest.Approx as the type of the return value of pytest.approx().
    This can be used in type annotations and isinstance checks.

  • #​14463: Assertion diffs for mappings now match up keys the way the compared mappings match them themselves.

    Key comparison goes through the mappings' key views instead of plain sets of their keys, so a mapping
    with its own notion of key equivalence, such as case-insensitive header mappings, no longer
    reports its common keys as missing on either side.

  • #​14523: Large assertion comparison diffs are now built lazily and capped to the
    truncation budget, so a huge diff is no longer formatted in full just to
    be truncated. As a consequence, the truncation footer no longer
    reports the exact number of hidden lines.

  • #​14687: The default fd-level output capturing (--capture=fd) now skips reading the capture buffer for tests which produce no output, reducing per-test overhead in large test suites.

  • #​14724: --no-summary now only applies directly to pytest's own summary.

    The flag no longer skips the pytest_terminal_summary hook, so third-party
    plugins (for example coverage) can still write their terminal summaries.

  • #​14751: parser.addini <pytest.Parser.addini> now also accepts type expressions for its type argument, in addition to the existing string tags: the plain Python types str, bool, int, float, a Literal of strings, and unions of these (for example int | str or int | Literal["auto"]).

  • #​14754: pytest.approx() now rejects infinite relative tolerances for datetime.timedelta values with a clear ValueError instead of raising OverflowError.

  • #​14791: The junit_family, junit_logging, junit_duration_report, console_output_style, empty_parameter_set_mark, assertion_text_diff_style and tmp_path_retention_policy configuration options are now registered with the set of values they accept: an invalid value now fails with a clean usage error instead of being silently ignored or crashing later, and pytest --help lists the accepted values.

    config.getini <pytest.Config.getini> now raises pytest.UsageError instead of TypeError or ValueError when the value read from the configuration file does not match the registered type, so a bad value is always reported as a short usage error message instead of an internal error traceback. Asking for an option name that was never registered still raises ValueError.

  • #​15006: pytest.approx now emits pytest.PytestApproxDecimalToleranceWarning when a rel or abs tolerance is given as a float that cannot be represented exactly for a ~decimal.Decimal comparison, since such a tolerance is widened to the float's exact binary value.

  • #​14675, #​14953, #​15153, #​15132: The truncation_limit_lines, truncation_limit_chars, max_warnings, verbosity_assertions, verbosity_test_cases, verbosity_subtests, tmp_path_retention_count, log_level, log_cli_level, log_file_level,log_auto_indent and faulthandler_timeout configuration options now accept native integer (and, for log_auto_indent, boolean) values in TOML configuration files, while still accepting strings for backward compatibility.
    log_file_mode now reports invalid values as a usage error.

  • #​15254: tmp_path and TempPathFactory.mktemp <pytest.TempPathFactory.mktemp> no longer list the whole base temporary directory on every call, which made sessions with many temporary directories quadratically slow. Numbers are now handed out from a per-prefix counter, skipping names that already exist.

  • #​4569: Node.add_marker <_pytest.nodes.Node.add_marker> now stores a ~pytest.Mark in node.keywords, matching what marks applied during collection store. Previously it stored a ~pytest.MarkDecorator.

  • #​9436: Avoided formatting tracebacks for tests marked with xfail(run=False).

Bug fixes

  • #​10128: Configured warning filters now apply to warnings emitted by pytest_configure hooks.

  • #​10210: pytest.approx now raises a TypeError for any nested container, not
    only for one of the same type as its parent. Previously approx([{"k": 1}])
    and approx({"k": [1]}) were accepted and then compared unequal to every
    value, silently ignoring the tolerance, while approx([[1]]) correctly
    reported that nesting is unsupported.

  • #​10644: monkeypatch.setattr() no longer leaves a new entry in the instance __dict__ when it patches an attribute that the instance inherits from its class.

    Previously undo() assigned the inherited value onto the instance, which shadowed the class attribute -- permanently freezing the result for descriptors that resolve dynamically.

  • #​1139: Fixed tracebacks showing ??? instead of source lines for code compiled with a filename relative to a directory other than the current working directory -- e.g. compiled Cython modules, when running pytest from a subdirectory of the project root.
    The source lookup now falls back to searching sys.path for such filenames, as the standard traceback module does.

  • #​11502: The rootdir is no longer derived from a configuration file that is not a regular file, such as --config-file=/dev/null.

  • #​11783: Plugins installed in editable mode (660) are marked for assertion rewriting again.

    Such installs do not record the package's Python files in the distribution metadata, so pytest found nothing to rewrite and assertions inside the plugin lost their introspection. The plugin's entry point is now used to locate the package in that case.

  • #​11933: pytest.warns no longer re-emits unmatched warnings with module="warnings", which caused module-based warning filters to be applied to the wrong warnings. Since warnings.WarningMessage does not record the module a warning was emitted with, re-emitted warnings now use the standard warnings.warn_explicit default of deriving the module from the warning's filename.

  • #​12175: ExceptionInfo.exconly(tryshort=True) <pytest.ExceptionInfo.exconly> now strips the AssertionError: prefix of failed assert statements for ~pytest.ExceptionInfo objects created by pytest.raises, and failures inside unittest.TestCase.subTest report the same short crash line as regular tests.

  • #​12223: The message attribute of <failure> and <error> elements in --junitxml reports is now shortened to whole lines within 500 characters. When something is dropped, it ends with a note pointing to the element text, which still holds the full report. Previously, very verbose assertion output (e.g. with -vv) produced attributes tens of thousands of characters long, which some XML parsers fail on.

  • #​12307: Fixed truncation of long collections (lists, dicts, sets, etc.) in the "message" part of failed assertions (as in assert condition, msg) when using -vv: the message now uses the same unlimited repr as the rest of the assertion output, instead of eliding collection elements with ....

  • #​12824: Custom command-line options now fail with a clear error when their implicit
    destination conflicts with an existing option, avoiding silent overrides of
    built-in options such as -k.

  • #​13292: Fixed a stale rewritten module being used when a test file is modified twice
    within the same second without changing its size -- the pyc header could only
    record whole-second timestamps. The cache is now keyed on a hash of the source.

  • #​14036: __tracebackhide__ is now respected in ExceptionGroup tracebacks, both for the group itself and for its sub-exceptions.

  • #​14189: Nested usage of caplog.filtering <pytest.LogCaptureFixture.filtering> no longer removes filters early if they were already present.

  • #​14412: Fixed incorrect timing information for subtests and parent tests when console_output_style = "times".

  • #​14445: Assertion rewriting no longer evaluates walrus operator (:=) expressions more than once, which reported results the test never computed when the expression had side effects.

    An operand evaluated before a later walrus is now reported with the value it actually saw, so assert value != identity(value := value.lower()) keeps Python's left-to-right order.

  • #​14552: Fixed stale co_filename values after a test module or directory is moved. Assertion-rewrite caches are reused when the source hash still matches, and in-memory code objects are pointed at the current path.

  • #​14586: Negative values passed to log_auto_indent now correctly fall back to no indentation, matching the documented behavior.

  • #​14619: Fixed a crash in pytest.mark.parametrize when a non-sequence value is passed for a parameter set. It now provides a clear error message during collection.

  • #​14635: Fixed fixture resolution failures when specifying multiple test paths on the command line that share common parent directories. Parametrized fixtures defined in a conftest.py could be silently dropped depending on the order of the specified paths.

  • #​14637: Pytest now correctly skips identical trailing characters in assertion text diffs even when the compared strings differ at the beginning.

  • #​14638: Top-level options in pytest.toml and .pytest.toml files now raise an error instead of being silently ignored when no [pytest] table is present.

  • #​14683: Fixed a regression in pytest 9.1 where a conftest.py located in the rootdir was no longer visible to tests and doctests collected from outside the rootdir (for example when passing a parent directory of the rootdir as a collection argument, or setting --rootdir to a subdirectory).

    As a result, fixtures defined in such a conftest -- including doctest_namespace injections -- were not available, causing errors such as NameError in doctests.

  • #​14700: Fixed an internal error on Python 3.12 when a doctest on a fixture-decorated function was skipped.

  • #​14705: TOML configuration files passed via -c now also read their configuration from the [pytest] table, the one pytest's own configuration files use. The [tool.pytest] and [tool.pytest.ini_options] tables such files were previously restricted to keep working; using both styles in one file is an error.

  • #​14828: Exception groups raised from another exception now print their cause chain only once in the traceback output.

  • #​14864: Fixed collection on Windows collecting the whole suite instead of the given path, on file systems which do not support file IDs (st_ino is 0 for every file, as seen for example on sshfs-win/WinFsp mounts).

  • #​14884: Cache.get(), Cache.set() and Cache.mkdir() now reject keys that resolve outside the cache directory, and --cache-show ignores glob matches outside it.

  • #​14909: Fixed MonkeyPatch.delattr <pytest.MonkeyPatch.delattr>, MonkeyPatch.setitem <pytest.MonkeyPatch.setitem> and MonkeyPatch.delitem <pytest.MonkeyPatch.delitem> recording a stale undo entry when the underlying mutation failed, which could cause spurious errors during teardown. The undo entry is now recorded only after the mutation succeeds, matching the behavior of MonkeyPatch.setattr <pytest.MonkeyPatch.setattr>.

  • #​14912: ~pytest.PytestWarning instances defined in _pytest.deprecated are no longer reused across warnings.warn calls, so warnings raised as errors (e.g. with -W error) no longer accumulate stale traceback frames from previous raises.

  • #​14942: Fixed a regression in 9.1.0 that made collection very slow (quadratic) when many fixtures are defined with the same name.
    This can happen particularly when many tests are defined in separate classes which inherit from a base class which defines fixtures (the fixtures are repeated per class).

  • #​15005: pytest.approx no longer treats finite ~decimal.Decimal values outside the float range (such as Decimal("1e400")) as infinite, which made in-tolerance comparisons fail.

  • #​15006: Restored the tolerance shown by pytest.approx for ~decimal.Decimal values, which since 9.0 displayed ??? for the default tolerance and reported the raw rel ratio instead of the absolute tolerance actually compared against.

  • #​15006: The failure message for pytest.approx comparisons of ~decimal.Decimal collections no longer reports Max absolute difference: -inf, or crashes for mappings, when the decimal.FloatOperation trap is set.

  • #​15006: pytest.approx no longer raises AttributeError when comparing numpy.ndarray instances with object dtype, such as arrays of ~decimal.Decimal.

  • #​15032: Help texts for command-line options and configuration options now keep their indentation
    and blank lines. Previously each line was stripped before wrapping, so a wrapped
    list item was indistinguishable from a new one and blank lines were dropped; configuration
    option help lost its line breaks entirely.

  • #​15043: Cache writes now emit pytest.PytestCacheWarning when writing or closing the cache file fails, consistent with errors opening the file.

  • #​15052: Fixed possible off-by-one line numbers in --fixtures and --fixtures-per-test output.

  • #​15145: With console_output_style set to times, durations just below a unit boundary are now shown in the next unit (e.g. 1.000ms instead of 1000.0us).

  • #​15172: Assertion diffs now pretty-print the built-in frozendict (Python 3.15+) across multiple lines, similarly to how dict is displayed.

  • #​4569: -k no longer matches attributes that end up in a test function's __dict__ without being meant as keywords, such as pytest's own pytestmark attribute and the bookkeeping left behind by functools.wraps and functools.lru_cache.

  • #​6750: Fix fixture argument discovery for custom collectors that pass bound methods to pytest.Function.

  • #​6997: XFAIL reports from teardown phases now include the phase in the short test
    summary.

  • #​7940: Fixed cleanup of temporary directories failing when subdirectories have their execute permission removed.

  • #​8321: Fixed exception chains being duplicated in the traceback output when an exception in the chain (__cause__/__context__) has no traceback of its own, and that exception is itself linked to further exceptions.

  • #​8914: Tests parametrized with higher-than-function scope are now reordered by
    parameter value rather than by parameter index, so that items using equal
    parameter values are grouped together and share a single fixture setup, even
    when they are parametrized by separate @pytest.mark.parametrize <pytest.mark.parametrize ref>
    calls. Previously, an expensive higher-scoped fixture could be set up and torn
    down repeatedly for the same parameter value.

    Unhashable parameter values (e.g. dicts) keep the previous index-based
    grouping behavior.

    As a consequence, the order in which parametrized tests run may change
    (fixture setup/teardown counts can only decrease, not increase).

  • #​9924: Fixed doctest option flags leaking between docstrings after a failure, skip, or expected failure.

Improved documentation

  • #​10721: Replaced setup.py usage with pyproject.toml in writing-plugins.
  • #​12658: Moved the fixture setup and finalizer hooks to the test running hooks section in the reference documentation.
  • #​12857: Documented how --confcutdir affects initial conftest.py discovery.
  • #​14157: Added the pytest-flakefighters plugin to the list of flaky test plugins in plugins.rst.
    Added flaky test papers published on the Test FLARE project to the list of flaky papers in plugins.rst.
  • #​15127: Added guidance on typing yield fixtures, including annotating fixture functions with their generator type and fixture consumers with the yielded value type.
  • #​15242: The installation instructions <installation> and install-package-with-pip now show how to install pytest into a virtual environment with venv and pip, uv or virtualenv, and explain the externally-managed-environment error.
  • #​4569: pytest --help, the -k reference entry and select-tests no longer describe -k as a Python expression matched against test names only; keyword expressions now shows marker names being matched as well. The examples that looked up a marker use Node.get_closest_marker <_pytest.nodes.Node.get_closest_marker> rather than item.keywords.
  • #​9614: Clarified that session.items and session.testscollected exclude deselected tests in the pytest_collection documentation.

Packaging updates and notes for downstreams

  • #​11680: Source archives of arbitrary commits -- the tarballs GitHub serves under
    /archive/, not just the ones for tags -- can now be built into an sdist or a
    wheel with a correct version. A .git_archival.txt file, expanded by git archive via export-subst, carries the describe output that
    setuptools-scm would otherwise have to ask git for.

    The archival file deliberately omits ref-names. Recording it would make the
    content of an archive -- and therefore its checksum -- change whenever a branch
    that points at the archived commit is moved afterwards, which is how the
    equivalent tarballs of tagged releases became unstable for other projects.
    Describe output alone depends only on the commit and on the tags reachable from
    it.

    The archival file is not shipped in the sdist -- an sdist carries its version
    in its metadata, and the template is only ever expanded by git archive.

    Building pytest from a git checkout now requires setuptools-scm >= 10.1.

  • #​9236: pluggy>=1.6 is now required.

    Older pluggy versions stopped running the remaining hook wrapper teardowns once an
    old-style hookwrapper raised in its own teardown. The logging plugin's wrapper was
    left suspended, and when it was finalised later it detached the handler that
    caplog of a subsequent test was recording through, so that test saw no records.

Contributor-facing changes

  • #​14813: Added a systematic coverage matrix for assertion rewriting.
  • #​14921: Added scripts/diff-assert-rewrite.py, which diffs the assert-rewritten form of a snippet between the plain source, this checkout and released pytest versions.
  • #​15190: Fixed the documentation build with Pluggy 1.7 by ignoring a reference to its undocumented DistFacade compatibility class.

Miscellaneous internal changes

  • #​13888: FixtureRequest.node <pytest.FixtureRequest.node> is now explicitly annotated as returning Any, so strict pyright no longer reports it as Unknown. The return type stays deliberately broad, see 13888.
  • #​14758: Internal node ids (the ::-separated strings identifying collected items, e.g. path/to/test_file.py::TestClass::test_method[param]) are now represented internally by a single structured ~_pytest.nodeid.NodeId dataclass instead of being repeatedly re-parsed as plain strings. The public nodeid: str attribute on nodes and reports is unchanged and remains fully backward compatible for plugins.

v9.1.1

Compare Source

pytest 9.1.1 (2026-06-19)

Bug fixes

  • #​14220: Fixed a logic bug in pytest.RaisesGroup which would might cause it to display incorrect "It matches FooError() which was paired with BarError" messages.
  • #​14591: Fixed a regression in pytest 9.1.0 which caused overriding a parametrized fixture with an indirect @​pytest.mark.parametrize to fail with "duplicate parametrization of '<fixture name>'".
  • #​14606: Fixed list-item typing errors from mypy in @pytest.mark.parametrize <pytest.mark.parametrize ref> argvalues parameter.
  • #​14608: Fixed a regression in pytest 9.1.0 where conftest.py files located in <invocation dir>/test* were no longer loaded as initial conftests when invoked without arguments.
    This could cause certain hooks (like pytest_addoption) in these files to not fire.

v9.1.0

Compare Source

pytest 9.1.0 (2026-06-13)

Removals and backward incompatible breaking changes

  • #​14533: When using --doctest-modules, autouse fixtures with module, package or session scope that are defined inline in Python test modules (not plugins or conftests) will now possibly execute twice.

    If this is undesirable, move the fixture definition to a conftest.py file if possible.

    Technical explanation for those interested:
    When using --doctest-modules, pytest possibly collects Python modules twice, once as pytest.Module and once as a DoctestModule (depending on the configuration).
    Due to improvements in pytest's fixture implementation, if e.g. the DoctestModule collects a fixture, it is now visible to it only, and not to the Module.
    This means that both need to register the fixtures independently.

Deprecations (removal in next major release)

  • #​10819: Added a deprecation warning for class-scoped fixtures defined as instance methods (without @classmethod). Such fixtures set attributes on a different instance than the test methods use, leading to unexpected behavior. Use @classmethod decorator instead -- by yastcher.

    See 10819 and 14011.

  • #​12882: Calling request.getfixturevalue() <pytest.FixtureRequest.getfixturevalue> during teardown to request a fixture that was not already requested is now deprecated and will become an error in pytest 10.

    See dynamic-fixture-request-during-teardown for details.

  • #​13409: Using non-~collections.abc.Collection iterables (such as generators, iterators, or custom iterable objects) for the argvalues parameter in @pytest.mark.parametrize <pytest.mark.parametrize ref> and metafunc.parametrize <pytest.Metafunc.parametrize> is now deprecated.

    These iterables get exhausted after the first iteration,
    leading to tests getting unexpectedly skipped in cases such as running pytest.main() multiple times,
    using class-level parametrize decorators,
    or collecting tests multiple times.

    See parametrize-iterators for details and suggestions.

  • #​13946: The private config.inicfg attribute is now deprecated.
    Use config.getini() <pytest.Config.getini> to access configuration values instead.

    See config-inicfg for more details.

  • #​14004: Passing baseid to ~pytest.FixtureDef or nodeid strings to fixture registration APIs is now deprecated. These are internal pytest APIs that are used by some plugins.

    Use the node parameter instead for fixture scoping. This enables more robust node-based
    matching instead of string prefix matching.
    If you've used nodeid=None, pass node=session instead.

    This will be removed in pytest 10.

  • #​14335: The method of configuring hooks using markers, deprecated since pytest 7.2, is now scheduled to be removed in pytest 10.
    See hook-markers for more details.

  • #​14434: The --pastebin option is now deprecated.
    The same functionality is now available in an external plugin, pytest-pastebin.
    See pastebin-deprecated for more details.

  • #​14513: The private FixtureDef.has_location attribute is now deprecated and will be removed in pytest 10.
    See fixturedef-has-location-deprecated for details.

  • #​1764: pytest.console_main is now deprecated and will be removed in pytest 10.
    It was never intended for programmatic use; use pytest.main instead.

New features

  • #​12376: Added pytest.register_fixture() to register fixtures using an imperative interface.

    This is an advanced function intended for use by plugins.

    Normally, fixtures should be registered declaratively using the @pytest.fixture <pytest.fixture> decorator.
    Pytest looks for these fixture definitions during the collection phase and registers them automatically.
    For some plugin usecases the declarative interface can be cumbersome or unviable, in which case this imperative interface can be used.

  • #​14023: Added --report-chars long CLI option.

  • #​14371: Added --max-warnings command-line option and max_warnings configuration option to fail the test run when the number of warnings exceeds a given threshold -- by miketheman.

  • #​6757: Added the assertion_text_diff_style configuration option, allowing
    string equality failures to be rendered as separate Left: and Right:
    blocks instead of ndiff output.

  • #​8395: Added support for ~datetime.datetime and ~datetime.timedelta comparisons with pytest.approx. An explicit abs or rel tolerance as a ~datetime.timedelta is required and relative tolerance is not supported for datetime comparisons -- by hamza-mobeen.

Improvements in existing functionality

  • #​11225: pytest.warns now shows "Regex pattern did not match" instead of "DID NOT WARN" when warnings were emitted but the match pattern did not match.

  • #​11295: Improved output of --fixtures-per-test by excluding internal-implementation fixtures generated by @pytest.mark.parametrize and similar.

  • #​13241: pytest.raises, pytest.warns and pytest.deprecated_call now uses ParamSpec for the type hint to the (old and not recommended) callable overload, instead of Any. This allows type checkers to raise errors when passing incorrect function parameters.
    func can now also be passed as a kwarg, which the type hint previously showed as possible but didn't accept.

  • #​13862: Improved the readability of "DID NOT RAISE" error messages by using the exception type's name instead of its repr.

  • #​14026: Added test coverage for compiled regex patterns in pytest.raises match parameter.

  • #​14137: pytest.ScopeName is now public to allow using it in function signatures.

  • #​14342: Marked yield_fixture as deprecated to type checkers using the deprecated decorator. Note it
    has originally been deprecated <yield-fixture-deprecated> in pytest 6.2 already.

  • #​14373: Added type annotations for pytest.approx.

  • #​14430: When using --setup-show, a space is now printed after the test name (and possibly used fixtures), to separate it from the test result.

  • #​14441: Reduced the default number of gc.collect() passes in the unraisableexception plugin from 5 to 1 on CPython, where reference counting makes a single pass sufficient. PyPy retains 5 passes due to object resurrection via __del__. This can noticeably speed up test suites that trigger many pytester runs.

  • #​14461: Improved assertion failure explanations for equality comparisons between mapping objects that are not dict instances.

  • #​14513: The order in which fixture definitions overriding each other are resolved is now determined first by their visibility in the collection tree rather than by the order in which they are registered.

    A fixture defined for a more specific node (e.g. a module or an item) now always takes precedence over one with the same name defined for a more general node (e.g. the session), even when the more general one was registered later.
    Fixtures with non-comparable visibility or the same visibility keep the existing behavior of "last registered wins".
    This change is supposed to only affect plugins which register multiple fixtures programmatically with the same name.

  • #​14524: Add official Python 3.15 support.

  • #​1764: Improved argparse program name to show pytest, python -m pytest, or pytest.main() based on how pytest was invoked, making help and error messages clearer.

  • #​8265: Emit a PytestCollectionWarning when a module-level __getattr__ returns None for pytestmark instead of raising AttributeError.

    Previously this caused a cryptic TypeError: got None instead of Mark error.
    Now pytest issues a helpful warning and continues collecting the module normally.

Bug fixes

  • #​13192: Fixed | (pipe) not being treated as a regex meta-character that needs escaping in pytest.raises(match=...) <pytest.raises>.

  • #​13484: Fixed -W option values being duplicated in Config.known_args_namespace.

  • #​13626: Fixed function-scoped fixture values being kept alive after a test was interrupted by KeyboardInterrupt or early exit,
    allowing them to potentially be released more promptly.

  • #​13784: Fixed capteesys producing doubled output when used with --capture=no (-s).

  • #​13817: Fixed a secondary AttributeError masking the original error when an option argument fails to initialize.

  • #​13884: Fixed rare internal IndexError caused by builtins.compile being overridden in client code.

  • #​13885: Fixed autouse fixtures defined inside a unittest.TestCase class running even when the class is decorated with unittest.skip or unittest.skipIf -- regression since pytest 8.1.0.

  • #​13917: unittest.SkipTest is no longer considered an interactive exception, i.e. pytest_exception_interact is no longer called for it.

  • #​13963: Fixed subtests running with pytest-xdist when their contexts contain objects that are not JSON-serializable.

    Fixes pytest-dev/pytest-xdist#1273.

  • #​14004: Fixed conftest.py fixture scoping when testpaths points outside of the rootdir <rootdir>.

    Previously, fixtures from nested conftest.py files would incorrectly leak to sibling directories
    when using a relative testpaths like ../tests/sdk.

    Conftest fixtures are now parsed during Directory <pytest.Directory> collection, using the Directory node for proper scoping.

  • #​14050: Display dictionary differences in assertion failures using the original key insertion order instead of sorted order.

  • #​14080: fix missing type annotations on Pytester.makepyfile and Pytester.maketxtfile methods.

  • #​14114: An exception from pytest_fixture_post_finalizer no longer prevents fixtures from being torn down, causing additional errors in the following tests.

  • #​14161: Fixed monkeypatch.setattr() <pytest.MonkeyPatch.setattr> leaving a stale entry on the undo stack when the underlying setattr() call fails (e.g. on immutable targets), causing an AttributeError crash during teardown.

  • #​14214: Fixed -v hint in pytest.raises match diff not working because assertion verbosity was not propagated.

  • #​14234: Allow pytest.HIDDEN_PARAM <hidden-param> in @pytest.mark.parametrize(ids=...) <pytest.mark.parametrize ref> typing.

  • #​14248: Fixed direct parametrization causing the static fixture closure (as reflected in request.fixturenames <pytest.FixtureRequest.fixturenames>) to omit fixtures that are requested transitively from overridden fixtures.

  • #​14263: Unraisable exceptions from finalizers are now collected during pytest_unconfigure, before pytest tears down the warning filters installed for the session. Previously the collection ran from a cleanup callback whose order relative to other plugins' cleanups was not guaranteed, so an active error filter could be removed before the exception surfaced and a late resource leak would pass silently. A -W error filter, or any filter matching pytest.PytestUnraisableExceptionWarning, now promotes these exceptions to failures regardless of plugin cleanup order.

  • #​14377: Fixed crash in Config.get_terminal_writer when an assertion fails with the terminalreporter plugin disabled.

  • #​14381: Fixed -V (short form of --version) to properly display the current version.

  • #​14389: Improved pytest.raises(..., match=...) <pytest.raises> failures to suppress the mismatched exception as a cause of the resulting AssertionError.

  • #​14392: Fixed a bug in pytest.raises(match=...) <pytest.raises> "fully escaped" detection, causing the regex diff display to be shown in some instances when the raw string diff display should be shown instead.

  • #​14442: Fixed a regression in pytest 9.0 where --strict-markers and --strict-config specified through addopts were silently ignored.

    Note that when targeting pytest >= 9.0, it's nicer to use strict_markers and strict_config, or strict mode <strict mode>.

  • #​14456: Fixed pytest.approx not recognizing types with __array_interface__ as numpy-like arrays.

  • #​14474: Fixed a regression where -k and -m expressions containing both backslash characters in identifiers and string literal arguments would incorrectly raise a SyntaxError about escaping.

  • #​14483: Fixed JUnit XML report incorrectly escaping high Unicode codepoints (supplementary plane characters like emoji) in test failure messages. -- by EternalRights

  • #​14492: Fixed Code.getargs() incorrectly including local variable names in the returned argument tuple for functions with *args and/or **kwargs. The method was using co_flags bitmask values (4 and 8) directly as counts instead of converting them to 1 via bool(), and was not accounting for co_kwonlyargcount when var=True.

  • #​3697: Logging capture now works for non-propagating loggers.
    Previously only logs which reached the root logger were captured.
    This includes caplog and the "Captured log calls" test reporting.

  • #​3850: Fixed JUnit XML report: the tests attribute of the <testsuite> element now always matches the number of <testcase> elements in the file. In some cases (test passes but fails during teardown) the tests attribute would report an incorrect number of testcases in the XML file.

  • #​5848: pytest_fixture_post_finalizer is no longer called extra times for the same fixture teardown in some cases.

  • #​719: Fixed @pytest.mark.parametrize <pytest.mark.parametrize ref> not unpacking single-element tuple values when using a string argnames with a trailing comma (e.g., "arg,").

    The trailing comma form now correctly behaves like the tuple form ("arg",), treating argvalues as a list of tuples to unpack.

Improved documentation

  • #​11022: Document safer alternatives and scope guidance for monkeypatching standard library functions.
  • #​11307: Document that @pytest.hookimpl(specname=...) only works for function names starting with pytest_.
  • #​13038: Document that doctests do not support parametrized fixtures, including parametrized autouse fixtures.
  • #​13155: Clarified how the request fixture provides indirect parametrization values via request.param.
  • #​13304: Clarified in the documentation that hook implementations defined in conftest.py files are not available to other plugins during their pytest_addoption() execution, as conftest files are discovered and loaded after builtin and third-party plugins have been initialized. However, initial conftest files themselves can implement pytest_addoption() to add their own command-line options.
  • #​13902: Clarified how subtest progress markers are shown in the documentation.
  • #​14012: The ini options ref section of the API Reference now specified the type and default value of every configuration option.
  • #​14148: Documented a safe pytestconfig.cache access pattern when the
    cacheprovider plugin is disabled.
  • #​14303: The documentation is now built with Sphinx >= 9.
  • #​14465: Updated the hooks how-to page to link the newhooks.py file in pytest-xdist at tag v3.8.0 instead of an unrelated 2017-era commit under the old layout. Pointing at a tag keeps the example in sync with the version users actually install, while remaining stable when the project's main branch moves on.

Miscellaneous internal changes

  • #​14582: Improved the recursion traceback test to exercise all requested traceback styles.

v9.0.3

Compare Source

pytest 9.0.3 (2026-04-07)

Bug fixes

  • #​12444: Fixed pytest.approx which now correctly takes into account ~collections.abc.Mapping keys order to compare them.

  • #​13634: Blocking a conftest.py file using the -p no: option is now explicitly disallowed.

    Previously this resulted in an internal assertion failure during plugin loading.

    Pytest now raises a clear UsageError explaining that conftest files are not plugins and cannot be disabled via -p.

  • #​13734: Fixed crash when a test raises an exceptiongroup with __tracebackhide__ = True.

  • #​14195: Fixed an issue where non-string messages passed to unittest.TestCase.subTest() were not printed.

  • #​14343: Fixed use of insecure temporary directory (CVE-2025-71176).

Improved documentation

  • #​13388: Clarified documentation for -p vs PYTEST_PLUGINS plugin loading and fixed an incorrect -p example.
  • #​13731: Clarified that capture fixtures (e.g. capsys and capfd) take precedence over the -s / --capture=no command-line options in Accessing captured output from a test function <accessing-captured-output>.
  • #​14088: Clarified that the default pytest_collection ho

[!IMPORTANT]
✂ PR body was truncated to here.


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate Bot added the dependencies Pull requests that update a dependency file label Oct 27, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants