diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 95ec0d174..e1b95efa7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -47,7 +47,7 @@ jobs: path: dist - name: Publish package to PyPI - uses: pypa/gh-action-pypi-publish@v1.8.10 + uses: pypa/gh-action-pypi-publish@v1.8.11 - name: Push tag run: | diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1fb026602..9b1ee9cda 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -56,7 +56,7 @@ repos: hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.7.0 + rev: v1.7.1 hooks: - id: mypy files: ^(src/|testing/) diff --git a/AUTHORS b/AUTHORS index e30131d1a..669ec537e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -188,6 +188,7 @@ Javier Romero Jeff Rackauckas Jeff Widman Jenni Rinker +Jens Tröger John Eddie Ayson John Litborn John Towler diff --git a/changelog/11520.improvement.rst b/changelog/11520.improvement.rst index 46e4992dd..d9b7b4933 100644 --- a/changelog/11520.improvement.rst +++ b/changelog/11520.improvement.rst @@ -1 +1,3 @@ Improved very verbose diff output to color it as a diff instead of only red. + +Improved the error reporting to better separate each section. diff --git a/changelog/11572.bugfix.rst b/changelog/11572.bugfix.rst new file mode 100644 index 000000000..7a235a071 --- /dev/null +++ b/changelog/11572.bugfix.rst @@ -0,0 +1 @@ +Handle an edge case where :data:`sys.stderr` and :data:`sys.__stderr__` might already be closed when :ref:`faulthandler` is tearing down. diff --git a/changelog/11610.feature.rst b/changelog/11610.feature.rst new file mode 100644 index 000000000..34df34705 --- /dev/null +++ b/changelog/11610.feature.rst @@ -0,0 +1,2 @@ +Added :func:`LogCaptureFixture.filtering() ` context manager that +adds a given :class:`logging.Filter` object to the caplog fixture. diff --git a/changelog/11638.trivial.rst b/changelog/11638.trivial.rst new file mode 100644 index 000000000..374960b89 --- /dev/null +++ b/changelog/11638.trivial.rst @@ -0,0 +1 @@ +Fixed the selftests to pass correctly if ``FORCE_COLOR``, ``NO_COLOR`` or ``PY_COLORS`` is set in the calling environment. diff --git a/changelog/11667.breaking.rst b/changelog/11667.breaking.rst new file mode 100644 index 000000000..7c05d39b2 --- /dev/null +++ b/changelog/11667.breaking.rst @@ -0,0 +1,3 @@ +pytest's ``setup.py`` file is removed. +If you relied on this file, e.g. to install pytest using ``setup.py install``, +please see `Why you shouldn't invoke setup.py directly `_ for alternatives. diff --git a/changelog/1531.improvement.rst b/changelog/1531.improvement.rst new file mode 100644 index 000000000..d444ea2e7 --- /dev/null +++ b/changelog/1531.improvement.rst @@ -0,0 +1,4 @@ +Improved the very verbose diff for every standard library container types: the indentation is now consistent and the markers are on their own separate lines, which should reduce the diffs shown to users. + +Previously, the default python pretty printer was used to generate the output, which puts opening and closing +markers on the same line as the first/last entry, in addition to not having consistent indentation. diff --git a/doc/en/how-to/logging.rst b/doc/en/how-to/logging.rst index b9f522fa4..bdcfbe34f 100644 --- a/doc/en/how-to/logging.rst +++ b/doc/en/how-to/logging.rst @@ -241,7 +241,7 @@ through ``add_color_level()``. Example: .. code-block:: python - @pytest.hookimpl + @pytest.hookimpl(trylast=True) def pytest_configure(config): logging_plugin = config.pluginmanager.get_plugin("logging-plugin") diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 2e39b4153..2ded9bf9d 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -27,7 +27,7 @@ please refer to `the update script =7.1.1,<8.0.0) :pypi:`pytest-api-soup` Validate multiple endpoints with unit testing using a single source of truth. Aug 27, 2022 N/A N/A @@ -89,7 +89,7 @@ This list contains 1346 plugins. :pypi:`pytest-approvaltests-geo` Extension for ApprovalTests.Python specific to geo data verification Sep 06, 2023 5 - Production/Stable pytest :pypi:`pytest-archon` Rule your architecture like a real developer Jul 11, 2023 5 - Production/Stable pytest (>=7.2) :pypi:`pytest-argus` pyest results colection plugin Jun 24, 2021 5 - Production/Stable pytest (>=6.2.4) - :pypi:`pytest-arraydiff` pytest plugin to help with comparing array output from tests Nov 15, 2023 4 - Beta pytest >=4.6 + :pypi:`pytest-arraydiff` pytest plugin to help with comparing array output from tests Nov 27, 2023 4 - Beta pytest >=4.6 :pypi:`pytest-asgi-server` Convenient ASGI client/server fixtures for Pytest Dec 12, 2020 N/A pytest (>=5.4.1) :pypi:`pytest-aspec` A rspec format reporter for pytest Oct 23, 2023 4 - Beta N/A :pypi:`pytest-asptest` test Answer Set Programming programs Apr 28, 2018 4 - Beta N/A @@ -105,12 +105,12 @@ This list contains 1346 plugins. :pypi:`pytest-astropy-header` pytest plugin to add diagnostic information to the header of the test output Sep 06, 2022 3 - Alpha pytest (>=4.6) :pypi:`pytest-ast-transformer` May 04, 2019 3 - Alpha pytest :pypi:`pytest-async-generators` Pytest fixtures for async generators Jul 05, 2023 N/A N/A - :pypi:`pytest-asyncio` Pytest support for asyncio Nov 16, 2023 4 - Beta pytest (>=7.0.0) - :pypi:`pytest-asyncio-cooperative` Run all your asynchronous tests cooperatively. Aug 06, 2023 N/A N/A + :pypi:`pytest-asyncio` Pytest support for asyncio Nov 27, 2023 4 - Beta pytest (>=7.0.0) + :pypi:`pytest-asyncio-cooperative` Run all your asynchronous tests cooperatively. Nov 30, 2023 N/A N/A :pypi:`pytest-asyncio-network-simulator` pytest-asyncio-network-simulator: Plugin for pytest for simulator the network in tests Jul 31, 2018 3 - Alpha pytest (<3.7.0,>=3.3.2) :pypi:`pytest-async-mongodb` pytest plugin for async MongoDB Oct 18, 2017 5 - Production/Stable pytest (>=2.5.2) :pypi:`pytest-async-sqlalchemy` Database testing fixtures using the SQLAlchemy asyncio API Oct 07, 2021 4 - Beta pytest (>=6.0.0) - :pypi:`pytest-atf-allure` 基于allure-pytest进行自定义 Oct 22, 2023 N/A pytest (>=7.4.2,<8.0.0) + :pypi:`pytest-atf-allure` 基于allure-pytest进行自定义 Nov 29, 2023 N/A pytest (>=7.4.2,<8.0.0) :pypi:`pytest-atomic` Skip rest of tests if previous test failed. Nov 24, 2018 4 - Beta N/A :pypi:`pytest-attrib` pytest plugin to select tests based on attributes similar to the nose-attrib plugin May 24, 2016 4 - Beta N/A :pypi:`pytest-austin` Austin plugin for pytest Oct 11, 2020 4 - Beta N/A @@ -207,7 +207,7 @@ This list contains 1346 plugins. :pypi:`pytest-check-mk` pytest plugin to test Check_MK checks Nov 19, 2015 4 - Beta pytest :pypi:`pytest-check-requirements` A package to prevent Dependency Confusion attacks against Yandex. Feb 10, 2023 N/A N/A :pypi:`pytest-chic-report` A pytest plugin to send a report and printing summary of tests. Jan 31, 2023 5 - Production/Stable N/A - :pypi:`pytest-choose` Provide the pytest with the ability to collect use cases based on rules in text files Nov 02, 2023 N/A pytest >=7.0.0 + :pypi:`pytest-choose` Provide the pytest with the ability to collect use cases based on rules in text files Nov 30, 2023 N/A pytest >=7.0.0 :pypi:`pytest-chunks` Run only a chunk of your test suite Jul 05, 2022 N/A pytest (>=6.0.0) :pypi:`pytest-circleci` py.test plugin for CircleCI May 03, 2019 N/A N/A :pypi:`pytest-circleci-parallelized` Parallelize pytest across CircleCI workers. Oct 20, 2022 N/A N/A @@ -379,7 +379,7 @@ This list contains 1346 plugins. :pypi:`pytest-doctest-import` A simple pytest plugin to import names and add them to the doctest namespace. Nov 13, 2018 4 - Beta pytest (>=3.3.0) :pypi:`pytest-doctestplus` Pytest plugin with advanced doctest features. Aug 11, 2023 3 - Alpha pytest >=4.6 :pypi:`pytest-dogu-report` pytest plugin for dogu report Jul 07, 2023 N/A N/A - :pypi:`pytest-dogu-sdk` pytest plugin for the Dogu Aug 04, 2023 N/A N/A + :pypi:`pytest-dogu-sdk` pytest plugin for the Dogu Nov 20, 2023 N/A N/A :pypi:`pytest-dolphin` Some extra stuff that we use ininternally Nov 30, 2016 4 - Beta pytest (==3.0.4) :pypi:`pytest-donde` record pytest session characteristics per test item (coverage and duration) into a persistent file and use them in your own plugin or script. Oct 01, 2023 4 - Beta pytest >=7.3.1 :pypi:`pytest-doorstop` A pytest plugin for adding test results into doorstop items. Jun 09, 2020 4 - Beta pytest (>=3.5.0) @@ -410,14 +410,14 @@ This list contains 1346 plugins. :pypi:`pytest-eliot` An eliot plugin for pytest. Aug 31, 2022 1 - Planning pytest (>=5.4.0) :pypi:`pytest-elk-reporter` A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0) :pypi:`pytest-email` Send execution result email Jul 08, 2020 N/A pytest - :pypi:`pytest-embedded` A pytest plugin that designed for embedded testing. Nov 07, 2023 5 - Production/Stable pytest>=7.0 - :pypi:`pytest-embedded-arduino` Make pytest-embedded plugin work with Arduino. Nov 07, 2023 5 - Production/Stable N/A - :pypi:`pytest-embedded-idf` Make pytest-embedded plugin work with ESP-IDF. Nov 07, 2023 5 - Production/Stable N/A - :pypi:`pytest-embedded-jtag` Make pytest-embedded plugin work with JTAG. Nov 07, 2023 5 - Production/Stable N/A - :pypi:`pytest-embedded-qemu` Make pytest-embedded plugin work with QEMU. Nov 07, 2023 5 - Production/Stable N/A - :pypi:`pytest-embedded-serial` Make pytest-embedded plugin work with Serial. Nov 07, 2023 5 - Production/Stable N/A - :pypi:`pytest-embedded-serial-esp` Make pytest-embedded plugin work with Espressif target boards. Nov 07, 2023 5 - Production/Stable N/A - :pypi:`pytest-embedded-wokwi` Make pytest-embedded plugin work with the Wokwi CLI. Nov 07, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded` A pytest plugin that designed for embedded testing. Nov 27, 2023 5 - Production/Stable pytest>=7.0 + :pypi:`pytest-embedded-arduino` Make pytest-embedded plugin work with Arduino. Nov 27, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-idf` Make pytest-embedded plugin work with ESP-IDF. Nov 27, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-jtag` Make pytest-embedded plugin work with JTAG. Nov 27, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-qemu` Make pytest-embedded plugin work with QEMU. Nov 27, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-serial` Make pytest-embedded plugin work with Serial. Nov 27, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-serial-esp` Make pytest-embedded plugin work with Espressif target boards. Nov 27, 2023 5 - Production/Stable N/A + :pypi:`pytest-embedded-wokwi` Make pytest-embedded plugin work with the Wokwi CLI. Nov 27, 2023 5 - Production/Stable N/A :pypi:`pytest-embrace` 💝 Dataclasses-as-tests. Describe the runtime once and multiply coverage with no boilerplate. Mar 25, 2023 N/A pytest (>=7.0,<8.0) :pypi:`pytest-emoji` A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1) :pypi:`pytest-emoji-output` Pytest plugin to represent test output with emoji support Apr 09, 2023 4 - Beta pytest (==7.0.1) @@ -427,7 +427,7 @@ This list contains 1346 plugins. :pypi:`pytest-encoding` set your encoding and logger Aug 11, 2023 N/A pytest :pypi:`pytest-enhanced-reports` Enhanced test reports for pytest Dec 15, 2022 N/A N/A :pypi:`pytest-enhancements` Improvements for pytest (rejected upstream) Oct 30, 2019 4 - Beta N/A - :pypi:`pytest-env` pytest plugin that allows you to add environment variables. Oct 30, 2023 5 - Production/Stable pytest>=7.4.3 + :pypi:`pytest-env` pytest plugin that allows you to add environment variables. Nov 28, 2023 5 - Production/Stable pytest>=7.4.3 :pypi:`pytest-envfiles` A py.test plugin that parses environment files before running tests Oct 08, 2015 3 - Alpha N/A :pypi:`pytest-env-info` Push information about the running pytest into envvars Nov 25, 2017 4 - Beta pytest (>=3.1.1) :pypi:`pytest-envraw` py.test plugin that allows you to add environment variables. Aug 27, 2020 4 - Beta pytest (>=2.6.0) @@ -527,15 +527,16 @@ This list contains 1346 plugins. :pypi:`pytest-funparam` An alternative way to parametrize test cases. Dec 02, 2021 4 - Beta pytest >=4.6.0 :pypi:`pytest-fxa` pytest plugin for Firefox Accounts Aug 28, 2018 5 - Production/Stable N/A :pypi:`pytest-fxtest` Oct 27, 2020 N/A N/A - :pypi:`pytest-fzf` fzf-based test selector for pytest Nov 15, 2023 1 - Planning pytest >=7.1.2 + :pypi:`pytest-fzf` fzf-based test selector for pytest Nov 28, 2023 4 - Beta pytest >=6.0.0 :pypi:`pytest-gather-fixtures` set up asynchronous pytest fixtures concurrently Apr 12, 2022 N/A pytest (>=6.0.0) :pypi:`pytest-gc` The garbage collector plugin for py.test Feb 01, 2018 N/A N/A :pypi:`pytest-gcov` Uses gcov to measure test coverage of a C library Feb 01, 2018 3 - Alpha N/A + :pypi:`pytest-gee` The Python plugin for your GEE based packages. Nov 22, 2023 3 - Alpha pytest; extra == 'test' :pypi:`pytest-gevent` Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest :pypi:`pytest-gherkin` A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) :pypi:`pytest-gh-log-group` pytest plugin for gh actions Jan 11, 2022 3 - Alpha pytest :pypi:`pytest-ghostinspector` For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A - :pypi:`pytest-girder` A set of pytest fixtures for testing Girder applications. Nov 08, 2023 N/A N/A + :pypi:`pytest-girder` A set of pytest fixtures for testing Girder applications. Nov 20, 2023 N/A N/A :pypi:`pytest-git` Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest :pypi:`pytest-gitconfig` Provide a gitconfig sandbox for testing Oct 15, 2023 4 - Beta pytest>=7.1.2 :pypi:`pytest-gitcov` Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A @@ -570,12 +571,13 @@ This list contains 1346 plugins. :pypi:`pytest-hidecaptured` Hide captured output May 04, 2018 4 - Beta pytest (>=2.8.5) :pypi:`pytest-historic` Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest :pypi:`pytest-historic-hook` Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest + :pypi:`pytest-history` Pytest plugin to keep a history of your pytest runs Nov 20, 2023 N/A pytest (>=7.4.3,<8.0.0) :pypi:`pytest-home` Home directory fixtures Oct 09, 2023 5 - Production/Stable pytest :pypi:`pytest-homeassistant` A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A - :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Nov 11, 2023 3 - Alpha pytest ==7.4.3 + :pypi:`pytest-homeassistant-custom-component` Experimental package to automatically extract test plugins for Home Assistant custom components Dec 01, 2023 3 - Alpha pytest ==7.4.3 :pypi:`pytest-honey` A simple plugin to use with pytest Jan 07, 2022 4 - Beta pytest (>=3.5.0) :pypi:`pytest-honors` Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A - :pypi:`pytest-hot-reloading` Jun 23, 2023 N/A N/A + :pypi:`pytest-hot-reloading` Dec 01, 2023 N/A N/A :pypi:`pytest-hot-test` A plugin that tracks test changes Dec 10, 2022 4 - Beta pytest (>=3.5.0) :pypi:`pytest-houdini` pytest plugin for testing code in Houdini. Nov 10, 2023 N/A pytest :pypi:`pytest-hoverfly` Simplify working with Hoverfly from pytest Jan 30, 2023 N/A pytest (>=5.0) @@ -611,7 +613,7 @@ This list contains 1346 plugins. :pypi:`pytest-ignore-flaky` ignore failures from flaky tests (pytest plugin) Oct 11, 2023 5 - Production/Stable pytest >=6.0 :pypi:`pytest-ignore-test-results` A pytest plugin to ignore test results. Aug 17, 2023 2 - Pre-Alpha pytest>=7.0 :pypi:`pytest-image-diff` Mar 09, 2023 3 - Alpha pytest - :pypi:`pytest-image-snapshot` A pytest plugin for image snapshot management and comparison. Nov 16, 2023 4 - Beta pytest >=3.5.0 + :pypi:`pytest-image-snapshot` A pytest plugin for image snapshot management and comparison. Dec 01, 2023 4 - Beta pytest >=3.5.0 :pypi:`pytest-incremental` an incremental test runner (pytest plugin) Apr 24, 2021 5 - Production/Stable N/A :pypi:`pytest-influxdb` Plugin for influxdb and pytest integration. Apr 20, 2021 N/A N/A :pypi:`pytest-info-collector` pytest plugin to collect information from tests May 26, 2019 3 - Alpha N/A @@ -620,9 +622,9 @@ This list contains 1346 plugins. :pypi:`pytest-infrastructure` pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A :pypi:`pytest-ini` Reuse pytest.ini to store env variables Apr 26, 2022 N/A N/A :pypi:`pytest-inline` A pytest plugin for writing inline tests. Oct 19, 2023 4 - Beta pytest >=7.0.0 - :pypi:`pytest-inmanta` A py.test plugin providing fixtures to simplify inmanta modules testing. Aug 03, 2023 5 - Production/Stable N/A + :pypi:`pytest-inmanta` A py.test plugin providing fixtures to simplify inmanta modules testing. Nov 29, 2023 5 - Production/Stable N/A :pypi:`pytest-inmanta-extensions` Inmanta tests package Oct 13, 2023 5 - Production/Stable N/A - :pypi:`pytest-inmanta-lsm` Common fixtures for inmanta LSM related modules May 17, 2023 5 - Production/Stable N/A + :pypi:`pytest-inmanta-lsm` Common fixtures for inmanta LSM related modules Nov 29, 2023 5 - Production/Stable N/A :pypi:`pytest-inmanta-yang` Common fixtures used in inmanta yang related modules Jun 16, 2022 4 - Beta N/A :pypi:`pytest-Inomaly` A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A :pypi:`pytest-insta` A practical snapshot testing plugin for pytest Nov 02, 2022 N/A pytest (>=7.2.0,<8.0.0) @@ -632,7 +634,7 @@ This list contains 1346 plugins. :pypi:`pytest-integration-mark` Automatic integration test marking and excluding plugin for pytest May 22, 2023 N/A pytest (>=5.2) :pypi:`pytest-interactive` A pytest plugin for console based interactive test selection just after the collection phase Nov 30, 2017 3 - Alpha N/A :pypi:`pytest-intercept-remote` Pytest plugin for intercepting outgoing connection requests during pytest run. May 24, 2021 4 - Beta pytest (>=4.6) - :pypi:`pytest-interface-tester` Pytest plugin for checking charm relation interface protocol compliance. Sep 14, 2023 4 - Beta pytest + :pypi:`pytest-interface-tester` Pytest plugin for checking charm relation interface protocol compliance. Nov 29, 2023 4 - Beta pytest :pypi:`pytest-invenio` Pytest fixtures for Invenio. Oct 31, 2023 5 - Production/Stable pytest <7.2.0,>=6 :pypi:`pytest-involve` Run tests covering a specific file or changeset Feb 02, 2020 4 - Beta pytest (>=3.5.0) :pypi:`pytest-ipdb` A py.test plug-in to enable drop to ipdb debugger on test failure. Mar 20, 2013 2 - Pre-Alpha N/A @@ -697,7 +699,7 @@ This list contains 1346 plugins. :pypi:`pytest-lisa` Pytest plugin for organizing tests. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) :pypi:`pytest-listener` A simple network listener May 28, 2019 5 - Production/Stable pytest :pypi:`pytest-litf` A pytest plugin that stream output in LITF format Jan 18, 2021 4 - Beta pytest (>=3.1.1) - :pypi:`pytest-litter` Pytest plugin which verifies that tests do not modify file trees. Aug 08, 2023 4 - Beta pytest >=6.1 + :pypi:`pytest-litter` Pytest plugin which verifies that tests do not modify file trees. Nov 23, 2023 4 - Beta pytest >=6.1 :pypi:`pytest-live` Live results for pytest Mar 08, 2020 N/A pytest :pypi:`pytest-local-badge` Generate local badges (shields) reporting your test suite status. Jan 15, 2023 N/A pytest (>=6.1.0) :pypi:`pytest-localftpserver` A PyTest plugin which provides an FTP fixture for your tests Oct 14, 2023 5 - Production/Stable pytest @@ -747,7 +749,7 @@ This list contains 1346 plugins. :pypi:`pytest-messenger` Pytest to Slack reporting plugin Nov 24, 2022 5 - Production/Stable N/A :pypi:`pytest-metadata` pytest plugin for test session metadata May 27, 2023 5 - Production/Stable pytest>=7.0.0 :pypi:`pytest-metrics` Custom metrics report for pytest Apr 04, 2020 N/A pytest - :pypi:`pytest-mh` Pytest multihost plugin Sep 27, 2023 N/A pytest + :pypi:`pytest-mh` Pytest multihost plugin Nov 21, 2023 N/A pytest :pypi:`pytest-mimesis` Mimesis integration with the pytest test runner Mar 21, 2020 5 - Production/Stable pytest (>=4.2) :pypi:`pytest-minecraft` A pytest plugin for running tests against Minecraft releases Apr 06, 2022 N/A pytest (>=6.0.1) :pypi:`pytest-mini` A plugin to test mp Feb 06, 2023 N/A pytest (>=7.2.0,<8.0.0) @@ -838,7 +840,7 @@ This list contains 1346 plugins. :pypi:`pytest-optional` include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A :pypi:`pytest-optional-tests` Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) :pypi:`pytest-orchestration` A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A - :pypi:`pytest-order` pytest plugin to run your tests in a specific order Mar 10, 2023 4 - Beta pytest (>=5.0) ; python_version < "3.10" + :pypi:`pytest-order` pytest plugin to run your tests in a specific order Nov 18, 2023 4 - Beta pytest >=5.0 ; python_version < "3.10" :pypi:`pytest-ordering` pytest plugin to run your tests in a specific order Nov 14, 2018 4 - Beta pytest :pypi:`pytest-order-modify` 新增run_marker 来自定义用例的执行顺序 Nov 04, 2022 N/A N/A :pypi:`pytest-osxnotify` OS X notifications for py.test results. May 15, 2015 N/A N/A @@ -863,6 +865,7 @@ This list contains 1346 plugins. :pypi:`pytest-paste-config` Allow setting the path to a paste config file Sep 18, 2013 3 - Alpha N/A :pypi:`pytest-patch` An automagic \`patch\` fixture that can patch objects directly or by name. Apr 29, 2023 3 - Alpha pytest (>=7.0.0) :pypi:`pytest-patches` A contextmanager pytest fixture for handling multiple mock patches Aug 30, 2021 4 - Beta pytest (>=3.5.0) + :pypi:`pytest-patterns` pytest plugin to make testing complicated long string output easy to write and easy to debug Nov 17, 2023 4 - Beta N/A :pypi:`pytest-pdb` pytest plugin which adds pdb helper commands related to pytest. Jul 31, 2018 N/A N/A :pypi:`pytest-peach` pytest plugin for fuzzing with Peach API Security Apr 12, 2019 4 - Beta pytest (>=2.8.7) :pypi:`pytest-pep257` py.test plugin for pep257 Jul 09, 2016 N/A N/A @@ -939,7 +942,7 @@ This list contains 1346 plugins. :pypi:`pytest-pydocstyle` pytest plugin to run pydocstyle Jan 05, 2023 3 - Alpha N/A :pypi:`pytest-pylint` pytest plugin to check source code with pylint Oct 06, 2023 5 - Production/Stable pytest >=7.0 :pypi:`pytest-pymysql-autorecord` Record PyMySQL queries and mock with the stored data. Sep 02, 2022 N/A N/A - :pypi:`pytest-pyodide` Pytest plugin for testing applications that use Pyodide Nov 04, 2023 N/A pytest + :pypi:`pytest-pyodide` Pytest plugin for testing applications that use Pyodide Nov 18, 2023 N/A pytest :pypi:`pytest-pypi` Easily test your HTTP library against a local copy of pypi Mar 04, 2018 3 - Alpha N/A :pypi:`pytest-pypom-navigation` Core engine for cookiecutter-qa and pytest-play packages Feb 18, 2019 4 - Beta pytest (>=3.0.7) :pypi:`pytest-pyppeteer` A plugin to run pyppeteer in pytest Apr 28, 2022 N/A pytest (>=6.2.5,<7.0.0) @@ -957,7 +960,7 @@ This list contains 1346 plugins. :pypi:`pytest-qaseio` Pytest plugin for Qase.io integration Sep 12, 2023 4 - Beta pytest (>=7.2.2,<8.0.0) :pypi:`pytest-qasync` Pytest support for qasync. Jul 12, 2021 4 - Beta pytest (>=5.4.0) :pypi:`pytest-qatouch` Pytest plugin for uploading test results to your QA Touch Testrun. Feb 14, 2023 4 - Beta pytest (>=6.2.0) - :pypi:`pytest-qgis` A pytest plugin for testing QGIS python plugins Jun 30, 2023 5 - Production/Stable pytest (>=6.2.5) + :pypi:`pytest-qgis` A pytest plugin for testing QGIS python plugins Nov 29, 2023 5 - Production/Stable pytest >=6.0 :pypi:`pytest-qml` Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) :pypi:`pytest-qr` pytest plugin to generate test result QR codes Nov 25, 2021 4 - Beta N/A :pypi:`pytest-qt` pytest support for PyQt and PySide applications Oct 25, 2022 5 - Production/Stable pytest (>=3.0.0) @@ -978,8 +981,8 @@ This list contains 1346 plugins. :pypi:`pytest-random-num` Randomise the order in which pytest tests are run with some control over the randomness Oct 19, 2020 5 - Production/Stable N/A :pypi:`pytest-random-order` Randomise the order in which pytest tests are run with some control over the randomness Dec 03, 2022 5 - Production/Stable pytest (>=3.0.0) :pypi:`pytest-readme` Test your README.md file Sep 02, 2022 5 - Production/Stable N/A - :pypi:`pytest-reana` Pytest fixtures for REANA. Sep 26, 2023 3 - Alpha N/A - :pypi:`pytest-recorder` Pytest plugin, meant to facilitate unit tests writing for tools consumming Web APIs. Mar 30, 2023 N/A N/A + :pypi:`pytest-reana` Pytest fixtures for REANA. Nov 30, 2023 3 - Alpha N/A + :pypi:`pytest-recorder` Pytest plugin, meant to facilitate unit tests writing for tools consumming Web APIs. Nov 21, 2023 N/A N/A :pypi:`pytest-recording` A pytest plugin that allows you recording of network interactions via VCR.py Jul 31, 2023 4 - Beta pytest>=3.5.0 :pypi:`pytest-recordings` Provides pytest plugins for reporting request/response traffic, screenshots, and more to ReportPortal Aug 13, 2020 N/A N/A :pypi:`pytest-redis` Redis fixtures and fixture factories for Pytest. Apr 19, 2023 5 - Production/Stable pytest (>=6.2) @@ -1020,10 +1023,10 @@ This list contains 1346 plugins. :pypi:`pytest-reraise` Make multi-threaded pytest test cases fail when they should Sep 20, 2022 5 - Production/Stable pytest (>=4.6) :pypi:`pytest-rerun` Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) :pypi:`pytest-rerun-all` Rerun testsuite for a certain time or iterations Nov 16, 2023 3 - Alpha pytest (>=7.0.0) - :pypi:`pytest-rerunfailures` pytest plugin to re-run tests to eliminate flaky failures Jul 05, 2023 5 - Production/Stable pytest (>=6.2) + :pypi:`pytest-rerunfailures` pytest plugin to re-run tests to eliminate flaky failures Nov 22, 2023 5 - Production/Stable pytest >=7 :pypi:`pytest-rerunfailures-all-logs` pytest plugin to re-run tests to eliminate flaky failures Mar 07, 2022 5 - Production/Stable N/A :pypi:`pytest-reserial` Pytest fixture for recording and replaying serial port traffic. Aug 31, 2023 4 - Beta pytest - :pypi:`pytest-resilient-circuits` Resilient Circuits fixtures for PyTest Oct 05, 2023 N/A pytest ~=4.6 ; python_version == "2.7" + :pypi:`pytest-resilient-circuits` Resilient Circuits fixtures for PyTest Nov 22, 2023 N/A pytest ~=4.6 ; python_version == "2.7" :pypi:`pytest-resource` Load resource fixture plugin to use with pytest Nov 14, 2018 4 - Beta N/A :pypi:`pytest-resource-path` Provides path for uniform access to test resources in isolated directory May 01, 2021 5 - Production/Stable pytest (>=3.5.0) :pypi:`pytest-resource-usage` Pytest plugin for reporting running time and peak memory usage Nov 06, 2022 5 - Production/Stable pytest>=7.0.0 @@ -1064,22 +1067,22 @@ This list contains 1346 plugins. :pypi:`pytest-saccharin` pytest-saccharin is a updated fork of pytest-sugar, a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly). Oct 31, 2022 3 - Alpha N/A :pypi:`pytest-salt` Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A :pypi:`pytest-salt-containers` A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A - :pypi:`pytest-salt-factories` Pytest Salt Plugin Sep 27, 2023 4 - Beta pytest (>=6.0.0) + :pypi:`pytest-salt-factories` Pytest Salt Plugin Nov 25, 2023 4 - Beta pytest (>=6.0.0) :pypi:`pytest-salt-from-filenames` Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) :pypi:`pytest-salt-runtests-bridge` Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) :pypi:`pytest-sanic` a pytest plugin for Sanic Oct 25, 2021 N/A pytest (>=5.2) :pypi:`pytest-sanity` Dec 07, 2020 N/A N/A :pypi:`pytest-sa-pg` May 14, 2019 N/A N/A - :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Nov 17, 2023 5 - Production/Stable N/A + :pypi:`pytest-sbase` A complete web automation framework for end-to-end testing. Dec 01, 2023 5 - Production/Stable N/A :pypi:`pytest-scenario` pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A :pypi:`pytest-schedule` The job of test scheduling for humans. Jan 07, 2023 5 - Production/Stable N/A :pypi:`pytest-schema` 👍 Validate return values against a schema-like object in testing Mar 14, 2022 5 - Production/Stable pytest (>=3.5.0) :pypi:`pytest-screenshot-on-failure` Saves a screenshot when a test case from a pytest execution fails Jul 21, 2023 4 - Beta N/A :pypi:`pytest-securestore` An encrypted password store for use within pytest cases Nov 08, 2021 4 - Beta N/A :pypi:`pytest-select` A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) - :pypi:`pytest-selenium` pytest plugin for Selenium May 28, 2023 5 - Production/Stable pytest>=6.0.0 + :pypi:`pytest-selenium` pytest plugin for Selenium Nov 20, 2023 5 - Production/Stable pytest>=6.0.0 :pypi:`pytest-selenium-auto` pytest plugin to automatically capture screenshots upon selenium webdriver events Nov 07, 2023 N/A pytest >= 7.0.0 - :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Nov 17, 2023 5 - Production/Stable N/A + :pypi:`pytest-seleniumbase` A complete web automation framework for end-to-end testing. Dec 01, 2023 5 - Production/Stable N/A :pypi:`pytest-selenium-enhancer` pytest plugin for Selenium Apr 29, 2022 5 - Production/Stable N/A :pypi:`pytest-selenium-pdiff` A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A :pypi:`pytest-send-email` Send pytest execution result email Dec 04, 2019 N/A N/A @@ -1111,6 +1114,7 @@ This list contains 1346 plugins. :pypi:`pytest-skipper` A plugin that selects only tests with changes in execution path Mar 26, 2017 3 - Alpha pytest (>=3.0.6) :pypi:`pytest-skippy` Automatically skip tests that don't need to run! Jan 27, 2018 3 - Alpha pytest (>=2.3.4) :pypi:`pytest-skip-slow` A pytest plugin to skip \`@pytest.mark.slow\` tests by default. Feb 09, 2023 N/A pytest>=6.2.0 + :pypi:`pytest-skipuntil` A simple pytest plugin to skip flapping test with deadline Nov 25, 2023 4 - Beta pytest >=3.8.0 :pypi:`pytest-slack` Pytest to Slack reporting plugin Dec 15, 2020 5 - Production/Stable N/A :pypi:`pytest-slow` A pytest plugin to skip \`@pytest.mark.slow\` tests by default. Sep 28, 2021 N/A N/A :pypi:`pytest-slowest-first` Sort tests by their last duration, slowest first Dec 11, 2022 4 - Beta N/A @@ -1139,7 +1143,7 @@ This list contains 1346 plugins. :pypi:`pytest-spark` pytest plugin to run the tests with support of pyspark. Feb 23, 2020 4 - Beta pytest :pypi:`pytest-spawner` py.test plugin to spawn process and communicate with them. Jul 31, 2015 4 - Beta N/A :pypi:`pytest-spec` Library pytest-spec is a pytest plugin to display test execution output like a SPECIFICATION. May 04, 2021 N/A N/A - :pypi:`pytest-spec2md` Library pytest-spec2md is a pytest plugin to create a markdown specification while running pytest. Sep 15, 2023 N/A pytest (>7.0) + :pypi:`pytest-spec2md` Library pytest-spec2md is a pytest plugin to create a markdown specification while running pytest. Nov 21, 2023 N/A pytest (>7.0) :pypi:`pytest-speed` Modern benchmarking library for python with pytest integration. Jan 22, 2023 3 - Alpha pytest>=7 :pypi:`pytest-sphinx` Doctest plugin for pytest with support for Sphinx-specific doctest-directives Sep 06, 2022 4 - Beta pytest (>=7.0.0) :pypi:`pytest-spiratest` Exports unit tests as test runs in SpiraTest/Team/Plan Feb 08, 2022 N/A N/A @@ -1150,8 +1154,8 @@ This list contains 1346 plugins. :pypi:`pytest-splitio` Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) :pypi:`pytest-split-tests` A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. Jul 30, 2021 5 - Production/Stable pytest (>=2.5) :pypi:`pytest-split-tests-tresorit` Feb 22, 2021 1 - Planning N/A - :pypi:`pytest-splunk-addon` A Dynamic test tool for Splunk Apps and Add-ons Oct 23, 2023 N/A pytest (>5.4.0,<8) - :pypi:`pytest-splunk-addon-ui-smartx` Library to support testing Splunk Add-on UX Nov 15, 2023 N/A N/A + :pypi:`pytest-splunk-addon` A Dynamic test tool for Splunk Apps and Add-ons Nov 25, 2023 N/A pytest (>5.4.0,<8) + :pypi:`pytest-splunk-addon-ui-smartx` Library to support testing Splunk Add-on UX Dec 01, 2023 N/A N/A :pypi:`pytest-splunk-env` pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) :pypi:`pytest-sqitch` sqitch for pytest Apr 06, 2020 4 - Beta N/A :pypi:`pytest-sqlalchemy` pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A @@ -1178,6 +1182,7 @@ This list contains 1346 plugins. :pypi:`pytest-stub` Stub packages, modules and attributes. Apr 28, 2020 5 - Production/Stable N/A :pypi:`pytest-stubprocess` Provide stub implementations for subprocesses in Python tests Sep 17, 2018 3 - Alpha pytest (>=3.5.0) :pypi:`pytest-study` A pytest plugin to organize long run tests (named studies) without interfering the regular tests Sep 26, 2017 3 - Alpha pytest (>=2.0) + :pypi:`pytest-subinterpreter` Run pytest in a subinterpreter Nov 25, 2023 N/A pytest>=7.0.0 :pypi:`pytest-subprocess` A plugin to fake subprocess for pytest Jan 28, 2023 5 - Production/Stable pytest (>=4.0.0) :pypi:`pytest-subtesthack` A hack to explicitly set up and tear down fixtures. Jul 16, 2022 N/A N/A :pypi:`pytest-subtests` unittest subTest() support and subtests fixture May 15, 2023 4 - Beta pytest (>=7.0) @@ -1217,7 +1222,7 @@ This list contains 1346 plugins. :pypi:`pytest-testinfra-jpic` Test infrastructures Sep 21, 2023 5 - Production/Stable N/A :pypi:`pytest-testinfra-winrm-transport` Test infrastructures Sep 21, 2023 5 - Production/Stable N/A :pypi:`pytest-testlink-adaptor` pytest reporting plugin for testlink Dec 20, 2018 4 - Beta pytest (>=2.6) - :pypi:`pytest-testmon` selects tests affected by changed files and methods Nov 07, 2023 4 - Beta pytest <8,>=5 + :pypi:`pytest-testmon` selects tests affected by changed files and methods Nov 23, 2023 4 - Beta pytest <8,>=5 :pypi:`pytest-testmon-dev` selects tests affected by changed files and methods Mar 30, 2023 4 - Beta pytest (<8,>=5) :pypi:`pytest-testmon-oc` nOly selects tests affected by changed files and methods Jun 01, 2022 4 - Beta pytest (<8,>=5) :pypi:`pytest-testmon-skip-libraries` selects tests affected by changed files and methods Mar 03, 2023 4 - Beta pytest (<8,>=5) @@ -1290,7 +1295,7 @@ This list contains 1346 plugins. :pypi:`pytest-twisted` A twisted plugin for pytest. Oct 16, 2022 5 - Production/Stable pytest (>=2.3) :pypi:`pytest-typechecker` Run type checkers on specified test files Feb 04, 2022 N/A pytest (>=6.2.5,<7.0.0) :pypi:`pytest-typhoon-config` A Typhoon HIL plugin that facilitates test parameter configuration at runtime Apr 07, 2022 5 - Production/Stable N/A - :pypi:`pytest-typhoon-polarion` Typhoontest plugin for Siemens Polarion Nov 17, 2023 4 - Beta N/A + :pypi:`pytest-typhoon-polarion` Typhoontest plugin for Siemens Polarion Dec 01, 2023 4 - Beta N/A :pypi:`pytest-typhoon-xray` Typhoon HIL plugin for pytest Aug 15, 2023 4 - Beta N/A :pypi:`pytest-tytest` Typhoon HIL plugin for pytest May 25, 2020 4 - Beta pytest (>=5.4.2) :pypi:`pytest-ubersmith` Easily mock calls to ubersmith at the \`requests\` level. Apr 13, 2015 N/A N/A @@ -1315,8 +1320,8 @@ This list contains 1346 plugins. :pypi:`pytest-vcr-delete-on-fail` A pytest plugin that automates vcrpy cassettes deletion on test failure. Jun 20, 2022 5 - Production/Stable pytest (>=6.2.2) :pypi:`pytest-vcrpandas` Test from HTTP interactions to dataframe processed. Jan 12, 2019 4 - Beta pytest :pypi:`pytest-vcs` Sep 22, 2022 4 - Beta N/A - :pypi:`pytest-venv` py.test fixture for creating a virtual environment Aug 04, 2020 4 - Beta pytest - :pypi:`pytest-ver` Pytest module with Verification Protocol, Verification Report and Trace Matrix Nov 18, 2023 4 - Beta pytest + :pypi:`pytest-venv` py.test fixture for creating a virtual environment Nov 23, 2023 4 - Beta pytest + :pypi:`pytest-ver` Pytest module with Verification Protocol, Verification Report and Trace Matrix Nov 23, 2023 4 - Beta pytest :pypi:`pytest-verbose-parametrize` More descriptive output for parametrized py.test tests May 28, 2019 5 - Production/Stable pytest :pypi:`pytest-vimqf` A simple pytest plugin that will shrink pytest output when specified, to fit vim quickfix window. Feb 08, 2021 4 - Beta pytest (>=6.2.2,<7.0.0) :pypi:`pytest-virtualenv` Virtualenv fixture for py.test May 28, 2019 5 - Production/Stable pytest @@ -1346,7 +1351,7 @@ This list contains 1346 plugins. :pypi:`pytest-wiremock` A pytest plugin for programmatically using wiremock in integration tests Mar 27, 2022 N/A pytest (>=7.1.1,<8.0.0) :pypi:`pytest-with-docker` pytest with docker helpers. Nov 09, 2021 N/A pytest :pypi:`pytest-workflow` A pytest plugin for configuring workflow/pipeline tests using YAML files Jan 13, 2023 5 - Production/Stable pytest (>=7.0.0) - :pypi:`pytest-xdist` pytest xdist plugin for distributed testing, most importantly across multiple CPUs Nov 11, 2023 5 - Production/Stable pytest >=6.2.0 + :pypi:`pytest-xdist` pytest xdist plugin for distributed testing, most importantly across multiple CPUs Nov 21, 2023 5 - Production/Stable pytest >=6.2.0 :pypi:`pytest-xdist-debug-for-graingert` pytest xdist plugin for distributed testing and loop-on-failing modes Jul 24, 2019 5 - Production/Stable pytest (>=4.4.0) :pypi:`pytest-xdist-forked` forked from pytest-xdist Feb 10, 2020 5 - Production/Stable pytest (>=4.4.0) :pypi:`pytest-xdist-tracker` pytest plugin helps to reproduce failures for particular xdist node Nov 18, 2021 3 - Alpha pytest (>=3.5.1) @@ -1365,7 +1370,7 @@ This list contains 1346 plugins. :pypi:`pytest-xvfb` A pytest plugin to run Xvfb (or Xephyr/Xvnc) for tests. May 29, 2023 4 - Beta pytest (>=2.8.1) :pypi:`pytest-xvirt` A pytest plugin to virtualize test. For example to transparently running them on a remote box. Oct 01, 2023 4 - Beta pytest >=7.1.0 :pypi:`pytest-yaml` This plugin is used to load yaml output to your test using pytest framework. Oct 05, 2018 N/A pytest - :pypi:`pytest-yaml-sanmu` pytest plugin for generating test cases by yaml Jul 03, 2023 N/A pytest>=7.4.0 + :pypi:`pytest-yaml-sanmu` pytest plugin for generating test cases by yaml Nov 30, 2023 N/A pytest>=7.4.0 :pypi:`pytest-yamltree` Create or check file/directory trees described by YAML Mar 02, 2020 4 - Beta pytest (>=3.1.1) :pypi:`pytest-yamlwsgi` Run tests against wsgi apps defined in yaml May 11, 2010 N/A N/A :pypi:`pytest-yaml-yoyo` http/https API run by yaml Jun 19, 2023 N/A pytest (>=7.2.0) @@ -1694,7 +1699,7 @@ This list contains 1346 plugins. Pytest fixtures to assert anything and something :pypi:`pytest-aoc` - *last release*: Sep 19, 2023, + *last release*: Dec 02, 2023, *status*: 5 - Production/Stable, *requires*: pytest ; extra == 'test' @@ -1771,7 +1776,7 @@ This list contains 1346 plugins. pyest results colection plugin :pypi:`pytest-arraydiff` - *last release*: Nov 15, 2023, + *last release*: Nov 27, 2023, *status*: 4 - Beta, *requires*: pytest >=4.6 @@ -1883,14 +1888,14 @@ This list contains 1346 plugins. Pytest fixtures for async generators :pypi:`pytest-asyncio` - *last release*: Nov 16, 2023, + *last release*: Nov 27, 2023, *status*: 4 - Beta, *requires*: pytest (>=7.0.0) Pytest support for asyncio :pypi:`pytest-asyncio-cooperative` - *last release*: Aug 06, 2023, + *last release*: Nov 30, 2023, *status*: N/A, *requires*: N/A @@ -1918,7 +1923,7 @@ This list contains 1346 plugins. Database testing fixtures using the SQLAlchemy asyncio API :pypi:`pytest-atf-allure` - *last release*: Oct 22, 2023, + *last release*: Nov 29, 2023, *status*: N/A, *requires*: pytest (>=7.4.2,<8.0.0) @@ -2597,7 +2602,7 @@ This list contains 1346 plugins. A pytest plugin to send a report and printing summary of tests. :pypi:`pytest-choose` - *last release*: Nov 02, 2023, + *last release*: Nov 30, 2023, *status*: N/A, *requires*: pytest >=7.0.0 @@ -3801,7 +3806,7 @@ This list contains 1346 plugins. pytest plugin for dogu report :pypi:`pytest-dogu-sdk` - *last release*: Aug 04, 2023, + *last release*: Nov 20, 2023, *status*: N/A, *requires*: N/A @@ -4018,56 +4023,56 @@ This list contains 1346 plugins. Send execution result email :pypi:`pytest-embedded` - *last release*: Nov 07, 2023, + *last release*: Nov 27, 2023, *status*: 5 - Production/Stable, *requires*: pytest>=7.0 A pytest plugin that designed for embedded testing. :pypi:`pytest-embedded-arduino` - *last release*: Nov 07, 2023, + *last release*: Nov 27, 2023, *status*: 5 - Production/Stable, *requires*: N/A Make pytest-embedded plugin work with Arduino. :pypi:`pytest-embedded-idf` - *last release*: Nov 07, 2023, + *last release*: Nov 27, 2023, *status*: 5 - Production/Stable, *requires*: N/A Make pytest-embedded plugin work with ESP-IDF. :pypi:`pytest-embedded-jtag` - *last release*: Nov 07, 2023, + *last release*: Nov 27, 2023, *status*: 5 - Production/Stable, *requires*: N/A Make pytest-embedded plugin work with JTAG. :pypi:`pytest-embedded-qemu` - *last release*: Nov 07, 2023, + *last release*: Nov 27, 2023, *status*: 5 - Production/Stable, *requires*: N/A Make pytest-embedded plugin work with QEMU. :pypi:`pytest-embedded-serial` - *last release*: Nov 07, 2023, + *last release*: Nov 27, 2023, *status*: 5 - Production/Stable, *requires*: N/A Make pytest-embedded plugin work with Serial. :pypi:`pytest-embedded-serial-esp` - *last release*: Nov 07, 2023, + *last release*: Nov 27, 2023, *status*: 5 - Production/Stable, *requires*: N/A Make pytest-embedded plugin work with Espressif target boards. :pypi:`pytest-embedded-wokwi` - *last release*: Nov 07, 2023, + *last release*: Nov 27, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -4137,7 +4142,7 @@ This list contains 1346 plugins. Improvements for pytest (rejected upstream) :pypi:`pytest-env` - *last release*: Oct 30, 2023, + *last release*: Nov 28, 2023, *status*: 5 - Production/Stable, *requires*: pytest>=7.4.3 @@ -4837,9 +4842,9 @@ This list contains 1346 plugins. :pypi:`pytest-fzf` - *last release*: Nov 15, 2023, - *status*: 1 - Planning, - *requires*: pytest >=7.1.2 + *last release*: Nov 28, 2023, + *status*: 4 - Beta, + *requires*: pytest >=6.0.0 fzf-based test selector for pytest @@ -4864,6 +4869,13 @@ This list contains 1346 plugins. Uses gcov to measure test coverage of a C library + :pypi:`pytest-gee` + *last release*: Nov 22, 2023, + *status*: 3 - Alpha, + *requires*: pytest; extra == 'test' + + The Python plugin for your GEE based packages. + :pypi:`pytest-gevent` *last release*: Feb 25, 2020, *status*: N/A, @@ -4893,7 +4905,7 @@ This list contains 1346 plugins. For finding/executing Ghost Inspector tests :pypi:`pytest-girder` - *last release*: Nov 08, 2023, + *last release*: Nov 20, 2023, *status*: N/A, *requires*: N/A @@ -5137,6 +5149,13 @@ This list contains 1346 plugins. Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report + :pypi:`pytest-history` + *last release*: Nov 20, 2023, + *status*: N/A, + *requires*: pytest (>=7.4.3,<8.0.0) + + Pytest plugin to keep a history of your pytest runs + :pypi:`pytest-home` *last release*: Oct 09, 2023, *status*: 5 - Production/Stable, @@ -5152,7 +5171,7 @@ This list contains 1346 plugins. A pytest plugin for use with homeassistant custom components. :pypi:`pytest-homeassistant-custom-component` - *last release*: Nov 11, 2023, + *last release*: Dec 01, 2023, *status*: 3 - Alpha, *requires*: pytest ==7.4.3 @@ -5173,7 +5192,7 @@ This list contains 1346 plugins. Report on tests that honor constraints, and guard against regressions :pypi:`pytest-hot-reloading` - *last release*: Jun 23, 2023, + *last release*: Dec 01, 2023, *status*: N/A, *requires*: N/A @@ -5425,7 +5444,7 @@ This list contains 1346 plugins. :pypi:`pytest-image-snapshot` - *last release*: Nov 16, 2023, + *last release*: Dec 01, 2023, *status*: 4 - Beta, *requires*: pytest >=3.5.0 @@ -5488,7 +5507,7 @@ This list contains 1346 plugins. A pytest plugin for writing inline tests. :pypi:`pytest-inmanta` - *last release*: Aug 03, 2023, + *last release*: Nov 29, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -5502,7 +5521,7 @@ This list contains 1346 plugins. Inmanta tests package :pypi:`pytest-inmanta-lsm` - *last release*: May 17, 2023, + *last release*: Nov 29, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -5572,7 +5591,7 @@ This list contains 1346 plugins. Pytest plugin for intercepting outgoing connection requests during pytest run. :pypi:`pytest-interface-tester` - *last release*: Sep 14, 2023, + *last release*: Nov 29, 2023, *status*: 4 - Beta, *requires*: pytest @@ -6027,7 +6046,7 @@ This list contains 1346 plugins. A pytest plugin that stream output in LITF format :pypi:`pytest-litter` - *last release*: Aug 08, 2023, + *last release*: Nov 23, 2023, *status*: 4 - Beta, *requires*: pytest >=6.1 @@ -6377,7 +6396,7 @@ This list contains 1346 plugins. Custom metrics report for pytest :pypi:`pytest-mh` - *last release*: Sep 27, 2023, + *last release*: Nov 21, 2023, *status*: N/A, *requires*: pytest @@ -7014,9 +7033,9 @@ This list contains 1346 plugins. A pytest plugin for orchestrating tests :pypi:`pytest-order` - *last release*: Mar 10, 2023, + *last release*: Nov 18, 2023, *status*: 4 - Beta, - *requires*: pytest (>=5.0) ; python_version < "3.10" + *requires*: pytest >=5.0 ; python_version < "3.10" pytest plugin to run your tests in a specific order @@ -7188,6 +7207,13 @@ This list contains 1346 plugins. A contextmanager pytest fixture for handling multiple mock patches + :pypi:`pytest-patterns` + *last release*: Nov 17, 2023, + *status*: 4 - Beta, + *requires*: N/A + + pytest plugin to make testing complicated long string output easy to write and easy to debug + :pypi:`pytest-pdb` *last release*: Jul 31, 2018, *status*: N/A, @@ -7721,7 +7747,7 @@ This list contains 1346 plugins. Record PyMySQL queries and mock with the stored data. :pypi:`pytest-pyodide` - *last release*: Nov 04, 2023, + *last release*: Nov 18, 2023, *status*: N/A, *requires*: pytest @@ -7847,9 +7873,9 @@ This list contains 1346 plugins. Pytest plugin for uploading test results to your QA Touch Testrun. :pypi:`pytest-qgis` - *last release*: Jun 30, 2023, + *last release*: Nov 29, 2023, *status*: 5 - Production/Stable, - *requires*: pytest (>=6.2.5) + *requires*: pytest >=6.0 A pytest plugin for testing QGIS python plugins @@ -7994,14 +8020,14 @@ This list contains 1346 plugins. Test your README.md file :pypi:`pytest-reana` - *last release*: Sep 26, 2023, + *last release*: Nov 30, 2023, *status*: 3 - Alpha, *requires*: N/A Pytest fixtures for REANA. :pypi:`pytest-recorder` - *last release*: Mar 30, 2023, + *last release*: Nov 21, 2023, *status*: N/A, *requires*: N/A @@ -8288,9 +8314,9 @@ This list contains 1346 plugins. Rerun testsuite for a certain time or iterations :pypi:`pytest-rerunfailures` - *last release*: Jul 05, 2023, + *last release*: Nov 22, 2023, *status*: 5 - Production/Stable, - *requires*: pytest (>=6.2) + *requires*: pytest >=7 pytest plugin to re-run tests to eliminate flaky failures @@ -8309,7 +8335,7 @@ This list contains 1346 plugins. Pytest fixture for recording and replaying serial port traffic. :pypi:`pytest-resilient-circuits` - *last release*: Oct 05, 2023, + *last release*: Nov 22, 2023, *status*: N/A, *requires*: pytest ~=4.6 ; python_version == "2.7" @@ -8596,7 +8622,7 @@ This list contains 1346 plugins. A Pytest plugin that builds and creates docker containers :pypi:`pytest-salt-factories` - *last release*: Sep 27, 2023, + *last release*: Nov 25, 2023, *status*: 4 - Beta, *requires*: pytest (>=6.0.0) @@ -8638,7 +8664,7 @@ This list contains 1346 plugins. :pypi:`pytest-sbase` - *last release*: Nov 17, 2023, + *last release*: Dec 01, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -8687,7 +8713,7 @@ This list contains 1346 plugins. A pytest plugin which allows to (de-)select tests from a file. :pypi:`pytest-selenium` - *last release*: May 28, 2023, + *last release*: Nov 20, 2023, *status*: 5 - Production/Stable, *requires*: pytest>=6.0.0 @@ -8701,7 +8727,7 @@ This list contains 1346 plugins. pytest plugin to automatically capture screenshots upon selenium webdriver events :pypi:`pytest-seleniumbase` - *last release*: Nov 17, 2023, + *last release*: Dec 01, 2023, *status*: 5 - Production/Stable, *requires*: N/A @@ -8924,6 +8950,13 @@ This list contains 1346 plugins. A pytest plugin to skip \`@pytest.mark.slow\` tests by default. + :pypi:`pytest-skipuntil` + *last release*: Nov 25, 2023, + *status*: 4 - Beta, + *requires*: pytest >=3.8.0 + + A simple pytest plugin to skip flapping test with deadline + :pypi:`pytest-slack` *last release*: Dec 15, 2020, *status*: 5 - Production/Stable, @@ -9121,7 +9154,7 @@ This list contains 1346 plugins. Library pytest-spec is a pytest plugin to display test execution output like a SPECIFICATION. :pypi:`pytest-spec2md` - *last release*: Sep 15, 2023, + *last release*: Nov 21, 2023, *status*: N/A, *requires*: pytest (>7.0) @@ -9198,14 +9231,14 @@ This list contains 1346 plugins. :pypi:`pytest-splunk-addon` - *last release*: Oct 23, 2023, + *last release*: Nov 25, 2023, *status*: N/A, *requires*: pytest (>5.4.0,<8) A Dynamic test tool for Splunk Apps and Add-ons :pypi:`pytest-splunk-addon-ui-smartx` - *last release*: Nov 15, 2023, + *last release*: Dec 01, 2023, *status*: N/A, *requires*: N/A @@ -9393,6 +9426,13 @@ This list contains 1346 plugins. A pytest plugin to organize long run tests (named studies) without interfering the regular tests + :pypi:`pytest-subinterpreter` + *last release*: Nov 25, 2023, + *status*: N/A, + *requires*: pytest>=7.0.0 + + Run pytest in a subinterpreter + :pypi:`pytest-subprocess` *last release*: Jan 28, 2023, *status*: 5 - Production/Stable, @@ -9667,7 +9707,7 @@ This list contains 1346 plugins. pytest reporting plugin for testlink :pypi:`pytest-testmon` - *last release*: Nov 07, 2023, + *last release*: Nov 23, 2023, *status*: 4 - Beta, *requires*: pytest <8,>=5 @@ -10178,7 +10218,7 @@ This list contains 1346 plugins. A Typhoon HIL plugin that facilitates test parameter configuration at runtime :pypi:`pytest-typhoon-polarion` - *last release*: Nov 17, 2023, + *last release*: Dec 01, 2023, *status*: 4 - Beta, *requires*: N/A @@ -10353,14 +10393,14 @@ This list contains 1346 plugins. :pypi:`pytest-venv` - *last release*: Aug 04, 2020, + *last release*: Nov 23, 2023, *status*: 4 - Beta, *requires*: pytest py.test fixture for creating a virtual environment :pypi:`pytest-ver` - *last release*: Nov 18, 2023, + *last release*: Nov 23, 2023, *status*: 4 - Beta, *requires*: pytest @@ -10570,7 +10610,7 @@ This list contains 1346 plugins. A pytest plugin for configuring workflow/pipeline tests using YAML files :pypi:`pytest-xdist` - *last release*: Nov 11, 2023, + *last release*: Nov 21, 2023, *status*: 5 - Production/Stable, *requires*: pytest >=6.2.0 @@ -10703,7 +10743,7 @@ This list contains 1346 plugins. This plugin is used to load yaml output to your test using pytest framework. :pypi:`pytest-yaml-sanmu` - *last release*: Jul 03, 2023, + *last release*: Nov 30, 2023, *status*: N/A, *requires*: pytest>=7.4.0 diff --git a/pyproject.toml b/pyproject.toml index cdbdd3880..d45597b77 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,5 @@ [build-system] requires = [ - # sync with setup.py until we discard non-pep-517/518 "setuptools>=45.0", "setuptools-scm[toml]>=6.2.3", ] diff --git a/setup.py b/setup.py deleted file mode 100644 index 7f1a1763c..000000000 --- a/setup.py +++ /dev/null @@ -1,4 +0,0 @@ -from setuptools import setup - -if __name__ == "__main__": - setup() diff --git a/src/_pytest/_io/pprint.py b/src/_pytest/_io/pprint.py index 9923d0a62..ad1238709 100644 --- a/src/_pytest/_io/pprint.py +++ b/src/_pytest/_io/pprint.py @@ -15,14 +15,17 @@ import collections as _collections import dataclasses as _dataclasses import re -import sys as _sys import types as _types from io import StringIO as _StringIO from typing import Any from typing import Callable from typing import Dict from typing import IO +from typing import Iterator from typing import List +from typing import Optional +from typing import Set +from typing import Tuple class _safe_key: @@ -58,15 +61,13 @@ def _safe_tuple(t): class PrettyPrinter: def __init__( self, - indent=1, - width=80, - depth=None, - stream=None, + indent: int = 4, + width: int = 80, + depth: Optional[int] = None, *, - compact=False, - sort_dicts=True, - underscore_numbers=False, - ): + sort_dicts: bool = True, + underscore_numbers: bool = False, + ) -> None: """Handle pretty printing operations onto a stream using a set of configured parameters. @@ -79,13 +80,6 @@ class PrettyPrinter: depth The maximum depth to print out nested structures. - stream - The desired output stream. If omitted (or false), the standard - output stream available at construction will be used. - - compact - If true, several items will be combined in one line. - sort_dicts If true, dict keys are sorted. @@ -101,32 +95,33 @@ class PrettyPrinter: self._depth = depth self._indent_per_level = indent self._width = width - if stream is not None: - self._stream = stream - else: - self._stream = _sys.stdout - self._compact = bool(compact) self._sort_dicts = sort_dicts self._underscore_numbers = underscore_numbers def pformat(self, object: Any) -> str: sio = _StringIO() - self._format(object, sio, 0, 0, {}, 0) + self._format(object, sio, 0, 0, set(), 0) return sio.getvalue() - def _format(self, object, stream, indent, allowance, context, level): + def _format( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: objid = id(object) if objid in context: stream.write(_recursion(object)) - self._recursive = True - self._readable = False return p = self._dispatch.get(type(object).__repr__, None) if p is not None: - context[objid] = 1 + context.add(objid) p(self, object, stream, indent, allowance, context, level + 1) - del context[objid] + context.remove(objid) elif ( _dataclasses.is_dataclass(object) and not isinstance(object, type) @@ -136,17 +131,24 @@ class PrettyPrinter: hasattr(object.__repr__, "__wrapped__") and "__create_fn__" in object.__repr__.__wrapped__.__qualname__ ): - context[objid] = 1 + context.add(objid) self._pprint_dataclass( object, stream, indent, allowance, context, level + 1 ) - del context[objid] + context.remove(objid) else: stream.write(self._repr(object, context, level)) - def _pprint_dataclass(self, object, stream, indent, allowance, context, level): + def _pprint_dataclass( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: cls_name = object.__class__.__name__ - indent += len(cls_name) + 1 items = [ (f.name, getattr(object, f.name)) for f in _dataclasses.fields(object) @@ -158,63 +160,87 @@ class PrettyPrinter: _dispatch: Dict[ Callable[..., str], - Callable[["PrettyPrinter", Any, IO[str], int, int, Dict[int, int], int], str], + Callable[["PrettyPrinter", Any, IO[str], int, int, Set[int], int], None], ] = {} - def _pprint_dict(self, object, stream, indent, allowance, context, level): + def _pprint_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: write = stream.write write("{") - if self._indent_per_level > 1: - write((self._indent_per_level - 1) * " ") - length = len(object) - if length: - if self._sort_dicts: - items = sorted(object.items(), key=_safe_tuple) - else: - items = object.items() - self._format_dict_items( - items, stream, indent, allowance + 1, context, level - ) + if self._sort_dicts: + items = sorted(object.items(), key=_safe_tuple) + else: + items = object.items() + self._format_dict_items(items, stream, indent, allowance, context, level) write("}") _dispatch[dict.__repr__] = _pprint_dict - def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level): + def _pprint_ordered_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: if not len(object): stream.write(repr(object)) return cls = object.__class__ stream.write(cls.__name__ + "(") - self._format( - list(object.items()), - stream, - indent + len(cls.__name__) + 1, - allowance + 1, - context, - level, - ) + self._pprint_dict(object, stream, indent, allowance, context, level) stream.write(")") _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict - def _pprint_list(self, object, stream, indent, allowance, context, level): + def _pprint_list( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: stream.write("[") - self._format_items(object, stream, indent, allowance + 1, context, level) + self._format_items(object, stream, indent, allowance, context, level) stream.write("]") _dispatch[list.__repr__] = _pprint_list - def _pprint_tuple(self, object, stream, indent, allowance, context, level): + def _pprint_tuple( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: stream.write("(") - endchar = ",)" if len(object) == 1 else ")" - self._format_items( - object, stream, indent, allowance + len(endchar), context, level - ) - stream.write(endchar) + self._format_items(object, stream, indent, allowance, context, level) + stream.write(")") _dispatch[tuple.__repr__] = _pprint_tuple - def _pprint_set(self, object, stream, indent, allowance, context, level): + def _pprint_set( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: if not len(object): stream.write(repr(object)) return @@ -225,17 +251,22 @@ class PrettyPrinter: else: stream.write(typ.__name__ + "({") endchar = "})" - indent += len(typ.__name__) + 1 object = sorted(object, key=_safe_key) - self._format_items( - object, stream, indent, allowance + len(endchar), context, level - ) + self._format_items(object, stream, indent, allowance, context, level) stream.write(endchar) _dispatch[set.__repr__] = _pprint_set _dispatch[frozenset.__repr__] = _pprint_set - def _pprint_str(self, object, stream, indent, allowance, context, level): + def _pprint_str( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: write = stream.write if not len(object): write(repr(object)) @@ -286,7 +317,15 @@ class PrettyPrinter: _dispatch[str.__repr__] = _pprint_str - def _pprint_bytes(self, object, stream, indent, allowance, context, level): + def _pprint_bytes( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: write = stream.write if len(object) <= 4: write(repr(object)) @@ -307,7 +346,15 @@ class PrettyPrinter: _dispatch[bytes.__repr__] = _pprint_bytes - def _pprint_bytearray(self, object, stream, indent, allowance, context, level): + def _pprint_bytearray( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: write = stream.write write("bytearray(") self._pprint_bytes( @@ -317,23 +364,36 @@ class PrettyPrinter: _dispatch[bytearray.__repr__] = _pprint_bytearray - def _pprint_mappingproxy(self, object, stream, indent, allowance, context, level): + def _pprint_mappingproxy( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: stream.write("mappingproxy(") - self._format(object.copy(), stream, indent + 13, allowance + 1, context, level) + self._format(object.copy(), stream, indent, allowance, context, level) stream.write(")") _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy def _pprint_simplenamespace( - self, object, stream, indent, allowance, context, level - ): + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: if type(object) is _types.SimpleNamespace: # The SimpleNamespace repr is "namespace" instead of the class # name, so we do the same here. For subclasses; use the class name. cls_name = "namespace" else: cls_name = object.__class__.__name__ - indent += len(cls_name) + 1 items = object.__dict__.items() stream.write(cls_name + "(") self._format_namespace_items(items, stream, indent, allowance, context, level) @@ -341,33 +401,47 @@ class PrettyPrinter: _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace - def _format_dict_items(self, items, stream, indent, allowance, context, level): - write = stream.write - indent += self._indent_per_level - delimnl = ",\n" + " " * indent - last_index = len(items) - 1 - for i, (key, ent) in enumerate(items): - last = i == last_index - rep = self._repr(key, context, level) - write(rep) - write(": ") - self._format( - ent, - stream, - indent + len(rep) + 2, - allowance if last else 1, - context, - level, - ) - if not last: - write(delimnl) + def _format_dict_items( + self, + items: List[Tuple[Any, Any]], + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: + if not items: + return - def _format_namespace_items(self, items, stream, indent, allowance, context, level): write = stream.write - delimnl = ",\n" + " " * indent - last_index = len(items) - 1 - for i, (key, ent) in enumerate(items): - last = i == last_index + item_indent = indent + self._indent_per_level + delimnl = "\n" + " " * item_indent + for key, ent in items: + write(delimnl) + write(self._repr(key, context, level)) + write(": ") + self._format(ent, stream, item_indent, 1, context, level) + write(",") + + write("\n" + " " * indent) + + def _format_namespace_items( + self, + items: List[Tuple[Any, Any]], + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: + if not items: + return + + write = stream.write + item_indent = indent + self._indent_per_level + delimnl = "\n" + " " * item_indent + for key, ent in items: + write(delimnl) write(key) write("=") if id(ent) in context: @@ -378,174 +452,185 @@ class PrettyPrinter: self._format( ent, stream, - indent + len(key) + 1, - allowance if last else 1, + item_indent + len(key) + 1, + 1, context, level, ) - if not last: - write(delimnl) - def _format_items(self, items, stream, indent, allowance, context, level): - write = stream.write - indent += self._indent_per_level - if self._indent_per_level > 1: - write((self._indent_per_level - 1) * " ") - delimnl = ",\n" + " " * indent - delim = "" - width = max_width = self._width - indent + 1 - it = iter(items) - try: - next_ent = next(it) - except StopIteration: + write(",") + + write("\n" + " " * indent) + + def _format_items( + self, + items: List[Any], + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: + if not items: return - last = False - while not last: - ent = next_ent - try: - next_ent = next(it) - except StopIteration: - last = True - max_width -= allowance - width -= allowance - if self._compact: - rep = self._repr(ent, context, level) - w = len(rep) + 2 - if width < w: - width = max_width - if delim: - delim = delimnl - if width >= w: - width -= w - write(delim) - delim = ", " - write(rep) - continue - write(delim) - delim = delimnl - self._format(ent, stream, indent, allowance if last else 1, context, level) - def _repr(self, object, context, level): - repr, readable, recursive = self.format( - object, context.copy(), self._depth, level - ) - if not readable: - self._readable = False - if recursive: - self._recursive = True - return repr + write = stream.write + item_indent = indent + self._indent_per_level + delimnl = "\n" + " " * item_indent - def format(self, object, context, maxlevels, level): - """Format object for a specific context, returning a string - and flags indicating whether the representation is 'readable' - and whether the object represents a recursive construct. - """ + for item in items: + write(delimnl) + self._format(item, stream, item_indent, 1, context, level) + write(",") + + write("\n" + " " * indent) + + def _repr(self, object: Any, context: Set[int], level: int) -> str: + return self.format(object, context.copy(), self._depth, level) + + def format( + self, object: Any, context: Set[int], maxlevels: Optional[int], level: int + ) -> str: return self._safe_repr(object, context, maxlevels, level) - def _pprint_default_dict(self, object, stream, indent, allowance, context, level): - if not len(object): - stream.write(repr(object)) - return + def _pprint_default_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: rdf = self._repr(object.default_factory, context, level) - cls = object.__class__ - indent += len(cls.__name__) + 1 - stream.write(f"{cls.__name__}({rdf},\n{' ' * indent}") - self._pprint_dict(object, stream, indent, allowance + 1, context, level) + stream.write(f"{object.__class__.__name__}({rdf}, ") + self._pprint_dict(object, stream, indent, allowance, context, level) stream.write(")") _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict - def _pprint_counter(self, object, stream, indent, allowance, context, level): - if not len(object): - stream.write(repr(object)) - return - cls = object.__class__ - stream.write(cls.__name__ + "({") - if self._indent_per_level > 1: - stream.write((self._indent_per_level - 1) * " ") - items = object.most_common() - self._format_dict_items( - items, stream, indent + len(cls.__name__) + 1, allowance + 2, context, level - ) - stream.write("})") + def _pprint_counter( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: + stream.write(object.__class__.__name__ + "(") + + if object: + stream.write("{") + items = object.most_common() + self._format_dict_items(items, stream, indent, allowance, context, level) + stream.write("}") + + stream.write(")") _dispatch[_collections.Counter.__repr__] = _pprint_counter - def _pprint_chain_map(self, object, stream, indent, allowance, context, level): - if not len(object.maps): + def _pprint_chain_map( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: + if not len(object.maps) or (len(object.maps) == 1 and not len(object.maps[0])): stream.write(repr(object)) return - cls = object.__class__ - stream.write(cls.__name__ + "(") - indent += len(cls.__name__) + 1 - for i, m in enumerate(object.maps): - if i == len(object.maps) - 1: - self._format(m, stream, indent, allowance + 1, context, level) - stream.write(")") - else: - self._format(m, stream, indent, 1, context, level) - stream.write(",\n" + " " * indent) + + stream.write(object.__class__.__name__ + "(") + self._format_items(object.maps, stream, indent, allowance, context, level) + stream.write(")") _dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map - def _pprint_deque(self, object, stream, indent, allowance, context, level): - if not len(object): - stream.write(repr(object)) - return - cls = object.__class__ - stream.write(cls.__name__ + "(") - indent += len(cls.__name__) + 1 + def _pprint_deque( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: + stream.write(object.__class__.__name__ + "(") + if object.maxlen is not None: + stream.write("maxlen=%d, " % object.maxlen) stream.write("[") - if object.maxlen is None: - self._format_items(object, stream, indent, allowance + 2, context, level) - stream.write("])") - else: - self._format_items(object, stream, indent, 2, context, level) - rml = self._repr(object.maxlen, context, level) - stream.write(f"],\n{' ' * indent}maxlen={rml})") + + self._format_items(object, stream, indent, allowance + 1, context, level) + stream.write("])") _dispatch[_collections.deque.__repr__] = _pprint_deque - def _pprint_user_dict(self, object, stream, indent, allowance, context, level): + def _pprint_user_dict( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: self._format(object.data, stream, indent, allowance, context, level - 1) _dispatch[_collections.UserDict.__repr__] = _pprint_user_dict - def _pprint_user_list(self, object, stream, indent, allowance, context, level): + def _pprint_user_list( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: self._format(object.data, stream, indent, allowance, context, level - 1) _dispatch[_collections.UserList.__repr__] = _pprint_user_list - def _pprint_user_string(self, object, stream, indent, allowance, context, level): + def _pprint_user_string( + self, + object: Any, + stream: IO[str], + indent: int, + allowance: int, + context: Set[int], + level: int, + ) -> None: self._format(object.data, stream, indent, allowance, context, level - 1) _dispatch[_collections.UserString.__repr__] = _pprint_user_string - def _safe_repr(self, object, context, maxlevels, level): - # Return triple (repr_string, isreadable, isrecursive). + def _safe_repr( + self, object: Any, context: Set[int], maxlevels: Optional[int], level: int + ) -> str: typ = type(object) if typ in _builtin_scalars: - return repr(object), True, False + return repr(object) r = getattr(typ, "__repr__", None) if issubclass(typ, int) and r is int.__repr__: if self._underscore_numbers: - return f"{object:_d}", True, False + return f"{object:_d}" else: - return repr(object), True, False + return repr(object) if issubclass(typ, dict) and r is dict.__repr__: if not object: - return "{}", True, False + return "{}" objid = id(object) if maxlevels and level >= maxlevels: - return "{...}", False, objid in context + return "{...}" if objid in context: - return _recursion(object), False, True - context[objid] = 1 - readable = True - recursive = False + return _recursion(object) + context.add(objid) components: List[str] = [] append = components.append level += 1 @@ -554,61 +639,51 @@ class PrettyPrinter: else: items = object.items() for k, v in items: - krepr, kreadable, krecur = self.format(k, context, maxlevels, level) - vrepr, vreadable, vrecur = self.format(v, context, maxlevels, level) + krepr = self.format(k, context, maxlevels, level) + vrepr = self.format(v, context, maxlevels, level) append(f"{krepr}: {vrepr}") - readable = readable and kreadable and vreadable - if krecur or vrecur: - recursive = True - del context[objid] - return "{%s}" % ", ".join(components), readable, recursive + context.remove(objid) + return "{%s}" % ", ".join(components) if (issubclass(typ, list) and r is list.__repr__) or ( issubclass(typ, tuple) and r is tuple.__repr__ ): if issubclass(typ, list): if not object: - return "[]", True, False + return "[]" format = "[%s]" elif len(object) == 1: format = "(%s,)" else: if not object: - return "()", True, False + return "()" format = "(%s)" objid = id(object) if maxlevels and level >= maxlevels: - return format % "...", False, objid in context + return format % "..." if objid in context: - return _recursion(object), False, True - context[objid] = 1 - readable = True - recursive = False + return _recursion(object) + context.add(objid) components = [] append = components.append level += 1 for o in object: - orepr, oreadable, orecur = self.format(o, context, maxlevels, level) + orepr = self.format(o, context, maxlevels, level) append(orepr) - if not oreadable: - readable = False - if orecur: - recursive = True - del context[objid] - return format % ", ".join(components), readable, recursive + context.remove(objid) + return format % ", ".join(components) - rep = repr(object) - return rep, (rep and not rep.startswith("<")), False + return repr(object) _builtin_scalars = frozenset({str, bytes, bytearray, float, complex, bool, type(None)}) -def _recursion(object): +def _recursion(object: Any) -> str: return f"" -def _wrap_bytes_repr(object, width, allowance): +def _wrap_bytes_repr(object: Any, width: int, allowance: int) -> Iterator[str]: current = b"" last = len(object) // 4 * 4 for i in range(0, len(object), 4): diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 4d9fd114b..fe8904e15 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -230,6 +230,8 @@ def assertrepr_compare( if not explanation: return None + if explanation[0] != "": + explanation = [""] + explanation return [summary] + explanation @@ -318,18 +320,6 @@ def _diff_text(left: str, right: str, verbose: int = 0) -> List[str]: return explanation -def _surrounding_parens_on_own_lines(lines: List[str]) -> None: - """Move opening/closing parenthesis/bracket to own lines.""" - opening = lines[0][:1] - if opening in ["(", "[", "{"]: - lines[0] = " " + lines[0][1:] - lines[:] = [opening] + lines - closing = lines[-1][-1:] - if closing in [")", "]", "}"]: - lines[-1] = lines[-1][:-1] + "," - lines[:] = lines + [closing] - - def _compare_eq_iterable( left: Iterable[Any], right: Iterable[Any], @@ -341,22 +331,10 @@ def _compare_eq_iterable( # dynamic import to speedup pytest import difflib - left_formatting = pprint.pformat(left).splitlines() - right_formatting = pprint.pformat(right).splitlines() + left_formatting = PrettyPrinter().pformat(left).splitlines() + right_formatting = PrettyPrinter().pformat(right).splitlines() - # Re-format for different output lengths. - lines_left = len(left_formatting) - lines_right = len(right_formatting) - if lines_left != lines_right: - printer = PrettyPrinter() - left_formatting = printer.pformat(left).splitlines() - right_formatting = printer.pformat(right).splitlines() - - if lines_left > 1 or lines_right > 1: - _surrounding_parens_on_own_lines(left_formatting) - _surrounding_parens_on_own_lines(right_formatting) - - explanation = ["Full diff:"] + explanation = ["", "Full diff:"] # "right" is the expected base against which we compare "left", # see https://github.com/pytest-dev/pytest/issues/3333 explanation.extend( diff --git a/src/_pytest/faulthandler.py b/src/_pytest/faulthandler.py index a9418f34e..1bccd18c6 100644 --- a/src/_pytest/faulthandler.py +++ b/src/_pytest/faulthandler.py @@ -9,8 +9,8 @@ from _pytest.nodes import Item from _pytest.stash import StashKey +fault_handler_original_stderr_fd_key = StashKey[int]() fault_handler_stderr_fd_key = StashKey[int]() -fault_handler_originally_enabled_key = StashKey[bool]() def pytest_addoption(parser: Parser) -> None: @@ -24,8 +24,15 @@ def pytest_addoption(parser: Parser) -> None: def pytest_configure(config: Config) -> None: import faulthandler - config.stash[fault_handler_stderr_fd_key] = os.dup(get_stderr_fileno()) - config.stash[fault_handler_originally_enabled_key] = faulthandler.is_enabled() + # at teardown we want to restore the original faulthandler fileno + # but faulthandler has no api to return the original fileno + # so here we stash the stderr fileno to be used at teardown + # sys.stderr and sys.__stderr__ may be closed or patched during the session + # so we can't rely on their values being good at that point (#11572). + stderr_fileno = get_stderr_fileno() + if faulthandler.is_enabled(): + config.stash[fault_handler_original_stderr_fd_key] = stderr_fileno + config.stash[fault_handler_stderr_fd_key] = os.dup(stderr_fileno) faulthandler.enable(file=config.stash[fault_handler_stderr_fd_key]) @@ -37,9 +44,10 @@ def pytest_unconfigure(config: Config) -> None: if fault_handler_stderr_fd_key in config.stash: os.close(config.stash[fault_handler_stderr_fd_key]) del config.stash[fault_handler_stderr_fd_key] - if config.stash.get(fault_handler_originally_enabled_key, False): - # Re-enable the faulthandler if it was originally enabled. - faulthandler.enable(file=get_stderr_fileno()) + # Re-enable the faulthandler if it was originally enabled. + if fault_handler_original_stderr_fd_key in config.stash: + faulthandler.enable(config.stash[fault_handler_original_stderr_fd_key]) + del config.stash[fault_handler_original_stderr_fd_key] def get_stderr_fileno() -> int: diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index bacca4b2a..246a4ab6e 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -564,6 +564,22 @@ class LogCaptureFixture: self.handler.setLevel(handler_orig_level) logging.disable(original_disable_level) + @contextmanager + def filtering(self, filter_: logging.Filter) -> Generator[None, None, None]: + """Context manager that temporarily adds the given filter to the caplog's + :meth:`handler` for the 'with' statement block, and removes that filter at the + end of the block. + + :param filter_: A custom :class:`logging.Filter` object. + + .. versionadded:: 7.5 + """ + self.handler.addFilter(filter_) + try: + yield + finally: + self.handler.removeFilter(filter_) + @fixture def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture, None, None]: diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 99d33954d..f93e9c94a 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -121,13 +121,18 @@ def pytest_configure(config: Config) -> None: class LsofFdLeakChecker: def get_open_files(self) -> List[Tuple[str, str]]: + if sys.version_info >= (3, 11): + # New in Python 3.11, ignores utf-8 mode + encoding = locale.getencoding() + else: + encoding = locale.getpreferredencoding(False) out = subprocess.run( ("lsof", "-Ffn0", "-p", str(os.getpid())), stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, check=True, text=True, - encoding=locale.getpreferredencoding(False), + encoding=encoding, ).stdout def isopen(line: str) -> bool: diff --git a/testing/conftest.py b/testing/conftest.py index 6a50e810f..bcb05339b 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -31,6 +31,17 @@ def set_column_width(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("COLUMNS", "80") +@pytest.fixture(autouse=True) +def reset_colors(monkeypatch: pytest.MonkeyPatch) -> None: + """ + Reset all color-related variables to prevent them from affecting internal pytest output + in tests that depend on it. + """ + monkeypatch.delenv("PY_COLORS", raising=False) + monkeypatch.delenv("NO_COLOR", raising=False) + monkeypatch.delenv("FORCE_COLOR", raising=False) + + @pytest.hookimpl(wrapper=True, tryfirst=True) def pytest_collection_modifyitems(items) -> Generator[None, None, None]: """Prefer faster tests. diff --git a/testing/io/test_pprint.py b/testing/io/test_pprint.py index 8c15740bd..3432c63f6 100644 --- a/testing/io/test_pprint.py +++ b/testing/io/test_pprint.py @@ -40,15 +40,19 @@ class DataclassWithTwoItems: pytest.param( DataclassWithOneItem(foo="bar"), """ - DataclassWithOneItem(foo='bar') + DataclassWithOneItem( + foo='bar', + ) """, id="dataclass-one-item", ), pytest.param( DataclassWithTwoItems(foo="foo", bar="bar"), """ - DataclassWithTwoItems(foo='foo', - bar='bar') + DataclassWithTwoItems( + foo='foo', + bar='bar', + ) """, id="dataclass-two-items", ), @@ -60,15 +64,19 @@ class DataclassWithTwoItems: pytest.param( {"one": 1}, """ - {'one': 1} + { + 'one': 1, + } """, id="dict-one-item", ), pytest.param( {"one": 1, "two": 2}, """ - {'one': 1, - 'two': 2} + { + 'one': 1, + 'two': 2, + } """, id="dict-two-items", ), @@ -76,18 +84,19 @@ class DataclassWithTwoItems: pytest.param( OrderedDict({"one": 1}), """ - OrderedDict([('one', - 1)]) + OrderedDict({ + 'one': 1, + }) """, id="ordereddict-one-item", ), pytest.param( OrderedDict({"one": 1, "two": 2}), """ - OrderedDict([('one', - 1), - ('two', - 2)]) + OrderedDict({ + 'one': 1, + 'two': 2, + }) """, id="ordereddict-two-items", ), @@ -99,15 +108,19 @@ class DataclassWithTwoItems: pytest.param( [1], """ - [1] + [ + 1, + ] """, id="list-one-item", ), pytest.param( [1, 2], """ - [1, - 2] + [ + 1, + 2, + ] """, id="list-two-items", ), @@ -119,15 +132,19 @@ class DataclassWithTwoItems: pytest.param( (1,), """ - (1,) + ( + 1, + ) """, id="tuple-one-item", ), pytest.param( (1, 2), """ - (1, - 2) + ( + 1, + 2, + ) """, id="tuple-two-items", ), @@ -139,15 +156,19 @@ class DataclassWithTwoItems: pytest.param( {1}, """ - {1} + { + 1, + } """, id="set-one-item", ), pytest.param( {1, 2}, """ - {1, - 2} + { + 1, + 2, + } """, id="set-two-items", ), @@ -159,15 +180,19 @@ class DataclassWithTwoItems: pytest.param( MappingProxyType({"one": 1}), """ - mappingproxy({'one': 1}) + mappingproxy({ + 'one': 1, + }) """, id="mappingproxy-one-item", ), pytest.param( MappingProxyType({"one": 1, "two": 2}), """ - mappingproxy({'one': 1, - 'two': 2}) + mappingproxy({ + 'one': 1, + 'two': 2, + }) """, id="mappingproxy-two-items", ), @@ -179,15 +204,19 @@ class DataclassWithTwoItems: pytest.param( SimpleNamespace(one=1), """ - namespace(one=1) + namespace( + one=1, + ) """, id="simplenamespace-one-item", ), pytest.param( SimpleNamespace(one=1, two=2), """ - namespace(one=1, - two=2) + namespace( + one=1, + two=2, + ) """, id="simplenamespace-two-items", ), @@ -197,17 +226,19 @@ class DataclassWithTwoItems: pytest.param( defaultdict(str, {"one": "1"}), """ - defaultdict(, - {'one': '1'}) + defaultdict(, { + 'one': '1', + }) """, id="defaultdict-one-item", ), pytest.param( defaultdict(str, {"one": "1", "two": "2"}), """ - defaultdict(, - {'one': '1', - 'two': '2'}) + defaultdict(, { + 'one': '1', + 'two': '2', + }) """, id="defaultdict-two-items", ), @@ -219,15 +250,19 @@ class DataclassWithTwoItems: pytest.param( Counter("1"), """ - Counter({'1': 1}) + Counter({ + '1': 1, + }) """, id="counter-one-item", ), pytest.param( Counter("121"), """ - Counter({'1': 2, - '2': 1}) + Counter({ + '1': 2, + '2': 1, + }) """, id="counter-two-items", ), @@ -235,16 +270,26 @@ class DataclassWithTwoItems: pytest.param( ChainMap({"one": 1, "two": 2}), """ - ChainMap({'one': 1, - 'two': 2}) + ChainMap( + { + 'one': 1, + 'two': 2, + }, + ) """, id="chainmap-one-item", ), pytest.param( ChainMap({"one": 1}, {"two": 2}), """ - ChainMap({'one': 1}, - {'two': 2}) + ChainMap( + { + 'one': 1, + }, + { + 'two': 2, + }, + ) """, id="chainmap-two-items", ), @@ -256,24 +301,29 @@ class DataclassWithTwoItems: pytest.param( deque([1]), """ - deque([1]) + deque([ + 1, + ]) """, id="deque-one-item", ), pytest.param( deque([1, 2]), """ - deque([1, - 2]) + deque([ + 1, + 2, + ]) """, id="deque-two-items", ), pytest.param( deque([1, 2], maxlen=3), """ - deque([1, - 2], - maxlen=3) + deque(maxlen=3, [ + 1, + 2, + ]) """, id="deque-maxlen", ), @@ -293,34 +343,60 @@ class DataclassWithTwoItems: "tuple": (1, 2), }, """ - {'chainmap': ChainMap({'one': 1}, - {'two': 2}), - 'counter': Counter({'2': 2, - '1': 1}), - 'dataclass': DataclassWithTwoItems(foo='foo', - bar='bar'), - 'defaultdict': defaultdict(, - {'one': '1', - 'two': '2'}), - 'deque': deque([1, - 2], - maxlen=3), - 'dict': {'one': 1, - 'two': 2}, - 'list': [1, - 2], - 'mappingproxy': mappingproxy({'one': 1, - 'two': 2}), - 'ordereddict': OrderedDict([('one', - 1), - ('two', - 2)]), - 'set': {1, - 2}, - 'simplenamespace': namespace(one=1, - two=2), - 'tuple': (1, - 2)} + { + 'chainmap': ChainMap( + { + 'one': 1, + }, + { + 'two': 2, + }, + ), + 'counter': Counter({ + '2': 2, + '1': 1, + }), + 'dataclass': DataclassWithTwoItems( + foo='foo', + bar='bar', + ), + 'defaultdict': defaultdict(, { + 'one': '1', + 'two': '2', + }), + 'deque': deque(maxlen=3, [ + 1, + 2, + ]), + 'dict': { + 'one': 1, + 'two': 2, + }, + 'list': [ + 1, + 2, + ], + 'mappingproxy': mappingproxy({ + 'one': 1, + 'two': 2, + }), + 'ordereddict': OrderedDict({ + 'one': 1, + 'two': 2, + }), + 'set': { + 1, + 2, + }, + 'simplenamespace': namespace( + one=1, + two=2, + ), + 'tuple': ( + 1, + 2, + ), + } """, id="deep-example", ), diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index 753cf5fcd..f4912aecc 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -144,7 +144,7 @@ def test_change_level_undos_handler_level(pytester: Pytester) -> None: result.assert_outcomes(passed=3) -def test_with_statement(caplog: pytest.LogCaptureFixture) -> None: +def test_with_statement_at_level(caplog: pytest.LogCaptureFixture) -> None: with caplog.at_level(logging.INFO): logger.debug("handler DEBUG level") logger.info("handler INFO level") @@ -159,7 +159,9 @@ def test_with_statement(caplog: pytest.LogCaptureFixture) -> None: assert "CRITICAL" in caplog.text -def test_with_statement_logging_disabled(caplog: pytest.LogCaptureFixture) -> None: +def test_with_statement_at_level_logging_disabled( + caplog: pytest.LogCaptureFixture, +) -> None: logging.disable(logging.CRITICAL) assert logging.root.manager.disable == logging.CRITICAL with caplog.at_level(logging.WARNING): @@ -185,6 +187,22 @@ def test_with_statement_logging_disabled(caplog: pytest.LogCaptureFixture) -> No assert logging.root.manager.disable == logging.CRITICAL +def test_with_statement_filtering(caplog: pytest.LogCaptureFixture) -> None: + class TestFilter(logging.Filter): + def filter(self, record: logging.LogRecord) -> bool: + record.msg = "filtered handler call" + return True + + with caplog.at_level(logging.INFO): + with caplog.filtering(TestFilter()): + logger.info("handler call") + logger.info("handler call") + + filtered_tuple, unfiltered_tuple = caplog.record_tuples + assert filtered_tuple == ("test_fixture", 20, "filtered handler call") + assert unfiltered_tuple == ("test_fixture", 20, "handler call") + + @pytest.mark.parametrize( "level_str,expected_disable_level", [ diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index fcbe3f168..1166d14c9 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,13 +1,13 @@ -anyio[curio,trio]==4.0.0 +anyio[curio,trio]==4.1.0 django==4.2.7 -pytest-asyncio==0.21.1 -pytest-bdd==7.0.0 +pytest-asyncio==0.23.1 +pytest-bdd==7.0.1 pytest-cov==4.1.0 pytest-django==4.7.0 pytest-flakes==4.0.5 pytest-html==4.1.1 pytest-mock==3.12.0 -pytest-rerunfailures==12.0 +pytest-rerunfailures==13.0 pytest-sugar==0.9.7 pytest-trio==0.7.0 pytest-twisted==1.14.0 diff --git a/testing/python/approx.py b/testing/python/approx.py index 6ad411a3e..3b87e58f9 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -99,6 +99,7 @@ class TestApprox: 2.0, 1.0, [ + "", " comparison failed", f" Obtained: {SOME_FLOAT}", f" Expected: {SOME_FLOAT} ± {SOME_FLOAT}", @@ -113,6 +114,7 @@ class TestApprox: "c": 3000000.0, }, [ + r"", r" comparison failed. Mismatched elements: 2 / 3:", rf" Max absolute difference: {SOME_FLOAT}", rf" Max relative difference: {SOME_FLOAT}", @@ -130,6 +132,7 @@ class TestApprox: "c": None, }, [ + r"", r" comparison failed. Mismatched elements: 2 / 3:", r" Max absolute difference: -inf", r" Max relative difference: -inf", @@ -143,6 +146,7 @@ class TestApprox: [1.0, 2.0, 3.0, 4.0], [1.0, 3.0, 3.0, 5.0], [ + r"", r" comparison failed. Mismatched elements: 2 / 4:", rf" Max absolute difference: {SOME_FLOAT}", rf" Max relative difference: {SOME_FLOAT}", @@ -156,6 +160,7 @@ class TestApprox: (1, 2.2, 4), (1, 3.2, 4), [ + r"", r" comparison failed. Mismatched elements: 1 / 3:", rf" Max absolute difference: {SOME_FLOAT}", rf" Max relative difference: {SOME_FLOAT}", @@ -169,6 +174,7 @@ class TestApprox: [0.0], [1.0], [ + r"", r" comparison failed. Mismatched elements: 1 / 1:", rf" Max absolute difference: {SOME_FLOAT}", r" Max relative difference: inf", @@ -187,6 +193,7 @@ class TestApprox: a, b, [ + r"", r" comparison failed. Mismatched elements: 1 / 20:", rf" Max absolute difference: {SOME_FLOAT}", rf" Max relative difference: {SOME_FLOAT}", @@ -209,6 +216,7 @@ class TestApprox: ] ), [ + r"", r" comparison failed. Mismatched elements: 3 / 8:", rf" Max absolute difference: {SOME_FLOAT}", rf" Max relative difference: {SOME_FLOAT}", @@ -224,6 +232,7 @@ class TestApprox: np.array([0.0]), np.array([1.0]), [ + r"", r" comparison failed. Mismatched elements: 1 / 1:", rf" Max absolute difference: {SOME_FLOAT}", r" Max relative difference: inf", @@ -241,6 +250,7 @@ class TestApprox: message = "\n".join(str(e.value).split("\n")[1:]) assert message == "\n".join( [ + " ", " Impossible to compare arrays with different shapes.", " Shapes: (2, 1) and (2, 2)", ] @@ -251,6 +261,7 @@ class TestApprox: message = "\n".join(str(e.value).split("\n")[1:]) assert message == "\n".join( [ + " ", " Impossible to compare lists with different sizes.", " Lengths: 2 and 3", ] @@ -264,6 +275,7 @@ class TestApprox: 2.0, 1.0, [ + "", " comparison failed", f" Obtained: {SOME_FLOAT}", f" Expected: {SOME_FLOAT} ± {SOME_FLOAT}", @@ -277,15 +289,15 @@ class TestApprox: a, b, [ - r" comparison failed. Mismatched elements: 20 / 20:", - rf" Max absolute difference: {SOME_FLOAT}", - rf" Max relative difference: {SOME_FLOAT}", - r" Index \| Obtained\s+\| Expected", - rf" \(0,\)\s+\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", - rf" \(1,\)\s+\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", - rf" \(2,\)\s+\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}...", - "", - rf"\s*...Full output truncated \({SOME_INT} lines hidden\), use '-vv' to show", + r"^ $", + r"^ comparison failed. Mismatched elements: 20 / 20:$", + rf"^ Max absolute difference: {SOME_FLOAT}$", + rf"^ Max relative difference: {SOME_FLOAT}$", + r"^ Index \| Obtained\s+\| Expected\s+$", + rf"^ \(0,\)\s+\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}e-{SOME_INT}$", + rf"^ \(1,\)\s+\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}e-{SOME_INT}\.\.\.$", + "^ $", + rf"^ ...Full output truncated \({SOME_INT} lines hidden\), use '-vv' to show$", ], verbosity_level=0, ) @@ -294,6 +306,7 @@ class TestApprox: a, b, [ + r" ", r" comparison failed. Mismatched elements: 20 / 20:", rf" Max absolute difference: {SOME_FLOAT}", rf" Max relative difference: {SOME_FLOAT}", @@ -652,6 +665,7 @@ class TestApprox: {"foo": 42.0}, {"foo": 0.0}, [ + r"", r" comparison failed. Mismatched elements: 1 / 1:", rf" Max absolute difference: {SOME_FLOAT}", r" Max relative difference: inf", diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 7c8c01556..4d751f8db 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -392,6 +392,7 @@ class TestAssert_reprcompare: def test_text_diff(self) -> None: assert callequal("spam", "eggs") == [ "'spam' == 'eggs'", + "", "- eggs", "+ spam", ] @@ -399,7 +400,7 @@ class TestAssert_reprcompare: def test_text_skipping(self) -> None: lines = callequal("a" * 50 + "spam", "a" * 50 + "eggs") assert lines is not None - assert "Skipping" in lines[1] + assert "Skipping" in lines[2] for line in lines: assert "a" * 50 not in line @@ -423,6 +424,7 @@ class TestAssert_reprcompare: assert diff == [ "b'spam' == b'eggs'", + "", "At index 0 diff: b's' != b'e'", "Use -v to get more diff", ] @@ -432,7 +434,9 @@ class TestAssert_reprcompare: diff = callequal(b"spam", b"eggs", verbose=1) assert diff == [ "b'spam' == b'eggs'", + "", "At index 0 diff: b's' != b'e'", + "", "Full diff:", "- b'eggs'", "+ b'spam'", @@ -451,11 +455,14 @@ class TestAssert_reprcompare: [0, 2], """ Full diff: - - [0, 2] + [ + 0, + - 2, ? ^ - + [0, 1] + + 1, ? ^ - """, + ] + """, id="lists", ), pytest.param( @@ -463,10 +470,12 @@ class TestAssert_reprcompare: {0: 2}, """ Full diff: - - {0: 2} - ? ^ - + {0: 1} - ? ^ + { + - 0: 2, + ? ^ + + 0: 1, + ? ^ + } """, id="dicts", ), @@ -475,10 +484,13 @@ class TestAssert_reprcompare: {0, 2}, """ Full diff: - - {0, 2} + { + 0, + - 2, ? ^ - + {0, 1} + + 1, ? ^ + } """, id="sets", ), @@ -501,6 +513,7 @@ class TestAssert_reprcompare: expl = callequal([1, 2], [10, 2], verbose=-1) assert expl == [ "[1, 2] == [10, 2]", + "", "At index 0 diff: 1 != 10", "Use -v to get more diff", ] @@ -539,26 +552,30 @@ class TestAssert_reprcompare: diff = callequal(l1, l2, verbose=True) assert diff == [ "['a', 'b', 'c'] == ['a', 'b', 'c...dddddddddddd']", + "", "Right contains one more item: '" + long_d + "'", + "", "Full diff:", " [", - " 'a',", - " 'b',", - " 'c',", - "- '" + long_d + "',", + " 'a',", + " 'b',", + " 'c',", + "- '" + long_d + "',", " ]", ] diff = callequal(l2, l1, verbose=True) assert diff == [ "['a', 'b', 'c...dddddddddddd'] == ['a', 'b', 'c']", + "", "Left contains one more item: '" + long_d + "'", + "", "Full diff:", " [", - " 'a',", - " 'b',", - " 'c',", - "+ '" + long_d + "',", + " 'a',", + " 'b',", + " 'c',", + "+ '" + long_d + "',", " ]", ] @@ -571,13 +588,15 @@ class TestAssert_reprcompare: diff = callequal(l1, l2, verbose=True) assert diff == [ "['aaaaaaaaaaa...cccccccccccc'] == ['bbbbbbbbbbb...aaaaaaaaaaaa']", + "", "At index 0 diff: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' != 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'", + "", "Full diff:", " [", - "+ 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',", - " 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',", - " 'cccccccccccccccccccccccccccccc',", - "- 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',", + "+ 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',", + " 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',", + " 'cccccccccccccccccccccccccccccc',", + "- 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',", " ]", ] @@ -588,19 +607,21 @@ class TestAssert_reprcompare: diff = callequal(l1, l2, verbose=True) assert diff == [ "['a', 'aaaaaa...aaaaaaa', ...] == ['should not get wrapped']", + "", "At index 0 diff: 'a' != 'should not get wrapped'", "Left contains 7 more items, first extra item: 'aaaaaaaaaa'", + "", "Full diff:", " [", - "- 'should not get wrapped',", - "+ 'a',", - "+ 'aaaaaaaaaa',", - "+ 'aaaaaaaaaa',", - "+ 'aaaaaaaaaa',", - "+ 'aaaaaaaaaa',", - "+ 'aaaaaaaaaa',", - "+ 'aaaaaaaaaa',", - "+ 'aaaaaaaaaa',", + "- 'should not get wrapped',", + "+ 'a',", + "+ 'aaaaaaaaaa',", + "+ 'aaaaaaaaaa',", + "+ 'aaaaaaaaaa',", + "+ 'aaaaaaaaaa',", + "+ 'aaaaaaaaaa',", + "+ 'aaaaaaaaaa',", + "+ 'aaaaaaaaaa',", " ]", ] @@ -611,31 +632,45 @@ class TestAssert_reprcompare: diff = callequal(d1, d2, verbose=True) assert diff == [ "{'common': 1,...1, 'env2': 2}} == {'common': 1,...: {'env1': 1}}", + "", "Omitting 1 identical items, use -vv to show", "Differing items:", "{'env': {'env1': 1, 'env2': 2}} != {'env': {'env1': 1}}", + "", "Full diff:", - "- {'common': 1, 'env': {'env1': 1}}", - "+ {'common': 1, 'env': {'env1': 1, 'env2': 2}}", - "? +++++++++++", + " {", + " 'common': 1,", + " 'env': {", + " 'env1': 1,", + "+ 'env2': 2,", + " },", + " }", ] long_a = "a" * 80 - sub = {"long_a": long_a, "sub1": {"long_a": "substring that gets wrapped " * 2}} + sub = {"long_a": long_a, "sub1": {"long_a": "substring that gets wrapped " * 3}} d1 = {"env": {"sub": sub}} d2 = {"env": {"sub": sub}, "new": 1} diff = callequal(d1, d2, verbose=True) assert diff == [ "{'env': {'sub... wrapped '}}}} == {'env': {'sub...}}}, 'new': 1}", + "", "Omitting 1 identical items, use -vv to show", "Right contains 1 more item:", "{'new': 1}", + "", "Full diff:", " {", - " 'env': {'sub': {'long_a': '" + long_a + "',", - " 'sub1': {'long_a': 'substring that gets wrapped substring '", - " 'that gets wrapped '}}},", - "- 'new': 1,", + " 'env': {", + " 'sub': {", + f" 'long_a': '{long_a}',", + " 'sub1': {", + " 'long_a': 'substring that gets wrapped substring that gets wrapped '", + " 'substring that gets wrapped ',", + " },", + " },", + " },", + "- 'new': 1,", " }", ] @@ -647,7 +682,7 @@ class TestAssert_reprcompare: def test_dict_omitting(self) -> None: lines = callequal({"a": 0, "b": 1}, {"a": 1, "b": 1}) assert lines is not None - assert lines[1].startswith("Omitting 1 identical item") + assert lines[2].startswith("Omitting 1 identical item") assert "Common items" not in lines for line in lines[1:]: assert "b" not in line @@ -656,60 +691,109 @@ class TestAssert_reprcompare: """Ensure differing items are visible for verbosity=1 (#1512).""" lines = callequal({"a": 0, "b": 1}, {"a": 1, "b": 1}, verbose=1) assert lines is not None - assert lines[1].startswith("Omitting 1 identical item") - assert lines[2].startswith("Differing items") - assert lines[3] == "{'a': 0} != {'a': 1}" + assert lines[1] == "" + assert lines[2].startswith("Omitting 1 identical item") + assert lines[3].startswith("Differing items") + assert lines[4] == "{'a': 0} != {'a': 1}" assert "Common items" not in lines def test_dict_omitting_with_verbosity_2(self) -> None: lines = callequal({"a": 0, "b": 1}, {"a": 1, "b": 1}, verbose=2) assert lines is not None - assert lines[1].startswith("Common items:") - assert "Omitting" not in lines[1] - assert lines[2] == "{'b': 1}" + assert lines[2].startswith("Common items:") + assert "Omitting" not in lines[2] + assert lines[3] == "{'b': 1}" def test_dict_different_items(self) -> None: lines = callequal({"a": 0}, {"b": 1, "c": 2}, verbose=2) assert lines == [ "{'a': 0} == {'b': 1, 'c': 2}", + "", "Left contains 1 more item:", "{'a': 0}", "Right contains 2 more items:", "{'b': 1, 'c': 2}", + "", "Full diff:", - "- {'b': 1, 'c': 2}", - "+ {'a': 0}", + " {", + "- 'b': 1,", + "? ^ ^", + "+ 'a': 0,", + "? ^ ^", + "- 'c': 2,", + " }", ] lines = callequal({"b": 1, "c": 2}, {"a": 0}, verbose=2) assert lines == [ "{'b': 1, 'c': 2} == {'a': 0}", + "", "Left contains 2 more items:", "{'b': 1, 'c': 2}", "Right contains 1 more item:", "{'a': 0}", + "", "Full diff:", - "- {'a': 0}", - "+ {'b': 1, 'c': 2}", + " {", + "- 'a': 0,", + "? ^ ^", + "+ 'b': 1,", + "? ^ ^", + "+ 'c': 2,", + " }", ] def test_sequence_different_items(self) -> None: lines = callequal((1, 2), (3, 4, 5), verbose=2) assert lines == [ "(1, 2) == (3, 4, 5)", + "", "At index 0 diff: 1 != 3", "Right contains one more item: 5", + "", "Full diff:", - "- (3, 4, 5)", - "+ (1, 2)", + " (", + "- 3,", + "? ^", + "+ 1,", + "? ^", + "- 4,", + "? ^", + "+ 2,", + "? ^", + "- 5,", + " )", ] lines = callequal((1, 2, 3), (4,), verbose=2) assert lines == [ "(1, 2, 3) == (4,)", + "", "At index 0 diff: 1 != 4", "Left contains 2 more items, first extra item: 2", + "", "Full diff:", - "- (4,)", - "+ (1, 2, 3)", + " (", + "- 4,", + "? ^", + "+ 1,", + "? ^", + "+ 2,", + "+ 3,", + " )", + ] + lines = callequal((1, 2, 3), (1, 20, 3), verbose=2) + assert lines == [ + "(1, 2, 3) == (1, 20, 3)", + "", + "At index 1 diff: 2 != 20", + "", + "Full diff:", + " (", + " 1,", + "- 20,", + "? -", + "+ 2,", + " 3,", + " )", ] def test_set(self) -> None: @@ -767,7 +851,7 @@ class TestAssert_reprcompare: assert expl is not None assert expl[0].startswith("{} == <[ValueError") assert "raised in repr" in expl[0] - assert expl[1:] == [ + assert expl[2:] == [ "(pytest_assertion plugin: representation of details failed:" " {}:{}: ValueError: 42.".format( __file__, A.__repr__.__code__.co_firstlineno + 1 @@ -793,6 +877,7 @@ class TestAssert_reprcompare: def test_unicode(self) -> None: assert callequal("£€", "£") == [ "'£€' == '£'", + "", "- £", "+ £€", ] @@ -808,7 +893,7 @@ class TestAssert_reprcompare: return "\xff" expl = callequal(A(), "1") - assert expl == ["ÿ == '1'", "- 1"] + assert expl == ["ÿ == '1'", "", "- 1"] def test_format_nonascii_explanation(self) -> None: assert util.format_explanation("λ") @@ -831,6 +916,7 @@ class TestAssert_reprcompare: expl = callequal(left, right) assert expl == [ r"'hyv\xe4' == 'hyva\u0308'", + "", f"- {str(right)}", f"+ {str(left)}", ] @@ -838,6 +924,7 @@ class TestAssert_reprcompare: expl = callequal(left, right, verbose=2) assert expl == [ r"'hyv\xe4' == 'hyva\u0308'", + "", f"- {str(right)}", f"+ {str(left)}", ] @@ -1126,6 +1213,7 @@ class TestAssert_reprcompare_namedtuple: # Because the types are different, uses the generic sequence matcher. assert lines == [ "NT1(a=1, b='b') == NT2(a=2, b='b')", + "", "At index 0 diff: 1 != 2", "Use -v to get more diff", ] @@ -1313,7 +1401,7 @@ class TestTruncateExplanation: line_count = 7 line_len = 100 - expected_truncated_lines = 1 + expected_truncated_lines = 2 pytester.makepyfile( r""" def test_many_lines(): @@ -1333,8 +1421,7 @@ class TestTruncateExplanation: [ "*+ 1*", "*+ 3*", - "*+ 5*", - "*truncated (%d line hidden)*use*-vv*" % expected_truncated_lines, + "*truncated (%d lines hidden)*use*-vv*" % expected_truncated_lines, ] ) @@ -1377,6 +1464,7 @@ def test_rewritten(pytester: Pytester) -> None: def test_reprcompare_notin() -> None: assert callop("not in", "foo", "aaafoobbb") == [ "'foo' not in 'aaafoobbb'", + "", "'foo' is contained here:", " aaafoobbb", "? +++", @@ -1386,6 +1474,7 @@ def test_reprcompare_notin() -> None: def test_reprcompare_whitespaces() -> None: assert callequal("\r\n", "\n") == [ r"'\r\n' == '\n'", + "", r"Strings contain only whitespace, escaping them using repr()", r"- '\n'", r"+ '\r\n'", @@ -1844,8 +1933,8 @@ def test_reprcompare_verbose_long() -> None: assert [0, 1] == [0, 2] """, [ - "{bold}{red}E {light-red}- [0, 2]{hl-reset}{endline}{reset}", - "{bold}{red}E {light-green}+ [0, 1]{hl-reset}{endline}{reset}", + "{bold}{red}E {light-red}- 2,{hl-reset}{endline}{reset}", + "{bold}{red}E {light-green}+ 1,{hl-reset}{endline}{reset}", ], ), ( @@ -1857,8 +1946,8 @@ def test_reprcompare_verbose_long() -> None: """, [ "{bold}{red}E {light-gray} {hl-reset} {{{endline}{reset}", - "{bold}{red}E {light-gray} {hl-reset} 'number-is-1': 1,{endline}{reset}", - "{bold}{red}E {light-green}+ 'number-is-5': 5,{hl-reset}{endline}{reset}", + "{bold}{red}E {light-gray} {hl-reset} 'number-is-1': 1,{endline}{reset}", + "{bold}{red}E {light-green}+ 'number-is-5': 5,{hl-reset}{endline}{reset}", ], ), ), @@ -1917,14 +2006,32 @@ def test_fine_grained_assertion_verbosity(pytester: Pytester): f"{p.name} .FFF [100%]", "E At index 2 diff: 'grapes' != 'orange'", "E Full diff:", - "E - ['banana', 'apple', 'orange', 'melon', 'kiwi']", - "E ? ^ ^^", - "E + ['banana', 'apple', 'grapes', 'melon', 'kiwi']", - "E ? ^ ^ +", + "E [", + "E 'banana',", + "E 'apple',", + "E - 'orange',", + "E ? ^ ^^", + "E + 'grapes',", + "E ? ^ ^ +", + "E 'melon',", + "E 'kiwi',", + "E ]", "E Full diff:", - "E - {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}", - "E ? - - - - - - - -", - "E + {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4}", + "E {", + "E '0': 0,", + "E - '10': 10,", + "E ? - -", + "E + '1': 1,", + "E - '20': 20,", + "E ? - -", + "E + '2': 2,", + "E - '30': 30,", + "E ? - -", + "E + '3': 3,", + "E - '40': 40,", + "E ? - -", + "E + '4': 4,", + "E }", f"E AssertionError: assert 'hello world' in '{long_text}'", ] ) diff --git a/testing/test_config.py b/testing/test_config.py index 58671e6ed..900cccee8 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1959,16 +1959,6 @@ def test_invocation_args(pytester: Pytester) -> None: ], ) def test_config_blocked_default_plugins(pytester: Pytester, plugin: str) -> None: - if plugin == "debugging": - # Fixed in xdist (after 1.27.0). - # https://github.com/pytest-dev/pytest-xdist/pull/422 - try: - import xdist # noqa: F401 - except ImportError: - pass - else: - pytest.skip("does not work with xdist currently") - p = pytester.makepyfile("def test(): pass") result = pytester.runpytest(str(p), "-pno:%s" % plugin) diff --git a/testing/test_error_diffs.py b/testing/test_error_diffs.py index eb7812108..cad7a17c0 100644 --- a/testing/test_error_diffs.py +++ b/testing/test_error_diffs.py @@ -21,10 +21,14 @@ TESTCASES = [ E assert [1, 4, 3] == [1, 2, 3] E At index 1 diff: 4 != 2 E Full diff: - E - [1, 2, 3] + E [ + E 1, + E - 2, E ? ^ - E + [1, 4, 3] + E + 4, E ? ^ + E 3, + E ] """, id="Compare lists, one item differs", ), @@ -40,9 +44,11 @@ TESTCASES = [ E assert [1, 2, 3] == [1, 2] E Left contains one more item: 3 E Full diff: - E - [1, 2] - E + [1, 2, 3] - E ? +++ + E [ + E 1, + E 2, + E + 3, + E ] """, id="Compare lists, one extra item", ), @@ -59,9 +65,11 @@ TESTCASES = [ E At index 1 diff: 3 != 2 E Right contains one more item: 3 E Full diff: - E - [1, 2, 3] - E ? --- - E + [1, 3] + E [ + E 1, + E - 2, + E 3, + E ] """, id="Compare lists, one item missing", ), @@ -77,10 +85,14 @@ TESTCASES = [ E assert (1, 4, 3) == (1, 2, 3) E At index 1 diff: 4 != 2 E Full diff: - E - (1, 2, 3) + E ( + E 1, + E - 2, E ? ^ - E + (1, 4, 3) + E + 4, E ? ^ + E 3, + E ) """, id="Compare tuples", ), @@ -99,10 +111,12 @@ TESTCASES = [ E Extra items in the right set: E 2 E Full diff: - E - {1, 2, 3} - E ? ^ ^ - E + {1, 3, 4} - E ? ^ ^ + E { + E 1, + E - 2, + E 3, + E + 4, + E } """, id="Compare sets", ), @@ -123,10 +137,13 @@ TESTCASES = [ E Right contains 1 more item: E {2: 'eggs'} E Full diff: - E - {1: 'spam', 2: 'eggs'} - E ? ^ - E + {1: 'spam', 3: 'eggs'} - E ? ^ + E { + E 1: 'spam', + E - 2: 'eggs', + E ? ^ + E + 3: 'eggs', + E ? ^ + E } """, id="Compare dicts with differing keys", ), @@ -145,10 +162,11 @@ TESTCASES = [ E Differing items: E {2: 'eggs'} != {2: 'bacon'} E Full diff: - E - {1: 'spam', 2: 'bacon'} - E ? ^^^^^ - E + {1: 'spam', 2: 'eggs'} - E ? ^^^^ + E { + E 1: 'spam', + E - 2: 'bacon', + E + 2: 'eggs', + E } """, id="Compare dicts with differing values", ), @@ -169,10 +187,11 @@ TESTCASES = [ E Right contains 1 more item: E {3: 'bacon'} E Full diff: - E - {1: 'spam', 3: 'bacon'} - E ? ^ ^^^^^ - E + {1: 'spam', 2: 'eggs'} - E ? ^ ^^^^ + E { + E 1: 'spam', + E - 3: 'bacon', + E + 2: 'eggs', + E } """, id="Compare dicts with differing items", ), diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index b6df035aa..1b80883ee 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -290,10 +290,10 @@ class TestParser: def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None: - try: + if sys.version_info >= (3, 11): # New in Python 3.11, ignores utf-8 mode - encoding = locale.getencoding() # type: ignore[attr-defined] - except AttributeError: + encoding = locale.getencoding() + else: encoding = locale.getpreferredencoding(False) try: bash_version = subprocess.run( diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 1473d6e75..ead458d04 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1812,7 +1812,7 @@ def test_terminal_no_summary_warnings_header_once(pytester: Pytester) -> None: @pytest.fixture(scope="session") def tr() -> TerminalReporter: - config = _pytest.config._prepareconfig() + config = _pytest.config._prepareconfig([]) return TerminalReporter(config)