Merge branch 'main' into improve-high-scope-fixtures-teardown-issue-3806
This commit is contained in:
commit
4633fa50f6
|
@ -43,6 +43,7 @@ jobs:
|
||||||
"windows-py39",
|
"windows-py39",
|
||||||
"windows-py310",
|
"windows-py310",
|
||||||
"windows-py311",
|
"windows-py311",
|
||||||
|
"windows-py312",
|
||||||
|
|
||||||
"ubuntu-py37",
|
"ubuntu-py37",
|
||||||
"ubuntu-py37-pluggy",
|
"ubuntu-py37-pluggy",
|
||||||
|
@ -51,12 +52,13 @@ jobs:
|
||||||
"ubuntu-py39",
|
"ubuntu-py39",
|
||||||
"ubuntu-py310",
|
"ubuntu-py310",
|
||||||
"ubuntu-py311",
|
"ubuntu-py311",
|
||||||
|
"ubuntu-py312",
|
||||||
"ubuntu-pypy3",
|
"ubuntu-pypy3",
|
||||||
|
|
||||||
"macos-py37",
|
"macos-py37",
|
||||||
"macos-py38",
|
|
||||||
"macos-py39",
|
"macos-py39",
|
||||||
"macos-py310",
|
"macos-py310",
|
||||||
|
"macos-py312",
|
||||||
|
|
||||||
"docs",
|
"docs",
|
||||||
"doctesting",
|
"doctesting",
|
||||||
|
@ -86,9 +88,13 @@ jobs:
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
tox_env: "py310-xdist"
|
tox_env: "py310-xdist"
|
||||||
- name: "windows-py311"
|
- name: "windows-py311"
|
||||||
python: "3.11-dev"
|
python: "3.11"
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
tox_env: "py311"
|
tox_env: "py311"
|
||||||
|
- name: "windows-py312"
|
||||||
|
python: "3.12-dev"
|
||||||
|
os: windows-latest
|
||||||
|
tox_env: "py312"
|
||||||
|
|
||||||
- name: "ubuntu-py37"
|
- name: "ubuntu-py37"
|
||||||
python: "3.7"
|
python: "3.7"
|
||||||
|
@ -116,10 +122,15 @@ jobs:
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
tox_env: "py310-xdist"
|
tox_env: "py310-xdist"
|
||||||
- name: "ubuntu-py311"
|
- name: "ubuntu-py311"
|
||||||
python: "3.11-dev"
|
python: "3.11"
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
tox_env: "py311"
|
tox_env: "py311"
|
||||||
use_coverage: true
|
use_coverage: true
|
||||||
|
- name: "ubuntu-py312"
|
||||||
|
python: "3.12-dev"
|
||||||
|
os: ubuntu-latest
|
||||||
|
tox_env: "py312"
|
||||||
|
use_coverage: true
|
||||||
- name: "ubuntu-pypy3"
|
- name: "ubuntu-pypy3"
|
||||||
python: "pypy-3.7"
|
python: "pypy-3.7"
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
|
@ -129,19 +140,19 @@ jobs:
|
||||||
python: "3.7"
|
python: "3.7"
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
tox_env: "py37-xdist"
|
tox_env: "py37-xdist"
|
||||||
- name: "macos-py38"
|
|
||||||
python: "3.8"
|
|
||||||
os: macos-latest
|
|
||||||
tox_env: "py38-xdist"
|
|
||||||
use_coverage: true
|
|
||||||
- name: "macos-py39"
|
- name: "macos-py39"
|
||||||
python: "3.9"
|
python: "3.9"
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
tox_env: "py39-xdist"
|
tox_env: "py39-xdist"
|
||||||
|
use_coverage: true
|
||||||
- name: "macos-py310"
|
- name: "macos-py310"
|
||||||
python: "3.10"
|
python: "3.10"
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
tox_env: "py310-xdist"
|
tox_env: "py310-xdist"
|
||||||
|
- name: "macos-py312"
|
||||||
|
python: "3.12-dev"
|
||||||
|
os: macos-latest
|
||||||
|
tox_env: "py312-xdist"
|
||||||
|
|
||||||
- name: "plugins"
|
- name: "plugins"
|
||||||
python: "3.9"
|
python: "3.9"
|
||||||
|
@ -168,6 +179,7 @@ jobs:
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python }}
|
||||||
|
check-latest: ${{ endsWith(matrix.python, '-dev') }}
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -38,7 +38,7 @@ jobs:
|
||||||
run: python scripts/update-plugin-list.py
|
run: python scripts/update-plugin-list.py
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
uses: peter-evans/create-pull-request@5b4a9f6a9e2af26e5f02351490b90d01eb8ec1e5
|
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38
|
||||||
with:
|
with:
|
||||||
commit-message: '[automated] Update plugin list'
|
commit-message: '[automated] Update plugin list'
|
||||||
author: 'pytest bot <pytestbot@users.noreply.github.com>'
|
author: 'pytest bot <pytestbot@users.noreply.github.com>'
|
||||||
|
|
|
@ -5,7 +5,7 @@ repos:
|
||||||
- id: black
|
- id: black
|
||||||
args: [--safe, --quiet]
|
args: [--safe, --quiet]
|
||||||
- repo: https://github.com/asottile/blacken-docs
|
- repo: https://github.com/asottile/blacken-docs
|
||||||
rev: 1.13.0
|
rev: 1.14.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: blacken-docs
|
- id: blacken-docs
|
||||||
additional_dependencies: [black==23.1.0]
|
additional_dependencies: [black==23.1.0]
|
||||||
|
@ -21,7 +21,7 @@ repos:
|
||||||
exclude: _pytest/(debugging|hookspec).py
|
exclude: _pytest/(debugging|hookspec).py
|
||||||
language_version: python3
|
language_version: python3
|
||||||
- repo: https://github.com/PyCQA/autoflake
|
- repo: https://github.com/PyCQA/autoflake
|
||||||
rev: v2.1.1
|
rev: v2.2.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: autoflake
|
- id: autoflake
|
||||||
name: autoflake
|
name: autoflake
|
||||||
|
@ -37,26 +37,26 @@ repos:
|
||||||
- flake8-typing-imports==1.12.0
|
- flake8-typing-imports==1.12.0
|
||||||
- flake8-docstrings==1.5.0
|
- flake8-docstrings==1.5.0
|
||||||
- repo: https://github.com/asottile/reorder-python-imports
|
- repo: https://github.com/asottile/reorder-python-imports
|
||||||
rev: v3.9.0
|
rev: v3.10.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: reorder-python-imports
|
- id: reorder-python-imports
|
||||||
args: ['--application-directories=.:src', --py37-plus]
|
args: ['--application-directories=.:src', --py37-plus]
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v3.4.0
|
rev: v3.7.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [--py37-plus]
|
args: [--py37-plus]
|
||||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||||
rev: v2.2.0
|
rev: v2.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: setup-cfg-fmt
|
- id: setup-cfg-fmt
|
||||||
args: ["--max-py-version=3.11", "--include-version-classifiers"]
|
args: ["--max-py-version=3.12", "--include-version-classifiers"]
|
||||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||||
rev: v1.10.0
|
rev: v1.10.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: python-use-type-annotations
|
- id: python-use-type-annotations
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
rev: v1.3.0
|
rev: v1.4.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
files: ^(src/|testing/)
|
files: ^(src/|testing/)
|
||||||
|
|
2
AUTHORS
2
AUTHORS
|
@ -129,6 +129,7 @@ Eric Hunsberger
|
||||||
Eric Liu
|
Eric Liu
|
||||||
Eric Siegerman
|
Eric Siegerman
|
||||||
Erik Aronesty
|
Erik Aronesty
|
||||||
|
Erik Hasse
|
||||||
Erik M. Bray
|
Erik M. Bray
|
||||||
Evan Kepner
|
Evan Kepner
|
||||||
Evgeny Seliverstov
|
Evgeny Seliverstov
|
||||||
|
@ -378,6 +379,7 @@ Victor Maryama
|
||||||
Victor Rodriguez
|
Victor Rodriguez
|
||||||
Victor Uriarte
|
Victor Uriarte
|
||||||
Vidar T. Fauske
|
Vidar T. Fauske
|
||||||
|
Vijay Arora
|
||||||
Virgil Dupras
|
Virgil Dupras
|
||||||
Vitaly Lashmanov
|
Vitaly Lashmanov
|
||||||
Vivaan Verma
|
Vivaan Verma
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Fix bug where very long option names could cause pytest to break with ``OSError: [Errno 36] File name too long`` on some systems.
|
|
|
@ -1 +0,0 @@
|
||||||
Terminal Reporting: Fixed bug when running in ``--tb=line`` mode where ``pytest.fail(pytrace=False)`` tests report ``None``.
|
|
|
@ -1 +0,0 @@
|
||||||
Update test log report annotation to named tuple and fixed inconsistency in docs for :hook:`pytest_report_teststatus` hook.
|
|
|
@ -1,2 +0,0 @@
|
||||||
Added :func:`ExceptionInfo.from_exception() <pytest.ExceptionInfo.from_exception>`, a simpler way to create an :class:`~pytest.ExceptionInfo` from an exception.
|
|
||||||
This can replace :func:`ExceptionInfo.from_exc_info() <pytest.ExceptionInfo.from_exc_info()>` for most uses.
|
|
|
@ -1,5 +0,0 @@
|
||||||
When an exception traceback to be displayed is completely filtered out (by mechanisms such as ``__tracebackhide__``, internal frames, and similar), now only the exception string and the following message are shown:
|
|
||||||
|
|
||||||
"All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames.".
|
|
||||||
|
|
||||||
Previously, the last frame of the traceback was shown, even though it was hidden.
|
|
|
@ -1,3 +0,0 @@
|
||||||
Improved verbose output (``-vv``) of ``skip`` and ``xfail`` reasons by performing text wrapping while leaving a clear margin for progress output.
|
|
||||||
|
|
||||||
Added :func:`TerminalReporter.wrap_write() <pytest.TerminalReporter.wrap_write>` as a helper for that.
|
|
|
@ -1 +0,0 @@
|
||||||
:confval:`testpaths` is now honored to load root ``conftests``.
|
|
|
@ -1 +0,0 @@
|
||||||
Added handling of ``%f`` directive to print microseconds in log format options, such as ``log-date-format``.
|
|
|
@ -1 +0,0 @@
|
||||||
The `monkeypatch` `setitem`/`delitem` type annotations now allow `TypedDict` arguments.
|
|
|
@ -1 +0,0 @@
|
||||||
Added underlying exception to cache provider path creation and write warning messages.
|
|
|
@ -0,0 +1 @@
|
||||||
|
Added a warning about modifying the root logger during tests when using ``caplog``.
|
|
@ -1 +0,0 @@
|
||||||
Added warning when :confval:`testpaths` is set, but paths are not found by glob. In this case, pytest will fall back to searching from the current directory.
|
|
|
@ -1 +0,0 @@
|
||||||
Fixed bug in assertion rewriting where a variable assigned with the walrus operator could not be used later in a function call.
|
|
|
@ -1 +0,0 @@
|
||||||
Enhanced the CLI flag for ``-c`` to now include ``--config-file`` to make it clear that this flag applies to the usage of a custom config file.
|
|
|
@ -1,3 +0,0 @@
|
||||||
When `--confcutdir` is not specified, and there is no config file present, the conftest cutoff directory (`--confcutdir`) is now set to the :ref:`rootdir`.
|
|
||||||
Previously in such cases, `conftest.py` files would be probed all the way to the root directory of the filesystem.
|
|
||||||
If you are badly affected by this change, consider adding an empty config file to your desired cutoff directory, or explicitly set `--confcutdir`.
|
|
|
@ -1 +0,0 @@
|
||||||
Fixed ``--last-failed``'s "(skipped N files)" functionality for files inside of packages (directories with `__init__.py` files).
|
|
|
@ -1 +0,0 @@
|
||||||
Fixed the ``--last-failed`` whole-file skipping functionality ("skipped N files") for :ref:`non-python test files <non-python tests>`.
|
|
|
@ -1 +0,0 @@
|
||||||
Fixed traceback entries hidden with ``__tracebackhide__ = True`` still being shown for chained exceptions (parts after "... the above exception ..." message).
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Applying a mark to a fixture function now issues a warning: marks in fixtures never had any effect, but it is a common user error to apply a mark to a fixture (for example ``usefixtures``) and expect it to work.
|
||||||
|
|
||||||
|
This will become an error in the future.
|
|
@ -0,0 +1,22 @@
|
||||||
|
**PytestRemovedIn8Warning deprecation warnings are now errors by default.**
|
||||||
|
|
||||||
|
Following our plan to remove deprecated features with as little disruption as
|
||||||
|
possible, all warnings of type ``PytestRemovedIn8Warning`` now generate errors
|
||||||
|
instead of warning messages by default.
|
||||||
|
|
||||||
|
**The affected features will be effectively removed in pytest 8.1**, so please consult the
|
||||||
|
:ref:`deprecations` section in the docs for directions on how to update existing code.
|
||||||
|
|
||||||
|
In the pytest ``8.0.X`` series, it is possible to change the errors back into warnings as a
|
||||||
|
stopgap measure by adding this to your ``pytest.ini`` file:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[pytest]
|
||||||
|
filterwarnings =
|
||||||
|
ignore::pytest.PytestRemovedIn8Warning
|
||||||
|
|
||||||
|
But this will stop working when pytest ``8.1`` is released.
|
||||||
|
|
||||||
|
**If you have concerns** about the removal of a specific feature, please add a
|
||||||
|
comment to :issue:`7363`.
|
|
@ -1,3 +0,0 @@
|
||||||
:func:`_pytest.logging.LogCaptureFixture.set_level` and :func:`_pytest.logging.LogCaptureFixture.at_level`
|
|
||||||
will temporarily enable the requested ``level`` if ``level`` was disabled globally via
|
|
||||||
``logging.disable(LEVEL)``.
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
Running `pytest pkg/__init__.py` now collects the `pkg/__init__.py` file (module) only.
|
||||||
|
Previously, it collected the entire `pkg` package, including other test files in the directory, but excluding tests in the `__init__.py` file itself
|
||||||
|
(unless :confval:`python_files` was changed to allow `__init__.py` file).
|
||||||
|
|
||||||
|
To collect the entire package, specify just the directory: `pytest pkg`.
|
|
@ -6,6 +6,8 @@ Release announcements
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
|
|
||||||
|
release-7.4.0
|
||||||
|
release-7.3.2
|
||||||
release-7.3.1
|
release-7.3.1
|
||||||
release-7.3.0
|
release-7.3.0
|
||||||
release-7.2.2
|
release-7.2.2
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
pytest-7.3.2
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
pytest 7.3.2 has just been released to PyPI.
|
||||||
|
|
||||||
|
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||||
|
|
||||||
|
pip install --upgrade pytest
|
||||||
|
|
||||||
|
The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
|
||||||
|
|
||||||
|
Thanks to all of the contributors to this release:
|
||||||
|
|
||||||
|
* Adam J. Stewart
|
||||||
|
* Alessio Izzo
|
||||||
|
* Bruno Oliveira
|
||||||
|
* Ran Benita
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -0,0 +1,49 @@
|
||||||
|
pytest-7.4.0
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
The pytest team is proud to announce the 7.4.0 release!
|
||||||
|
|
||||||
|
This release contains new features, improvements, and bug fixes,
|
||||||
|
the full list of changes is available in the changelog:
|
||||||
|
|
||||||
|
https://docs.pytest.org/en/stable/changelog.html
|
||||||
|
|
||||||
|
For complete documentation, please visit:
|
||||||
|
|
||||||
|
https://docs.pytest.org/en/stable/
|
||||||
|
|
||||||
|
As usual, you can upgrade from PyPI via:
|
||||||
|
|
||||||
|
pip install -U pytest
|
||||||
|
|
||||||
|
Thanks to all of the contributors to this release:
|
||||||
|
|
||||||
|
* Adam J. Stewart
|
||||||
|
* Alessio Izzo
|
||||||
|
* Alex
|
||||||
|
* Alex Lambson
|
||||||
|
* Brian Larsen
|
||||||
|
* Bruno Oliveira
|
||||||
|
* Bryan Ricker
|
||||||
|
* Chris Mahoney
|
||||||
|
* Facundo Batista
|
||||||
|
* Florian Bruhin
|
||||||
|
* Jarrett Keifer
|
||||||
|
* Kenny Y
|
||||||
|
* Miro Hrončok
|
||||||
|
* Ran Benita
|
||||||
|
* Roberto Aldera
|
||||||
|
* Ronny Pfannschmidt
|
||||||
|
* Sergey Kim
|
||||||
|
* Stefanie Molin
|
||||||
|
* Vijay Arora
|
||||||
|
* Ville Skyttä
|
||||||
|
* Zac Hatfield-Dodds
|
||||||
|
* bzoracler
|
||||||
|
* leeyueh
|
||||||
|
* nondescryptid
|
||||||
|
* theirix
|
||||||
|
|
||||||
|
|
||||||
|
Happy testing,
|
||||||
|
The pytest Development Team
|
|
@ -22,7 +22,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
cachedir: .pytest_cache
|
cachedir: .pytest_cache
|
||||||
rootdir: /home/sweet/project
|
rootdir: /home/sweet/project
|
||||||
collected 0 items
|
collected 0 items
|
||||||
cache -- .../_pytest/cacheprovider.py:510
|
cache -- .../_pytest/cacheprovider.py:528
|
||||||
Return a cache object that can persist state between testing sessions.
|
Return a cache object that can persist state between testing sessions.
|
||||||
|
|
||||||
cache.get(key, default)
|
cache.get(key, default)
|
||||||
|
@ -119,7 +119,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
|
|
||||||
For more details: :ref:`doctest_namespace`.
|
For more details: :ref:`doctest_namespace`.
|
||||||
|
|
||||||
pytestconfig [session scope] -- .../_pytest/fixtures.py:1360
|
pytestconfig [session scope] -- .../_pytest/fixtures.py:1353
|
||||||
Session-scoped fixture that returns the session's :class:`pytest.Config`
|
Session-scoped fixture that returns the session's :class:`pytest.Config`
|
||||||
object.
|
object.
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
|
|
||||||
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
|
.. _legacy_path: https://py.readthedocs.io/en/latest/path.html
|
||||||
|
|
||||||
caplog -- .../_pytest/logging.py:498
|
caplog -- .../_pytest/logging.py:570
|
||||||
Access and control log capturing.
|
Access and control log capturing.
|
||||||
|
|
||||||
Captured logs are available through the following properties/methods::
|
Captured logs are available through the following properties/methods::
|
||||||
|
@ -207,7 +207,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a
|
||||||
* caplog.record_tuples -> list of (logger_name, level, message) tuples
|
* caplog.record_tuples -> list of (logger_name, level, message) tuples
|
||||||
* caplog.clear() -> clear captured records and formatted log output string
|
* caplog.clear() -> clear captured records and formatted log output string
|
||||||
|
|
||||||
monkeypatch -- .../_pytest/monkeypatch.py:29
|
monkeypatch -- .../_pytest/monkeypatch.py:30
|
||||||
A convenient fixture for monkey-patching.
|
A convenient fixture for monkey-patching.
|
||||||
|
|
||||||
The fixture provides these methods to modify objects, dictionaries, or
|
The fixture provides these methods to modify objects, dictionaries, or
|
||||||
|
|
|
@ -28,6 +28,122 @@ with advance notice in the **Deprecations** section of releases.
|
||||||
|
|
||||||
.. towncrier release notes start
|
.. towncrier release notes start
|
||||||
|
|
||||||
|
pytest 7.4.0 (2023-06-23)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
- `#10901 <https://github.com/pytest-dev/pytest/issues/10901>`_: Added :func:`ExceptionInfo.from_exception() <pytest.ExceptionInfo.from_exception>`, a simpler way to create an :class:`~pytest.ExceptionInfo` from an exception.
|
||||||
|
This can replace :func:`ExceptionInfo.from_exc_info() <pytest.ExceptionInfo.from_exc_info()>` for most uses.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Improvements
|
||||||
|
------------
|
||||||
|
|
||||||
|
- `#10872 <https://github.com/pytest-dev/pytest/issues/10872>`_: Update test log report annotation to named tuple and fixed inconsistency in docs for :hook:`pytest_report_teststatus` hook.
|
||||||
|
|
||||||
|
|
||||||
|
- `#10907 <https://github.com/pytest-dev/pytest/issues/10907>`_: When an exception traceback to be displayed is completely filtered out (by mechanisms such as ``__tracebackhide__``, internal frames, and similar), now only the exception string and the following message are shown:
|
||||||
|
|
||||||
|
"All traceback entries are hidden. Pass `--full-trace` to see hidden and internal frames.".
|
||||||
|
|
||||||
|
Previously, the last frame of the traceback was shown, even though it was hidden.
|
||||||
|
|
||||||
|
|
||||||
|
- `#10940 <https://github.com/pytest-dev/pytest/issues/10940>`_: Improved verbose output (``-vv``) of ``skip`` and ``xfail`` reasons by performing text wrapping while leaving a clear margin for progress output.
|
||||||
|
|
||||||
|
Added ``TerminalReporter.wrap_write()`` as a helper for that.
|
||||||
|
|
||||||
|
|
||||||
|
- `#10991 <https://github.com/pytest-dev/pytest/issues/10991>`_: Added handling of ``%f`` directive to print microseconds in log format options, such as ``log-date-format``.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11005 <https://github.com/pytest-dev/pytest/issues/11005>`_: Added the underlying exception to the cache provider's path creation and write warning messages.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11013 <https://github.com/pytest-dev/pytest/issues/11013>`_: Added warning when :confval:`testpaths` is set, but paths are not found by glob. In this case, pytest will fall back to searching from the current directory.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11043 <https://github.com/pytest-dev/pytest/issues/11043>`_: When `--confcutdir` is not specified, and there is no config file present, the conftest cutoff directory (`--confcutdir`) is now set to the :ref:`rootdir <rootdir>`.
|
||||||
|
Previously in such cases, `conftest.py` files would be probed all the way to the root directory of the filesystem.
|
||||||
|
If you are badly affected by this change, consider adding an empty config file to your desired cutoff directory, or explicitly set `--confcutdir`.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11081 <https://github.com/pytest-dev/pytest/issues/11081>`_: The :confval:`norecursedirs` check is now performed in a :hook:`pytest_ignore_collect` implementation, so plugins can affect it.
|
||||||
|
|
||||||
|
If after updating to this version you see that your `norecursedirs` setting is not being respected,
|
||||||
|
it means that a conftest or a plugin you use has a bad `pytest_ignore_collect` implementation.
|
||||||
|
Most likely, your hook returns `False` for paths it does not want to ignore,
|
||||||
|
which ends the processing and doesn't allow other plugins, including pytest itself, to ignore the path.
|
||||||
|
The fix is to return `None` instead of `False` for paths your hook doesn't want to ignore.
|
||||||
|
|
||||||
|
|
||||||
|
- `#8711 <https://github.com/pytest-dev/pytest/issues/8711>`_: :func:`caplog.set_level() <pytest.LogCaptureFixture.set_level>` and :func:`caplog.at_level() <pytest.LogCaptureFixture.at_level>`
|
||||||
|
will temporarily enable the requested ``level`` if ``level`` was disabled globally via
|
||||||
|
``logging.disable(LEVEL)``.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#10831 <https://github.com/pytest-dev/pytest/issues/10831>`_: Terminal Reporting: Fixed bug when running in ``--tb=line`` mode where ``pytest.fail(pytrace=False)`` tests report ``None``.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11068 <https://github.com/pytest-dev/pytest/issues/11068>`_: Fixed the ``--last-failed`` whole-file skipping functionality ("skipped N files") for :ref:`non-python test files <non-python tests>`.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11104 <https://github.com/pytest-dev/pytest/issues/11104>`_: Fixed a regression in pytest 7.3.2 which caused to :confval:`testpaths` to be considered for loading initial conftests,
|
||||||
|
even when it was not utilized (e.g. when explicit paths were given on the command line).
|
||||||
|
Now the ``testpaths`` are only considered when they are in use.
|
||||||
|
|
||||||
|
|
||||||
|
- `#1904 <https://github.com/pytest-dev/pytest/issues/1904>`_: Fixed traceback entries hidden with ``__tracebackhide__ = True`` still being shown for chained exceptions (parts after "... the above exception ..." message).
|
||||||
|
|
||||||
|
|
||||||
|
- `#7781 <https://github.com/pytest-dev/pytest/issues/7781>`_: Fix writing non-encodable text to log file when using ``--debug``.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Improved Documentation
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
- `#9146 <https://github.com/pytest-dev/pytest/issues/9146>`_: Improved documentation for :func:`caplog.set_level() <pytest.LogCaptureFixture.set_level>`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Trivial/Internal Changes
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
- `#11031 <https://github.com/pytest-dev/pytest/issues/11031>`_: Enhanced the CLI flag for ``-c`` to now include ``--config-file`` to make it clear that this flag applies to the usage of a custom config file.
|
||||||
|
|
||||||
|
|
||||||
|
pytest 7.3.2 (2023-06-10)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- `#10169 <https://github.com/pytest-dev/pytest/issues/10169>`_: Fix bug where very long option names could cause pytest to break with ``OSError: [Errno 36] File name too long`` on some systems.
|
||||||
|
|
||||||
|
|
||||||
|
- `#10894 <https://github.com/pytest-dev/pytest/issues/10894>`_: Support for Python 3.12 (beta at the time of writing).
|
||||||
|
|
||||||
|
|
||||||
|
- `#10987 <https://github.com/pytest-dev/pytest/issues/10987>`_: :confval:`testpaths` is now honored to load root ``conftests``.
|
||||||
|
|
||||||
|
|
||||||
|
- `#10999 <https://github.com/pytest-dev/pytest/issues/10999>`_: The `monkeypatch` `setitem`/`delitem` type annotations now allow `TypedDict` arguments.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11028 <https://github.com/pytest-dev/pytest/issues/11028>`_: Fixed bug in assertion rewriting where a variable assigned with the walrus operator could not be used later in a function call.
|
||||||
|
|
||||||
|
|
||||||
|
- `#11054 <https://github.com/pytest-dev/pytest/issues/11054>`_: Fixed ``--last-failed``'s "(skipped N files)" functionality for files inside of packages (directories with `__init__.py` files).
|
||||||
|
|
||||||
|
|
||||||
pytest 7.3.1 (2023-04-14)
|
pytest 7.3.1 (2023-04-14)
|
||||||
=========================
|
=========================
|
||||||
|
|
||||||
|
|
|
@ -380,6 +380,25 @@ conflicts (such as :class:`pytest.File` now taking ``path`` instead of
|
||||||
``fspath``, as :ref:`outlined above <node-ctor-fspath-deprecation>`), a
|
``fspath``, as :ref:`outlined above <node-ctor-fspath-deprecation>`), a
|
||||||
deprecation warning is now raised.
|
deprecation warning is now raised.
|
||||||
|
|
||||||
|
Applying a mark to a fixture function
|
||||||
|
-------------------------------------
|
||||||
|
|
||||||
|
.. deprecated:: 7.4
|
||||||
|
|
||||||
|
Applying a mark to a fixture function never had any effect, but it is a common user error.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("clean_database")
|
||||||
|
@pytest.fixture
|
||||||
|
def user() -> User:
|
||||||
|
...
|
||||||
|
|
||||||
|
Users expected in this case that the ``usefixtures`` mark would have its intended effect of using the ``clean_database`` fixture when ``user`` was invoked, when in fact it has no effect at all.
|
||||||
|
|
||||||
|
Now pytest will issue a warning when it encounters this problem, and will raise an error in the future versions.
|
||||||
|
|
||||||
|
|
||||||
Backward compatibilities in ``Parser.addoption``
|
Backward compatibilities in ``Parser.addoption``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -467,12 +486,26 @@ The ``yield_fixture`` function/decorator
|
||||||
It has been so for a very long time, so can be search/replaced safely.
|
It has been so for a very long time, so can be search/replaced safely.
|
||||||
|
|
||||||
|
|
||||||
Removed Features
|
Removed Features and Breaking Changes
|
||||||
----------------
|
-------------------------------------
|
||||||
|
|
||||||
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
||||||
an appropriate period of deprecation has passed.
|
an appropriate period of deprecation has passed.
|
||||||
|
|
||||||
|
Some breaking changes which could not be deprecated are also listed.
|
||||||
|
|
||||||
|
|
||||||
|
Collecting ``__init__.py`` files no longer collects package
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. versionremoved:: 8.0
|
||||||
|
|
||||||
|
Running `pytest pkg/__init__.py` now collects the `pkg/__init__.py` file (module) only.
|
||||||
|
Previously, it collected the entire `pkg` package, including other test files in the directory, but excluding tests in the `__init__.py` file itself
|
||||||
|
(unless :confval:`python_files` was changed to allow `__init__.py` file).
|
||||||
|
|
||||||
|
To collect the entire package, specify just the directory: `pytest pkg`.
|
||||||
|
|
||||||
|
|
||||||
The ``pytest.collect`` module
|
The ``pytest.collect`` module
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
|
@ -691,7 +691,7 @@ Here is an example for making a ``db`` fixture available in a directory:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="package")
|
||||||
def db():
|
def db():
|
||||||
return DB()
|
return DB()
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ Install ``pytest``
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ pytest --version
|
$ pytest --version
|
||||||
pytest 7.3.1
|
pytest 7.4.0
|
||||||
|
|
||||||
.. _`simpletest`:
|
.. _`simpletest`:
|
||||||
|
|
||||||
|
|
|
@ -1752,8 +1752,7 @@ into an ini-file:
|
||||||
def my_fixture_that_sadly_wont_use_my_other_fixture():
|
def my_fixture_that_sadly_wont_use_my_other_fixture():
|
||||||
...
|
...
|
||||||
|
|
||||||
Currently this will not generate any error or warning, but this is intended
|
This generates a deprecation warning, and will become an error in Pytest 8.
|
||||||
to be handled by :issue:`3664`.
|
|
||||||
|
|
||||||
.. _`override fixtures`:
|
.. _`override fixtures`:
|
||||||
|
|
||||||
|
|
|
@ -172,6 +172,13 @@ the records for the ``setup`` and ``call`` stages during teardown like so:
|
||||||
|
|
||||||
The full API is available at :class:`pytest.LogCaptureFixture`.
|
The full API is available at :class:`pytest.LogCaptureFixture`.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
The ``caplog`` fixture adds a handler to the root logger to capture logs. If the root logger is
|
||||||
|
modified during a test, for example with ``logging.config.dictConfig``, this handler may be
|
||||||
|
removed and cause no logs to be captured. To avoid this, ensure that any root logger configuration
|
||||||
|
only adds to the existing handlers.
|
||||||
|
|
||||||
|
|
||||||
.. _live_logs:
|
.. _live_logs:
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
.. sidebar:: Next Open Trainings
|
.. sidebar:: Next Open Trainings
|
||||||
|
|
||||||
|
- `pytest tips and tricks for a better testsuite <https://ep2023.europython.eu/session/pytest-tips-and-tricks-for-a-better-testsuite>`_, at `Europython 2023 <https://ep2023.europython.eu/>`_, July 18th (3h), Prague/Remote
|
||||||
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, March 5th to 7th 2024 (3 day in-depth training), Leipzig/Remote
|
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_, March 5th to 7th 2024 (3 day in-depth training), Leipzig/Remote
|
||||||
|
|
||||||
Also see :doc:`previous talks and blogposts <talks>`.
|
Also see :doc:`previous talks and blogposts <talks>`.
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1153,6 +1153,9 @@ Custom warnings generated in some situations such as improper usage or deprecate
|
||||||
.. autoclass:: pytest.PytestRemovedIn8Warning
|
.. autoclass:: pytest.PytestRemovedIn8Warning
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
.. autoclass:: pytest.PytestRemovedIn9Warning
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
.. autoclass:: pytest.PytestUnhandledCoroutineWarning
|
.. autoclass:: pytest.PytestUnhandledCoroutineWarning
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
@ -1703,6 +1706,11 @@ passed multiple times. The expected format is ``name=value``. For example::
|
||||||
[pytest]
|
[pytest]
|
||||||
pythonpath = src1 src2
|
pythonpath = src1 src2
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
``pythonpath`` does not affect some imports that happen very early,
|
||||||
|
most notably plugins loaded using the ``-p`` command line option.
|
||||||
|
|
||||||
|
|
||||||
.. confval:: required_plugins
|
.. confval:: required_plugins
|
||||||
|
|
||||||
|
@ -1918,9 +1926,9 @@ All the command-line flags can be obtained by running ``pytest --help``::
|
||||||
--strict-markers Markers not registered in the `markers` section of
|
--strict-markers Markers not registered in the `markers` section of
|
||||||
the configuration file raise errors
|
the configuration file raise errors
|
||||||
--strict (Deprecated) alias to --strict-markers
|
--strict (Deprecated) alias to --strict-markers
|
||||||
-c, --config-file FILE
|
-c FILE, --config-file=FILE
|
||||||
Load configuration from `FILE` instead of trying to
|
Load configuration from `FILE` instead of trying to
|
||||||
locate one of the implicit configuration files
|
locate one of the implicit configuration files.
|
||||||
--continue-on-collection-errors
|
--continue-on-collection-errors
|
||||||
Force test execution even if collection errors occur
|
Force test execution even if collection errors occur
|
||||||
--rootdir=ROOTDIR Define root directory for tests. Can be relative
|
--rootdir=ROOTDIR Define root directory for tests. Can be relative
|
||||||
|
|
|
@ -7,7 +7,9 @@ def main():
|
||||||
Platform agnostic wrapper script for towncrier.
|
Platform agnostic wrapper script for towncrier.
|
||||||
Fixes the issue (#7251) where windows users are unable to natively run tox -e docs to build pytest docs.
|
Fixes the issue (#7251) where windows users are unable to natively run tox -e docs to build pytest docs.
|
||||||
"""
|
"""
|
||||||
with open("doc/en/_changelog_towncrier_draft.rst", "w") as draft_file:
|
with open(
|
||||||
|
"doc/en/_changelog_towncrier_draft.rst", "w", encoding="utf-8"
|
||||||
|
) as draft_file:
|
||||||
return call(("towncrier", "--draft"), stdout=draft_file)
|
return call(("towncrier", "--draft"), stdout=draft_file)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,9 @@ Plugin List
|
||||||
===========
|
===========
|
||||||
|
|
||||||
PyPI projects that match "pytest-\*" are considered plugins and are listed
|
PyPI projects that match "pytest-\*" are considered plugins and are listed
|
||||||
automatically. Packages classified as inactive are excluded.
|
automatically together with a manually-maintained list in `the source
|
||||||
|
code <https://github.com/pytest-dev/pytest/blob/main/scripts/update-plugin-list.py>`_.
|
||||||
|
Packages classified as inactive are excluded.
|
||||||
|
|
||||||
.. The following conditional uses a different format for this list when
|
.. The following conditional uses a different format for this list when
|
||||||
creating a PDF, because otherwise the table gets far too wide for the
|
creating a PDF, because otherwise the table gets far too wide for the
|
||||||
|
@ -33,6 +35,9 @@ DEVELOPMENT_STATUS_CLASSIFIERS = (
|
||||||
"Development Status :: 6 - Mature",
|
"Development Status :: 6 - Mature",
|
||||||
"Development Status :: 7 - Inactive",
|
"Development Status :: 7 - Inactive",
|
||||||
)
|
)
|
||||||
|
ADDITIONAL_PROJECTS = { # set of additional projects to consider as plugins
|
||||||
|
"logassert",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def escape_rst(text: str) -> str:
|
def escape_rst(text: str) -> str:
|
||||||
|
@ -52,18 +57,18 @@ def iter_plugins():
|
||||||
regex = r">([\d\w-]*)</a>"
|
regex = r">([\d\w-]*)</a>"
|
||||||
response = requests.get("https://pypi.org/simple")
|
response = requests.get("https://pypi.org/simple")
|
||||||
|
|
||||||
matches = list(
|
match_names = (match.groups()[0] for match in re.finditer(regex, response.text))
|
||||||
match
|
plugin_names = [
|
||||||
for match in re.finditer(regex, response.text)
|
name
|
||||||
if match.groups()[0].startswith("pytest-")
|
for name in match_names
|
||||||
)
|
if name.startswith("pytest-") or name in ADDITIONAL_PROJECTS
|
||||||
|
]
|
||||||
|
|
||||||
for match in tqdm(matches, smoothing=0):
|
for name in tqdm(plugin_names, smoothing=0):
|
||||||
name = match.groups()[0]
|
|
||||||
response = requests.get(f"https://pypi.org/pypi/{name}/json")
|
response = requests.get(f"https://pypi.org/pypi/{name}/json")
|
||||||
if response.status_code == 404:
|
if response.status_code == 404:
|
||||||
# Some packages, like pytest-azurepipelines42, are included in https://pypi.org/simple but
|
# Some packages, like pytest-azurepipelines42, are included in https://pypi.org/simple
|
||||||
# return 404 on the JSON API. Skip.
|
# but return 404 on the JSON API. Skip.
|
||||||
continue
|
continue
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
info = response.json()["info"]
|
info = response.json()["info"]
|
||||||
|
|
|
@ -6,7 +6,7 @@ long_description_content_type = text/x-rst
|
||||||
url = https://docs.pytest.org/en/latest/
|
url = https://docs.pytest.org/en/latest/
|
||||||
author = Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others
|
author = Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others
|
||||||
license = MIT
|
license = MIT
|
||||||
license_file = LICENSE
|
license_files = LICENSE
|
||||||
platforms = unix, linux, osx, cygwin, win32
|
platforms = unix, linux, osx, cygwin, win32
|
||||||
classifiers =
|
classifiers =
|
||||||
Development Status :: 6 - Mature
|
Development Status :: 6 - Mature
|
||||||
|
@ -22,6 +22,7 @@ classifiers =
|
||||||
Programming Language :: Python :: 3.9
|
Programming Language :: Python :: 3.9
|
||||||
Programming Language :: Python :: 3.10
|
Programming Language :: Python :: 3.10
|
||||||
Programming Language :: Python :: 3.11
|
Programming Language :: Python :: 3.11
|
||||||
|
Programming Language :: Python :: 3.12
|
||||||
Topic :: Software Development :: Libraries
|
Topic :: Software Development :: Libraries
|
||||||
Topic :: Software Development :: Testing
|
Topic :: Software Development :: Testing
|
||||||
Topic :: Utilities
|
Topic :: Utilities
|
||||||
|
@ -73,6 +74,7 @@ testing =
|
||||||
nose
|
nose
|
||||||
pygments>=2.7.2
|
pygments>=2.7.2
|
||||||
requests
|
requests
|
||||||
|
setuptools
|
||||||
xmlschema
|
xmlschema
|
||||||
|
|
||||||
[options.package_data]
|
[options.package_data]
|
||||||
|
|
|
@ -46,8 +46,14 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
if sys.version_info >= (3, 8):
|
if sys.version_info >= (3, 8):
|
||||||
namedExpr = ast.NamedExpr
|
namedExpr = ast.NamedExpr
|
||||||
|
astNameConstant = ast.Constant
|
||||||
|
astStr = ast.Constant
|
||||||
|
astNum = ast.Constant
|
||||||
else:
|
else:
|
||||||
namedExpr = ast.Expr
|
namedExpr = ast.Expr
|
||||||
|
astNameConstant = ast.NameConstant
|
||||||
|
astStr = ast.Str
|
||||||
|
astNum = ast.Num
|
||||||
|
|
||||||
|
|
||||||
assertstate_key = StashKey["AssertionState"]()
|
assertstate_key = StashKey["AssertionState"]()
|
||||||
|
@ -680,9 +686,12 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
if (
|
if (
|
||||||
expect_docstring
|
expect_docstring
|
||||||
and isinstance(item, ast.Expr)
|
and isinstance(item, ast.Expr)
|
||||||
and isinstance(item.value, ast.Str)
|
and isinstance(item.value, astStr)
|
||||||
):
|
):
|
||||||
doc = item.value.s
|
if sys.version_info >= (3, 8):
|
||||||
|
doc = item.value.value
|
||||||
|
else:
|
||||||
|
doc = item.value.s
|
||||||
if self.is_rewrite_disabled(doc):
|
if self.is_rewrite_disabled(doc):
|
||||||
return
|
return
|
||||||
expect_docstring = False
|
expect_docstring = False
|
||||||
|
@ -814,7 +823,7 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
current = self.stack.pop()
|
current = self.stack.pop()
|
||||||
if self.stack:
|
if self.stack:
|
||||||
self.explanation_specifiers = self.stack[-1]
|
self.explanation_specifiers = self.stack[-1]
|
||||||
keys = [ast.Str(key) for key in current.keys()]
|
keys = [astStr(key) for key in current.keys()]
|
||||||
format_dict = ast.Dict(keys, list(current.values()))
|
format_dict = ast.Dict(keys, list(current.values()))
|
||||||
form = ast.BinOp(expl_expr, ast.Mod(), format_dict)
|
form = ast.BinOp(expl_expr, ast.Mod(), format_dict)
|
||||||
name = "@py_format" + str(next(self.variable_counter))
|
name = "@py_format" + str(next(self.variable_counter))
|
||||||
|
@ -868,16 +877,16 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
negation = ast.UnaryOp(ast.Not(), top_condition)
|
negation = ast.UnaryOp(ast.Not(), top_condition)
|
||||||
|
|
||||||
if self.enable_assertion_pass_hook: # Experimental pytest_assertion_pass hook
|
if self.enable_assertion_pass_hook: # Experimental pytest_assertion_pass hook
|
||||||
msg = self.pop_format_context(ast.Str(explanation))
|
msg = self.pop_format_context(astStr(explanation))
|
||||||
|
|
||||||
# Failed
|
# Failed
|
||||||
if assert_.msg:
|
if assert_.msg:
|
||||||
assertmsg = self.helper("_format_assertmsg", assert_.msg)
|
assertmsg = self.helper("_format_assertmsg", assert_.msg)
|
||||||
gluestr = "\n>assert "
|
gluestr = "\n>assert "
|
||||||
else:
|
else:
|
||||||
assertmsg = ast.Str("")
|
assertmsg = astStr("")
|
||||||
gluestr = "assert "
|
gluestr = "assert "
|
||||||
err_explanation = ast.BinOp(ast.Str(gluestr), ast.Add(), msg)
|
err_explanation = ast.BinOp(astStr(gluestr), ast.Add(), msg)
|
||||||
err_msg = ast.BinOp(assertmsg, ast.Add(), err_explanation)
|
err_msg = ast.BinOp(assertmsg, ast.Add(), err_explanation)
|
||||||
err_name = ast.Name("AssertionError", ast.Load())
|
err_name = ast.Name("AssertionError", ast.Load())
|
||||||
fmt = self.helper("_format_explanation", err_msg)
|
fmt = self.helper("_format_explanation", err_msg)
|
||||||
|
@ -893,8 +902,8 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
hook_call_pass = ast.Expr(
|
hook_call_pass = ast.Expr(
|
||||||
self.helper(
|
self.helper(
|
||||||
"_call_assertion_pass",
|
"_call_assertion_pass",
|
||||||
ast.Num(assert_.lineno),
|
astNum(assert_.lineno),
|
||||||
ast.Str(orig),
|
astStr(orig),
|
||||||
fmt_pass,
|
fmt_pass,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -913,7 +922,7 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
variables = [
|
variables = [
|
||||||
ast.Name(name, ast.Store()) for name in self.format_variables
|
ast.Name(name, ast.Store()) for name in self.format_variables
|
||||||
]
|
]
|
||||||
clear_format = ast.Assign(variables, ast.NameConstant(None))
|
clear_format = ast.Assign(variables, astNameConstant(None))
|
||||||
self.statements.append(clear_format)
|
self.statements.append(clear_format)
|
||||||
|
|
||||||
else: # Original assertion rewriting
|
else: # Original assertion rewriting
|
||||||
|
@ -924,9 +933,9 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
assertmsg = self.helper("_format_assertmsg", assert_.msg)
|
assertmsg = self.helper("_format_assertmsg", assert_.msg)
|
||||||
explanation = "\n>assert " + explanation
|
explanation = "\n>assert " + explanation
|
||||||
else:
|
else:
|
||||||
assertmsg = ast.Str("")
|
assertmsg = astStr("")
|
||||||
explanation = "assert " + explanation
|
explanation = "assert " + explanation
|
||||||
template = ast.BinOp(assertmsg, ast.Add(), ast.Str(explanation))
|
template = ast.BinOp(assertmsg, ast.Add(), astStr(explanation))
|
||||||
msg = self.pop_format_context(template)
|
msg = self.pop_format_context(template)
|
||||||
fmt = self.helper("_format_explanation", msg)
|
fmt = self.helper("_format_explanation", msg)
|
||||||
err_name = ast.Name("AssertionError", ast.Load())
|
err_name = ast.Name("AssertionError", ast.Load())
|
||||||
|
@ -938,7 +947,7 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
# Clear temporary variables by setting them to None.
|
# Clear temporary variables by setting them to None.
|
||||||
if self.variables:
|
if self.variables:
|
||||||
variables = [ast.Name(name, ast.Store()) for name in self.variables]
|
variables = [ast.Name(name, ast.Store()) for name in self.variables]
|
||||||
clear = ast.Assign(variables, ast.NameConstant(None))
|
clear = ast.Assign(variables, astNameConstant(None))
|
||||||
self.statements.append(clear)
|
self.statements.append(clear)
|
||||||
# Fix locations (line numbers/column offsets).
|
# Fix locations (line numbers/column offsets).
|
||||||
for stmt in self.statements:
|
for stmt in self.statements:
|
||||||
|
@ -952,20 +961,20 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
# thinks it's acceptable.
|
# thinks it's acceptable.
|
||||||
locs = ast.Call(self.builtin("locals"), [], [])
|
locs = ast.Call(self.builtin("locals"), [], [])
|
||||||
target_id = name.target.id # type: ignore[attr-defined]
|
target_id = name.target.id # type: ignore[attr-defined]
|
||||||
inlocs = ast.Compare(ast.Str(target_id), [ast.In()], [locs])
|
inlocs = ast.Compare(astStr(target_id), [ast.In()], [locs])
|
||||||
dorepr = self.helper("_should_repr_global_name", name)
|
dorepr = self.helper("_should_repr_global_name", name)
|
||||||
test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
|
test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
|
||||||
expr = ast.IfExp(test, self.display(name), ast.Str(target_id))
|
expr = ast.IfExp(test, self.display(name), astStr(target_id))
|
||||||
return name, self.explanation_param(expr)
|
return name, self.explanation_param(expr)
|
||||||
|
|
||||||
def visit_Name(self, name: ast.Name) -> Tuple[ast.Name, str]:
|
def visit_Name(self, name: ast.Name) -> Tuple[ast.Name, str]:
|
||||||
# Display the repr of the name if it's a local variable or
|
# Display the repr of the name if it's a local variable or
|
||||||
# _should_repr_global_name() thinks it's acceptable.
|
# _should_repr_global_name() thinks it's acceptable.
|
||||||
locs = ast.Call(self.builtin("locals"), [], [])
|
locs = ast.Call(self.builtin("locals"), [], [])
|
||||||
inlocs = ast.Compare(ast.Str(name.id), [ast.In()], [locs])
|
inlocs = ast.Compare(astStr(name.id), [ast.In()], [locs])
|
||||||
dorepr = self.helper("_should_repr_global_name", name)
|
dorepr = self.helper("_should_repr_global_name", name)
|
||||||
test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
|
test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
|
||||||
expr = ast.IfExp(test, self.display(name), ast.Str(name.id))
|
expr = ast.IfExp(test, self.display(name), astStr(name.id))
|
||||||
return name, self.explanation_param(expr)
|
return name, self.explanation_param(expr)
|
||||||
|
|
||||||
def visit_BoolOp(self, boolop: ast.BoolOp) -> Tuple[ast.Name, str]:
|
def visit_BoolOp(self, boolop: ast.BoolOp) -> Tuple[ast.Name, str]:
|
||||||
|
@ -1003,7 +1012,7 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
self.push_format_context()
|
self.push_format_context()
|
||||||
res, expl = self.visit(v)
|
res, expl = self.visit(v)
|
||||||
body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))
|
body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))
|
||||||
expl_format = self.pop_format_context(ast.Str(expl))
|
expl_format = self.pop_format_context(astStr(expl))
|
||||||
call = ast.Call(app, [expl_format], [])
|
call = ast.Call(app, [expl_format], [])
|
||||||
self.expl_stmts.append(ast.Expr(call))
|
self.expl_stmts.append(ast.Expr(call))
|
||||||
if i < levels:
|
if i < levels:
|
||||||
|
@ -1015,7 +1024,7 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
self.statements = body = inner
|
self.statements = body = inner
|
||||||
self.statements = save
|
self.statements = save
|
||||||
self.expl_stmts = fail_save
|
self.expl_stmts = fail_save
|
||||||
expl_template = self.helper("_format_boolop", expl_list, ast.Num(is_or))
|
expl_template = self.helper("_format_boolop", expl_list, astNum(is_or))
|
||||||
expl = self.pop_format_context(expl_template)
|
expl = self.pop_format_context(expl_template)
|
||||||
return ast.Name(res_var, ast.Load()), self.explanation_param(expl)
|
return ast.Name(res_var, ast.Load()), self.explanation_param(expl)
|
||||||
|
|
||||||
|
@ -1118,9 +1127,9 @@ class AssertionRewriter(ast.NodeVisitor):
|
||||||
next_expl = f"({next_expl})"
|
next_expl = f"({next_expl})"
|
||||||
results.append(next_res)
|
results.append(next_res)
|
||||||
sym = BINOP_MAP[op.__class__]
|
sym = BINOP_MAP[op.__class__]
|
||||||
syms.append(ast.Str(sym))
|
syms.append(astStr(sym))
|
||||||
expl = f"{left_expl} {sym} {next_expl}"
|
expl = f"{left_expl} {sym} {next_expl}"
|
||||||
expls.append(ast.Str(expl))
|
expls.append(astStr(expl))
|
||||||
res_expr = ast.Compare(left_res, [op], [next_res])
|
res_expr = ast.Compare(left_res, [op], [next_res])
|
||||||
self.statements.append(ast.Assign([store_names[i]], res_expr))
|
self.statements.append(ast.Assign([store_names[i]], res_expr))
|
||||||
left_res, left_expl = next_res, next_expl
|
left_res, left_expl = next_res, next_expl
|
||||||
|
|
|
@ -527,9 +527,12 @@ class PytestPluginManager(PluginManager):
|
||||||
#
|
#
|
||||||
def _set_initial_conftests(
|
def _set_initial_conftests(
|
||||||
self,
|
self,
|
||||||
namespace: argparse.Namespace,
|
args: Sequence[Union[str, Path]],
|
||||||
|
pyargs: bool,
|
||||||
|
noconftest: bool,
|
||||||
rootpath: Path,
|
rootpath: Path,
|
||||||
testpaths_ini: Sequence[str],
|
confcutdir: Optional[Path],
|
||||||
|
importmode: Union[ImportMode, str],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Load initial conftest files given a preparsed "namespace".
|
"""Load initial conftest files given a preparsed "namespace".
|
||||||
|
|
||||||
|
@ -539,17 +542,12 @@ class PytestPluginManager(PluginManager):
|
||||||
common options will not confuse our logic here.
|
common options will not confuse our logic here.
|
||||||
"""
|
"""
|
||||||
current = Path.cwd()
|
current = Path.cwd()
|
||||||
self._confcutdir = (
|
self._confcutdir = absolutepath(current / confcutdir) if confcutdir else None
|
||||||
absolutepath(current / namespace.confcutdir)
|
self._noconftest = noconftest
|
||||||
if namespace.confcutdir
|
self._using_pyargs = pyargs
|
||||||
else None
|
|
||||||
)
|
|
||||||
self._noconftest = namespace.noconftest
|
|
||||||
self._using_pyargs = namespace.pyargs
|
|
||||||
testpaths = namespace.file_or_dir + testpaths_ini
|
|
||||||
foundanchor = False
|
foundanchor = False
|
||||||
for testpath in testpaths:
|
for intitial_path in args:
|
||||||
path = str(testpath)
|
path = str(intitial_path)
|
||||||
# remove node-id syntax
|
# remove node-id syntax
|
||||||
i = path.find("::")
|
i = path.find("::")
|
||||||
if i != -1:
|
if i != -1:
|
||||||
|
@ -563,10 +561,10 @@ class PytestPluginManager(PluginManager):
|
||||||
except OSError: # pragma: no cover
|
except OSError: # pragma: no cover
|
||||||
anchor_exists = False
|
anchor_exists = False
|
||||||
if anchor_exists:
|
if anchor_exists:
|
||||||
self._try_load_conftest(anchor, namespace.importmode, rootpath)
|
self._try_load_conftest(anchor, importmode, rootpath)
|
||||||
foundanchor = True
|
foundanchor = True
|
||||||
if not foundanchor:
|
if not foundanchor:
|
||||||
self._try_load_conftest(current, namespace.importmode, rootpath)
|
self._try_load_conftest(current, importmode, rootpath)
|
||||||
|
|
||||||
def _is_in_confcutdir(self, path: Path) -> bool:
|
def _is_in_confcutdir(self, path: Path) -> bool:
|
||||||
"""Whether a path is within the confcutdir.
|
"""Whether a path is within the confcutdir.
|
||||||
|
@ -1140,10 +1138,25 @@ class Config:
|
||||||
|
|
||||||
@hookimpl(trylast=True)
|
@hookimpl(trylast=True)
|
||||||
def pytest_load_initial_conftests(self, early_config: "Config") -> None:
|
def pytest_load_initial_conftests(self, early_config: "Config") -> None:
|
||||||
self.pluginmanager._set_initial_conftests(
|
# We haven't fully parsed the command line arguments yet, so
|
||||||
early_config.known_args_namespace,
|
# early_config.args it not set yet. But we need it for
|
||||||
|
# discovering the initial conftests. So "pre-run" the logic here.
|
||||||
|
# It will be done for real in `parse()`.
|
||||||
|
args, args_source = early_config._decide_args(
|
||||||
|
args=early_config.known_args_namespace.file_or_dir,
|
||||||
|
pyargs=early_config.known_args_namespace.pyargs,
|
||||||
|
testpaths=early_config.getini("testpaths"),
|
||||||
|
invocation_dir=early_config.invocation_params.dir,
|
||||||
rootpath=early_config.rootpath,
|
rootpath=early_config.rootpath,
|
||||||
testpaths_ini=self.getini("testpaths"),
|
warn=False,
|
||||||
|
)
|
||||||
|
self.pluginmanager._set_initial_conftests(
|
||||||
|
args=args,
|
||||||
|
pyargs=early_config.known_args_namespace.pyargs,
|
||||||
|
noconftest=early_config.known_args_namespace.noconftest,
|
||||||
|
rootpath=early_config.rootpath,
|
||||||
|
confcutdir=early_config.known_args_namespace.confcutdir,
|
||||||
|
importmode=early_config.known_args_namespace.importmode,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _initini(self, args: Sequence[str]) -> None:
|
def _initini(self, args: Sequence[str]) -> None:
|
||||||
|
@ -1223,6 +1236,49 @@ class Config:
|
||||||
|
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
def _decide_args(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
args: List[str],
|
||||||
|
pyargs: List[str],
|
||||||
|
testpaths: List[str],
|
||||||
|
invocation_dir: Path,
|
||||||
|
rootpath: Path,
|
||||||
|
warn: bool,
|
||||||
|
) -> Tuple[List[str], ArgsSource]:
|
||||||
|
"""Decide the args (initial paths/nodeids) to use given the relevant inputs.
|
||||||
|
|
||||||
|
:param warn: Whether can issue warnings.
|
||||||
|
"""
|
||||||
|
if args:
|
||||||
|
source = Config.ArgsSource.ARGS
|
||||||
|
result = args
|
||||||
|
else:
|
||||||
|
if invocation_dir == rootpath:
|
||||||
|
source = Config.ArgsSource.TESTPATHS
|
||||||
|
if pyargs:
|
||||||
|
result = testpaths
|
||||||
|
else:
|
||||||
|
result = []
|
||||||
|
for path in testpaths:
|
||||||
|
result.extend(sorted(glob.iglob(path, recursive=True)))
|
||||||
|
if testpaths and not result:
|
||||||
|
if warn:
|
||||||
|
warning_text = (
|
||||||
|
"No files were found in testpaths; "
|
||||||
|
"consider removing or adjusting your testpaths configuration. "
|
||||||
|
"Searching recursively from the current directory instead."
|
||||||
|
)
|
||||||
|
self.issue_config_time_warning(
|
||||||
|
PytestConfigWarning(warning_text), stacklevel=3
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
result = []
|
||||||
|
if not result:
|
||||||
|
source = Config.ArgsSource.INCOVATION_DIR
|
||||||
|
result = [str(invocation_dir)]
|
||||||
|
return result, source
|
||||||
|
|
||||||
def _preparse(self, args: List[str], addopts: bool = True) -> None:
|
def _preparse(self, args: List[str], addopts: bool = True) -> None:
|
||||||
if addopts:
|
if addopts:
|
||||||
env_addopts = os.environ.get("PYTEST_ADDOPTS", "")
|
env_addopts = os.environ.get("PYTEST_ADDOPTS", "")
|
||||||
|
@ -1371,34 +1427,17 @@ class Config:
|
||||||
self.hook.pytest_cmdline_preparse(config=self, args=args)
|
self.hook.pytest_cmdline_preparse(config=self, args=args)
|
||||||
self._parser.after_preparse = True # type: ignore
|
self._parser.after_preparse = True # type: ignore
|
||||||
try:
|
try:
|
||||||
source = Config.ArgsSource.ARGS
|
|
||||||
args = self._parser.parse_setoption(
|
args = self._parser.parse_setoption(
|
||||||
args, self.option, namespace=self.option
|
args, self.option, namespace=self.option
|
||||||
)
|
)
|
||||||
if not args:
|
self.args, self.args_source = self._decide_args(
|
||||||
if self.invocation_params.dir == self.rootpath:
|
args=args,
|
||||||
source = Config.ArgsSource.TESTPATHS
|
pyargs=self.known_args_namespace.pyargs,
|
||||||
testpaths: List[str] = self.getini("testpaths")
|
testpaths=self.getini("testpaths"),
|
||||||
if self.known_args_namespace.pyargs:
|
invocation_dir=self.invocation_params.dir,
|
||||||
args = testpaths
|
rootpath=self.rootpath,
|
||||||
else:
|
warn=True,
|
||||||
args = []
|
)
|
||||||
for path in testpaths:
|
|
||||||
args.extend(sorted(glob.iglob(path, recursive=True)))
|
|
||||||
if testpaths and not args:
|
|
||||||
warning_text = (
|
|
||||||
"No files were found in testpaths; "
|
|
||||||
"consider removing or adjusting your testpaths configuration. "
|
|
||||||
"Searching recursively from the current directory instead."
|
|
||||||
)
|
|
||||||
self.issue_config_time_warning(
|
|
||||||
PytestConfigWarning(warning_text), stacklevel=3
|
|
||||||
)
|
|
||||||
if not args:
|
|
||||||
source = Config.ArgsSource.INCOVATION_DIR
|
|
||||||
args = [str(self.invocation_params.dir)]
|
|
||||||
self.args = args
|
|
||||||
self.args_source = source
|
|
||||||
except PrintHelp:
|
except PrintHelp:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -122,6 +122,11 @@ HOOK_LEGACY_MARKING = UnformattedWarning(
|
||||||
"#configuring-hook-specs-impls-using-markers",
|
"#configuring-hook-specs-impls-using-markers",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
MARKED_FIXTURE = PytestRemovedIn8Warning(
|
||||||
|
"Marks applied to fixtures have no effect\n"
|
||||||
|
"See docs: https://docs.pytest.org/en/stable/deprecations.html#applying-a-mark-to-a-fixture-function"
|
||||||
|
)
|
||||||
|
|
||||||
# You want to make some `__init__` or function "private".
|
# You want to make some `__init__` or function "private".
|
||||||
#
|
#
|
||||||
# def my_private_function(some, args):
|
# def my_private_function(some, args):
|
||||||
|
|
|
@ -54,6 +54,7 @@ from _pytest.config import _PluggyPlugin
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.deprecated import check_ispytest
|
from _pytest.deprecated import check_ispytest
|
||||||
|
from _pytest.deprecated import MARKED_FIXTURE
|
||||||
from _pytest.deprecated import YIELD_FIXTURE
|
from _pytest.deprecated import YIELD_FIXTURE
|
||||||
from _pytest.mark import Mark
|
from _pytest.mark import Mark
|
||||||
from _pytest.mark import ParameterSet
|
from _pytest.mark import ParameterSet
|
||||||
|
@ -1271,6 +1272,9 @@ class FixtureFunctionMarker:
|
||||||
"fixture is being applied more than once to the same function"
|
"fixture is being applied more than once to the same function"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if hasattr(function, "pytestmark"):
|
||||||
|
warnings.warn(MARKED_FIXTURE, stacklevel=2)
|
||||||
|
|
||||||
function = wrap_function_to_error_out_if_called_directly(function, self)
|
function = wrap_function_to_error_out_if_called_directly(function, self)
|
||||||
|
|
||||||
name = self.name or function.__name__
|
name = self.name or function.__name__
|
||||||
|
|
|
@ -105,7 +105,7 @@ def pytest_cmdline_parse():
|
||||||
if config.option.debug:
|
if config.option.debug:
|
||||||
# --debug | --debug <file.log> was provided.
|
# --debug | --debug <file.log> was provided.
|
||||||
path = config.option.debug
|
path = config.option.debug
|
||||||
debugfile = open(path, "w")
|
debugfile = open(path, "w", encoding="utf-8")
|
||||||
debugfile.write(
|
debugfile.write(
|
||||||
"versions pytest-%s, "
|
"versions pytest-%s, "
|
||||||
"python-%s\ncwd=%s\nargs=%s\n\n"
|
"python-%s\ncwd=%s\nargs=%s\n\n"
|
||||||
|
|
|
@ -515,7 +515,9 @@ class LogCaptureFixture:
|
||||||
return original_disable_level
|
return original_disable_level
|
||||||
|
|
||||||
def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None:
|
def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None:
|
||||||
"""Set the level of a logger for the duration of a test.
|
"""Set the threshold level of a logger for the duration of a test.
|
||||||
|
|
||||||
|
Logging messages which are less severe than this level will not be captured.
|
||||||
|
|
||||||
.. versionchanged:: 3.4
|
.. versionchanged:: 3.4
|
||||||
The levels of the loggers changed by this function will be
|
The levels of the loggers changed by this function will be
|
||||||
|
|
|
@ -400,6 +400,12 @@ def pytest_ignore_collect(collection_path: Path, config: Config) -> Optional[boo
|
||||||
allow_in_venv = config.getoption("collect_in_virtualenv")
|
allow_in_venv = config.getoption("collect_in_virtualenv")
|
||||||
if not allow_in_venv and _in_venv(collection_path):
|
if not allow_in_venv and _in_venv(collection_path):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
if collection_path.is_dir():
|
||||||
|
norecursepatterns = config.getini("norecursedirs")
|
||||||
|
if any(fnmatch_ex(pat, collection_path) for pat in norecursepatterns):
|
||||||
|
return True
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@ -563,9 +569,6 @@ class Session(nodes.FSCollector):
|
||||||
ihook = self.gethookproxy(fspath.parent)
|
ihook = self.gethookproxy(fspath.parent)
|
||||||
if ihook.pytest_ignore_collect(collection_path=fspath, config=self.config):
|
if ihook.pytest_ignore_collect(collection_path=fspath, config=self.config):
|
||||||
return False
|
return False
|
||||||
norecursepatterns = self.config.getini("norecursedirs")
|
|
||||||
if any(fnmatch_ex(pat, fspath) for pat in norecursepatterns):
|
|
||||||
return False
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _collectfile(
|
def _collectfile(
|
||||||
|
@ -687,8 +690,8 @@ class Session(nodes.FSCollector):
|
||||||
# are not collected more than once.
|
# are not collected more than once.
|
||||||
matchnodes_cache: Dict[Tuple[Type[nodes.Collector], str], CollectReport] = {}
|
matchnodes_cache: Dict[Tuple[Type[nodes.Collector], str], CollectReport] = {}
|
||||||
|
|
||||||
# Dirnames of pkgs with dunder-init files.
|
# Directories of pkgs with dunder-init files.
|
||||||
pkg_roots: Dict[str, Package] = {}
|
pkg_roots: Dict[Path, Package] = {}
|
||||||
|
|
||||||
for argpath, names in self._initial_parts:
|
for argpath, names in self._initial_parts:
|
||||||
self.trace("processing argument", (argpath, names))
|
self.trace("processing argument", (argpath, names))
|
||||||
|
@ -709,7 +712,7 @@ class Session(nodes.FSCollector):
|
||||||
col = self._collectfile(pkginit, handle_dupes=False)
|
col = self._collectfile(pkginit, handle_dupes=False)
|
||||||
if col:
|
if col:
|
||||||
if isinstance(col[0], Package):
|
if isinstance(col[0], Package):
|
||||||
pkg_roots[str(parent)] = col[0]
|
pkg_roots[parent] = col[0]
|
||||||
node_cache1[col[0].path] = [col[0]]
|
node_cache1[col[0].path] = [col[0]]
|
||||||
|
|
||||||
# If it's a directory argument, recurse and look for any Subpackages.
|
# If it's a directory argument, recurse and look for any Subpackages.
|
||||||
|
@ -718,7 +721,7 @@ class Session(nodes.FSCollector):
|
||||||
assert not names, f"invalid arg {(argpath, names)!r}"
|
assert not names, f"invalid arg {(argpath, names)!r}"
|
||||||
|
|
||||||
seen_dirs: Set[Path] = set()
|
seen_dirs: Set[Path] = set()
|
||||||
for direntry in visit(str(argpath), self._recurse):
|
for direntry in visit(argpath, self._recurse):
|
||||||
if not direntry.is_file():
|
if not direntry.is_file():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -733,8 +736,8 @@ class Session(nodes.FSCollector):
|
||||||
for x in self._collectfile(pkginit):
|
for x in self._collectfile(pkginit):
|
||||||
yield x
|
yield x
|
||||||
if isinstance(x, Package):
|
if isinstance(x, Package):
|
||||||
pkg_roots[str(dirpath)] = x
|
pkg_roots[dirpath] = x
|
||||||
if str(dirpath) in pkg_roots:
|
if dirpath in pkg_roots:
|
||||||
# Do not collect packages here.
|
# Do not collect packages here.
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -751,7 +754,7 @@ class Session(nodes.FSCollector):
|
||||||
if argpath in node_cache1:
|
if argpath in node_cache1:
|
||||||
col = node_cache1[argpath]
|
col = node_cache1[argpath]
|
||||||
else:
|
else:
|
||||||
collect_root = pkg_roots.get(str(argpath.parent), self)
|
collect_root = pkg_roots.get(argpath.parent, self)
|
||||||
col = collect_root._collectfile(argpath, handle_dupes=False)
|
col = collect_root._collectfile(argpath, handle_dupes=False)
|
||||||
if col:
|
if col:
|
||||||
node_cache1[argpath] = col
|
node_cache1[argpath] = col
|
||||||
|
|
|
@ -18,6 +18,7 @@ import ast
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import enum
|
import enum
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
import types
|
import types
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
|
@ -26,6 +27,11 @@ from typing import NoReturn
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 8):
|
||||||
|
astNameConstant = ast.Constant
|
||||||
|
else:
|
||||||
|
astNameConstant = ast.NameConstant
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Expression",
|
"Expression",
|
||||||
|
@ -132,7 +138,7 @@ IDENT_PREFIX = "$"
|
||||||
|
|
||||||
def expression(s: Scanner) -> ast.Expression:
|
def expression(s: Scanner) -> ast.Expression:
|
||||||
if s.accept(TokenType.EOF):
|
if s.accept(TokenType.EOF):
|
||||||
ret: ast.expr = ast.NameConstant(False)
|
ret: ast.expr = astNameConstant(False)
|
||||||
else:
|
else:
|
||||||
ret = expr(s)
|
ret = expr(s)
|
||||||
s.accept(TokenType.EOF, reject=True)
|
s.accept(TokenType.EOF, reject=True)
|
||||||
|
|
|
@ -28,6 +28,7 @@ from ..compat import NOTSET
|
||||||
from ..compat import NotSetType
|
from ..compat import NotSetType
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.deprecated import check_ispytest
|
from _pytest.deprecated import check_ispytest
|
||||||
|
from _pytest.deprecated import MARKED_FIXTURE
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
from _pytest.warning_types import PytestUnknownMarkWarning
|
from _pytest.warning_types import PytestUnknownMarkWarning
|
||||||
|
|
||||||
|
@ -412,6 +413,12 @@ def store_mark(obj, mark: Mark) -> None:
|
||||||
This is used to implement the Mark declarations/decorators correctly.
|
This is used to implement the Mark declarations/decorators correctly.
|
||||||
"""
|
"""
|
||||||
assert isinstance(mark, Mark), mark
|
assert isinstance(mark, Mark), mark
|
||||||
|
|
||||||
|
from ..fixtures import getfixturemarker
|
||||||
|
|
||||||
|
if getfixturemarker(obj) is not None:
|
||||||
|
warnings.warn(MARKED_FIXTURE, stacklevel=2)
|
||||||
|
|
||||||
# Always reassign name to avoid updating pytestmark in a reference that
|
# Always reassign name to avoid updating pytestmark in a reference that
|
||||||
# was only borrowed.
|
# was only borrowed.
|
||||||
obj.pytestmark = [*get_unpacked_marks(obj, consider_mro=False), mark]
|
obj.pytestmark = [*get_unpacked_marks(obj, consider_mro=False), mark]
|
||||||
|
|
|
@ -6,6 +6,7 @@ import collections.abc
|
||||||
import contextlib
|
import contextlib
|
||||||
import gc
|
import gc
|
||||||
import importlib
|
import importlib
|
||||||
|
import locale
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
|
@ -129,6 +130,7 @@ class LsofFdLeakChecker:
|
||||||
stderr=subprocess.DEVNULL,
|
stderr=subprocess.DEVNULL,
|
||||||
check=True,
|
check=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
encoding=locale.getpreferredencoding(False),
|
||||||
).stdout
|
).stdout
|
||||||
|
|
||||||
def isopen(line: str) -> bool:
|
def isopen(line: str) -> bool:
|
||||||
|
|
|
@ -734,9 +734,6 @@ class Package(Module):
|
||||||
ihook = self.session.gethookproxy(fspath.parent)
|
ihook = self.session.gethookproxy(fspath.parent)
|
||||||
if ihook.pytest_ignore_collect(collection_path=fspath, config=self.config):
|
if ihook.pytest_ignore_collect(collection_path=fspath, config=self.config):
|
||||||
return False
|
return False
|
||||||
norecursepatterns = self.config.getini("norecursedirs")
|
|
||||||
if any(fnmatch_ex(pat, fspath) for pat in norecursepatterns):
|
|
||||||
return False
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _collectfile(
|
def _collectfile(
|
||||||
|
@ -767,7 +764,9 @@ class Package(Module):
|
||||||
this_path = self.path.parent
|
this_path = self.path.parent
|
||||||
|
|
||||||
# Always collect the __init__ first.
|
# Always collect the __init__ first.
|
||||||
if path_matches_patterns(self.path, self.config.getini("python_files")):
|
if self.session.isinitpath(self.path) or path_matches_patterns(
|
||||||
|
self.path, self.config.getini("python_files")
|
||||||
|
):
|
||||||
yield Module.from_parent(self, path=self.path)
|
yield Module.from_parent(self, path=self.path)
|
||||||
|
|
||||||
pkg_prefixes: Set[Path] = set()
|
pkg_prefixes: Set[Path] = set()
|
||||||
|
|
|
@ -56,6 +56,12 @@ class PytestRemovedIn8Warning(PytestDeprecationWarning):
|
||||||
__module__ = "pytest"
|
__module__ = "pytest"
|
||||||
|
|
||||||
|
|
||||||
|
class PytestRemovedIn9Warning(PytestDeprecationWarning):
|
||||||
|
"""Warning class for features that will be removed in pytest 9."""
|
||||||
|
|
||||||
|
__module__ = "pytest"
|
||||||
|
|
||||||
|
|
||||||
class PytestReturnNotNoneWarning(PytestRemovedIn8Warning):
|
class PytestReturnNotNoneWarning(PytestRemovedIn8Warning):
|
||||||
"""Warning emitted when a test function is returning value other than None."""
|
"""Warning emitted when a test function is returning value other than None."""
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,8 @@ def catch_warnings_for_item(
|
||||||
warnings.filterwarnings("always", category=DeprecationWarning)
|
warnings.filterwarnings("always", category=DeprecationWarning)
|
||||||
warnings.filterwarnings("always", category=PendingDeprecationWarning)
|
warnings.filterwarnings("always", category=PendingDeprecationWarning)
|
||||||
|
|
||||||
|
warnings.filterwarnings("error", category=pytest.PytestRemovedIn8Warning)
|
||||||
|
|
||||||
apply_warning_filters(config_filters, cmdline_filters)
|
apply_warning_filters(config_filters, cmdline_filters)
|
||||||
|
|
||||||
# apply filters from "filterwarnings" marks
|
# apply filters from "filterwarnings" marks
|
||||||
|
|
|
@ -71,6 +71,7 @@ from _pytest.warning_types import PytestConfigWarning
|
||||||
from _pytest.warning_types import PytestDeprecationWarning
|
from _pytest.warning_types import PytestDeprecationWarning
|
||||||
from _pytest.warning_types import PytestExperimentalApiWarning
|
from _pytest.warning_types import PytestExperimentalApiWarning
|
||||||
from _pytest.warning_types import PytestRemovedIn8Warning
|
from _pytest.warning_types import PytestRemovedIn8Warning
|
||||||
|
from _pytest.warning_types import PytestRemovedIn9Warning
|
||||||
from _pytest.warning_types import PytestReturnNotNoneWarning
|
from _pytest.warning_types import PytestReturnNotNoneWarning
|
||||||
from _pytest.warning_types import PytestUnhandledCoroutineWarning
|
from _pytest.warning_types import PytestUnhandledCoroutineWarning
|
||||||
from _pytest.warning_types import PytestUnhandledThreadExceptionWarning
|
from _pytest.warning_types import PytestUnhandledThreadExceptionWarning
|
||||||
|
@ -131,6 +132,7 @@ __all__ = [
|
||||||
"PytestDeprecationWarning",
|
"PytestDeprecationWarning",
|
||||||
"PytestExperimentalApiWarning",
|
"PytestExperimentalApiWarning",
|
||||||
"PytestRemovedIn8Warning",
|
"PytestRemovedIn8Warning",
|
||||||
|
"PytestRemovedIn9Warning",
|
||||||
"PytestReturnNotNoneWarning",
|
"PytestReturnNotNoneWarning",
|
||||||
"Pytester",
|
"Pytester",
|
||||||
"PytestPluginManager",
|
"PytestPluginManager",
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import contextlib
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import warnings
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -9,6 +11,14 @@ from py import error
|
||||||
from py.path import local
|
from py.path import local
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def ignore_encoding_warning():
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
with contextlib.suppress(NameError): # new in 3.10
|
||||||
|
warnings.simplefilter("ignore", EncodingWarning)
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
class CommonFSTests:
|
class CommonFSTests:
|
||||||
def test_constructor_equality(self, path1):
|
def test_constructor_equality(self, path1):
|
||||||
p = path1.__class__(path1)
|
p = path1.__class__(path1)
|
||||||
|
@ -223,7 +233,8 @@ class CommonFSTests:
|
||||||
assert not (path1 < path1)
|
assert not (path1 < path1)
|
||||||
|
|
||||||
def test_simple_read(self, path1):
|
def test_simple_read(self, path1):
|
||||||
x = path1.join("samplefile").read("r")
|
with ignore_encoding_warning():
|
||||||
|
x = path1.join("samplefile").read("r")
|
||||||
assert x == "samplefile\n"
|
assert x == "samplefile\n"
|
||||||
|
|
||||||
def test_join_div_operator(self, path1):
|
def test_join_div_operator(self, path1):
|
||||||
|
@ -265,12 +276,14 @@ class CommonFSTests:
|
||||||
|
|
||||||
def test_readlines(self, path1):
|
def test_readlines(self, path1):
|
||||||
fn = path1.join("samplefile")
|
fn = path1.join("samplefile")
|
||||||
contents = fn.readlines()
|
with ignore_encoding_warning():
|
||||||
|
contents = fn.readlines()
|
||||||
assert contents == ["samplefile\n"]
|
assert contents == ["samplefile\n"]
|
||||||
|
|
||||||
def test_readlines_nocr(self, path1):
|
def test_readlines_nocr(self, path1):
|
||||||
fn = path1.join("samplefile")
|
fn = path1.join("samplefile")
|
||||||
contents = fn.readlines(cr=0)
|
with ignore_encoding_warning():
|
||||||
|
contents = fn.readlines(cr=0)
|
||||||
assert contents == ["samplefile", ""]
|
assert contents == ["samplefile", ""]
|
||||||
|
|
||||||
def test_file(self, path1):
|
def test_file(self, path1):
|
||||||
|
@ -362,8 +375,8 @@ class CommonFSTests:
|
||||||
initpy.copy(copied)
|
initpy.copy(copied)
|
||||||
try:
|
try:
|
||||||
assert copied.check()
|
assert copied.check()
|
||||||
s1 = initpy.read()
|
s1 = initpy.read_text(encoding="utf-8")
|
||||||
s2 = copied.read()
|
s2 = copied.read_text(encoding="utf-8")
|
||||||
assert s1 == s2
|
assert s1 == s2
|
||||||
finally:
|
finally:
|
||||||
if copied.check():
|
if copied.check():
|
||||||
|
@ -376,8 +389,8 @@ class CommonFSTests:
|
||||||
otherdir.copy(copied)
|
otherdir.copy(copied)
|
||||||
assert copied.check(dir=1)
|
assert copied.check(dir=1)
|
||||||
assert copied.join("__init__.py").check(file=1)
|
assert copied.join("__init__.py").check(file=1)
|
||||||
s1 = otherdir.join("__init__.py").read()
|
s1 = otherdir.join("__init__.py").read_text(encoding="utf-8")
|
||||||
s2 = copied.join("__init__.py").read()
|
s2 = copied.join("__init__.py").read_text(encoding="utf-8")
|
||||||
assert s1 == s2
|
assert s1 == s2
|
||||||
finally:
|
finally:
|
||||||
if copied.check(dir=1):
|
if copied.check(dir=1):
|
||||||
|
@ -463,13 +476,13 @@ def setuptestfs(path):
|
||||||
return
|
return
|
||||||
# print "setting up test fs for", repr(path)
|
# print "setting up test fs for", repr(path)
|
||||||
samplefile = path.ensure("samplefile")
|
samplefile = path.ensure("samplefile")
|
||||||
samplefile.write("samplefile\n")
|
samplefile.write_text("samplefile\n", encoding="utf-8")
|
||||||
|
|
||||||
execfile = path.ensure("execfile")
|
execfile = path.ensure("execfile")
|
||||||
execfile.write("x=42")
|
execfile.write_text("x=42", encoding="utf-8")
|
||||||
|
|
||||||
execfilepy = path.ensure("execfile.py")
|
execfilepy = path.ensure("execfile.py")
|
||||||
execfilepy.write("x=42")
|
execfilepy.write_text("x=42", encoding="utf-8")
|
||||||
|
|
||||||
d = {1: 2, "hello": "world", "answer": 42}
|
d = {1: 2, "hello": "world", "answer": 42}
|
||||||
path.ensure("samplepickle").dump(d)
|
path.ensure("samplepickle").dump(d)
|
||||||
|
@ -481,22 +494,24 @@ def setuptestfs(path):
|
||||||
otherdir.ensure("__init__.py")
|
otherdir.ensure("__init__.py")
|
||||||
|
|
||||||
module_a = otherdir.ensure("a.py")
|
module_a = otherdir.ensure("a.py")
|
||||||
module_a.write("from .b import stuff as result\n")
|
module_a.write_text("from .b import stuff as result\n", encoding="utf-8")
|
||||||
module_b = otherdir.ensure("b.py")
|
module_b = otherdir.ensure("b.py")
|
||||||
module_b.write('stuff="got it"\n')
|
module_b.write_text('stuff="got it"\n', encoding="utf-8")
|
||||||
module_c = otherdir.ensure("c.py")
|
module_c = otherdir.ensure("c.py")
|
||||||
module_c.write(
|
module_c.write_text(
|
||||||
"""import py;
|
"""import py;
|
||||||
import otherdir.a
|
import otherdir.a
|
||||||
value = otherdir.a.result
|
value = otherdir.a.result
|
||||||
"""
|
""",
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
module_d = otherdir.ensure("d.py")
|
module_d = otherdir.ensure("d.py")
|
||||||
module_d.write(
|
module_d.write_text(
|
||||||
"""import py;
|
"""import py;
|
||||||
from otherdir import a
|
from otherdir import a
|
||||||
value2 = a.result
|
value2 = a.result
|
||||||
"""
|
""",
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -534,9 +549,11 @@ def batch_make_numbered_dirs(rootdir, repeats):
|
||||||
for i in range(repeats):
|
for i in range(repeats):
|
||||||
dir_ = local.make_numbered_dir(prefix="repro-", rootdir=rootdir)
|
dir_ = local.make_numbered_dir(prefix="repro-", rootdir=rootdir)
|
||||||
file_ = dir_.join("foo")
|
file_ = dir_.join("foo")
|
||||||
file_.write("%s" % i)
|
file_.write_text("%s" % i, encoding="utf-8")
|
||||||
actual = int(file_.read())
|
actual = int(file_.read_text(encoding="utf-8"))
|
||||||
assert actual == i, f"int(file_.read()) is {actual} instead of {i}"
|
assert (
|
||||||
|
actual == i
|
||||||
|
), f"int(file_.read_text(encoding='utf-8')) is {actual} instead of {i}"
|
||||||
dir_.join(".lock").remove(ignore_errors=True)
|
dir_.join(".lock").remove(ignore_errors=True)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -692,14 +709,14 @@ class TestLocalPath(CommonFSTests):
|
||||||
|
|
||||||
def test_open_and_ensure(self, path1):
|
def test_open_and_ensure(self, path1):
|
||||||
p = path1.join("sub1", "sub2", "file")
|
p = path1.join("sub1", "sub2", "file")
|
||||||
with p.open("w", ensure=1) as f:
|
with p.open("w", ensure=1, encoding="utf-8") as f:
|
||||||
f.write("hello")
|
f.write("hello")
|
||||||
assert p.read() == "hello"
|
assert p.read_text(encoding="utf-8") == "hello"
|
||||||
|
|
||||||
def test_write_and_ensure(self, path1):
|
def test_write_and_ensure(self, path1):
|
||||||
p = path1.join("sub1", "sub2", "file")
|
p = path1.join("sub1", "sub2", "file")
|
||||||
p.write("hello", ensure=1)
|
p.write_text("hello", ensure=1, encoding="utf-8")
|
||||||
assert p.read() == "hello"
|
assert p.read_text(encoding="utf-8") == "hello"
|
||||||
|
|
||||||
@pytest.mark.parametrize("bin", (False, True))
|
@pytest.mark.parametrize("bin", (False, True))
|
||||||
def test_dump(self, tmpdir, bin):
|
def test_dump(self, tmpdir, bin):
|
||||||
|
@ -770,9 +787,9 @@ class TestLocalPath(CommonFSTests):
|
||||||
newfile = tmpdir.join("test1", "test")
|
newfile = tmpdir.join("test1", "test")
|
||||||
newfile.ensure()
|
newfile.ensure()
|
||||||
assert newfile.check(file=1)
|
assert newfile.check(file=1)
|
||||||
newfile.write("42")
|
newfile.write_text("42", encoding="utf-8")
|
||||||
newfile.ensure()
|
newfile.ensure()
|
||||||
s = newfile.read()
|
s = newfile.read_text(encoding="utf-8")
|
||||||
assert s == "42"
|
assert s == "42"
|
||||||
|
|
||||||
def test_ensure_filepath_withoutdir(self, tmpdir):
|
def test_ensure_filepath_withoutdir(self, tmpdir):
|
||||||
|
@ -806,9 +823,9 @@ class TestLocalPath(CommonFSTests):
|
||||||
newfilename = "/test" * 60 # type:ignore[unreachable]
|
newfilename = "/test" * 60 # type:ignore[unreachable]
|
||||||
l1 = tmpdir.join(newfilename)
|
l1 = tmpdir.join(newfilename)
|
||||||
l1.ensure(file=True)
|
l1.ensure(file=True)
|
||||||
l1.write("foo")
|
l1.write_text("foo", encoding="utf-8")
|
||||||
l2 = tmpdir.join(newfilename)
|
l2 = tmpdir.join(newfilename)
|
||||||
assert l2.read() == "foo"
|
assert l2.read_text(encoding="utf-8") == "foo"
|
||||||
|
|
||||||
def test_visit_depth_first(self, tmpdir):
|
def test_visit_depth_first(self, tmpdir):
|
||||||
tmpdir.ensure("a", "1")
|
tmpdir.ensure("a", "1")
|
||||||
|
@ -1278,14 +1295,14 @@ class TestPOSIXLocalPath:
|
||||||
def test_hardlink(self, tmpdir):
|
def test_hardlink(self, tmpdir):
|
||||||
linkpath = tmpdir.join("test")
|
linkpath = tmpdir.join("test")
|
||||||
filepath = tmpdir.join("file")
|
filepath = tmpdir.join("file")
|
||||||
filepath.write("Hello")
|
filepath.write_text("Hello", encoding="utf-8")
|
||||||
nlink = filepath.stat().nlink
|
nlink = filepath.stat().nlink
|
||||||
linkpath.mklinkto(filepath)
|
linkpath.mklinkto(filepath)
|
||||||
assert filepath.stat().nlink == nlink + 1
|
assert filepath.stat().nlink == nlink + 1
|
||||||
|
|
||||||
def test_symlink_are_identical(self, tmpdir):
|
def test_symlink_are_identical(self, tmpdir):
|
||||||
filepath = tmpdir.join("file")
|
filepath = tmpdir.join("file")
|
||||||
filepath.write("Hello")
|
filepath.write_text("Hello", encoding="utf-8")
|
||||||
linkpath = tmpdir.join("test")
|
linkpath = tmpdir.join("test")
|
||||||
linkpath.mksymlinkto(filepath)
|
linkpath.mksymlinkto(filepath)
|
||||||
assert linkpath.readlink() == str(filepath)
|
assert linkpath.readlink() == str(filepath)
|
||||||
|
@ -1293,7 +1310,7 @@ class TestPOSIXLocalPath:
|
||||||
def test_symlink_isfile(self, tmpdir):
|
def test_symlink_isfile(self, tmpdir):
|
||||||
linkpath = tmpdir.join("test")
|
linkpath = tmpdir.join("test")
|
||||||
filepath = tmpdir.join("file")
|
filepath = tmpdir.join("file")
|
||||||
filepath.write("")
|
filepath.write_text("", encoding="utf-8")
|
||||||
linkpath.mksymlinkto(filepath)
|
linkpath.mksymlinkto(filepath)
|
||||||
assert linkpath.check(file=1)
|
assert linkpath.check(file=1)
|
||||||
assert not linkpath.check(link=0, file=1)
|
assert not linkpath.check(link=0, file=1)
|
||||||
|
@ -1302,10 +1319,12 @@ class TestPOSIXLocalPath:
|
||||||
def test_symlink_relative(self, tmpdir):
|
def test_symlink_relative(self, tmpdir):
|
||||||
linkpath = tmpdir.join("test")
|
linkpath = tmpdir.join("test")
|
||||||
filepath = tmpdir.join("file")
|
filepath = tmpdir.join("file")
|
||||||
filepath.write("Hello")
|
filepath.write_text("Hello", encoding="utf-8")
|
||||||
linkpath.mksymlinkto(filepath, absolute=False)
|
linkpath.mksymlinkto(filepath, absolute=False)
|
||||||
assert linkpath.readlink() == "file"
|
assert linkpath.readlink() == "file"
|
||||||
assert filepath.read() == linkpath.read()
|
assert filepath.read_text(encoding="utf-8") == linkpath.read_text(
|
||||||
|
encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
def test_symlink_not_existing(self, tmpdir):
|
def test_symlink_not_existing(self, tmpdir):
|
||||||
linkpath = tmpdir.join("testnotexisting")
|
linkpath = tmpdir.join("testnotexisting")
|
||||||
|
@ -1338,7 +1357,7 @@ class TestPOSIXLocalPath:
|
||||||
def test_realpath_file(self, tmpdir):
|
def test_realpath_file(self, tmpdir):
|
||||||
linkpath = tmpdir.join("test")
|
linkpath = tmpdir.join("test")
|
||||||
filepath = tmpdir.join("file")
|
filepath = tmpdir.join("file")
|
||||||
filepath.write("")
|
filepath.write_text("", encoding="utf-8")
|
||||||
linkpath.mksymlinkto(filepath)
|
linkpath.mksymlinkto(filepath)
|
||||||
realpath = linkpath.realpath()
|
realpath = linkpath.realpath()
|
||||||
assert realpath.basename == "file"
|
assert realpath.basename == "file"
|
||||||
|
@ -1383,7 +1402,7 @@ class TestPOSIXLocalPath:
|
||||||
atime1 = path.atime()
|
atime1 = path.atime()
|
||||||
# we could wait here but timer resolution is very
|
# we could wait here but timer resolution is very
|
||||||
# system dependent
|
# system dependent
|
||||||
path.read()
|
path.read_binary()
|
||||||
time.sleep(ATIME_RESOLUTION)
|
time.sleep(ATIME_RESOLUTION)
|
||||||
atime2 = path.atime()
|
atime2 = path.atime()
|
||||||
time.sleep(ATIME_RESOLUTION)
|
time.sleep(ATIME_RESOLUTION)
|
||||||
|
@ -1467,7 +1486,7 @@ class TestPOSIXLocalPath:
|
||||||
test_files = ["a", "b", "c"]
|
test_files = ["a", "b", "c"]
|
||||||
src = tmpdir.join("src")
|
src = tmpdir.join("src")
|
||||||
for f in test_files:
|
for f in test_files:
|
||||||
src.join(f).write(f, ensure=True)
|
src.join(f).write_text(f, ensure=True, encoding="utf-8")
|
||||||
dst = tmpdir.join("dst")
|
dst = tmpdir.join("dst")
|
||||||
# a small delay before the copy
|
# a small delay before the copy
|
||||||
time.sleep(ATIME_RESOLUTION)
|
time.sleep(ATIME_RESOLUTION)
|
||||||
|
@ -1521,10 +1540,11 @@ class TestUnicodePy2Py3:
|
||||||
def test_read_write(self, tmpdir):
|
def test_read_write(self, tmpdir):
|
||||||
x = tmpdir.join("hello")
|
x = tmpdir.join("hello")
|
||||||
part = "hällo"
|
part = "hällo"
|
||||||
x.write(part)
|
with ignore_encoding_warning():
|
||||||
assert x.read() == part
|
x.write(part)
|
||||||
x.write(part.encode(sys.getdefaultencoding()))
|
assert x.read() == part
|
||||||
assert x.read() == part.encode(sys.getdefaultencoding())
|
x.write(part.encode(sys.getdefaultencoding()))
|
||||||
|
assert x.read() == part.encode(sys.getdefaultencoding())
|
||||||
|
|
||||||
|
|
||||||
class TestBinaryAndTextMethods:
|
class TestBinaryAndTextMethods:
|
||||||
|
|
|
@ -267,7 +267,7 @@ class TestGeneralUsage:
|
||||||
def test_issue109_sibling_conftests_not_loaded(self, pytester: Pytester) -> None:
|
def test_issue109_sibling_conftests_not_loaded(self, pytester: Pytester) -> None:
|
||||||
sub1 = pytester.mkdir("sub1")
|
sub1 = pytester.mkdir("sub1")
|
||||||
sub2 = pytester.mkdir("sub2")
|
sub2 = pytester.mkdir("sub2")
|
||||||
sub1.joinpath("conftest.py").write_text("assert 0")
|
sub1.joinpath("conftest.py").write_text("assert 0", encoding="utf-8")
|
||||||
result = pytester.runpytest(sub2)
|
result = pytester.runpytest(sub2)
|
||||||
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
||||||
sub2.joinpath("__init__.py").touch()
|
sub2.joinpath("__init__.py").touch()
|
||||||
|
@ -467,7 +467,7 @@ class TestGeneralUsage:
|
||||||
assert "invalid" in str(excinfo.value)
|
assert "invalid" in str(excinfo.value)
|
||||||
|
|
||||||
p = pytester.path.joinpath("test_test_plugins_given_as_strings.py")
|
p = pytester.path.joinpath("test_test_plugins_given_as_strings.py")
|
||||||
p.write_text("def test_foo(): pass")
|
p.write_text("def test_foo(): pass", encoding="utf-8")
|
||||||
mod = types.ModuleType("myplugin")
|
mod = types.ModuleType("myplugin")
|
||||||
monkeypatch.setitem(sys.modules, "myplugin", mod)
|
monkeypatch.setitem(sys.modules, "myplugin", mod)
|
||||||
assert pytest.main(args=[str(pytester.path)], plugins=["myplugin"]) == 0
|
assert pytest.main(args=[str(pytester.path)], plugins=["myplugin"]) == 0
|
||||||
|
@ -587,7 +587,7 @@ class TestInvocationVariants:
|
||||||
def test_pyargs_importerror(self, pytester: Pytester, monkeypatch) -> None:
|
def test_pyargs_importerror(self, pytester: Pytester, monkeypatch) -> None:
|
||||||
monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", False)
|
monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", False)
|
||||||
path = pytester.mkpydir("tpkg")
|
path = pytester.mkpydir("tpkg")
|
||||||
path.joinpath("test_hello.py").write_text("raise ImportError")
|
path.joinpath("test_hello.py").write_text("raise ImportError", encoding="utf-8")
|
||||||
|
|
||||||
result = pytester.runpytest("--pyargs", "tpkg.test_hello", syspathinsert=True)
|
result = pytester.runpytest("--pyargs", "tpkg.test_hello", syspathinsert=True)
|
||||||
assert result.ret != 0
|
assert result.ret != 0
|
||||||
|
@ -597,10 +597,10 @@ class TestInvocationVariants:
|
||||||
def test_pyargs_only_imported_once(self, pytester: Pytester) -> None:
|
def test_pyargs_only_imported_once(self, pytester: Pytester) -> None:
|
||||||
pkg = pytester.mkpydir("foo")
|
pkg = pytester.mkpydir("foo")
|
||||||
pkg.joinpath("test_foo.py").write_text(
|
pkg.joinpath("test_foo.py").write_text(
|
||||||
"print('hello from test_foo')\ndef test(): pass"
|
"print('hello from test_foo')\ndef test(): pass", encoding="utf-8"
|
||||||
)
|
)
|
||||||
pkg.joinpath("conftest.py").write_text(
|
pkg.joinpath("conftest.py").write_text(
|
||||||
"def pytest_configure(config): print('configuring')"
|
"def pytest_configure(config): print('configuring')", encoding="utf-8"
|
||||||
)
|
)
|
||||||
|
|
||||||
result = pytester.runpytest(
|
result = pytester.runpytest(
|
||||||
|
@ -613,7 +613,7 @@ class TestInvocationVariants:
|
||||||
|
|
||||||
def test_pyargs_filename_looks_like_module(self, pytester: Pytester) -> None:
|
def test_pyargs_filename_looks_like_module(self, pytester: Pytester) -> None:
|
||||||
pytester.path.joinpath("conftest.py").touch()
|
pytester.path.joinpath("conftest.py").touch()
|
||||||
pytester.path.joinpath("t.py").write_text("def test(): pass")
|
pytester.path.joinpath("t.py").write_text("def test(): pass", encoding="utf-8")
|
||||||
result = pytester.runpytest("--pyargs", "t.py")
|
result = pytester.runpytest("--pyargs", "t.py")
|
||||||
assert result.ret == ExitCode.OK
|
assert result.ret == ExitCode.OK
|
||||||
|
|
||||||
|
@ -622,8 +622,12 @@ class TestInvocationVariants:
|
||||||
|
|
||||||
monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", False)
|
monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", False)
|
||||||
path = pytester.mkpydir("tpkg")
|
path = pytester.mkpydir("tpkg")
|
||||||
path.joinpath("test_hello.py").write_text("def test_hello(): pass")
|
path.joinpath("test_hello.py").write_text(
|
||||||
path.joinpath("test_world.py").write_text("def test_world(): pass")
|
"def test_hello(): pass", encoding="utf-8"
|
||||||
|
)
|
||||||
|
path.joinpath("test_world.py").write_text(
|
||||||
|
"def test_world(): pass", encoding="utf-8"
|
||||||
|
)
|
||||||
result = pytester.runpytest("--pyargs", "tpkg")
|
result = pytester.runpytest("--pyargs", "tpkg")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stdout.fnmatch_lines(["*2 passed*"])
|
result.stdout.fnmatch_lines(["*2 passed*"])
|
||||||
|
@ -662,13 +666,15 @@ class TestInvocationVariants:
|
||||||
ns = d.joinpath("ns_pkg")
|
ns = d.joinpath("ns_pkg")
|
||||||
ns.mkdir()
|
ns.mkdir()
|
||||||
ns.joinpath("__init__.py").write_text(
|
ns.joinpath("__init__.py").write_text(
|
||||||
"__import__('pkg_resources').declare_namespace(__name__)"
|
"__import__('pkg_resources').declare_namespace(__name__)",
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
lib = ns.joinpath(dirname)
|
lib = ns.joinpath(dirname)
|
||||||
lib.mkdir()
|
lib.mkdir()
|
||||||
lib.joinpath("__init__.py").touch()
|
lib.joinpath("__init__.py").touch()
|
||||||
lib.joinpath(f"test_{dirname}.py").write_text(
|
lib.joinpath(f"test_{dirname}.py").write_text(
|
||||||
f"def test_{dirname}(): pass\ndef test_other():pass"
|
f"def test_{dirname}(): pass\ndef test_other():pass",
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
# The structure of the test directory is now:
|
# The structure of the test directory is now:
|
||||||
|
@ -754,10 +760,10 @@ class TestInvocationVariants:
|
||||||
lib.mkdir()
|
lib.mkdir()
|
||||||
lib.joinpath("__init__.py").touch()
|
lib.joinpath("__init__.py").touch()
|
||||||
lib.joinpath("test_bar.py").write_text(
|
lib.joinpath("test_bar.py").write_text(
|
||||||
"def test_bar(): pass\ndef test_other(a_fixture):pass"
|
"def test_bar(): pass\ndef test_other(a_fixture):pass", encoding="utf-8"
|
||||||
)
|
)
|
||||||
lib.joinpath("conftest.py").write_text(
|
lib.joinpath("conftest.py").write_text(
|
||||||
"import pytest\n@pytest.fixture\ndef a_fixture():pass"
|
"import pytest\n@pytest.fixture\ndef a_fixture():pass", encoding="utf-8"
|
||||||
)
|
)
|
||||||
|
|
||||||
d_local = pytester.mkdir("symlink_root")
|
d_local = pytester.mkdir("symlink_root")
|
||||||
|
@ -1158,7 +1164,6 @@ def test_usage_error_code(pytester: Pytester) -> None:
|
||||||
assert result.ret == ExitCode.USAGE_ERROR
|
assert result.ret == ExitCode.USAGE_ERROR
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("default::pytest.PytestUnhandledCoroutineWarning")
|
|
||||||
def test_warn_on_async_function(pytester: Pytester) -> None:
|
def test_warn_on_async_function(pytester: Pytester) -> None:
|
||||||
# In the below we .close() the coroutine only to avoid
|
# In the below we .close() the coroutine only to avoid
|
||||||
# "RuntimeWarning: coroutine 'test_2' was never awaited"
|
# "RuntimeWarning: coroutine 'test_2' was never awaited"
|
||||||
|
@ -1175,7 +1180,7 @@ def test_warn_on_async_function(pytester: Pytester) -> None:
|
||||||
return coro
|
return coro
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest("-Wdefault")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"test_async.py::test_1",
|
"test_async.py::test_1",
|
||||||
|
@ -1191,7 +1196,6 @@ def test_warn_on_async_function(pytester: Pytester) -> None:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("default::pytest.PytestUnhandledCoroutineWarning")
|
|
||||||
def test_warn_on_async_gen_function(pytester: Pytester) -> None:
|
def test_warn_on_async_gen_function(pytester: Pytester) -> None:
|
||||||
pytester.makepyfile(
|
pytester.makepyfile(
|
||||||
test_async="""
|
test_async="""
|
||||||
|
@ -1203,7 +1207,7 @@ def test_warn_on_async_gen_function(pytester: Pytester) -> None:
|
||||||
return test_2()
|
return test_2()
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest("-Wdefault")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"test_async.py::test_1",
|
"test_async.py::test_1",
|
||||||
|
@ -1276,8 +1280,7 @@ def test_tee_stdio_captures_and_live_prints(pytester: Pytester) -> None:
|
||||||
result.stderr.fnmatch_lines(["*@this is stderr@*"])
|
result.stderr.fnmatch_lines(["*@this is stderr@*"])
|
||||||
|
|
||||||
# now ensure the output is in the junitxml
|
# now ensure the output is in the junitxml
|
||||||
with open(pytester.path.joinpath("output.xml")) as f:
|
fullXml = pytester.path.joinpath("output.xml").read_text(encoding="utf-8")
|
||||||
fullXml = f.read()
|
|
||||||
assert "@this is stdout@\n" in fullXml
|
assert "@this is stdout@\n" in fullXml
|
||||||
assert "@this is stderr@\n" in fullXml
|
assert "@this is stderr@\n" in fullXml
|
||||||
|
|
||||||
|
|
|
@ -374,7 +374,7 @@ def test_excinfo_no_sourcecode():
|
||||||
|
|
||||||
def test_excinfo_no_python_sourcecode(tmp_path: Path) -> None:
|
def test_excinfo_no_python_sourcecode(tmp_path: Path) -> None:
|
||||||
# XXX: simplified locally testable version
|
# XXX: simplified locally testable version
|
||||||
tmp_path.joinpath("test.txt").write_text("{{ h()}}:")
|
tmp_path.joinpath("test.txt").write_text("{{ h()}}:", encoding="utf-8")
|
||||||
|
|
||||||
jinja2 = pytest.importorskip("jinja2")
|
jinja2 = pytest.importorskip("jinja2")
|
||||||
loader = jinja2.FileSystemLoader(str(tmp_path))
|
loader = jinja2.FileSystemLoader(str(tmp_path))
|
||||||
|
@ -451,7 +451,7 @@ class TestFormattedExcinfo:
|
||||||
source = textwrap.dedent(source)
|
source = textwrap.dedent(source)
|
||||||
modpath = tmp_path.joinpath("mod.py")
|
modpath = tmp_path.joinpath("mod.py")
|
||||||
tmp_path.joinpath("__init__.py").touch()
|
tmp_path.joinpath("__init__.py").touch()
|
||||||
modpath.write_text(source)
|
modpath.write_text(source, encoding="utf-8")
|
||||||
importlib.invalidate_caches()
|
importlib.invalidate_caches()
|
||||||
return import_path(modpath, root=tmp_path)
|
return import_path(modpath, root=tmp_path)
|
||||||
|
|
||||||
|
@ -1023,7 +1023,7 @@ raise ValueError()
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
excinfo = pytest.raises(ValueError, mod.f)
|
excinfo = pytest.raises(ValueError, mod.f)
|
||||||
tmp_path.joinpath("mod.py").write_text("asdf")
|
tmp_path.joinpath("mod.py").write_text("asdf", encoding="utf-8")
|
||||||
excinfo.traceback = excinfo.traceback.filter(excinfo)
|
excinfo.traceback = excinfo.traceback.filter(excinfo)
|
||||||
repr = excinfo.getrepr()
|
repr = excinfo.getrepr()
|
||||||
repr.toterminal(tw_mock)
|
repr.toterminal(tw_mock)
|
||||||
|
|
|
@ -294,7 +294,7 @@ def test_source_of_class_at_eof_without_newline(_sys_snapshot, tmp_path: Path) -
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
path = tmp_path.joinpath("a.py")
|
path = tmp_path.joinpath("a.py")
|
||||||
path.write_text(str(source))
|
path.write_text(str(source), encoding="utf-8")
|
||||||
mod: Any = import_path(path, root=tmp_path)
|
mod: Any = import_path(path, root=tmp_path)
|
||||||
s2 = Source(mod.A)
|
s2 = Source(mod.A)
|
||||||
assert str(source).strip() == str(s2).strip()
|
assert str(source).strip() == str(s2).strip()
|
||||||
|
|
|
@ -103,7 +103,7 @@ def test_strict_option_is_deprecated(pytester: Pytester) -> None:
|
||||||
def test_foo(): pass
|
def test_foo(): pass
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest("--strict")
|
result = pytester.runpytest("--strict", "-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"'unknown' not found in `markers` configuration option",
|
"'unknown' not found in `markers` configuration option",
|
||||||
|
@ -189,7 +189,7 @@ class TestSkipMsgArgumentDeprecated:
|
||||||
pytest.skip(msg="skippedmsg")
|
pytest.skip(msg="skippedmsg")
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(p)
|
result = pytester.runpytest(p, "-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"*PytestRemovedIn8Warning: pytest.skip(msg=...) is now deprecated, "
|
"*PytestRemovedIn8Warning: pytest.skip(msg=...) is now deprecated, "
|
||||||
|
@ -208,7 +208,7 @@ class TestSkipMsgArgumentDeprecated:
|
||||||
pytest.fail(msg="failedmsg")
|
pytest.fail(msg="failedmsg")
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(p)
|
result = pytester.runpytest(p, "-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"*PytestRemovedIn8Warning: pytest.fail(msg=...) is now deprecated, "
|
"*PytestRemovedIn8Warning: pytest.fail(msg=...) is now deprecated, "
|
||||||
|
@ -227,7 +227,7 @@ class TestSkipMsgArgumentDeprecated:
|
||||||
pytest.exit(msg="exitmsg")
|
pytest.exit(msg="exitmsg")
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(p)
|
result = pytester.runpytest(p, "-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"*PytestRemovedIn8Warning: pytest.exit(msg=...) is now deprecated, "
|
"*PytestRemovedIn8Warning: pytest.exit(msg=...) is now deprecated, "
|
||||||
|
@ -245,7 +245,7 @@ def test_deprecation_of_cmdline_preparse(pytester: Pytester) -> None:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"*PytestRemovedIn8Warning: The pytest_cmdline_preparse hook is deprecated*",
|
"*PytestRemovedIn8Warning: The pytest_cmdline_preparse hook is deprecated*",
|
||||||
|
@ -281,6 +281,57 @@ def test_importing_instance_is_deprecated(pytester: Pytester) -> None:
|
||||||
from _pytest.python import Instance # noqa: F401
|
from _pytest.python import Instance # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
|
def test_fixture_disallow_on_marked_functions():
|
||||||
|
"""Test that applying @pytest.fixture to a marked function warns (#3364)."""
|
||||||
|
with pytest.warns(
|
||||||
|
pytest.PytestRemovedIn8Warning,
|
||||||
|
match=r"Marks applied to fixtures have no effect",
|
||||||
|
) as record:
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
@pytest.mark.parametrize("example", ["hello"])
|
||||||
|
@pytest.mark.usefixtures("tmp_path")
|
||||||
|
def foo():
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
# it's only possible to get one warning here because you're already prevented
|
||||||
|
# from applying @fixture twice
|
||||||
|
# ValueError("fixture is being applied more than once to the same function")
|
||||||
|
assert len(record) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_fixture_disallow_marks_on_fixtures():
|
||||||
|
"""Test that applying a mark to a fixture warns (#3364)."""
|
||||||
|
with pytest.warns(
|
||||||
|
pytest.PytestRemovedIn8Warning,
|
||||||
|
match=r"Marks applied to fixtures have no effect",
|
||||||
|
) as record:
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("example", ["hello"])
|
||||||
|
@pytest.mark.usefixtures("tmp_path")
|
||||||
|
@pytest.fixture
|
||||||
|
def foo():
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
assert len(record) == 2 # one for each mark decorator
|
||||||
|
|
||||||
|
|
||||||
|
def test_fixture_disallowed_between_marks():
|
||||||
|
"""Test that applying a mark to a fixture warns (#3364)."""
|
||||||
|
with pytest.warns(
|
||||||
|
pytest.PytestRemovedIn8Warning,
|
||||||
|
match=r"Marks applied to fixtures have no effect",
|
||||||
|
) as record:
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("example", ["hello"])
|
||||||
|
@pytest.fixture
|
||||||
|
@pytest.mark.usefixtures("tmp_path")
|
||||||
|
def foo():
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
assert len(record) == 2 # one for each mark decorator
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("default")
|
@pytest.mark.filterwarnings("default")
|
||||||
def test_nose_deprecated_with_setup(pytester: Pytester) -> None:
|
def test_nose_deprecated_with_setup(pytester: Pytester) -> None:
|
||||||
pytest.importorskip("nose")
|
pytest.importorskip("nose")
|
||||||
|
@ -299,7 +350,7 @@ def test_nose_deprecated_with_setup(pytester: Pytester) -> None:
|
||||||
...
|
...
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
output = pytester.runpytest()
|
output = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||||
message = [
|
message = [
|
||||||
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
|
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
|
||||||
"*test_nose_deprecated_with_setup.py::test_omits_warnings is using nose method: `setup_fn_no_op` (setup)",
|
"*test_nose_deprecated_with_setup.py::test_omits_warnings is using nose method: `setup_fn_no_op` (setup)",
|
||||||
|
@ -327,7 +378,7 @@ def test_nose_deprecated_setup_teardown(pytester: Pytester) -> None:
|
||||||
...
|
...
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
output = pytester.runpytest()
|
output = pytester.runpytest("-Wdefault::pytest.PytestRemovedIn8Warning")
|
||||||
message = [
|
message = [
|
||||||
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
|
"*PytestRemovedIn8Warning: Support for nose tests is deprecated and will be removed in a future release.",
|
||||||
"*test_nose_deprecated_setup_teardown.py::Test::test is using nose-specific method: `setup(self)`",
|
"*test_nose_deprecated_setup_teardown.py::Test::test is using nose-specific method: `setup(self)`",
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
def test_init():
|
||||||
|
pass
|
|
@ -1,2 +1,2 @@
|
||||||
def test():
|
def test_foo():
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -81,7 +81,7 @@ def test_root_logger_affected(pytester: Pytester) -> None:
|
||||||
# not the info one, because the default level of the root logger is
|
# not the info one, because the default level of the root logger is
|
||||||
# WARNING.
|
# WARNING.
|
||||||
assert os.path.isfile(log_file)
|
assert os.path.isfile(log_file)
|
||||||
with open(log_file) as rfh:
|
with open(log_file, encoding="utf-8") as rfh:
|
||||||
contents = rfh.read()
|
contents = rfh.read()
|
||||||
assert "info text going to logger" not in contents
|
assert "info text going to logger" not in contents
|
||||||
assert "warning text going to logger" in contents
|
assert "warning text going to logger" in contents
|
||||||
|
@ -656,7 +656,7 @@ def test_log_file_cli(pytester: Pytester) -> None:
|
||||||
# make sure that we get a '0' exit code for the testsuite
|
# make sure that we get a '0' exit code for the testsuite
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
assert os.path.isfile(log_file)
|
assert os.path.isfile(log_file)
|
||||||
with open(log_file) as rfh:
|
with open(log_file, encoding="utf-8") as rfh:
|
||||||
contents = rfh.read()
|
contents = rfh.read()
|
||||||
assert "This log message will be shown" in contents
|
assert "This log message will be shown" in contents
|
||||||
assert "This log message won't be shown" not in contents
|
assert "This log message won't be shown" not in contents
|
||||||
|
@ -687,7 +687,7 @@ def test_log_file_cli_level(pytester: Pytester) -> None:
|
||||||
# make sure that we get a '0' exit code for the testsuite
|
# make sure that we get a '0' exit code for the testsuite
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
assert os.path.isfile(log_file)
|
assert os.path.isfile(log_file)
|
||||||
with open(log_file) as rfh:
|
with open(log_file, encoding="utf-8") as rfh:
|
||||||
contents = rfh.read()
|
contents = rfh.read()
|
||||||
assert "This log message will be shown" in contents
|
assert "This log message will be shown" in contents
|
||||||
assert "This log message won't be shown" not in contents
|
assert "This log message won't be shown" not in contents
|
||||||
|
@ -738,7 +738,7 @@ def test_log_file_ini(pytester: Pytester) -> None:
|
||||||
# make sure that we get a '0' exit code for the testsuite
|
# make sure that we get a '0' exit code for the testsuite
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
assert os.path.isfile(log_file)
|
assert os.path.isfile(log_file)
|
||||||
with open(log_file) as rfh:
|
with open(log_file, encoding="utf-8") as rfh:
|
||||||
contents = rfh.read()
|
contents = rfh.read()
|
||||||
assert "This log message will be shown" in contents
|
assert "This log message will be shown" in contents
|
||||||
assert "This log message won't be shown" not in contents
|
assert "This log message won't be shown" not in contents
|
||||||
|
@ -777,7 +777,7 @@ def test_log_file_ini_level(pytester: Pytester) -> None:
|
||||||
# make sure that we get a '0' exit code for the testsuite
|
# make sure that we get a '0' exit code for the testsuite
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
assert os.path.isfile(log_file)
|
assert os.path.isfile(log_file)
|
||||||
with open(log_file) as rfh:
|
with open(log_file, encoding="utf-8") as rfh:
|
||||||
contents = rfh.read()
|
contents = rfh.read()
|
||||||
assert "This log message will be shown" in contents
|
assert "This log message will be shown" in contents
|
||||||
assert "This log message won't be shown" not in contents
|
assert "This log message won't be shown" not in contents
|
||||||
|
@ -985,7 +985,7 @@ def test_log_in_hooks(pytester: Pytester) -> None:
|
||||||
)
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
result.stdout.fnmatch_lines(["*sessionstart*", "*runtestloop*", "*sessionfinish*"])
|
result.stdout.fnmatch_lines(["*sessionstart*", "*runtestloop*", "*sessionfinish*"])
|
||||||
with open(log_file) as rfh:
|
with open(log_file, encoding="utf-8") as rfh:
|
||||||
contents = rfh.read()
|
contents = rfh.read()
|
||||||
assert "sessionstart" in contents
|
assert "sessionstart" in contents
|
||||||
assert "runtestloop" in contents
|
assert "runtestloop" in contents
|
||||||
|
@ -1021,7 +1021,7 @@ def test_log_in_runtest_logreport(pytester: Pytester) -> None:
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
pytester.runpytest()
|
pytester.runpytest()
|
||||||
with open(log_file) as rfh:
|
with open(log_file, encoding="utf-8") as rfh:
|
||||||
contents = rfh.read()
|
contents = rfh.read()
|
||||||
assert contents.count("logreport") == 3
|
assert contents.count("logreport") == 3
|
||||||
|
|
||||||
|
@ -1065,11 +1065,11 @@ def test_log_set_path(pytester: Pytester) -> None:
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
pytester.runpytest()
|
pytester.runpytest()
|
||||||
with open(os.path.join(report_dir_base, "test_first")) as rfh:
|
with open(os.path.join(report_dir_base, "test_first"), encoding="utf-8") as rfh:
|
||||||
content = rfh.read()
|
content = rfh.read()
|
||||||
assert "message from test 1" in content
|
assert "message from test 1" in content
|
||||||
|
|
||||||
with open(os.path.join(report_dir_base, "test_second")) as rfh:
|
with open(os.path.join(report_dir_base, "test_second"), encoding="utf-8") as rfh:
|
||||||
content = rfh.read()
|
content = rfh.read()
|
||||||
assert "message from test 2" in content
|
assert "message from test 2" in content
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
anyio[curio,trio]==3.7.0
|
anyio[curio,trio]==3.7.0
|
||||||
django==4.2.1
|
django==4.2.2
|
||||||
pytest-asyncio==0.21.0
|
pytest-asyncio==0.21.0
|
||||||
pytest-bdd==6.1.1
|
pytest-bdd==6.1.1
|
||||||
pytest-cov==4.1.0
|
pytest-cov==4.1.0
|
||||||
pytest-django==4.5.2
|
pytest-django==4.5.2
|
||||||
pytest-flakes==4.0.5
|
pytest-flakes==4.0.5
|
||||||
pytest-html==3.2.0
|
pytest-html==3.2.0
|
||||||
pytest-mock==3.10.0
|
pytest-mock==3.11.1
|
||||||
pytest-rerunfailures==11.1.2
|
pytest-rerunfailures==11.1.2
|
||||||
pytest-sugar==0.9.7
|
pytest-sugar==0.9.7
|
||||||
pytest-trio==0.7.0
|
pytest-trio==0.7.0
|
||||||
pytest-twisted==1.14.0
|
pytest-twisted==1.14.0
|
||||||
twisted==22.8.0
|
twisted==22.8.0
|
||||||
pytest-xvfb==2.0.0
|
pytest-xvfb==3.0.0
|
||||||
|
|
|
@ -60,7 +60,8 @@ class TestModule:
|
||||||
""".format(
|
""".format(
|
||||||
str(root2)
|
str(root2)
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.chdir(root2)
|
mp.chdir(root2)
|
||||||
|
@ -832,7 +833,8 @@ class TestConftestCustomization:
|
||||||
mod = outcome.get_result()
|
mod = outcome.get_result()
|
||||||
mod.obj.hello = "world"
|
mod.obj.hello = "world"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
b.joinpath("test_module.py").write_text(
|
b.joinpath("test_module.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -840,7 +842,8 @@ class TestConftestCustomization:
|
||||||
def test_hello():
|
def test_hello():
|
||||||
assert hello == "world"
|
assert hello == "world"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
reprec = pytester.inline_run()
|
reprec = pytester.inline_run()
|
||||||
reprec.assertoutcome(passed=1)
|
reprec.assertoutcome(passed=1)
|
||||||
|
@ -861,7 +864,8 @@ class TestConftestCustomization:
|
||||||
for func in result:
|
for func in result:
|
||||||
func._some123 = "world"
|
func._some123 = "world"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
b.joinpath("test_module.py").write_text(
|
b.joinpath("test_module.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -874,7 +878,8 @@ class TestConftestCustomization:
|
||||||
def test_hello(obj):
|
def test_hello(obj):
|
||||||
assert obj == "world"
|
assert obj == "world"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
reprec = pytester.inline_run()
|
reprec = pytester.inline_run()
|
||||||
reprec.assertoutcome(passed=1)
|
reprec.assertoutcome(passed=1)
|
||||||
|
@ -897,25 +902,29 @@ class TestConftestCustomization:
|
||||||
def test_issue2369_collect_module_fileext(self, pytester: Pytester) -> None:
|
def test_issue2369_collect_module_fileext(self, pytester: Pytester) -> None:
|
||||||
"""Ensure we can collect files with weird file extensions as Python
|
"""Ensure we can collect files with weird file extensions as Python
|
||||||
modules (#2369)"""
|
modules (#2369)"""
|
||||||
# We'll implement a little finder and loader to import files containing
|
# Implement a little meta path finder to import files containing
|
||||||
# Python source code whose file extension is ".narf".
|
# Python source code whose file extension is ".narf".
|
||||||
pytester.makeconftest(
|
pytester.makeconftest(
|
||||||
"""
|
"""
|
||||||
import sys, os, imp
|
import sys
|
||||||
|
import os.path
|
||||||
|
from importlib.util import spec_from_loader
|
||||||
|
from importlib.machinery import SourceFileLoader
|
||||||
from _pytest.python import Module
|
from _pytest.python import Module
|
||||||
|
|
||||||
class Loader(object):
|
class MetaPathFinder:
|
||||||
def load_module(self, name):
|
def find_spec(self, fullname, path, target=None):
|
||||||
return imp.load_source(name, name + ".narf")
|
if os.path.exists(fullname + ".narf"):
|
||||||
class Finder(object):
|
return spec_from_loader(
|
||||||
def find_module(self, name, path=None):
|
fullname,
|
||||||
if os.path.exists(name + ".narf"):
|
SourceFileLoader(fullname, fullname + ".narf"),
|
||||||
return Loader()
|
)
|
||||||
sys.meta_path.append(Finder())
|
sys.meta_path.append(MetaPathFinder())
|
||||||
|
|
||||||
def pytest_collect_file(file_path, parent):
|
def pytest_collect_file(file_path, parent):
|
||||||
if file_path.suffix == ".narf":
|
if file_path.suffix == ".narf":
|
||||||
return Module.from_parent(path=file_path, parent=parent)"""
|
return Module.from_parent(path=file_path, parent=parent)
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
pytester.makefile(
|
pytester.makefile(
|
||||||
".narf",
|
".narf",
|
||||||
|
@ -970,7 +979,8 @@ def test_setup_only_available_in_subdir(pytester: Pytester) -> None:
|
||||||
def pytest_runtest_teardown(item):
|
def pytest_runtest_teardown(item):
|
||||||
assert item.path.stem == "test_in_sub1"
|
assert item.path.stem == "test_in_sub1"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
sub2.joinpath("conftest.py").write_text(
|
sub2.joinpath("conftest.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -983,10 +993,11 @@ def test_setup_only_available_in_subdir(pytester: Pytester) -> None:
|
||||||
def pytest_runtest_teardown(item):
|
def pytest_runtest_teardown(item):
|
||||||
assert item.path.stem == "test_in_sub2"
|
assert item.path.stem == "test_in_sub2"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
sub1.joinpath("test_in_sub1.py").write_text("def test_1(): pass")
|
sub1.joinpath("test_in_sub1.py").write_text("def test_1(): pass", encoding="utf-8")
|
||||||
sub2.joinpath("test_in_sub2.py").write_text("def test_2(): pass")
|
sub2.joinpath("test_in_sub2.py").write_text("def test_2(): pass", encoding="utf-8")
|
||||||
result = pytester.runpytest("-v", "-s")
|
result = pytester.runpytest("-v", "-s")
|
||||||
result.assert_outcomes(passed=2)
|
result.assert_outcomes(passed=2)
|
||||||
|
|
||||||
|
@ -1374,7 +1385,8 @@ def test_skip_duplicates_by_default(pytester: Pytester) -> None:
|
||||||
def test_real():
|
def test_real():
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(str(a), str(a))
|
result = pytester.runpytest(str(a), str(a))
|
||||||
result.stdout.fnmatch_lines(["*collected 1 item*"])
|
result.stdout.fnmatch_lines(["*collected 1 item*"])
|
||||||
|
@ -1394,7 +1406,8 @@ def test_keep_duplicates(pytester: Pytester) -> None:
|
||||||
def test_real():
|
def test_real():
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest("--keep-duplicates", str(a), str(a))
|
result = pytester.runpytest("--keep-duplicates", str(a), str(a))
|
||||||
result.stdout.fnmatch_lines(["*collected 2 item*"])
|
result.stdout.fnmatch_lines(["*collected 2 item*"])
|
||||||
|
@ -1407,10 +1420,15 @@ def test_package_collection_infinite_recursion(pytester: Pytester) -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_package_collection_init_given_as_argument(pytester: Pytester) -> None:
|
def test_package_collection_init_given_as_argument(pytester: Pytester) -> None:
|
||||||
"""Regression test for #3749"""
|
"""Regression test for #3749, #8976, #9263, #9313.
|
||||||
|
|
||||||
|
Specifying an __init__.py file directly should collect only the __init__.py
|
||||||
|
Module, not the entire package.
|
||||||
|
"""
|
||||||
p = pytester.copy_example("collect/package_init_given_as_arg")
|
p = pytester.copy_example("collect/package_init_given_as_arg")
|
||||||
result = pytester.runpytest(p / "pkg" / "__init__.py")
|
items, hookrecorder = pytester.inline_genitems(p / "pkg" / "__init__.py")
|
||||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
assert len(items) == 1
|
||||||
|
assert items[0].name == "test_init"
|
||||||
|
|
||||||
|
|
||||||
def test_package_with_modules(pytester: Pytester) -> None:
|
def test_package_with_modules(pytester: Pytester) -> None:
|
||||||
|
@ -1439,8 +1457,12 @@ def test_package_with_modules(pytester: Pytester) -> None:
|
||||||
sub2_test = sub2.joinpath("test")
|
sub2_test = sub2.joinpath("test")
|
||||||
sub2_test.mkdir(parents=True)
|
sub2_test.mkdir(parents=True)
|
||||||
|
|
||||||
sub1_test.joinpath("test_in_sub1.py").write_text("def test_1(): pass")
|
sub1_test.joinpath("test_in_sub1.py").write_text(
|
||||||
sub2_test.joinpath("test_in_sub2.py").write_text("def test_2(): pass")
|
"def test_1(): pass", encoding="utf-8"
|
||||||
|
)
|
||||||
|
sub2_test.joinpath("test_in_sub2.py").write_text(
|
||||||
|
"def test_2(): pass", encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
# Execute from .
|
# Execute from .
|
||||||
result = pytester.runpytest("-v", "-s")
|
result = pytester.runpytest("-v", "-s")
|
||||||
|
@ -1484,9 +1506,11 @@ def test_package_ordering(pytester: Pytester) -> None:
|
||||||
sub2_test = sub2.joinpath("test")
|
sub2_test = sub2.joinpath("test")
|
||||||
sub2_test.mkdir(parents=True)
|
sub2_test.mkdir(parents=True)
|
||||||
|
|
||||||
root.joinpath("Test_root.py").write_text("def test_1(): pass")
|
root.joinpath("Test_root.py").write_text("def test_1(): pass", encoding="utf-8")
|
||||||
sub1.joinpath("Test_sub1.py").write_text("def test_2(): pass")
|
sub1.joinpath("Test_sub1.py").write_text("def test_2(): pass", encoding="utf-8")
|
||||||
sub2_test.joinpath("test_sub2.py").write_text("def test_3(): pass")
|
sub2_test.joinpath("test_sub2.py").write_text(
|
||||||
|
"def test_3(): pass", encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
# Execute from .
|
# Execute from .
|
||||||
result = pytester.runpytest("-v", "-s")
|
result = pytester.runpytest("-v", "-s")
|
||||||
|
|
|
@ -289,7 +289,8 @@ class TestFillFixtures:
|
||||||
def spam():
|
def spam():
|
||||||
return 'spam'
|
return 'spam'
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
testfile = subdir.joinpath("test_spam.py")
|
testfile = subdir.joinpath("test_spam.py")
|
||||||
testfile.write_text(
|
testfile.write_text(
|
||||||
|
@ -298,7 +299,8 @@ class TestFillFixtures:
|
||||||
def test_spam(spam):
|
def test_spam(spam):
|
||||||
assert spam == "spam"
|
assert spam == "spam"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||||
|
@ -361,7 +363,8 @@ class TestFillFixtures:
|
||||||
def spam(request):
|
def spam(request):
|
||||||
return request.param
|
return request.param
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
testfile = subdir.joinpath("test_spam.py")
|
testfile = subdir.joinpath("test_spam.py")
|
||||||
testfile.write_text(
|
testfile.write_text(
|
||||||
|
@ -373,7 +376,8 @@ class TestFillFixtures:
|
||||||
assert spam == params['spam']
|
assert spam == params['spam']
|
||||||
params['spam'] += 1
|
params['spam'] += 1
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
result.stdout.fnmatch_lines(["*3 passed*"])
|
result.stdout.fnmatch_lines(["*3 passed*"])
|
||||||
|
@ -405,7 +409,8 @@ class TestFillFixtures:
|
||||||
def spam(request):
|
def spam(request):
|
||||||
return request.param
|
return request.param
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
testfile = subdir.joinpath("test_spam.py")
|
testfile = subdir.joinpath("test_spam.py")
|
||||||
testfile.write_text(
|
testfile.write_text(
|
||||||
|
@ -417,7 +422,8 @@ class TestFillFixtures:
|
||||||
assert spam == params['spam']
|
assert spam == params['spam']
|
||||||
params['spam'] += 1
|
params['spam'] += 1
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
result.stdout.fnmatch_lines(["*3 passed*"])
|
result.stdout.fnmatch_lines(["*3 passed*"])
|
||||||
|
@ -1039,10 +1045,11 @@ class TestRequestBasic:
|
||||||
def arg1():
|
def arg1():
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
p = b.joinpath("test_module.py")
|
p = b.joinpath("test_module.py")
|
||||||
p.write_text("def test_func(arg1): pass")
|
p.write_text("def test_func(arg1): pass", encoding="utf-8")
|
||||||
result = pytester.runpytest(p, "--fixtures")
|
result = pytester.runpytest(p, "--fixtures")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
|
@ -1619,7 +1626,8 @@ class TestFixtureManagerParseFactories:
|
||||||
def one():
|
def one():
|
||||||
return 1
|
return 1
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
package.joinpath("test_x.py").write_text(
|
package.joinpath("test_x.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -1627,7 +1635,8 @@ class TestFixtureManagerParseFactories:
|
||||||
def test_x(one):
|
def test_x(one):
|
||||||
assert one == 1
|
assert one == 1
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
sub = package.joinpath("sub")
|
sub = package.joinpath("sub")
|
||||||
sub.mkdir()
|
sub.mkdir()
|
||||||
|
@ -1640,7 +1649,8 @@ class TestFixtureManagerParseFactories:
|
||||||
def one():
|
def one():
|
||||||
return 2
|
return 2
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
sub.joinpath("test_y.py").write_text(
|
sub.joinpath("test_y.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -1648,7 +1658,8 @@ class TestFixtureManagerParseFactories:
|
||||||
def test_x(one):
|
def test_x(one):
|
||||||
assert one == 2
|
assert one == 2
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
reprec = pytester.inline_run()
|
reprec = pytester.inline_run()
|
||||||
reprec.assertoutcome(passed=2)
|
reprec.assertoutcome(passed=2)
|
||||||
|
@ -1673,7 +1684,8 @@ class TestFixtureManagerParseFactories:
|
||||||
def teardown_module():
|
def teardown_module():
|
||||||
values[:] = []
|
values[:] = []
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
package.joinpath("test_x.py").write_text(
|
package.joinpath("test_x.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -1682,7 +1694,8 @@ class TestFixtureManagerParseFactories:
|
||||||
def test_x():
|
def test_x():
|
||||||
assert values == ["package"]
|
assert values == ["package"]
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
package = pytester.mkdir("package2")
|
package = pytester.mkdir("package2")
|
||||||
package.joinpath("__init__.py").write_text(
|
package.joinpath("__init__.py").write_text(
|
||||||
|
@ -1694,7 +1707,8 @@ class TestFixtureManagerParseFactories:
|
||||||
def teardown_module():
|
def teardown_module():
|
||||||
values[:] = []
|
values[:] = []
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
package.joinpath("test_x.py").write_text(
|
package.joinpath("test_x.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -1703,7 +1717,8 @@ class TestFixtureManagerParseFactories:
|
||||||
def test_x():
|
def test_x():
|
||||||
assert values == ["package2"]
|
assert values == ["package2"]
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
reprec = pytester.inline_run()
|
reprec = pytester.inline_run()
|
||||||
reprec.assertoutcome(passed=2)
|
reprec.assertoutcome(passed=2)
|
||||||
|
@ -1716,7 +1731,7 @@ class TestFixtureManagerParseFactories:
|
||||||
)
|
)
|
||||||
pytester.syspathinsert(pytester.path.name)
|
pytester.syspathinsert(pytester.path.name)
|
||||||
package = pytester.mkdir("package")
|
package = pytester.mkdir("package")
|
||||||
package.joinpath("__init__.py").write_text("")
|
package.joinpath("__init__.py").write_text("", encoding="utf-8")
|
||||||
package.joinpath("conftest.py").write_text(
|
package.joinpath("conftest.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
"""\
|
"""\
|
||||||
|
@ -1733,7 +1748,8 @@ class TestFixtureManagerParseFactories:
|
||||||
yield values
|
yield values
|
||||||
values.pop()
|
values.pop()
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
package.joinpath("test_x.py").write_text(
|
package.joinpath("test_x.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -1744,7 +1760,8 @@ class TestFixtureManagerParseFactories:
|
||||||
def test_package(one):
|
def test_package(one):
|
||||||
assert values == ["package-auto", "package"]
|
assert values == ["package-auto", "package"]
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
reprec = pytester.inline_run()
|
reprec = pytester.inline_run()
|
||||||
reprec.assertoutcome(passed=2)
|
reprec.assertoutcome(passed=2)
|
||||||
|
@ -1894,8 +1911,12 @@ class TestAutouseDiscovery:
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
conftest.rename(a.joinpath(conftest.name))
|
conftest.rename(a.joinpath(conftest.name))
|
||||||
a.joinpath("test_something.py").write_text("def test_func(): pass")
|
a.joinpath("test_something.py").write_text(
|
||||||
b.joinpath("test_otherthing.py").write_text("def test_func(): pass")
|
"def test_func(): pass", encoding="utf-8"
|
||||||
|
)
|
||||||
|
b.joinpath("test_otherthing.py").write_text(
|
||||||
|
"def test_func(): pass", encoding="utf-8"
|
||||||
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
"""
|
"""
|
||||||
|
@ -1941,7 +1962,8 @@ class TestAutouseManagement:
|
||||||
import sys
|
import sys
|
||||||
sys._myapp = "hello"
|
sys._myapp = "hello"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
sub = pkgdir.joinpath("tests")
|
sub = pkgdir.joinpath("tests")
|
||||||
sub.mkdir()
|
sub.mkdir()
|
||||||
|
@ -1954,7 +1976,8 @@ class TestAutouseManagement:
|
||||||
def test_app():
|
def test_app():
|
||||||
assert sys._myapp == "hello"
|
assert sys._myapp == "hello"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
reprec = pytester.inline_run("-s")
|
reprec = pytester.inline_run("-s")
|
||||||
reprec.assertoutcome(passed=1)
|
reprec.assertoutcome(passed=1)
|
||||||
|
@ -2887,7 +2910,7 @@ class TestFixtureMarker:
|
||||||
def browser(request):
|
def browser(request):
|
||||||
|
|
||||||
def finalize():
|
def finalize():
|
||||||
sys.stdout.write_text('Finalized')
|
sys.stdout.write_text('Finalized', encoding='utf-8')
|
||||||
request.addfinalizer(finalize)
|
request.addfinalizer(finalize)
|
||||||
return {}
|
return {}
|
||||||
"""
|
"""
|
||||||
|
@ -2905,7 +2928,8 @@ class TestFixtureMarker:
|
||||||
def test_browser(browser):
|
def test_browser(browser):
|
||||||
assert browser['visited'] is True
|
assert browser['visited'] is True
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
reprec = pytester.runpytest("-s")
|
reprec = pytester.runpytest("-s")
|
||||||
for test in ["test_browser"]:
|
for test in ["test_browser"]:
|
||||||
|
@ -3860,7 +3884,8 @@ class TestParameterizedSubRequest:
|
||||||
def fix_with_param(request):
|
def fix_with_param(request):
|
||||||
return request.param
|
return request.param
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
testfile = tests_dir.joinpath("test_foos.py")
|
testfile = tests_dir.joinpath("test_foos.py")
|
||||||
|
@ -3872,7 +3897,8 @@ class TestParameterizedSubRequest:
|
||||||
def test_foo(request):
|
def test_foo(request):
|
||||||
request.getfixturevalue('fix_with_param')
|
request.getfixturevalue('fix_with_param')
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
os.chdir(tests_dir)
|
os.chdir(tests_dir)
|
||||||
|
@ -4201,7 +4227,7 @@ class TestScopeOrdering:
|
||||||
└── test_2.py
|
└── test_2.py
|
||||||
"""
|
"""
|
||||||
root = pytester.mkdir("root")
|
root = pytester.mkdir("root")
|
||||||
root.joinpath("__init__.py").write_text("values = []")
|
root.joinpath("__init__.py").write_text("values = []", encoding="utf-8")
|
||||||
sub1 = root.joinpath("sub1")
|
sub1 = root.joinpath("sub1")
|
||||||
sub1.mkdir()
|
sub1.mkdir()
|
||||||
sub1.joinpath("__init__.py").touch()
|
sub1.joinpath("__init__.py").touch()
|
||||||
|
@ -4216,7 +4242,8 @@ class TestScopeOrdering:
|
||||||
yield values
|
yield values
|
||||||
assert values.pop() == "pre-sub1"
|
assert values.pop() == "pre-sub1"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
sub1.joinpath("test_1.py").write_text(
|
sub1.joinpath("test_1.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -4225,7 +4252,8 @@ class TestScopeOrdering:
|
||||||
def test_1(fix):
|
def test_1(fix):
|
||||||
assert values == ["pre-sub1"]
|
assert values == ["pre-sub1"]
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
sub2 = root.joinpath("sub2")
|
sub2 = root.joinpath("sub2")
|
||||||
sub2.mkdir()
|
sub2.mkdir()
|
||||||
|
@ -4241,7 +4269,8 @@ class TestScopeOrdering:
|
||||||
yield values
|
yield values
|
||||||
assert values.pop() == "pre-sub2"
|
assert values.pop() == "pre-sub2"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
sub2.joinpath("test_2.py").write_text(
|
sub2.joinpath("test_2.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -4250,7 +4279,8 @@ class TestScopeOrdering:
|
||||||
def test_2(fix):
|
def test_2(fix):
|
||||||
assert values == ["pre-sub2"]
|
assert values == ["pre-sub2"]
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
reprec = pytester.inline_run()
|
reprec = pytester.inline_run()
|
||||||
reprec.assertoutcome(passed=2)
|
reprec.assertoutcome(passed=2)
|
||||||
|
|
|
@ -1472,7 +1472,8 @@ class TestMetafuncFunctional:
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
assert metafunc.function.__name__ == "test_1"
|
assert metafunc.function.__name__ == "test_1"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
sub2.joinpath("conftest.py").write_text(
|
sub2.joinpath("conftest.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -1480,10 +1481,15 @@ class TestMetafuncFunctional:
|
||||||
def pytest_generate_tests(metafunc):
|
def pytest_generate_tests(metafunc):
|
||||||
assert metafunc.function.__name__ == "test_2"
|
assert metafunc.function.__name__ == "test_2"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
sub1.joinpath("test_in_sub1.py").write_text(
|
||||||
|
"def test_1(): pass", encoding="utf-8"
|
||||||
|
)
|
||||||
|
sub2.joinpath("test_in_sub2.py").write_text(
|
||||||
|
"def test_2(): pass", encoding="utf-8"
|
||||||
)
|
)
|
||||||
sub1.joinpath("test_in_sub1.py").write_text("def test_1(): pass")
|
|
||||||
sub2.joinpath("test_in_sub2.py").write_text("def test_2(): pass")
|
|
||||||
result = pytester.runpytest("--keep-duplicates", "-v", "-s", sub1, sub2, sub1)
|
result = pytester.runpytest("--keep-duplicates", "-v", "-s", sub1, sub2, sub1)
|
||||||
result.assert_outcomes(passed=3)
|
result.assert_outcomes(passed=3)
|
||||||
|
|
||||||
|
|
|
@ -1392,14 +1392,14 @@ def test_sequence_comparison_uses_repr(pytester: Pytester) -> None:
|
||||||
def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None:
|
def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None:
|
||||||
pytester.makepyfile(test_base=["def test_base(): assert 1 == 2"])
|
pytester.makepyfile(test_base=["def test_base(): assert 1 == 2"])
|
||||||
a = pytester.mkdir("a")
|
a = pytester.mkdir("a")
|
||||||
a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2")
|
a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2", encoding="utf-8")
|
||||||
a.joinpath("conftest.py").write_text(
|
a.joinpath("conftest.py").write_text(
|
||||||
'def pytest_assertrepr_compare(): return ["summary a"]'
|
'def pytest_assertrepr_compare(): return ["summary a"]', encoding="utf-8"
|
||||||
)
|
)
|
||||||
b = pytester.mkdir("b")
|
b = pytester.mkdir("b")
|
||||||
b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2")
|
b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2", encoding="utf-8")
|
||||||
b.joinpath("conftest.py").write_text(
|
b.joinpath("conftest.py").write_text(
|
||||||
'def pytest_assertrepr_compare(): return ["summary b"]'
|
'def pytest_assertrepr_compare(): return ["summary b"]', encoding="utf-8"
|
||||||
)
|
)
|
||||||
|
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
|
|
|
@ -160,7 +160,8 @@ class TestAssertionRewrite:
|
||||||
"def special_asserter():\n"
|
"def special_asserter():\n"
|
||||||
" def special_assert(x, y):\n"
|
" def special_assert(x, y):\n"
|
||||||
" assert x == y\n"
|
" assert x == y\n"
|
||||||
" return special_assert\n"
|
" return special_assert\n",
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
pytester.makeconftest('pytest_plugins = ["plugin"]')
|
pytester.makeconftest('pytest_plugins = ["plugin"]')
|
||||||
pytester.makepyfile("def test(special_asserter): special_asserter(1, 2)\n")
|
pytester.makepyfile("def test(special_asserter): special_asserter(1, 2)\n")
|
||||||
|
@ -173,7 +174,9 @@ class TestAssertionRewrite:
|
||||||
pytester.makepyfile(test_y="x = 1")
|
pytester.makepyfile(test_y="x = 1")
|
||||||
xdir = pytester.mkdir("x")
|
xdir = pytester.mkdir("x")
|
||||||
pytester.mkpydir(str(xdir.joinpath("test_Y")))
|
pytester.mkpydir(str(xdir.joinpath("test_Y")))
|
||||||
xdir.joinpath("test_Y").joinpath("__init__.py").write_text("x = 2")
|
xdir.joinpath("test_Y").joinpath("__init__.py").write_text(
|
||||||
|
"x = 2", encoding="utf-8"
|
||||||
|
)
|
||||||
pytester.makepyfile(
|
pytester.makepyfile(
|
||||||
"import test_y\n"
|
"import test_y\n"
|
||||||
"import test_Y\n"
|
"import test_Y\n"
|
||||||
|
@ -726,7 +729,7 @@ class TestAssertionRewrite:
|
||||||
|
|
||||||
class TestRewriteOnImport:
|
class TestRewriteOnImport:
|
||||||
def test_pycache_is_a_file(self, pytester: Pytester) -> None:
|
def test_pycache_is_a_file(self, pytester: Pytester) -> None:
|
||||||
pytester.path.joinpath("__pycache__").write_text("Hello")
|
pytester.path.joinpath("__pycache__").write_text("Hello", encoding="utf-8")
|
||||||
pytester.makepyfile(
|
pytester.makepyfile(
|
||||||
"""
|
"""
|
||||||
def test_rewritten():
|
def test_rewritten():
|
||||||
|
@ -903,7 +906,8 @@ def test_rewritten():
|
||||||
pkg.joinpath("test_blah.py").write_text(
|
pkg.joinpath("test_blah.py").write_text(
|
||||||
"""
|
"""
|
||||||
def test_rewritten():
|
def test_rewritten():
|
||||||
assert "@py_builtins" in globals()"""
|
assert "@py_builtins" in globals()""",
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
assert pytester.runpytest().ret == 0
|
assert pytester.runpytest().ret == 0
|
||||||
|
|
||||||
|
@ -1066,7 +1070,7 @@ class TestAssertionRewriteHookDetails:
|
||||||
source = tmp_path / "source.py"
|
source = tmp_path / "source.py"
|
||||||
pyc = Path(str(source) + "c")
|
pyc = Path(str(source) + "c")
|
||||||
|
|
||||||
source.write_text("def test(): pass")
|
source.write_text("def test(): pass", encoding="utf-8")
|
||||||
py_compile.compile(str(source), str(pyc))
|
py_compile.compile(str(source), str(pyc))
|
||||||
|
|
||||||
contents = pyc.read_bytes()
|
contents = pyc.read_bytes()
|
||||||
|
@ -1092,7 +1096,7 @@ class TestAssertionRewriteHookDetails:
|
||||||
fn = tmp_path / "source.py"
|
fn = tmp_path / "source.py"
|
||||||
pyc = Path(str(fn) + "c")
|
pyc = Path(str(fn) + "c")
|
||||||
|
|
||||||
fn.write_text("def test(): assert True")
|
fn.write_text("def test(): assert True", encoding="utf-8")
|
||||||
|
|
||||||
source_stat, co = _rewrite_test(fn, config)
|
source_stat, co = _rewrite_test(fn, config)
|
||||||
_write_pyc(state, co, source_stat, pyc)
|
_write_pyc(state, co, source_stat, pyc)
|
||||||
|
@ -1157,7 +1161,7 @@ class TestAssertionRewriteHookDetails:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def rewrite_self():
|
def rewrite_self():
|
||||||
with open(__file__, 'w') as self:
|
with open(__file__, 'w', encoding='utf-8') as self:
|
||||||
self.write('def reloaded(): return True')
|
self.write('def reloaded(): return True')
|
||||||
""",
|
""",
|
||||||
test_fun="""
|
test_fun="""
|
||||||
|
@ -1187,9 +1191,10 @@ class TestAssertionRewriteHookDetails:
|
||||||
data = pkgutil.get_data('foo.test_foo', 'data.txt')
|
data = pkgutil.get_data('foo.test_foo', 'data.txt')
|
||||||
assert data == b'Hey'
|
assert data == b'Hey'
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
path.joinpath("data.txt").write_text("Hey")
|
path.joinpath("data.txt").write_text("Hey", encoding="utf-8")
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,9 @@ class TestNewAPI:
|
||||||
@pytest.mark.filterwarnings("ignore:could not create cache path")
|
@pytest.mark.filterwarnings("ignore:could not create cache path")
|
||||||
def test_cache_writefail_cachfile_silent(self, pytester: Pytester) -> None:
|
def test_cache_writefail_cachfile_silent(self, pytester: Pytester) -> None:
|
||||||
pytester.makeini("[pytest]")
|
pytester.makeini("[pytest]")
|
||||||
pytester.path.joinpath(".pytest_cache").write_text("gone wrong")
|
pytester.path.joinpath(".pytest_cache").write_text(
|
||||||
|
"gone wrong", encoding="utf-8"
|
||||||
|
)
|
||||||
config = pytester.parseconfigure()
|
config = pytester.parseconfigure()
|
||||||
cache = config.cache
|
cache = config.cache
|
||||||
assert cache is not None
|
assert cache is not None
|
||||||
|
@ -1134,7 +1136,9 @@ class TestNewFirst:
|
||||||
["*test_2/test_2.py::test_1 PASSED*", "*test_1/test_1.py::test_1 PASSED*"]
|
["*test_2/test_2.py::test_1 PASSED*", "*test_1/test_1.py::test_1 PASSED*"]
|
||||||
)
|
)
|
||||||
|
|
||||||
p1.write_text("def test_1(): assert 1\n" "def test_2(): assert 1\n")
|
p1.write_text(
|
||||||
|
"def test_1(): assert 1\n" "def test_2(): assert 1\n", encoding="utf-8"
|
||||||
|
)
|
||||||
os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9)))
|
os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9)))
|
||||||
|
|
||||||
result = pytester.runpytest("--nf", "--collect-only", "-q")
|
result = pytester.runpytest("--nf", "--collect-only", "-q")
|
||||||
|
@ -1207,7 +1211,8 @@ class TestNewFirst:
|
||||||
p1.write_text(
|
p1.write_text(
|
||||||
"import pytest\n"
|
"import pytest\n"
|
||||||
"@pytest.mark.parametrize('num', [1, 2, 3])\n"
|
"@pytest.mark.parametrize('num', [1, 2, 3])\n"
|
||||||
"def test_1(num): assert num\n"
|
"def test_1(num): assert num\n",
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9)))
|
os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9)))
|
||||||
|
|
||||||
|
@ -1259,7 +1264,7 @@ def test_gitignore(pytester: Pytester) -> None:
|
||||||
assert gitignore_path.read_text(encoding="UTF-8") == msg
|
assert gitignore_path.read_text(encoding="UTF-8") == msg
|
||||||
|
|
||||||
# Does not overwrite existing/custom one.
|
# Does not overwrite existing/custom one.
|
||||||
gitignore_path.write_text("custom")
|
gitignore_path.write_text("custom", encoding="utf-8")
|
||||||
cache.set("something", "else")
|
cache.set("something", "else")
|
||||||
assert gitignore_path.read_text(encoding="UTF-8") == "custom"
|
assert gitignore_path.read_text(encoding="UTF-8") == "custom"
|
||||||
|
|
||||||
|
|
|
@ -750,9 +750,10 @@ def test_setup_failure_does_not_kill_capturing(pytester: Pytester) -> None:
|
||||||
def pytest_runtest_setup(item):
|
def pytest_runtest_setup(item):
|
||||||
raise ValueError(42)
|
raise ValueError(42)
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
sub1.joinpath("test_mod.py").write_text("def test_func1(): pass")
|
sub1.joinpath("test_mod.py").write_text("def test_func1(): pass", encoding="utf-8")
|
||||||
result = pytester.runpytest(pytester.path, "--traceconfig")
|
result = pytester.runpytest(pytester.path, "--traceconfig")
|
||||||
result.stdout.fnmatch_lines(["*ValueError(42)*", "*1 error*"])
|
result.stdout.fnmatch_lines(["*ValueError(42)*", "*1 error*"])
|
||||||
|
|
||||||
|
@ -1523,9 +1524,9 @@ def test_global_capture_with_live_logging(pytester: Pytester) -> None:
|
||||||
def pytest_runtest_logreport(report):
|
def pytest_runtest_logreport(report):
|
||||||
if "test_global" in report.nodeid:
|
if "test_global" in report.nodeid:
|
||||||
if report.when == "teardown":
|
if report.when == "teardown":
|
||||||
with open("caplog", "w") as f:
|
with open("caplog", "w", encoding="utf-8") as f:
|
||||||
f.write(report.caplog)
|
f.write(report.caplog)
|
||||||
with open("capstdout", "w") as f:
|
with open("capstdout", "w", encoding="utf-8") as f:
|
||||||
f.write(report.capstdout)
|
f.write(report.capstdout)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
@ -1555,14 +1556,14 @@ def test_global_capture_with_live_logging(pytester: Pytester) -> None:
|
||||||
result = pytester.runpytest_subprocess("--log-cli-level=INFO")
|
result = pytester.runpytest_subprocess("--log-cli-level=INFO")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
with open("caplog") as f:
|
with open("caplog", encoding="utf-8") as f:
|
||||||
caplog = f.read()
|
caplog = f.read()
|
||||||
|
|
||||||
assert "fix setup" in caplog
|
assert "fix setup" in caplog
|
||||||
assert "something in test" in caplog
|
assert "something in test" in caplog
|
||||||
assert "fix teardown" in caplog
|
assert "fix teardown" in caplog
|
||||||
|
|
||||||
with open("capstdout") as f:
|
with open("capstdout", encoding="utf-8") as f:
|
||||||
capstdout = f.read()
|
capstdout = f.read()
|
||||||
|
|
||||||
assert "fix setup" in capstdout
|
assert "fix setup" in capstdout
|
||||||
|
|
|
@ -140,7 +140,7 @@ class TestCollectFS:
|
||||||
ensure_file(tmp_path / ".bzr" / "test_notfound.py")
|
ensure_file(tmp_path / ".bzr" / "test_notfound.py")
|
||||||
ensure_file(tmp_path / "normal" / "test_found.py")
|
ensure_file(tmp_path / "normal" / "test_found.py")
|
||||||
for x in tmp_path.rglob("test_*.py"):
|
for x in tmp_path.rglob("test_*.py"):
|
||||||
x.write_text("def test_hello(): pass", "utf-8")
|
x.write_text("def test_hello(): pass", encoding="utf-8")
|
||||||
|
|
||||||
result = pytester.runpytest("--collect-only")
|
result = pytester.runpytest("--collect-only")
|
||||||
s = result.stdout.str()
|
s = result.stdout.str()
|
||||||
|
@ -162,7 +162,7 @@ class TestCollectFS:
|
||||||
bindir = "Scripts" if sys.platform.startswith("win") else "bin"
|
bindir = "Scripts" if sys.platform.startswith("win") else "bin"
|
||||||
ensure_file(pytester.path / "virtual" / bindir / fname)
|
ensure_file(pytester.path / "virtual" / bindir / fname)
|
||||||
testfile = ensure_file(pytester.path / "virtual" / "test_invenv.py")
|
testfile = ensure_file(pytester.path / "virtual" / "test_invenv.py")
|
||||||
testfile.write_text("def test_hello(): pass")
|
testfile.write_text("def test_hello(): pass", encoding="utf-8")
|
||||||
|
|
||||||
# by default, ignore tests inside a virtualenv
|
# by default, ignore tests inside a virtualenv
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
|
@ -192,7 +192,7 @@ class TestCollectFS:
|
||||||
# norecursedirs takes priority
|
# norecursedirs takes priority
|
||||||
ensure_file(pytester.path / ".virtual" / bindir / fname)
|
ensure_file(pytester.path / ".virtual" / bindir / fname)
|
||||||
testfile = ensure_file(pytester.path / ".virtual" / "test_invenv.py")
|
testfile = ensure_file(pytester.path / ".virtual" / "test_invenv.py")
|
||||||
testfile.write_text("def test_hello(): pass")
|
testfile.write_text("def test_hello(): pass", encoding="utf-8")
|
||||||
result = pytester.runpytest("--collect-in-virtualenv")
|
result = pytester.runpytest("--collect-in-virtualenv")
|
||||||
result.stdout.no_fnmatch_line("*test_invenv*")
|
result.stdout.no_fnmatch_line("*test_invenv*")
|
||||||
# ...unless the virtualenv is explicitly given on the CLI
|
# ...unless the virtualenv is explicitly given on the CLI
|
||||||
|
@ -231,10 +231,14 @@ class TestCollectFS:
|
||||||
)
|
)
|
||||||
tmp_path = pytester.path
|
tmp_path = pytester.path
|
||||||
ensure_file(tmp_path / "mydir" / "test_hello.py").write_text(
|
ensure_file(tmp_path / "mydir" / "test_hello.py").write_text(
|
||||||
"def test_1(): pass"
|
"def test_1(): pass", encoding="utf-8"
|
||||||
|
)
|
||||||
|
ensure_file(tmp_path / "xyz123" / "test_2.py").write_text(
|
||||||
|
"def test_2(): 0/0", encoding="utf-8"
|
||||||
|
)
|
||||||
|
ensure_file(tmp_path / "xy" / "test_ok.py").write_text(
|
||||||
|
"def test_3(): pass", encoding="utf-8"
|
||||||
)
|
)
|
||||||
ensure_file(tmp_path / "xyz123" / "test_2.py").write_text("def test_2(): 0/0")
|
|
||||||
ensure_file(tmp_path / "xy" / "test_ok.py").write_text("def test_3(): pass")
|
|
||||||
rec = pytester.inline_run()
|
rec = pytester.inline_run()
|
||||||
rec.assertoutcome(passed=1)
|
rec.assertoutcome(passed=1)
|
||||||
rec = pytester.inline_run("xyz123/test_2.py")
|
rec = pytester.inline_run("xyz123/test_2.py")
|
||||||
|
@ -248,12 +252,14 @@ class TestCollectFS:
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
tmp_path = pytester.path
|
tmp_path = pytester.path
|
||||||
ensure_file(tmp_path / "a" / "test_1.py").write_text("def test_a(): pass")
|
ensure_file(tmp_path / "a" / "test_1.py").write_text(
|
||||||
|
"def test_a(): pass", encoding="utf-8"
|
||||||
|
)
|
||||||
ensure_file(tmp_path / "b" / "tests" / "test_2.py").write_text(
|
ensure_file(tmp_path / "b" / "tests" / "test_2.py").write_text(
|
||||||
"def test_b(): pass"
|
"def test_b(): pass", encoding="utf-8"
|
||||||
)
|
)
|
||||||
ensure_file(tmp_path / "c" / "tests" / "test_3.py").write_text(
|
ensure_file(tmp_path / "c" / "tests" / "test_3.py").write_text(
|
||||||
"def test_c(): pass"
|
"def test_c(): pass", encoding="utf-8"
|
||||||
)
|
)
|
||||||
|
|
||||||
# executing from rootdir only tests from `testpaths` directories
|
# executing from rootdir only tests from `testpaths` directories
|
||||||
|
@ -349,8 +355,8 @@ class TestCustomConftests:
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
sub = pytester.mkdir("xy123")
|
sub = pytester.mkdir("xy123")
|
||||||
ensure_file(sub / "test_hello.py").write_text("syntax error")
|
ensure_file(sub / "test_hello.py").write_text("syntax error", encoding="utf-8")
|
||||||
sub.joinpath("conftest.py").write_text("syntax error")
|
sub.joinpath("conftest.py").write_text("syntax error", encoding="utf-8")
|
||||||
pytester.makepyfile("def test_hello(): pass")
|
pytester.makepyfile("def test_hello(): pass")
|
||||||
pytester.makepyfile(test_one="syntax error")
|
pytester.makepyfile(test_one="syntax error")
|
||||||
result = pytester.runpytest("--fulltrace")
|
result = pytester.runpytest("--fulltrace")
|
||||||
|
@ -1060,13 +1066,18 @@ def test_fixture_scope_sibling_conftests(pytester: Pytester) -> None:
|
||||||
def fix():
|
def fix():
|
||||||
return 1
|
return 1
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
foo_path.joinpath("test_foo.py").write_text(
|
||||||
|
"def test_foo(fix): assert fix == 1", encoding="utf-8"
|
||||||
)
|
)
|
||||||
foo_path.joinpath("test_foo.py").write_text("def test_foo(fix): assert fix == 1")
|
|
||||||
|
|
||||||
# Tests in `food/` should not see the conftest fixture from `foo/`
|
# Tests in `food/` should not see the conftest fixture from `foo/`
|
||||||
food_path = pytester.mkpydir("food")
|
food_path = pytester.mkpydir("food")
|
||||||
food_path.joinpath("test_food.py").write_text("def test_food(fix): assert fix == 1")
|
food_path.joinpath("test_food.py").write_text(
|
||||||
|
"def test_food(fix): assert fix == 1", encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
res = pytester.runpytest()
|
res = pytester.runpytest()
|
||||||
assert res.ret == 1
|
assert res.ret == 1
|
||||||
|
@ -1197,7 +1208,8 @@ def test_collect_with_chdir_during_import(pytester: Pytester) -> None:
|
||||||
os.chdir(%r)
|
os.chdir(%r)
|
||||||
"""
|
"""
|
||||||
% (str(subdir),)
|
% (str(subdir),)
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
pytester.makepyfile(
|
pytester.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
@ -1227,8 +1239,12 @@ def test_collect_pyargs_with_testpaths(
|
||||||
) -> None:
|
) -> None:
|
||||||
testmod = pytester.mkdir("testmod")
|
testmod = pytester.mkdir("testmod")
|
||||||
# NOTE: __init__.py is not collected since it does not match python_files.
|
# NOTE: __init__.py is not collected since it does not match python_files.
|
||||||
testmod.joinpath("__init__.py").write_text("def test_func(): pass")
|
testmod.joinpath("__init__.py").write_text(
|
||||||
testmod.joinpath("test_file.py").write_text("def test_func(): pass")
|
"def test_func(): pass", encoding="utf-8"
|
||||||
|
)
|
||||||
|
testmod.joinpath("test_file.py").write_text(
|
||||||
|
"def test_func(): pass", encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
root = pytester.mkdir("root")
|
root = pytester.mkdir("root")
|
||||||
root.joinpath("pytest.ini").write_text(
|
root.joinpath("pytest.ini").write_text(
|
||||||
|
@ -1238,7 +1254,8 @@ def test_collect_pyargs_with_testpaths(
|
||||||
addopts = --pyargs
|
addopts = --pyargs
|
||||||
testpaths = testmod
|
testpaths = testmod
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
monkeypatch.setenv("PYTHONPATH", str(pytester.path), prepend=os.pathsep)
|
monkeypatch.setenv("PYTHONPATH", str(pytester.path), prepend=os.pathsep)
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
|
@ -1256,7 +1273,8 @@ def test_initial_conftests_with_testpaths(pytester: Pytester) -> None:
|
||||||
def pytest_sessionstart(session):
|
def pytest_sessionstart(session):
|
||||||
raise Exception("pytest_sessionstart hook successfully run")
|
raise Exception("pytest_sessionstart hook successfully run")
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
pytester.makeini(
|
pytester.makeini(
|
||||||
"""
|
"""
|
||||||
|
@ -1264,11 +1282,18 @@ def test_initial_conftests_with_testpaths(pytester: Pytester) -> None:
|
||||||
testpaths = some_path
|
testpaths = some_path
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# No command line args - falls back to testpaths.
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
|
assert result.ret == ExitCode.INTERNAL_ERROR
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
"INTERNALERROR* Exception: pytest_sessionstart hook successfully run"
|
"INTERNALERROR* Exception: pytest_sessionstart hook successfully run"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# No fallback.
|
||||||
|
result = pytester.runpytest(".")
|
||||||
|
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
||||||
|
|
||||||
|
|
||||||
def test_large_option_breaks_initial_conftests(pytester: Pytester) -> None:
|
def test_large_option_breaks_initial_conftests(pytester: Pytester) -> None:
|
||||||
"""Long option values do not break initial conftests handling (#10169)."""
|
"""Long option values do not break initial conftests handling (#10169)."""
|
||||||
|
@ -1316,6 +1341,7 @@ def test_collect_symlink_out_of_tree(pytester: Pytester) -> None:
|
||||||
assert request.node.nodeid == "test_real.py::test_nodeid"
|
assert request.node.nodeid == "test_real.py::test_nodeid"
|
||||||
"""
|
"""
|
||||||
),
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
out_of_tree = pytester.mkdir("out_of_tree")
|
out_of_tree = pytester.mkdir("out_of_tree")
|
||||||
|
@ -1344,12 +1370,16 @@ def test_collect_symlink_dir(pytester: Pytester) -> None:
|
||||||
def test_collectignore_via_conftest(pytester: Pytester) -> None:
|
def test_collectignore_via_conftest(pytester: Pytester) -> None:
|
||||||
"""collect_ignore in parent conftest skips importing child (issue #4592)."""
|
"""collect_ignore in parent conftest skips importing child (issue #4592)."""
|
||||||
tests = pytester.mkpydir("tests")
|
tests = pytester.mkpydir("tests")
|
||||||
tests.joinpath("conftest.py").write_text("collect_ignore = ['ignore_me']")
|
tests.joinpath("conftest.py").write_text(
|
||||||
|
"collect_ignore = ['ignore_me']", encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
ignore_me = tests.joinpath("ignore_me")
|
ignore_me = tests.joinpath("ignore_me")
|
||||||
ignore_me.mkdir()
|
ignore_me.mkdir()
|
||||||
ignore_me.joinpath("__init__.py").touch()
|
ignore_me.joinpath("__init__.py").touch()
|
||||||
ignore_me.joinpath("conftest.py").write_text("assert 0, 'should_not_be_called'")
|
ignore_me.joinpath("conftest.py").write_text(
|
||||||
|
"assert 0, 'should_not_be_called'", encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
||||||
|
@ -1358,23 +1388,31 @@ def test_collectignore_via_conftest(pytester: Pytester) -> None:
|
||||||
def test_collect_pkg_init_and_file_in_args(pytester: Pytester) -> None:
|
def test_collect_pkg_init_and_file_in_args(pytester: Pytester) -> None:
|
||||||
subdir = pytester.mkdir("sub")
|
subdir = pytester.mkdir("sub")
|
||||||
init = subdir.joinpath("__init__.py")
|
init = subdir.joinpath("__init__.py")
|
||||||
init.write_text("def test_init(): pass")
|
init.write_text("def test_init(): pass", encoding="utf-8")
|
||||||
p = subdir.joinpath("test_file.py")
|
p = subdir.joinpath("test_file.py")
|
||||||
p.write_text("def test_file(): pass")
|
p.write_text("def test_file(): pass", encoding="utf-8")
|
||||||
|
|
||||||
# NOTE: without "-o python_files=*.py" this collects test_file.py twice.
|
# Just the package directory, the __init__.py module is filtered out.
|
||||||
# This changed/broke with "Add package scoped fixtures #2283" (2b1410895)
|
result = pytester.runpytest("-v", subdir)
|
||||||
# initially (causing a RecursionError).
|
|
||||||
result = pytester.runpytest("-v", str(init), str(p))
|
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"sub/test_file.py::test_file PASSED*",
|
"sub/test_file.py::test_file PASSED*",
|
||||||
|
"*1 passed in*",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# But it's included if specified directly.
|
||||||
|
result = pytester.runpytest("-v", init, p)
|
||||||
|
result.stdout.fnmatch_lines(
|
||||||
|
[
|
||||||
|
"sub/__init__.py::test_init PASSED*",
|
||||||
"sub/test_file.py::test_file PASSED*",
|
"sub/test_file.py::test_file PASSED*",
|
||||||
"*2 passed in*",
|
"*2 passed in*",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
result = pytester.runpytest("-v", "-o", "python_files=*.py", str(init), str(p))
|
# Or if the pattern allows it.
|
||||||
|
result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir)
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"sub/__init__.py::test_init PASSED*",
|
"sub/__init__.py::test_init PASSED*",
|
||||||
|
@ -1387,12 +1425,15 @@ def test_collect_pkg_init_and_file_in_args(pytester: Pytester) -> None:
|
||||||
def test_collect_pkg_init_only(pytester: Pytester) -> None:
|
def test_collect_pkg_init_only(pytester: Pytester) -> None:
|
||||||
subdir = pytester.mkdir("sub")
|
subdir = pytester.mkdir("sub")
|
||||||
init = subdir.joinpath("__init__.py")
|
init = subdir.joinpath("__init__.py")
|
||||||
init.write_text("def test_init(): pass")
|
init.write_text("def test_init(): pass", encoding="utf-8")
|
||||||
|
|
||||||
result = pytester.runpytest(str(init))
|
result = pytester.runpytest(subdir)
|
||||||
result.stdout.fnmatch_lines(["*no tests ran in*"])
|
result.stdout.fnmatch_lines(["*no tests ran in*"])
|
||||||
|
|
||||||
result = pytester.runpytest("-v", "-o", "python_files=*.py", str(init))
|
result = pytester.runpytest("-v", init)
|
||||||
|
result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"])
|
||||||
|
|
||||||
|
result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir)
|
||||||
result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"])
|
result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"])
|
||||||
|
|
||||||
|
|
||||||
|
@ -1402,7 +1443,7 @@ def test_collect_sub_with_symlinks(use_pkg: bool, pytester: Pytester) -> None:
|
||||||
sub = pytester.mkdir("sub")
|
sub = pytester.mkdir("sub")
|
||||||
if use_pkg:
|
if use_pkg:
|
||||||
sub.joinpath("__init__.py").touch()
|
sub.joinpath("__init__.py").touch()
|
||||||
sub.joinpath("test_file.py").write_text("def test_file(): pass")
|
sub.joinpath("test_file.py").write_text("def test_file(): pass", encoding="utf-8")
|
||||||
|
|
||||||
# Create a broken symlink.
|
# Create a broken symlink.
|
||||||
symlink_or_skip("test_doesnotexist.py", sub.joinpath("test_broken.py"))
|
symlink_or_skip("test_doesnotexist.py", sub.joinpath("test_broken.py"))
|
||||||
|
@ -1440,7 +1481,7 @@ def test_collector_respects_tbstyle(pytester: Pytester) -> None:
|
||||||
def test_does_not_eagerly_collect_packages(pytester: Pytester) -> None:
|
def test_does_not_eagerly_collect_packages(pytester: Pytester) -> None:
|
||||||
pytester.makepyfile("def test(): pass")
|
pytester.makepyfile("def test(): pass")
|
||||||
pydir = pytester.mkpydir("foopkg")
|
pydir = pytester.mkpydir("foopkg")
|
||||||
pydir.joinpath("__init__.py").write_text("assert False")
|
pydir.joinpath("__init__.py").write_text("assert False", encoding="utf-8")
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
assert result.ret == ExitCode.OK
|
assert result.ret == ExitCode.OK
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,8 @@ class TestParseIni:
|
||||||
[pytest]
|
[pytest]
|
||||||
addopts = --verbose
|
addopts = --verbose
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
config = pytester.parseconfig(tmp_path)
|
config = pytester.parseconfig(tmp_path)
|
||||||
assert config.option.color == "no"
|
assert config.option.color == "no"
|
||||||
|
@ -127,7 +128,8 @@ class TestParseIni:
|
||||||
""".format(
|
""".format(
|
||||||
section=section
|
section=section
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
config = pytester.parseconfig()
|
config = pytester.parseconfig()
|
||||||
assert config.getini("minversion") == "3.36"
|
assert config.getini("minversion") == "3.36"
|
||||||
|
@ -150,7 +152,8 @@ class TestParseIni:
|
||||||
[pytest]
|
[pytest]
|
||||||
minversion = 2.0
|
minversion = 2.0
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
pytester.path.joinpath("pytest.ini").write_text(
|
pytester.path.joinpath("pytest.ini").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -158,13 +161,16 @@ class TestParseIni:
|
||||||
[pytest]
|
[pytest]
|
||||||
minversion = 1.5
|
minversion = 1.5
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
config = pytester.parseconfigure(sub)
|
config = pytester.parseconfigure(sub)
|
||||||
assert config.getini("minversion") == "2.0"
|
assert config.getini("minversion") == "2.0"
|
||||||
|
|
||||||
def test_ini_parse_error(self, pytester: Pytester) -> None:
|
def test_ini_parse_error(self, pytester: Pytester) -> None:
|
||||||
pytester.path.joinpath("pytest.ini").write_text("addopts = -x")
|
pytester.path.joinpath("pytest.ini").write_text(
|
||||||
|
"addopts = -x", encoding="utf-8"
|
||||||
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
assert result.ret != 0
|
assert result.ret != 0
|
||||||
result.stderr.fnmatch_lines("ERROR: *pytest.ini:1: no section header defined")
|
result.stderr.fnmatch_lines("ERROR: *pytest.ini:1: no section header defined")
|
||||||
|
@ -634,7 +640,7 @@ class TestConfigAPI:
|
||||||
def test_getconftest_pathlist(self, pytester: Pytester, tmp_path: Path) -> None:
|
def test_getconftest_pathlist(self, pytester: Pytester, tmp_path: Path) -> None:
|
||||||
somepath = tmp_path.joinpath("x", "y", "z")
|
somepath = tmp_path.joinpath("x", "y", "z")
|
||||||
p = tmp_path.joinpath("conftest.py")
|
p = tmp_path.joinpath("conftest.py")
|
||||||
p.write_text(f"mylist = {['.', str(somepath)]}")
|
p.write_text(f"mylist = {['.', str(somepath)]}", encoding="utf-8")
|
||||||
config = pytester.parseconfigure(p)
|
config = pytester.parseconfigure(p)
|
||||||
assert (
|
assert (
|
||||||
config._getconftest_pathlist("notexist", path=tmp_path, rootpath=tmp_path)
|
config._getconftest_pathlist("notexist", path=tmp_path, rootpath=tmp_path)
|
||||||
|
@ -910,7 +916,8 @@ class TestConfigFromdictargs:
|
||||||
[pytest]
|
[pytest]
|
||||||
name = value
|
name = value
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
inifilename = "../../foo/bar.ini"
|
inifilename = "../../foo/bar.ini"
|
||||||
|
@ -927,7 +934,8 @@ class TestConfigFromdictargs:
|
||||||
name = wrong-value
|
name = wrong-value
|
||||||
should_not_be_set = true
|
should_not_be_set = true
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
with MonkeyPatch.context() as mp:
|
with MonkeyPatch.context() as mp:
|
||||||
mp.chdir(cwd)
|
mp.chdir(cwd)
|
||||||
|
@ -1176,7 +1184,7 @@ def test_cmdline_processargs_simple(pytester: Pytester) -> None:
|
||||||
args.append("-h")
|
args.append("-h")
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest("-Wignore::pytest.PytestRemovedIn8Warning")
|
||||||
result.stdout.fnmatch_lines(["*pytest*", "*-h*"])
|
result.stdout.fnmatch_lines(["*pytest*", "*-h*"])
|
||||||
|
|
||||||
|
|
||||||
|
@ -1387,7 +1395,7 @@ class TestRootdir:
|
||||||
)
|
)
|
||||||
def test_with_ini(self, tmp_path: Path, name: str, contents: str) -> None:
|
def test_with_ini(self, tmp_path: Path, name: str, contents: str) -> None:
|
||||||
inipath = tmp_path / name
|
inipath = tmp_path / name
|
||||||
inipath.write_text(contents, "utf-8")
|
inipath.write_text(contents, encoding="utf-8")
|
||||||
|
|
||||||
a = tmp_path / "a"
|
a = tmp_path / "a"
|
||||||
a.mkdir()
|
a.mkdir()
|
||||||
|
@ -1446,7 +1454,7 @@ class TestRootdir:
|
||||||
) -> None:
|
) -> None:
|
||||||
p = tmp_path / name
|
p = tmp_path / name
|
||||||
p.touch()
|
p.touch()
|
||||||
p.write_text(contents, "utf-8")
|
p.write_text(contents, encoding="utf-8")
|
||||||
rootpath, inipath, ini_config = determine_setup(str(p), [str(tmp_path)])
|
rootpath, inipath, ini_config = determine_setup(str(p), [str(tmp_path)])
|
||||||
assert rootpath == tmp_path
|
assert rootpath == tmp_path
|
||||||
assert inipath == p
|
assert inipath == p
|
||||||
|
@ -1542,7 +1550,8 @@ class TestOverrideIniArgs:
|
||||||
custom = 1.0""".format(
|
custom = 1.0""".format(
|
||||||
section=section
|
section=section
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
pytester.makeconftest(
|
pytester.makeconftest(
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import argparse
|
|
||||||
import os
|
import os
|
||||||
import textwrap
|
import textwrap
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -7,6 +6,8 @@ from typing import Dict
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from typing import Sequence
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.config import ExitCode
|
from _pytest.config import ExitCode
|
||||||
|
@ -24,18 +25,18 @@ def ConftestWithSetinitial(path) -> PytestPluginManager:
|
||||||
|
|
||||||
|
|
||||||
def conftest_setinitial(
|
def conftest_setinitial(
|
||||||
conftest: PytestPluginManager, args, confcutdir: Optional["os.PathLike[str]"] = None
|
conftest: PytestPluginManager,
|
||||||
|
args: Sequence[Union[str, Path]],
|
||||||
|
confcutdir: Optional[Path] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
class Namespace:
|
conftest._set_initial_conftests(
|
||||||
def __init__(self) -> None:
|
args=args,
|
||||||
self.file_or_dir = args
|
pyargs=False,
|
||||||
self.confcutdir = os.fspath(confcutdir) if confcutdir is not None else None
|
noconftest=False,
|
||||||
self.noconftest = False
|
rootpath=Path(args[0]),
|
||||||
self.pyargs = False
|
confcutdir=confcutdir,
|
||||||
self.importmode = "prepend"
|
importmode="prepend",
|
||||||
|
)
|
||||||
namespace = cast(argparse.Namespace, Namespace())
|
|
||||||
conftest._set_initial_conftests(namespace, rootpath=Path(args[0]), testpaths_ini=[])
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("_sys_snapshot")
|
@pytest.mark.usefixtures("_sys_snapshot")
|
||||||
|
@ -46,8 +47,12 @@ class TestConftestValueAccessGlobal:
|
||||||
) -> Generator[Path, None, None]:
|
) -> Generator[Path, None, None]:
|
||||||
tmp_path = tmp_path_factory.mktemp("basedir", numbered=True)
|
tmp_path = tmp_path_factory.mktemp("basedir", numbered=True)
|
||||||
tmp_path.joinpath("adir/b").mkdir(parents=True)
|
tmp_path.joinpath("adir/b").mkdir(parents=True)
|
||||||
tmp_path.joinpath("adir/conftest.py").write_text("a=1 ; Directory = 3")
|
tmp_path.joinpath("adir/conftest.py").write_text(
|
||||||
tmp_path.joinpath("adir/b/conftest.py").write_text("b=2 ; a = 1.5")
|
"a=1 ; Directory = 3", encoding="utf-8"
|
||||||
|
)
|
||||||
|
tmp_path.joinpath("adir/b/conftest.py").write_text(
|
||||||
|
"b=2 ; a = 1.5", encoding="utf-8"
|
||||||
|
)
|
||||||
if request.param == "inpackage":
|
if request.param == "inpackage":
|
||||||
tmp_path.joinpath("adir/__init__.py").touch()
|
tmp_path.joinpath("adir/__init__.py").touch()
|
||||||
tmp_path.joinpath("adir/b/__init__.py").touch()
|
tmp_path.joinpath("adir/b/__init__.py").touch()
|
||||||
|
@ -122,8 +127,12 @@ class TestConftestValueAccessGlobal:
|
||||||
|
|
||||||
def test_conftest_in_nonpkg_with_init(tmp_path: Path, _sys_snapshot) -> None:
|
def test_conftest_in_nonpkg_with_init(tmp_path: Path, _sys_snapshot) -> None:
|
||||||
tmp_path.joinpath("adir-1.0/b").mkdir(parents=True)
|
tmp_path.joinpath("adir-1.0/b").mkdir(parents=True)
|
||||||
tmp_path.joinpath("adir-1.0/conftest.py").write_text("a=1 ; Directory = 3")
|
tmp_path.joinpath("adir-1.0/conftest.py").write_text(
|
||||||
tmp_path.joinpath("adir-1.0/b/conftest.py").write_text("b=2 ; a = 1.5")
|
"a=1 ; Directory = 3", encoding="utf-8"
|
||||||
|
)
|
||||||
|
tmp_path.joinpath("adir-1.0/b/conftest.py").write_text(
|
||||||
|
"b=2 ; a = 1.5", encoding="utf-8"
|
||||||
|
)
|
||||||
tmp_path.joinpath("adir-1.0/b/__init__.py").touch()
|
tmp_path.joinpath("adir-1.0/b/__init__.py").touch()
|
||||||
tmp_path.joinpath("adir-1.0/__init__.py").touch()
|
tmp_path.joinpath("adir-1.0/__init__.py").touch()
|
||||||
ConftestWithSetinitial(tmp_path.joinpath("adir-1.0", "b"))
|
ConftestWithSetinitial(tmp_path.joinpath("adir-1.0", "b"))
|
||||||
|
@ -166,7 +175,7 @@ def test_conftest_global_import(pytester: Pytester) -> None:
|
||||||
sub = Path("sub")
|
sub = Path("sub")
|
||||||
sub.mkdir()
|
sub.mkdir()
|
||||||
subconf = sub / "conftest.py"
|
subconf = sub / "conftest.py"
|
||||||
subconf.write_text("y=4")
|
subconf.write_text("y=4", encoding="utf-8")
|
||||||
mod2 = conf._importconftest(subconf, importmode="prepend", rootpath=Path.cwd())
|
mod2 = conf._importconftest(subconf, importmode="prepend", rootpath=Path.cwd())
|
||||||
assert mod != mod2
|
assert mod != mod2
|
||||||
assert mod2.y == 4
|
assert mod2.y == 4
|
||||||
|
@ -245,7 +254,8 @@ def test_conftest_confcutdir(pytester: Pytester) -> None:
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption("--xyz", action="store_true")
|
parser.addoption("--xyz", action="store_true")
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest("-h", "--confcutdir=%s" % x, x)
|
result = pytester.runpytest("-h", "--confcutdir=%s" % x, x)
|
||||||
result.stdout.fnmatch_lines(["*--xyz*"])
|
result.stdout.fnmatch_lines(["*--xyz*"])
|
||||||
|
@ -273,9 +283,12 @@ def test_installed_conftest_is_picked_up(pytester: Pytester, tmp_path: Path) ->
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def fix(): return None
|
def fix(): return None
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
tmp_path.joinpath("foo", "test_it.py").write_text(
|
||||||
|
"def test_it(fix): pass", encoding="utf-8"
|
||||||
)
|
)
|
||||||
tmp_path.joinpath("foo", "test_it.py").write_text("def test_it(fix): pass")
|
|
||||||
result = pytester.runpytest("--pyargs", "foo")
|
result = pytester.runpytest("--pyargs", "foo")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
|
@ -400,7 +413,8 @@ def test_conftest_existing_junitxml(pytester: Pytester) -> None:
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption("--xyz", action="store_true")
|
parser.addoption("--xyz", action="store_true")
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
pytester.makefile(ext=".xml", junit="") # Writes junit.xml
|
pytester.makefile(ext=".xml", junit="") # Writes junit.xml
|
||||||
result = pytester.runpytest("-h", "--junitxml", "junit.xml")
|
result = pytester.runpytest("-h", "--junitxml", "junit.xml")
|
||||||
|
@ -411,7 +425,7 @@ def test_conftest_import_order(pytester: Pytester, monkeypatch: MonkeyPatch) ->
|
||||||
ct1 = pytester.makeconftest("")
|
ct1 = pytester.makeconftest("")
|
||||||
sub = pytester.mkdir("sub")
|
sub = pytester.mkdir("sub")
|
||||||
ct2 = sub / "conftest.py"
|
ct2 = sub / "conftest.py"
|
||||||
ct2.write_text("")
|
ct2.write_text("", encoding="utf-8")
|
||||||
|
|
||||||
def impct(p, importmode, root):
|
def impct(p, importmode, root):
|
||||||
return p
|
return p
|
||||||
|
@ -449,7 +463,8 @@ def test_fixture_dependency(pytester: Pytester) -> None:
|
||||||
def bar(foo):
|
def bar(foo):
|
||||||
return 'bar'
|
return 'bar'
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
subsub = sub.joinpath("subsub")
|
subsub = sub.joinpath("subsub")
|
||||||
subsub.mkdir()
|
subsub.mkdir()
|
||||||
|
@ -466,7 +481,8 @@ def test_fixture_dependency(pytester: Pytester) -> None:
|
||||||
def test_event_fixture(bar):
|
def test_event_fixture(bar):
|
||||||
assert bar == 'sub bar'
|
assert bar == 'sub bar'
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest("sub")
|
result = pytester.runpytest("sub")
|
||||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||||
|
@ -480,10 +496,11 @@ def test_conftest_found_with_double_dash(pytester: Pytester) -> None:
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption("--hello-world", action="store_true")
|
parser.addoption("--hello-world", action="store_true")
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
p = sub.joinpath("test_hello.py")
|
p = sub.joinpath("test_hello.py")
|
||||||
p.write_text("def test_hello(): pass")
|
p.write_text("def test_hello(): pass", encoding="utf-8")
|
||||||
result = pytester.runpytest(str(p) + "::test_hello", "-h")
|
result = pytester.runpytest(str(p) + "::test_hello", "-h")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
"""
|
"""
|
||||||
|
@ -507,7 +524,8 @@ class TestConftestVisibility:
|
||||||
def fxtr():
|
def fxtr():
|
||||||
return "from-package"
|
return "from-package"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
package.joinpath("test_pkgroot.py").write_text(
|
package.joinpath("test_pkgroot.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -515,7 +533,8 @@ class TestConftestVisibility:
|
||||||
def test_pkgroot(fxtr):
|
def test_pkgroot(fxtr):
|
||||||
assert fxtr == "from-package"
|
assert fxtr == "from-package"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
swc = package.joinpath("swc")
|
swc = package.joinpath("swc")
|
||||||
|
@ -529,7 +548,8 @@ class TestConftestVisibility:
|
||||||
def fxtr():
|
def fxtr():
|
||||||
return "from-swc"
|
return "from-swc"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
swc.joinpath("test_with_conftest.py").write_text(
|
swc.joinpath("test_with_conftest.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -537,7 +557,8 @@ class TestConftestVisibility:
|
||||||
def test_with_conftest(fxtr):
|
def test_with_conftest(fxtr):
|
||||||
assert fxtr == "from-swc"
|
assert fxtr == "from-swc"
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
snc = package.joinpath("snc")
|
snc = package.joinpath("snc")
|
||||||
|
@ -550,7 +571,8 @@ class TestConftestVisibility:
|
||||||
assert fxtr == "from-package" # No local conftest.py, so should
|
assert fxtr == "from-package" # No local conftest.py, so should
|
||||||
# use value from parent dir's
|
# use value from parent dir's
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
print("created directory structure:")
|
print("created directory structure:")
|
||||||
for x in pytester.path.glob("**/"):
|
for x in pytester.path.glob("**/"):
|
||||||
|
@ -616,7 +638,7 @@ def test_search_conftest_up_to_inifile(
|
||||||
root = pytester.path
|
root = pytester.path
|
||||||
src = root.joinpath("src")
|
src = root.joinpath("src")
|
||||||
src.mkdir()
|
src.mkdir()
|
||||||
src.joinpath("pytest.ini").write_text("[pytest]")
|
src.joinpath("pytest.ini").write_text("[pytest]", encoding="utf-8")
|
||||||
src.joinpath("conftest.py").write_text(
|
src.joinpath("conftest.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
"""\
|
"""\
|
||||||
|
@ -624,7 +646,8 @@ def test_search_conftest_up_to_inifile(
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def fix1(): pass
|
def fix1(): pass
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
src.joinpath("test_foo.py").write_text(
|
src.joinpath("test_foo.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -634,7 +657,8 @@ def test_search_conftest_up_to_inifile(
|
||||||
def test_2(out_of_reach):
|
def test_2(out_of_reach):
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
root.joinpath("conftest.py").write_text(
|
root.joinpath("conftest.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -643,7 +667,8 @@ def test_search_conftest_up_to_inifile(
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def out_of_reach(): pass
|
def out_of_reach(): pass
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
args = [str(src)]
|
args = [str(src)]
|
||||||
|
@ -726,7 +751,8 @@ def test_required_option_help(pytester: Pytester) -> None:
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addoption("--xyz", action="store_true", required=True)
|
parser.addoption("--xyz", action="store_true", required=True)
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest("-h", x)
|
result = pytester.runpytest("-h", x)
|
||||||
result.stdout.no_fnmatch_line("*argument --xyz is required*")
|
result.stdout.no_fnmatch_line("*argument --xyz is required*")
|
||||||
|
|
|
@ -114,7 +114,7 @@ class TestDoctests:
|
||||||
reprec.assertoutcome(failed=1)
|
reprec.assertoutcome(failed=1)
|
||||||
|
|
||||||
def test_importmode(self, pytester: Pytester):
|
def test_importmode(self, pytester: Pytester):
|
||||||
p = pytester.makepyfile(
|
pytester.makepyfile(
|
||||||
**{
|
**{
|
||||||
"namespacepkg/innerpkg/__init__.py": "",
|
"namespacepkg/innerpkg/__init__.py": "",
|
||||||
"namespacepkg/innerpkg/a.py": """
|
"namespacepkg/innerpkg/a.py": """
|
||||||
|
@ -132,7 +132,7 @@ class TestDoctests:
|
||||||
""",
|
""",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
reprec = pytester.inline_run(p, "--doctest-modules", "--import-mode=importlib")
|
reprec = pytester.inline_run("--doctest-modules", "--import-mode=importlib")
|
||||||
reprec.assertoutcome(passed=1)
|
reprec.assertoutcome(passed=1)
|
||||||
|
|
||||||
def test_new_pattern(self, pytester: Pytester):
|
def test_new_pattern(self, pytester: Pytester):
|
||||||
|
@ -357,7 +357,8 @@ class TestDoctests:
|
||||||
>>> 1/0
|
>>> 1/0
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest("--doctest-modules")
|
result = pytester.runpytest("--doctest-modules")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
|
@ -448,7 +449,8 @@ class TestDoctests:
|
||||||
"""\
|
"""\
|
||||||
import asdalsdkjaslkdjasd
|
import asdalsdkjaslkdjasd
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
pytester.maketxtfile(
|
pytester.maketxtfile(
|
||||||
"""
|
"""
|
||||||
|
@ -492,7 +494,8 @@ class TestDoctests:
|
||||||
2
|
2
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(p, "--doctest-modules")
|
result = pytester.runpytest(p, "--doctest-modules")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
|
@ -1566,7 +1569,9 @@ def test_warning_on_unwrap_of_broken_object(
|
||||||
|
|
||||||
def test_is_setup_py_not_named_setup_py(tmp_path: Path) -> None:
|
def test_is_setup_py_not_named_setup_py(tmp_path: Path) -> None:
|
||||||
not_setup_py = tmp_path.joinpath("not_setup.py")
|
not_setup_py = tmp_path.joinpath("not_setup.py")
|
||||||
not_setup_py.write_text('from setuptools import setup; setup(name="foo")')
|
not_setup_py.write_text(
|
||||||
|
'from setuptools import setup; setup(name="foo")', encoding="utf-8"
|
||||||
|
)
|
||||||
assert not _is_setup_py(not_setup_py)
|
assert not _is_setup_py(not_setup_py)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ from _pytest.stash import Stash
|
||||||
def schema() -> xmlschema.XMLSchema:
|
def schema() -> xmlschema.XMLSchema:
|
||||||
"""Return an xmlschema.XMLSchema object for the junit-10.xsd file."""
|
"""Return an xmlschema.XMLSchema object for the junit-10.xsd file."""
|
||||||
fn = Path(__file__).parent / "example_scripts/junit-10.xsd"
|
fn = Path(__file__).parent / "example_scripts/junit-10.xsd"
|
||||||
with fn.open() as f:
|
with fn.open(encoding="utf-8") as f:
|
||||||
return xmlschema.XMLSchema(f)
|
return xmlschema.XMLSchema(f)
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ class RunAndParse:
|
||||||
xml_path = self.pytester.path.joinpath("junit.xml")
|
xml_path = self.pytester.path.joinpath("junit.xml")
|
||||||
result = self.pytester.runpytest("--junitxml=%s" % xml_path, *args)
|
result = self.pytester.runpytest("--junitxml=%s" % xml_path, *args)
|
||||||
if family == "xunit2":
|
if family == "xunit2":
|
||||||
with xml_path.open() as f:
|
with xml_path.open(encoding="utf-8") as f:
|
||||||
self.schema.validate(f)
|
self.schema.validate(f)
|
||||||
xmldoc = minidom.parse(str(xml_path))
|
xmldoc = minidom.parse(str(xml_path))
|
||||||
return result, DomNode(xmldoc)
|
return result, DomNode(xmldoc)
|
||||||
|
@ -469,7 +469,7 @@ class TestPython:
|
||||||
self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
|
self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str
|
||||||
) -> None:
|
) -> None:
|
||||||
p = pytester.mkdir("sub").joinpath("test_hello.py")
|
p = pytester.mkdir("sub").joinpath("test_hello.py")
|
||||||
p.write_text("def test_func(): 0/0")
|
p.write_text("def test_func(): 0/0", encoding="utf-8")
|
||||||
result, dom = run_and_parse(family=xunit_family)
|
result, dom = run_and_parse(family=xunit_family)
|
||||||
assert result.ret
|
assert result.ret
|
||||||
node = dom.find_first_by_tag("testsuite")
|
node = dom.find_first_by_tag("testsuite")
|
||||||
|
@ -987,7 +987,7 @@ class TestNonPython:
|
||||||
return "custom item runtest failed"
|
return "custom item runtest failed"
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
pytester.path.joinpath("myfile.xyz").write_text("hello")
|
pytester.path.joinpath("myfile.xyz").write_text("hello", encoding="utf-8")
|
||||||
result, dom = run_and_parse(family=xunit_family)
|
result, dom = run_and_parse(family=xunit_family)
|
||||||
assert result.ret
|
assert result.ret
|
||||||
node = dom.find_first_by_tag("testsuite")
|
node = dom.find_first_by_tag("testsuite")
|
||||||
|
@ -1013,7 +1013,7 @@ def test_nullbyte(pytester: Pytester, junit_logging: str) -> None:
|
||||||
)
|
)
|
||||||
xmlf = pytester.path.joinpath("junit.xml")
|
xmlf = pytester.path.joinpath("junit.xml")
|
||||||
pytester.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging)
|
pytester.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging)
|
||||||
text = xmlf.read_text()
|
text = xmlf.read_text(encoding="utf-8")
|
||||||
assert "\x00" not in text
|
assert "\x00" not in text
|
||||||
if junit_logging == "system-out":
|
if junit_logging == "system-out":
|
||||||
assert "#x00" in text
|
assert "#x00" in text
|
||||||
|
@ -1035,7 +1035,7 @@ def test_nullbyte_replace(pytester: Pytester, junit_logging: str) -> None:
|
||||||
)
|
)
|
||||||
xmlf = pytester.path.joinpath("junit.xml")
|
xmlf = pytester.path.joinpath("junit.xml")
|
||||||
pytester.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging)
|
pytester.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging)
|
||||||
text = xmlf.read_text()
|
text = xmlf.read_text(encoding="utf-8")
|
||||||
if junit_logging == "system-out":
|
if junit_logging == "system-out":
|
||||||
assert "#x0" in text
|
assert "#x0" in text
|
||||||
if junit_logging == "no":
|
if junit_logging == "no":
|
||||||
|
|
|
@ -59,7 +59,8 @@ def test_link_resolve(pytester: Pytester) -> None:
|
||||||
def test_foo():
|
def test_foo():
|
||||||
raise AssertionError()
|
raise AssertionError()
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
subst = subst_path_linux
|
subst = subst_path_linux
|
||||||
|
|
|
@ -324,7 +324,8 @@ def test_importerror(pytester: Pytester) -> None:
|
||||||
|
|
||||||
x = 1
|
x = 1
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
pytester.path.joinpath("test_importerror.py").write_text(
|
pytester.path.joinpath("test_importerror.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -332,7 +333,8 @@ def test_importerror(pytester: Pytester) -> None:
|
||||||
def test_importerror(monkeypatch):
|
def test_importerror(monkeypatch):
|
||||||
monkeypatch.setattr('package.a.x', 2)
|
monkeypatch.setattr('package.a.x', 2)
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest()
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
|
@ -434,11 +436,13 @@ def test_syspath_prepend_with_namespace_packages(
|
||||||
ns = d.joinpath("ns_pkg")
|
ns = d.joinpath("ns_pkg")
|
||||||
ns.mkdir()
|
ns.mkdir()
|
||||||
ns.joinpath("__init__.py").write_text(
|
ns.joinpath("__init__.py").write_text(
|
||||||
"__import__('pkg_resources').declare_namespace(__name__)"
|
"__import__('pkg_resources').declare_namespace(__name__)", encoding="utf-8"
|
||||||
)
|
)
|
||||||
lib = ns.joinpath(dirname)
|
lib = ns.joinpath(dirname)
|
||||||
lib.mkdir()
|
lib.mkdir()
|
||||||
lib.joinpath("__init__.py").write_text("def check(): return %r" % dirname)
|
lib.joinpath("__init__.py").write_text(
|
||||||
|
"def check(): return %r" % dirname, encoding="utf-8"
|
||||||
|
)
|
||||||
|
|
||||||
monkeypatch.syspath_prepend("hello")
|
monkeypatch.syspath_prepend("hello")
|
||||||
import ns_pkg.hello
|
import ns_pkg.hello
|
||||||
|
@ -457,5 +461,5 @@ def test_syspath_prepend_with_namespace_packages(
|
||||||
# Should invalidate caches via importlib.invalidate_caches.
|
# Should invalidate caches via importlib.invalidate_caches.
|
||||||
modules_tmpdir = pytester.mkdir("modules_tmpdir")
|
modules_tmpdir = pytester.mkdir("modules_tmpdir")
|
||||||
monkeypatch.syspath_prepend(str(modules_tmpdir))
|
monkeypatch.syspath_prepend(str(modules_tmpdir))
|
||||||
modules_tmpdir.joinpath("main_app.py").write_text("app = True")
|
modules_tmpdir.joinpath("main_app.py").write_text("app = True", encoding="utf-8")
|
||||||
from main_app import app # noqa: F401
|
from main_app import app # noqa: F401
|
||||||
|
|
|
@ -23,7 +23,9 @@ def test_nose_setup(pytester: Pytester) -> None:
|
||||||
test_hello.teardown = lambda: values.append(2)
|
test_hello.teardown = lambda: values.append(2)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(p, "-p", "nose")
|
result = pytester.runpytest(
|
||||||
|
p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
|
||||||
|
)
|
||||||
result.assert_outcomes(passed=2)
|
result.assert_outcomes(passed=2)
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,7 +78,9 @@ def test_nose_setup_func(pytester: Pytester) -> None:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(p, "-p", "nose")
|
result = pytester.runpytest(
|
||||||
|
p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
|
||||||
|
)
|
||||||
result.assert_outcomes(passed=2)
|
result.assert_outcomes(passed=2)
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,7 +104,9 @@ def test_nose_setup_func_failure(pytester: Pytester) -> None:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(p, "-p", "nose")
|
result = pytester.runpytest(
|
||||||
|
p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
|
||||||
|
)
|
||||||
result.stdout.fnmatch_lines(["*TypeError: <lambda>()*"])
|
result.stdout.fnmatch_lines(["*TypeError: <lambda>()*"])
|
||||||
|
|
||||||
|
|
||||||
|
@ -154,7 +160,9 @@ def test_nose_setup_partial(pytester: Pytester) -> None:
|
||||||
test_hello.teardown = my_teardown_partial
|
test_hello.teardown = my_teardown_partial
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(p, "-p", "nose")
|
result = pytester.runpytest(
|
||||||
|
p, "-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
|
||||||
|
)
|
||||||
result.stdout.fnmatch_lines(["*2 passed*"])
|
result.stdout.fnmatch_lines(["*2 passed*"])
|
||||||
|
|
||||||
|
|
||||||
|
@ -193,7 +201,9 @@ def test_module_level_setup(pytester: Pytester) -> None:
|
||||||
assert items["setup2"] == ["up", "down", "up"]
|
assert items["setup2"] == ["up", "down", "up"]
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest("-p", "nose")
|
result = pytester.runpytest(
|
||||||
|
"-p", "nose", "-Wignore::pytest.PytestRemovedIn8Warning"
|
||||||
|
)
|
||||||
result.stdout.fnmatch_lines(["*4 passed*"])
|
result.stdout.fnmatch_lines(["*4 passed*"])
|
||||||
|
|
||||||
|
|
||||||
|
@ -278,7 +288,7 @@ def test_nose_setup_ordering(pytester: Pytester) -> None:
|
||||||
assert self.visited_cls
|
assert self.visited_cls
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest()
|
result = pytester.runpytest("-Wignore::pytest.PytestRemovedIn8Warning")
|
||||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||||
|
|
||||||
|
|
||||||
|
@ -494,7 +504,7 @@ def test_nose_setup_skipped_if_non_callable(pytester: Pytester) -> None:
|
||||||
pass
|
pass
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(p, "-p", "nose")
|
result = pytester.runpytest(p.parent, "-p", "nose")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import argparse
|
import argparse
|
||||||
|
import locale
|
||||||
import os
|
import os
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -289,6 +290,10 @@ class TestParser:
|
||||||
|
|
||||||
|
|
||||||
def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
|
def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
|
||||||
|
try:
|
||||||
|
encoding = locale.getencoding() # New in Python 3.11, ignores utf-8 mode
|
||||||
|
except AttributeError:
|
||||||
|
encoding = locale.getpreferredencoding(False)
|
||||||
try:
|
try:
|
||||||
bash_version = subprocess.run(
|
bash_version = subprocess.run(
|
||||||
["bash", "--version"],
|
["bash", "--version"],
|
||||||
|
@ -296,6 +301,7 @@ def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
|
||||||
stderr=subprocess.DEVNULL,
|
stderr=subprocess.DEVNULL,
|
||||||
check=True,
|
check=True,
|
||||||
text=True,
|
text=True,
|
||||||
|
encoding=encoding,
|
||||||
).stdout
|
).stdout
|
||||||
except (OSError, subprocess.CalledProcessError):
|
except (OSError, subprocess.CalledProcessError):
|
||||||
pytest.skip("bash is not available")
|
pytest.skip("bash is not available")
|
||||||
|
@ -305,7 +311,7 @@ def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None:
|
||||||
|
|
||||||
script = str(pytester.path.joinpath("test_argcomplete"))
|
script = str(pytester.path.joinpath("test_argcomplete"))
|
||||||
|
|
||||||
with open(str(script), "w") as fp:
|
with open(str(script), "w", encoding="utf-8") as fp:
|
||||||
# redirect output from argcomplete to stdin and stderr is not trivial
|
# redirect output from argcomplete to stdin and stderr is not trivial
|
||||||
# http://stackoverflow.com/q/12589419/1307905
|
# http://stackoverflow.com/q/12589419/1307905
|
||||||
# so we use bash
|
# so we use bash
|
||||||
|
|
|
@ -100,13 +100,13 @@ class TestImportPath:
|
||||||
def setuptestfs(self, path: Path) -> None:
|
def setuptestfs(self, path: Path) -> None:
|
||||||
# print "setting up test fs for", repr(path)
|
# print "setting up test fs for", repr(path)
|
||||||
samplefile = path / "samplefile"
|
samplefile = path / "samplefile"
|
||||||
samplefile.write_text("samplefile\n")
|
samplefile.write_text("samplefile\n", encoding="utf-8")
|
||||||
|
|
||||||
execfile = path / "execfile"
|
execfile = path / "execfile"
|
||||||
execfile.write_text("x=42")
|
execfile.write_text("x=42", encoding="utf-8")
|
||||||
|
|
||||||
execfilepy = path / "execfile.py"
|
execfilepy = path / "execfile.py"
|
||||||
execfilepy.write_text("x=42")
|
execfilepy.write_text("x=42", encoding="utf-8")
|
||||||
|
|
||||||
d = {1: 2, "hello": "world", "answer": 42}
|
d = {1: 2, "hello": "world", "answer": 42}
|
||||||
path.joinpath("samplepickle").write_bytes(pickle.dumps(d, 1))
|
path.joinpath("samplepickle").write_bytes(pickle.dumps(d, 1))
|
||||||
|
@ -120,9 +120,9 @@ class TestImportPath:
|
||||||
otherdir.joinpath("__init__.py").touch()
|
otherdir.joinpath("__init__.py").touch()
|
||||||
|
|
||||||
module_a = otherdir / "a.py"
|
module_a = otherdir / "a.py"
|
||||||
module_a.write_text("from .b import stuff as result\n")
|
module_a.write_text("from .b import stuff as result\n", encoding="utf-8")
|
||||||
module_b = otherdir / "b.py"
|
module_b = otherdir / "b.py"
|
||||||
module_b.write_text('stuff="got it"\n')
|
module_b.write_text('stuff="got it"\n', encoding="utf-8")
|
||||||
module_c = otherdir / "c.py"
|
module_c = otherdir / "c.py"
|
||||||
module_c.write_text(
|
module_c.write_text(
|
||||||
dedent(
|
dedent(
|
||||||
|
@ -131,7 +131,8 @@ class TestImportPath:
|
||||||
import otherdir.a
|
import otherdir.a
|
||||||
value = otherdir.a.result
|
value = otherdir.a.result
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
module_d = otherdir / "d.py"
|
module_d = otherdir / "d.py"
|
||||||
module_d.write_text(
|
module_d.write_text(
|
||||||
|
@ -141,7 +142,8 @@ class TestImportPath:
|
||||||
from otherdir import a
|
from otherdir import a
|
||||||
value2 = a.result
|
value2 = a.result
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_smoke_test(self, path1: Path) -> None:
|
def test_smoke_test(self, path1: Path) -> None:
|
||||||
|
@ -283,7 +285,7 @@ class TestImportPath:
|
||||||
def simple_module(self, tmp_path: Path) -> Path:
|
def simple_module(self, tmp_path: Path) -> Path:
|
||||||
fn = tmp_path / "_src/tests/mymod.py"
|
fn = tmp_path / "_src/tests/mymod.py"
|
||||||
fn.parent.mkdir(parents=True)
|
fn.parent.mkdir(parents=True)
|
||||||
fn.write_text("def foo(x): return 40 + x")
|
fn.write_text("def foo(x): return 40 + x", encoding="utf-8")
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
def test_importmode_importlib(self, simple_module: Path, tmp_path: Path) -> None:
|
def test_importmode_importlib(self, simple_module: Path, tmp_path: Path) -> None:
|
||||||
|
@ -447,7 +449,7 @@ def test_samefile_false_negatives(tmp_path: Path, monkeypatch: MonkeyPatch) -> N
|
||||||
return False, even when they are clearly equal.
|
return False, even when they are clearly equal.
|
||||||
"""
|
"""
|
||||||
module_path = tmp_path.joinpath("my_module.py")
|
module_path = tmp_path.joinpath("my_module.py")
|
||||||
module_path.write_text("def foo(): return 42")
|
module_path.write_text("def foo(): return 42", encoding="utf-8")
|
||||||
monkeypatch.syspath_prepend(tmp_path)
|
monkeypatch.syspath_prepend(tmp_path)
|
||||||
|
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
|
@ -473,7 +475,8 @@ class TestImportLibMode:
|
||||||
class Data:
|
class Data:
|
||||||
value: str
|
value: str
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
module = import_path(fn, mode="importlib", root=tmp_path)
|
module = import_path(fn, mode="importlib", root=tmp_path)
|
||||||
|
@ -498,7 +501,8 @@ class TestImportLibMode:
|
||||||
s = pickle.dumps(_action)
|
s = pickle.dumps(_action)
|
||||||
return pickle.loads(s)
|
return pickle.loads(s)
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
module = import_path(fn, mode="importlib", root=tmp_path)
|
module = import_path(fn, mode="importlib", root=tmp_path)
|
||||||
|
@ -525,7 +529,8 @@ class TestImportLibMode:
|
||||||
class Data:
|
class Data:
|
||||||
x: int = 42
|
x: int = 42
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
fn2 = tmp_path.joinpath("_src/m2/tests/test.py")
|
fn2 = tmp_path.joinpath("_src/m2/tests/test.py")
|
||||||
|
@ -540,7 +545,8 @@ class TestImportLibMode:
|
||||||
class Data:
|
class Data:
|
||||||
x: str = ""
|
x: str = ""
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
import pickle
|
import pickle
|
||||||
|
|
|
@ -347,7 +347,7 @@ class TestPytestPluginManager:
|
||||||
pytest.raises(ImportError, pytestpm.import_plugin, "pytest_qweqwex.y")
|
pytest.raises(ImportError, pytestpm.import_plugin, "pytest_qweqwex.y")
|
||||||
|
|
||||||
pytester.syspathinsert()
|
pytester.syspathinsert()
|
||||||
pytester.mkpydir("pkg").joinpath("plug.py").write_text("x=3")
|
pytester.mkpydir("pkg").joinpath("plug.py").write_text("x=3", encoding="utf-8")
|
||||||
pluginname = "pkg.plug"
|
pluginname = "pkg.plug"
|
||||||
pytestpm.import_plugin(pluginname)
|
pytestpm.import_plugin(pluginname)
|
||||||
mod = pytestpm.get_plugin("pkg.plug")
|
mod = pytestpm.get_plugin("pkg.plug")
|
||||||
|
|
|
@ -222,7 +222,7 @@ class TestInlineRunModulesCleanup:
|
||||||
result = pytester.inline_run(str(test_mod))
|
result = pytester.inline_run(str(test_mod))
|
||||||
assert result.ret == ExitCode.OK
|
assert result.ret == ExitCode.OK
|
||||||
# rewrite module, now test should fail if module was re-imported
|
# rewrite module, now test should fail if module was re-imported
|
||||||
test_mod.write_text("def test_foo(): assert False")
|
test_mod.write_text("def test_foo(): assert False", encoding="utf-8")
|
||||||
result2 = pytester.inline_run(str(test_mod))
|
result2 = pytester.inline_run(str(test_mod))
|
||||||
assert result2.ret == ExitCode.TESTS_FAILED
|
assert result2.ret == ExitCode.TESTS_FAILED
|
||||||
|
|
||||||
|
|
|
@ -410,7 +410,7 @@ class TestReportSerialization:
|
||||||
) -> None:
|
) -> None:
|
||||||
sub_dir = pytester.path.joinpath("ns")
|
sub_dir = pytester.path.joinpath("ns")
|
||||||
sub_dir.mkdir()
|
sub_dir.mkdir()
|
||||||
sub_dir.joinpath("conftest.py").write_text("import unknown")
|
sub_dir.joinpath("conftest.py").write_text("import unknown", encoding="utf-8")
|
||||||
|
|
||||||
result = pytester.runpytest_subprocess(".")
|
result = pytester.runpytest_subprocess(".")
|
||||||
result.stdout.fnmatch_lines(["E *Error: No module named 'unknown'"])
|
result.stdout.fnmatch_lines(["E *Error: No module named 'unknown'"])
|
||||||
|
|
|
@ -265,9 +265,9 @@ def test_plugin_already_exists(pytester: Pytester) -> None:
|
||||||
|
|
||||||
def test_exclude(pytester: Pytester) -> None:
|
def test_exclude(pytester: Pytester) -> None:
|
||||||
hellodir = pytester.mkdir("hello")
|
hellodir = pytester.mkdir("hello")
|
||||||
hellodir.joinpath("test_hello.py").write_text("x y syntaxerror")
|
hellodir.joinpath("test_hello.py").write_text("x y syntaxerror", encoding="utf-8")
|
||||||
hello2dir = pytester.mkdir("hello2")
|
hello2dir = pytester.mkdir("hello2")
|
||||||
hello2dir.joinpath("test_hello2.py").write_text("x y syntaxerror")
|
hello2dir.joinpath("test_hello2.py").write_text("x y syntaxerror", encoding="utf-8")
|
||||||
pytester.makepyfile(test_ok="def test_pass(): pass")
|
pytester.makepyfile(test_ok="def test_pass(): pass")
|
||||||
result = pytester.runpytest("--ignore=hello", "--ignore=hello2")
|
result = pytester.runpytest("--ignore=hello", "--ignore=hello2")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
@ -276,13 +276,13 @@ def test_exclude(pytester: Pytester) -> None:
|
||||||
|
|
||||||
def test_exclude_glob(pytester: Pytester) -> None:
|
def test_exclude_glob(pytester: Pytester) -> None:
|
||||||
hellodir = pytester.mkdir("hello")
|
hellodir = pytester.mkdir("hello")
|
||||||
hellodir.joinpath("test_hello.py").write_text("x y syntaxerror")
|
hellodir.joinpath("test_hello.py").write_text("x y syntaxerror", encoding="utf-8")
|
||||||
hello2dir = pytester.mkdir("hello2")
|
hello2dir = pytester.mkdir("hello2")
|
||||||
hello2dir.joinpath("test_hello2.py").write_text("x y syntaxerror")
|
hello2dir.joinpath("test_hello2.py").write_text("x y syntaxerror", encoding="utf-8")
|
||||||
hello3dir = pytester.mkdir("hallo3")
|
hello3dir = pytester.mkdir("hallo3")
|
||||||
hello3dir.joinpath("test_hello3.py").write_text("x y syntaxerror")
|
hello3dir.joinpath("test_hello3.py").write_text("x y syntaxerror", encoding="utf-8")
|
||||||
subdir = pytester.mkdir("sub")
|
subdir = pytester.mkdir("sub")
|
||||||
subdir.joinpath("test_hello4.py").write_text("x y syntaxerror")
|
subdir.joinpath("test_hello4.py").write_text("x y syntaxerror", encoding="utf-8")
|
||||||
pytester.makepyfile(test_ok="def test_pass(): pass")
|
pytester.makepyfile(test_ok="def test_pass(): pass")
|
||||||
result = pytester.runpytest("--ignore-glob=*h[ea]llo*")
|
result = pytester.runpytest("--ignore-glob=*h[ea]llo*")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
|
@ -195,7 +195,8 @@ class TestEvaluation:
|
||||||
def pytest_markeval_namespace():
|
def pytest_markeval_namespace():
|
||||||
return {"arg": "root"}
|
return {"arg": "root"}
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
root.joinpath("test_root.py").write_text(
|
root.joinpath("test_root.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -206,7 +207,8 @@ class TestEvaluation:
|
||||||
def test_root():
|
def test_root():
|
||||||
assert False
|
assert False
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
foo = root.joinpath("foo")
|
foo = root.joinpath("foo")
|
||||||
foo.mkdir()
|
foo.mkdir()
|
||||||
|
@ -219,7 +221,8 @@ class TestEvaluation:
|
||||||
def pytest_markeval_namespace():
|
def pytest_markeval_namespace():
|
||||||
return {"arg": "foo"}
|
return {"arg": "foo"}
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
foo.joinpath("test_foo.py").write_text(
|
foo.joinpath("test_foo.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -230,7 +233,8 @@ class TestEvaluation:
|
||||||
def test_foo():
|
def test_foo():
|
||||||
assert False
|
assert False
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
bar = root.joinpath("bar")
|
bar = root.joinpath("bar")
|
||||||
bar.mkdir()
|
bar.mkdir()
|
||||||
|
@ -243,7 +247,8 @@ class TestEvaluation:
|
||||||
def pytest_markeval_namespace():
|
def pytest_markeval_namespace():
|
||||||
return {"arg": "bar"}
|
return {"arg": "bar"}
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
bar.joinpath("test_bar.py").write_text(
|
bar.joinpath("test_bar.py").write_text(
|
||||||
textwrap.dedent(
|
textwrap.dedent(
|
||||||
|
@ -254,7 +259,8 @@ class TestEvaluation:
|
||||||
def test_bar():
|
def test_bar():
|
||||||
assert False
|
assert False
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
reprec = pytester.inline_run("-vs", "--capture=no")
|
reprec = pytester.inline_run("-vs", "--capture=no")
|
||||||
|
@ -629,7 +635,8 @@ class TestXFail:
|
||||||
|
|
||||||
@pytest.mark.xfail(reason='unsupported feature', strict=%s)
|
@pytest.mark.xfail(reason='unsupported feature', strict=%s)
|
||||||
def test_foo():
|
def test_foo():
|
||||||
with open('foo_executed', 'w'): pass # make sure test executes
|
with open('foo_executed', 'w', encoding='utf-8'):
|
||||||
|
pass # make sure test executes
|
||||||
"""
|
"""
|
||||||
% strict
|
% strict
|
||||||
)
|
)
|
||||||
|
|
|
@ -352,6 +352,6 @@ def test_one():
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
assert Path(stepwise_cache_file).exists()
|
assert Path(stepwise_cache_file).exists()
|
||||||
with stepwise_cache_file.open() as file_handle:
|
with stepwise_cache_file.open(encoding="utf-8") as file_handle:
|
||||||
observed_value = file_handle.readlines()
|
observed_value = file_handle.readlines()
|
||||||
assert [expected_value] == observed_value
|
assert [expected_value] == observed_value
|
||||||
|
|
|
@ -244,7 +244,8 @@ class TestTerminal:
|
||||||
def test_method(self):
|
def test_method(self):
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
)
|
),
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest("-vv")
|
result = pytester.runpytest("-vv")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
@ -1567,7 +1568,8 @@ class TestGenericReporting:
|
||||||
"""
|
"""
|
||||||
def pytest_report_header(config, start_path):
|
def pytest_report_header(config, start_path):
|
||||||
return ["line1", str(start_path)]
|
return ["line1", str(start_path)]
|
||||||
"""
|
""",
|
||||||
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
result = pytester.runpytest("a")
|
result = pytester.runpytest("a")
|
||||||
result.stdout.fnmatch_lines(["*hello: 42*", "line1", str(pytester.path)])
|
result.stdout.fnmatch_lines(["*hello: 42*", "line1", str(pytester.path)])
|
||||||
|
@ -1671,7 +1673,7 @@ def test_fdopen_kept_alive_issue124(pytester: Pytester) -> None:
|
||||||
import os, sys
|
import os, sys
|
||||||
k = []
|
k = []
|
||||||
def test_open_file_and_keep_alive(capfd):
|
def test_open_file_and_keep_alive(capfd):
|
||||||
stdout = os.fdopen(1, 'w', 1)
|
stdout = os.fdopen(1, 'w', buffering=1, encoding='utf-8')
|
||||||
k.append(stdout)
|
k.append(stdout)
|
||||||
|
|
||||||
def test_close_kept_alive_file():
|
def test_close_kept_alive_file():
|
||||||
|
|
|
@ -561,7 +561,7 @@ def test_basetemp_with_read_only_files(pytester: Pytester) -> None:
|
||||||
|
|
||||||
def test(tmp_path):
|
def test(tmp_path):
|
||||||
fn = tmp_path / 'foo.txt'
|
fn = tmp_path / 'foo.txt'
|
||||||
fn.write_text('hello')
|
fn.write_text('hello', encoding='utf-8')
|
||||||
mode = os.stat(str(fn)).st_mode
|
mode = os.stat(str(fn)).st_mode
|
||||||
os.chmod(str(fn), mode & ~stat.S_IREAD)
|
os.chmod(str(fn), mode & ~stat.S_IREAD)
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -518,7 +518,8 @@ class TestDeprecationWarningsByDefault:
|
||||||
assert WARNINGS_SUMMARY_HEADER not in result.stdout.str()
|
assert WARNINGS_SUMMARY_HEADER not in result.stdout.str()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skip("not relevant until pytest 8.0")
|
# In 8.1, uncomment below and change RemovedIn8 -> RemovedIn9.
|
||||||
|
# @pytest.mark.skip("not relevant until pytest 9.0")
|
||||||
@pytest.mark.parametrize("change_default", [None, "ini", "cmdline"])
|
@pytest.mark.parametrize("change_default", [None, "ini", "cmdline"])
|
||||||
def test_removed_in_x_warning_as_error(pytester: Pytester, change_default) -> None:
|
def test_removed_in_x_warning_as_error(pytester: Pytester, change_default) -> None:
|
||||||
"""This ensures that PytestRemovedInXWarnings raised by pytest are turned into errors.
|
"""This ensures that PytestRemovedInXWarnings raised by pytest are turned into errors.
|
||||||
|
@ -810,12 +811,12 @@ def test_resource_warning(pytester: Pytester, monkeypatch: pytest.MonkeyPatch) -
|
||||||
pytester.makepyfile(
|
pytester.makepyfile(
|
||||||
"""
|
"""
|
||||||
def open_file(p):
|
def open_file(p):
|
||||||
f = p.open("r")
|
f = p.open("r", encoding="utf-8")
|
||||||
assert p.read_text() == "hello"
|
assert p.read_text() == "hello"
|
||||||
|
|
||||||
def test_resource_warning(tmp_path):
|
def test_resource_warning(tmp_path):
|
||||||
p = tmp_path.joinpath("foo.txt")
|
p = tmp_path.joinpath("foo.txt")
|
||||||
p.write_text("hello")
|
p.write_text("hello", encoding="utf-8")
|
||||||
open_file(p)
|
open_file(p)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
4
tox.ini
4
tox.ini
|
@ -38,6 +38,10 @@ passenv =
|
||||||
setenv =
|
setenv =
|
||||||
_PYTEST_TOX_DEFAULT_POSARGS={env:_PYTEST_TOX_POSARGS_DOCTESTING:} {env:_PYTEST_TOX_POSARGS_LSOF:} {env:_PYTEST_TOX_POSARGS_XDIST:}
|
_PYTEST_TOX_DEFAULT_POSARGS={env:_PYTEST_TOX_POSARGS_DOCTESTING:} {env:_PYTEST_TOX_POSARGS_LSOF:} {env:_PYTEST_TOX_POSARGS_XDIST:}
|
||||||
|
|
||||||
|
# See https://docs.python.org/3/library/io.html#io-encoding-warning
|
||||||
|
# If we don't enable this, neither can any of our downstream users!
|
||||||
|
PYTHONWARNDEFAULTENCODING=1
|
||||||
|
|
||||||
# Configuration to run with coverage similar to CI, e.g.
|
# Configuration to run with coverage similar to CI, e.g.
|
||||||
# "tox -e py37-coverage".
|
# "tox -e py37-coverage".
|
||||||
coverage: _PYTEST_TOX_COVERAGE_RUN=coverage run -m
|
coverage: _PYTEST_TOX_COVERAGE_RUN=coverage run -m
|
||||||
|
|
Loading…
Reference in New Issue