Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3673c7429 | ||
|
|
25fe3706a4 | ||
|
|
1a323fbd3c | ||
|
|
9d971d33be | ||
|
|
bc009a8582 | ||
|
|
5e7d427df1 | ||
|
|
dd59ed3b18 | ||
|
|
20d0f0e56b | ||
|
|
d24a7e6c5a | ||
|
|
4dc73bda45 | ||
|
|
732cc2687d | ||
|
|
5d2d64c190 | ||
|
|
7a6d16c1eb | ||
|
|
c2179c3127 | ||
|
|
d8d7f73e1c | ||
|
|
3c23b5b010 | ||
|
|
783019a8e6 | ||
|
|
d2fc7ca6e0 | ||
|
|
2d06927a06 | ||
|
|
44d29d887e | ||
|
|
32c5a113e2 | ||
|
|
ba5630e0f8 | ||
|
|
808df48ee8 | ||
|
|
a089a9577e | ||
|
|
6be2136f20 | ||
|
|
f9ab81a493 | ||
|
|
1636522563 | ||
|
|
b1fbb2ab92 | ||
|
|
e85edf5212 | ||
|
|
b03bad5dbb | ||
|
|
19ec300b2a | ||
|
|
11442f2ad7 | ||
|
|
97748b6605 | ||
|
|
2b762337bd | ||
|
|
9899b8f1fb | ||
|
|
4474beeb82 | ||
|
|
eca3e781b6 | ||
|
|
c61ff31ffa | ||
|
|
ec57cbf82d | ||
|
|
3f6a46c2a4 | ||
|
|
4ba3cb25b0 | ||
|
|
650c458df9 | ||
|
|
58aa4f91f5 | ||
|
|
9b382ed16c | ||
|
|
f02dbaf97f | ||
|
|
41f6ea13ce | ||
|
|
f6eb39df33 | ||
|
|
7a5e11bbcf | ||
|
|
7122fa5613 | ||
|
|
7aff81739e | ||
|
|
27772f67c0 | ||
|
|
10b3b2dc68 | ||
|
|
c2841542af | ||
|
|
1f28096587 | ||
|
|
e86b01e831 | ||
|
|
d1fa8ae08e | ||
|
|
29dac03314 | ||
|
|
e7eb7e799b | ||
|
|
7f48f552c1 | ||
|
|
1e2e65f0fa | ||
|
|
28c9cc7321 | ||
|
|
ccb90b5c46 | ||
|
|
37d2469266 | ||
|
|
1df6d28080 | ||
|
|
03eaad376b | ||
|
|
739f9a4a4b | ||
|
|
93224f8cf9 | ||
|
|
bb57186dd4 | ||
|
|
2803eb9fbb | ||
|
|
f53eff93db | ||
|
|
86a14d007d | ||
|
|
a4dd6ee3ce | ||
|
|
130cf7e0db | ||
|
|
cbb41f1ae2 | ||
|
|
fa78da3c03 | ||
|
|
87ddb2dbd5 | ||
|
|
9aa6b0903b | ||
|
|
49800ea134 | ||
|
|
8fe55b1d18 | ||
|
|
a0ce9a4441 | ||
|
|
2cf2dc3d95 | ||
|
|
7537e94ddf | ||
|
|
2c90b3db9e | ||
|
|
826adafe2e | ||
|
|
3dd2933dbd | ||
|
|
b55351274e | ||
|
|
c00d934b21 | ||
|
|
6b526cbe6a | ||
|
|
e0539e6ede | ||
|
|
5eb85efa14 | ||
|
|
9ee8d72fd2 | ||
|
|
8c4ca383ca | ||
|
|
f2a427da25 | ||
|
|
e0466d0ad8 | ||
|
|
418a66a09f | ||
|
|
5e2bd17d18 | ||
|
|
ec6fca4aa7 |
@@ -1,4 +1,9 @@
|
||||
[run]
|
||||
source = _pytest,testing
|
||||
source = pytest,_pytest,testing/
|
||||
parallel = 1
|
||||
branch = 1
|
||||
|
||||
[paths]
|
||||
source = src/
|
||||
.tox/*/lib/python*/site-packages/
|
||||
.tox\*\Lib\site-packages\
|
||||
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1 +1 @@
|
||||
CHANGELOG merge=union
|
||||
*.bat text eol=crlf
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -24,6 +24,7 @@ src/_pytest/_version.py
|
||||
.eggs/
|
||||
|
||||
doc/*/_build
|
||||
doc/*/.doctrees
|
||||
build/
|
||||
dist/
|
||||
*.egg-info
|
||||
@@ -35,6 +36,8 @@ env/
|
||||
.cache
|
||||
.pytest_cache
|
||||
.coverage
|
||||
.coverage.*
|
||||
coverage.xml
|
||||
.ropeproject
|
||||
.idea
|
||||
.hypothesis
|
||||
|
||||
@@ -38,7 +38,8 @@ repos:
|
||||
language: python
|
||||
additional_dependencies: [pygments, restructuredtext_lint]
|
||||
- id: changelogs-rst
|
||||
name: changelog files must end in .rst
|
||||
entry: ./scripts/fail
|
||||
language: script
|
||||
files: 'changelog/.*(?<!\.rst)$'
|
||||
name: changelog filenames
|
||||
language: fail
|
||||
entry: 'changelog files must be named ####.(feature|bugfix|doc|removal|vendor|trivial).rst'
|
||||
exclude: changelog/(\d+\.(feature|bugfix|doc|removal|vendor|trivial).rst|README.rst|_template.rst)
|
||||
files: ^changelog/
|
||||
|
||||
18
.travis.yml
18
.travis.yml
@@ -18,12 +18,12 @@ env:
|
||||
- TOXENV=py27-xdist
|
||||
- TOXENV=py27-trial
|
||||
- TOXENV=py27-numpy
|
||||
- TOXENV=py27-pluggymaster
|
||||
- TOXENV=py27-pluggymaster PYTEST_NO_COVERAGE=1
|
||||
- TOXENV=py36-pexpect
|
||||
- TOXENV=py36-xdist
|
||||
- TOXENV=py36-trial
|
||||
- TOXENV=py36-numpy
|
||||
- TOXENV=py36-pluggymaster
|
||||
- TOXENV=py36-pluggymaster PYTEST_NO_COVERAGE=1
|
||||
- TOXENV=py27-nobyte
|
||||
- TOXENV=doctesting
|
||||
- TOXENV=docs PYTEST_NO_COVERAGE=1
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
|
||||
- stage: deploy
|
||||
python: '3.6'
|
||||
env:
|
||||
env: PYTEST_NO_COVERAGE=1
|
||||
install: pip install -U setuptools setuptools_scm
|
||||
script: skip
|
||||
deploy:
|
||||
@@ -82,7 +82,9 @@ jobs:
|
||||
before_script:
|
||||
- |
|
||||
if [[ "$PYTEST_NO_COVERAGE" != 1 ]]; then
|
||||
export _PYTEST_TOX_COVERAGE_RUN="env COVERAGE_FILE=$PWD/.coverage COVERAGE_PROCESS_START=$PWD/.coveragerc coverage run --source {envsitepackagesdir}/_pytest/,$PWD/testing -m"
|
||||
export COVERAGE_FILE="$PWD/.coverage"
|
||||
export COVERAGE_PROCESS_START="$PWD/.coveragerc"
|
||||
export _PYTEST_TOX_COVERAGE_RUN="coverage run -m"
|
||||
export _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess
|
||||
fi
|
||||
|
||||
@@ -92,11 +94,11 @@ after_success:
|
||||
- |
|
||||
if [[ "$PYTEST_NO_COVERAGE" != 1 ]]; then
|
||||
set -e
|
||||
pip install codecov
|
||||
pip install coverage
|
||||
coverage combine
|
||||
coverage xml
|
||||
coverage report -m
|
||||
codecov --required -X gcov pycov search -f coverage.xml --flags ${TOXENV//-/ }
|
||||
coverage xml --ignore-errors
|
||||
coverage report -m --ignore-errors
|
||||
bash <(curl -s https://codecov.io/bash) -Z -X gcov -X coveragepy -X search -X xcode -X gcovout -X fix -f coverage.xml -F "${TOXENV//-/,},linux"
|
||||
|
||||
# Coveralls does not support merged reports.
|
||||
if [[ "$TOXENV" = py37 ]]; then
|
||||
|
||||
6
AUTHORS
6
AUTHORS
@@ -10,6 +10,7 @@ Ahn Ki-Wook
|
||||
Alan Velasco
|
||||
Alexander Johnson
|
||||
Alexei Kozlenok
|
||||
Allan Feldman
|
||||
Anatoly Bubenkoff
|
||||
Anders Hovmöller
|
||||
Andras Tim
|
||||
@@ -93,6 +94,7 @@ Hui Wang (coldnight)
|
||||
Ian Bicking
|
||||
Ian Lesperance
|
||||
Ionuț Turturică
|
||||
Iwan Briquemont
|
||||
Jaap Broekhuizen
|
||||
Jan Balster
|
||||
Janne Vanhala
|
||||
@@ -178,6 +180,7 @@ Raphael Pierzina
|
||||
Raquel Alegre
|
||||
Ravi Chandra
|
||||
Roberto Polli
|
||||
Roland Puntaier
|
||||
Romain Dorgueil
|
||||
Roman Bolshakov
|
||||
Ronny Pfannschmidt
|
||||
@@ -222,6 +225,5 @@ Wim Glenn
|
||||
Wouter van Ackooy
|
||||
Xuan Luong
|
||||
Xuecong Liao
|
||||
Zac Hatfield-Dodds
|
||||
Zoltán Máté
|
||||
Roland Puntaier
|
||||
Allan Feldman
|
||||
|
||||
129
CHANGELOG.rst
129
CHANGELOG.rst
@@ -18,25 +18,112 @@ with advance notice in the **Deprecations** section of releases.
|
||||
|
||||
.. towncrier release notes start
|
||||
|
||||
pytest 3.8.2 (2018-10-02)
|
||||
=========================
|
||||
|
||||
Deprecations and Removals
|
||||
-------------------------
|
||||
|
||||
- `#4036 <https://github.com/pytest-dev/pytest/issues/4036>`_: The ``item`` parameter of ``pytest_warning_captured`` hook is now documented as deprecated. We realized only after
|
||||
the ``3.8`` release that this parameter is incompatible with ``pytest-xdist``.
|
||||
|
||||
Our policy is to not deprecate features during bugfix releases, but in this case we believe it makes sense as we are
|
||||
only documenting it as deprecated, without issuing warnings which might potentially break test suites. This will get
|
||||
the word out that hook implementers should not use this parameter at all.
|
||||
|
||||
In a future release ``item`` will always be ``None`` and will emit a proper warning when a hook implementation
|
||||
makes use of it.
|
||||
|
||||
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#3539 <https://github.com/pytest-dev/pytest/issues/3539>`_: Fix reload on assertion rewritten modules.
|
||||
|
||||
|
||||
- `#4034 <https://github.com/pytest-dev/pytest/issues/4034>`_: The ``.user_properties`` attribute of ``TestReport`` objects is a list
|
||||
of (name, value) tuples, but could sometimes be instantiated as a tuple
|
||||
of tuples. It is now always a list.
|
||||
|
||||
|
||||
- `#4039 <https://github.com/pytest-dev/pytest/issues/4039>`_: No longer issue warnings about using ``pytest_plugins`` in non-top-level directories when using ``--pyargs``: the
|
||||
current ``--pyargs`` mechanism is not reliable and might give false negatives.
|
||||
|
||||
|
||||
- `#4040 <https://github.com/pytest-dev/pytest/issues/4040>`_: Exclude empty reports for passed tests when ``-rP`` option is used.
|
||||
|
||||
|
||||
- `#4051 <https://github.com/pytest-dev/pytest/issues/4051>`_: Improve error message when an invalid Python expression is passed to the ``-m`` option.
|
||||
|
||||
|
||||
- `#4056 <https://github.com/pytest-dev/pytest/issues/4056>`_: ``MonkeyPatch.setenv`` and ``MonkeyPatch.delenv`` issue a warning if the environment variable name is not ``str`` on Python 2.
|
||||
|
||||
In Python 2, adding ``unicode`` keys to ``os.environ`` causes problems with ``subprocess`` (and possible other modules),
|
||||
making this a subtle bug specially susceptible when used with ``from __future__ import unicode_literals``.
|
||||
|
||||
|
||||
|
||||
Improved Documentation
|
||||
----------------------
|
||||
|
||||
- `#3928 <https://github.com/pytest-dev/pytest/issues/3928>`_: Add possible values for fixture scope to docs.
|
||||
|
||||
|
||||
pytest 3.8.1 (2018-09-22)
|
||||
=========================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#3286 <https://github.com/pytest-dev/pytest/issues/3286>`_: ``.pytest_cache`` directory is now automatically ignored by Git. Users who would like to contribute a solution for other SCMs please consult/comment on this issue.
|
||||
|
||||
|
||||
- `#3749 <https://github.com/pytest-dev/pytest/issues/3749>`_: Fix the following error during collection of tests inside packages::
|
||||
|
||||
TypeError: object of type 'Package' has no len()
|
||||
|
||||
|
||||
- `#3941 <https://github.com/pytest-dev/pytest/issues/3941>`_: Fix bug where indirect parametrization would consider the scope of all fixtures used by the test function to determine the parametrization scope, and not only the scope of the fixtures being parametrized.
|
||||
|
||||
|
||||
- `#3973 <https://github.com/pytest-dev/pytest/issues/3973>`_: Fix crash of the assertion rewriter if a test changed the current working directory without restoring it afterwards.
|
||||
|
||||
|
||||
- `#3998 <https://github.com/pytest-dev/pytest/issues/3998>`_: Fix issue that prevented some caplog properties (for example ``record_tuples``) from being available when entering the debugger with ``--pdb``.
|
||||
|
||||
|
||||
- `#3999 <https://github.com/pytest-dev/pytest/issues/3999>`_: Fix ``UnicodeDecodeError`` in python2.x when a class returns a non-ascii binary ``__repr__`` in an assertion which also contains non-ascii text.
|
||||
|
||||
|
||||
|
||||
Improved Documentation
|
||||
----------------------
|
||||
|
||||
- `#3996 <https://github.com/pytest-dev/pytest/issues/3996>`_: New `Deprecations and Removals <https://docs.pytest.org/en/latest/deprecations.html>`_ page shows all currently
|
||||
deprecated features, the rationale to do so, and alternatives to update your code. It also list features removed
|
||||
from pytest in past major releases to help those with ancient pytest versions to upgrade.
|
||||
|
||||
|
||||
|
||||
Trivial/Internal Changes
|
||||
------------------------
|
||||
|
||||
- `#3955 <https://github.com/pytest-dev/pytest/issues/3955>`_: Improve pre-commit detection for changelog filenames
|
||||
|
||||
|
||||
- `#3975 <https://github.com/pytest-dev/pytest/issues/3975>`_: Remove legacy code around im_func as that was python2 only
|
||||
|
||||
|
||||
pytest 3.8.0 (2018-09-05)
|
||||
=========================
|
||||
|
||||
Deprecations and Removals
|
||||
-------------------------
|
||||
|
||||
- `#2452 <https://github.com/pytest-dev/pytest/issues/2452>`_: ``Config.warn`` has been deprecated, it should be replaced by calls to the standard ``warnings.warn``.
|
||||
|
||||
``Node.warn`` now supports two signatures:
|
||||
|
||||
* ``node.warn(PytestWarning("some message"))``: is now the recommended way to call this function. The warning
|
||||
instance must be a ``PytestWarning`` or subclass instance.
|
||||
|
||||
* ``node.warn("CI", "some message")``: this code/message form is now deprecated and should be converted to
|
||||
the warning instance form above.
|
||||
|
||||
``RemovedInPytest4Warning`` and ``PytestExperimentalApiWarning`` are now part of the public API and should be accessed
|
||||
using ``pytest.RemovedInPytest4Warning`` and ``pytest.PytestExperimentalApiWarning``.
|
||||
|
||||
- `#2452 <https://github.com/pytest-dev/pytest/issues/2452>`_: ``Config.warn`` and ``Node.warn`` have been
|
||||
deprecated, see `<https://docs.pytest.org/en/latest/deprecations.html#config-warn-and-node-warn>`_ for rationale and
|
||||
examples.
|
||||
|
||||
- `#3936 <https://github.com/pytest-dev/pytest/issues/3936>`_: ``@pytest.mark.filterwarnings`` second parameter is no longer regex-escaped,
|
||||
making it possible to actually use regular expressions to check the warning message.
|
||||
@@ -63,7 +150,10 @@ Features
|
||||
more info.
|
||||
|
||||
|
||||
- `#3784 <https://github.com/pytest-dev/pytest/issues/3784>`_: Add option to disable plugin auto-loading.
|
||||
- `#3251 <https://github.com/pytest-dev/pytest/issues/3251>`_: Warnings are now captured and displayed during test collection.
|
||||
|
||||
|
||||
- `#3784 <https://github.com/pytest-dev/pytest/issues/3784>`_: ``PYTEST_DISABLE_PLUGIN_AUTOLOAD`` environment variable disables plugin auto-loading when set.
|
||||
|
||||
|
||||
- `#3829 <https://github.com/pytest-dev/pytest/issues/3829>`_: Added the ``count`` option to ``console_output_style`` to enable displaying the progress as a count instead of a percentage.
|
||||
@@ -250,15 +340,10 @@ pytest 3.7.0 (2018-07-30)
|
||||
Deprecations and Removals
|
||||
-------------------------
|
||||
|
||||
- `#2639 <https://github.com/pytest-dev/pytest/issues/2639>`_: ``pytest_namespace`` has been deprecated.
|
||||
|
||||
See the documentation for ``pytest_namespace`` hook for suggestions on how to deal
|
||||
with this in plugins which use this functionality.
|
||||
- `#2639 <https://github.com/pytest-dev/pytest/issues/2639>`_: ``pytest_namespace`` has been `deprecated <https://docs.pytest.org/en/latest/deprecations.html#pytest-namespace>`_.
|
||||
|
||||
|
||||
- `#3661 <https://github.com/pytest-dev/pytest/issues/3661>`_: Calling a fixture function directly, as opposed to request them in a test function, now issues a ``RemovedInPytest4Warning``. It will be changed into an error in pytest ``4.0``.
|
||||
|
||||
This is a great source of confusion to new users, which will often call the fixture functions and request them from test functions interchangeably, which breaks the fixture resolution model.
|
||||
- `#3661 <https://github.com/pytest-dev/pytest/issues/3661>`_: Calling a fixture function directly, as opposed to request them in a test function, now issues a ``RemovedInPytest4Warning``. See `the documentation for rationale and examples <https://docs.pytest.org/en/latest/deprecations.html#calling-fixtures-directly>`_.
|
||||
|
||||
|
||||
|
||||
@@ -620,7 +705,7 @@ Deprecations and Removals
|
||||
<https://github.com/pytest-dev/pytest/issues/2770>`_)
|
||||
|
||||
- Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py
|
||||
files, because they "leak" to the entire directory tree. (`#3084
|
||||
files, because they "leak" to the entire directory tree. `See the docs <https://docs.pytest.org/en/latest/deprecations.html#pytest-plugins-in-non-top-level-conftest-files>`_ for the rationale behind this decision (`#3084
|
||||
<https://github.com/pytest-dev/pytest/issues/3084>`_)
|
||||
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
|
||||
:target: https://pypi.org/project/pytest/
|
||||
|
||||
.. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg
|
||||
:target: https://coveralls.io/r/pytest-dev/pytest
|
||||
.. image:: https://codecov.io/gh/pytest-dev/pytest/branch/master/graph/badge.svg
|
||||
:target: https://codecov.io/gh/pytest-dev/pytest
|
||||
:alt: Code coverage Status
|
||||
|
||||
.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master
|
||||
:target: https://travis-ci.org/pytest-dev/pytest
|
||||
@@ -25,7 +26,7 @@
|
||||
:target: https://ci.appveyor.com/project/pytestbot/pytest
|
||||
|
||||
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
||||
:target: https://github.com/ambv/black
|
||||
:target: https://github.com/ambv/black
|
||||
|
||||
.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg
|
||||
:target: https://www.codetriage.com/pytest-dev/pytest
|
||||
|
||||
26
appveyor.yml
26
appveyor.yml
@@ -1,35 +1,30 @@
|
||||
environment:
|
||||
COVERALLS_REPO_TOKEN:
|
||||
secure: 2NJ5Ct55cHJ9WEg3xbSqCuv0rdgzzb6pnzOIG5OkMbTndw3wOBrXntWFoQrXiMFi
|
||||
# this is pytest's token in coveralls.io, encrypted
|
||||
# using pytestbot account as detailed here:
|
||||
# https://www.appveyor.com/docs/build-configuration#secure-variables
|
||||
|
||||
matrix:
|
||||
# coveralls is not in the default env list
|
||||
- TOXENV: "coveralls"
|
||||
# note: please use "tox --listenvs" to populate the build matrix below
|
||||
- TOXENV: "linting"
|
||||
PYTEST_NO_COVERAGE: "1"
|
||||
- TOXENV: "py27"
|
||||
- TOXENV: "py34"
|
||||
- TOXENV: "py35"
|
||||
- TOXENV: "py36"
|
||||
- TOXENV: "py37"
|
||||
- TOXENV: "pypy"
|
||||
- TOXENV: "py27-pexpect"
|
||||
PYTEST_NO_COVERAGE: "1"
|
||||
- TOXENV: "py27-xdist"
|
||||
- TOXENV: "py27-trial"
|
||||
- TOXENV: "py27-numpy"
|
||||
- TOXENV: "py27-pluggymaster"
|
||||
- TOXENV: "py36-pexpect"
|
||||
PYTEST_NO_COVERAGE: "1"
|
||||
- TOXENV: "py36-xdist"
|
||||
- TOXENV: "py36-trial"
|
||||
- TOXENV: "py36-numpy"
|
||||
- TOXENV: "py36-pluggymaster"
|
||||
PYTEST_NO_COVERAGE: "1"
|
||||
- TOXENV: "py27-nobyte"
|
||||
- TOXENV: "doctesting"
|
||||
- TOXENV: "py36-freeze"
|
||||
PYTEST_NO_COVERAGE: "1"
|
||||
- TOXENV: "docs"
|
||||
PYTEST_NO_COVERAGE: "1"
|
||||
|
||||
install:
|
||||
- echo Installed Pythons
|
||||
@@ -37,12 +32,19 @@ install:
|
||||
|
||||
- if "%TOXENV%" == "pypy" call scripts\install-pypy.bat
|
||||
|
||||
- C:\Python36\python -m pip install --upgrade pip
|
||||
- C:\Python36\python -m pip install --upgrade --pre tox
|
||||
|
||||
build: false # Not a C# project, build stuff at the test step instead.
|
||||
|
||||
before_test:
|
||||
- call scripts\prepare-coverage.bat
|
||||
|
||||
test_script:
|
||||
- call scripts\call-tox.bat
|
||||
- C:\Python36\python -m tox
|
||||
|
||||
on_success:
|
||||
- call scripts\upload-coverage.bat
|
||||
|
||||
cache:
|
||||
- '%LOCALAPPDATA%\pip\cache'
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Warnings are now captured and displayed during test collection.
|
||||
@@ -6,6 +6,8 @@ Release announcements
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
release-3.8.2
|
||||
release-3.8.1
|
||||
release-3.8.0
|
||||
release-3.7.4
|
||||
release-3.7.3
|
||||
|
||||
25
doc/en/announce/release-3.8.1.rst
Normal file
25
doc/en/announce/release-3.8.1.rst
Normal file
@@ -0,0 +1,25 @@
|
||||
pytest-3.8.1
|
||||
=======================================
|
||||
|
||||
pytest 3.8.1 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/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Ankit Goel
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Maximilian Albert
|
||||
* Ronny Pfannschmidt
|
||||
* William Jamir Silva
|
||||
* wim glenn
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
28
doc/en/announce/release-3.8.2.rst
Normal file
28
doc/en/announce/release-3.8.2.rst
Normal file
@@ -0,0 +1,28 @@
|
||||
pytest-3.8.2
|
||||
=======================================
|
||||
|
||||
pytest 3.8.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/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Ankit Goel
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Denis Otkidach
|
||||
* Harry Percival
|
||||
* Jeffrey Rackauckas
|
||||
* Jose Carlos Menezes
|
||||
* Ronny Pfannschmidt
|
||||
* Zac-HD
|
||||
* iwanb
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
@@ -264,8 +264,12 @@ Advanced assertion introspection
|
||||
Reporting details about a failing assertion is achieved by rewriting assert
|
||||
statements before they are run. Rewritten assert statements put introspection
|
||||
information into the assertion failure message. ``pytest`` only rewrites test
|
||||
modules directly discovered by its test collection process, so asserts in
|
||||
supporting modules which are not themselves test modules will not be rewritten.
|
||||
modules directly discovered by its test collection process, so **asserts in
|
||||
supporting modules which are not themselves test modules will not be rewritten**.
|
||||
|
||||
You can manually enable assertion rewriting for an imported module by calling
|
||||
`register_assert_rewrite <https://docs.pytest.org/en/latest/writing_plugins.html#assertion-rewriting>`_
|
||||
before you import it (a good place to do that is in ``conftest.py``).
|
||||
|
||||
.. note::
|
||||
|
||||
|
||||
@@ -7,14 +7,16 @@ Keeping backwards compatibility has a very high priority in the pytest project.
|
||||
|
||||
With the pytest 3.0 release we introduced a clear communication scheme for when we will actually remove the old busted joint and politely ask you to use the new hotness instead, while giving you enough time to adjust your tests or raise concerns if there are valid reasons to keep deprecated functionality around.
|
||||
|
||||
To communicate changes we are already issuing deprecation warnings, but they are not displayed by default. In pytest 3.0 we changed the default setting so that pytest deprecation warnings are displayed if not explicitly silenced (with ``--disable-pytest-warnings``).
|
||||
To communicate changes we issue deprecation warnings using a custom warning hierarchy (see :ref:`internal-warnings`). These warnings may be suppressed using the standard means: ``-W`` command-line flag or ``filterwarnings`` ini options (see :ref:`warnings`), but we suggest to use these sparingly and temporarily, and heed the warnings when possible.
|
||||
|
||||
We will only remove deprecated functionality in major releases (e.g. if we deprecate something in 3.0 we will remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we will not remove it in 4.0 but in 5.0).
|
||||
We will only start the removal of deprecated functionality in major releases (e.g. if we deprecate something in 3.0 we will start to remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we start to remove it in 5.0, not in 4.0).
|
||||
|
||||
When the deprecation expires (e.g. 4.0 is released), we won't remove the deprecated functionality immediately, but will use the standard warning filters to turn them into **errors** by default. This approach makes it explicit that removal is imminent, and still gives you time to turn the deprecated feature into a warning instead of an error so it can be dealt with in your own time. In the next minor release (e.g. 4.1), the feature will be effectively removed.
|
||||
|
||||
|
||||
Deprecation Roadmap
|
||||
-------------------
|
||||
|
||||
We track deprecation and removal of features using milestones and the `deprecation <https://github.com/pytest-dev/pytest/issues?q=label%3A%22type%3A+deprecation%22>`_ and `removal <https://github.com/pytest-dev/pytest/labels/type%3A%20removal>`_ labels on GitHub.
|
||||
Features currently deprecated and removed in previous releases can be found in :ref:`deprecations`.
|
||||
|
||||
Following our deprecation policy, after starting issuing deprecation warnings we keep features for *at least* two minor versions before considering removal.
|
||||
We track future deprecation and removal of features using milestones and the `deprecation <https://github.com/pytest-dev/pytest/issues?q=label%3A%22type%3A+deprecation%22>`_ and `removal <https://github.com/pytest-dev/pytest/labels/type%3A%20removal>`_ labels on GitHub.
|
||||
|
||||
@@ -39,6 +39,7 @@ Full pytest documentation
|
||||
bash-completion
|
||||
|
||||
backwards-compatibility
|
||||
deprecations
|
||||
historical-notes
|
||||
license
|
||||
contributing
|
||||
|
||||
@@ -32,7 +32,7 @@ Here's a summary what ``pytest`` uses ``rootdir`` for:
|
||||
class name, function name and parametrization (if any).
|
||||
|
||||
* Is used by plugins as a stable location to store project/test run specific information;
|
||||
for example, the internal :ref:`cache <cache>` plugin creates a ``.cache`` subdirectory
|
||||
for example, the internal :ref:`cache <cache>` plugin creates a ``.pytest_cache`` subdirectory
|
||||
in ``rootdir`` to store its cross-test run state.
|
||||
|
||||
Important to emphasize that ``rootdir`` is **NOT** used to modify ``sys.path``/``PYTHONPATH`` or
|
||||
|
||||
325
doc/en/deprecations.rst
Normal file
325
doc/en/deprecations.rst
Normal file
@@ -0,0 +1,325 @@
|
||||
.. _deprecations:
|
||||
|
||||
Deprecations and Removals
|
||||
=========================
|
||||
|
||||
This page lists all pytest features that are currently deprecated or have been removed in past major releases.
|
||||
The objective is to give users a clear rationale why a certain feature has been removed, and what alternatives
|
||||
should be used instead.
|
||||
|
||||
Deprecated Features
|
||||
-------------------
|
||||
|
||||
Below is a complete list of all pytest features which are considered deprecated. Using those features will issue
|
||||
:class:`_pytest.warning_types.PytestWarning` or subclasses, which can be filtered using
|
||||
:ref:`standard warning filters <warnings>`.
|
||||
|
||||
``Config.warn`` and ``Node.warn``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.8
|
||||
|
||||
Those methods were part of the internal pytest warnings system, but since ``3.8`` pytest is using the builtin warning
|
||||
system for its own warnings, so those two functions are now deprecated.
|
||||
|
||||
``Config.warn`` should be replaced by calls to the standard ``warnings.warn``, example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
config.warn("C1", "some warning")
|
||||
|
||||
Becomes:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
warnings.warn(pytest.PytestWarning("some warning"))
|
||||
|
||||
``Node.warn`` now supports two signatures:
|
||||
|
||||
* ``node.warn(PytestWarning("some message"))``: is now the **recommended** way to call this function.
|
||||
The warning instance must be a PytestWarning or subclass.
|
||||
|
||||
* ``node.warn("CI", "some message")``: this code/message form is now **deprecated** and should be converted to the warning instance form above.
|
||||
|
||||
|
||||
``pytest_namespace``
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.7
|
||||
|
||||
This hook is deprecated because it greatly complicates the pytest internals regarding configuration and initialization, making some
|
||||
bug fixes and refactorings impossible.
|
||||
|
||||
Example of usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MySymbol:
|
||||
...
|
||||
|
||||
|
||||
def pytest_namespace():
|
||||
return {"my_symbol": MySymbol()}
|
||||
|
||||
|
||||
Plugin authors relying on this hook should instead require that users now import the plugin modules directly (with an appropriate public API).
|
||||
|
||||
As a stopgap measure, plugin authors may still inject their names into pytest's namespace, usually during ``pytest_configure``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def pytest_configure():
|
||||
pytest.my_symbol = MySymbol()
|
||||
|
||||
|
||||
|
||||
Calling fixtures directly
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.7
|
||||
|
||||
Calling a fixture function directly, as opposed to request them in a test function, is deprecated.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.fixture
|
||||
def cell():
|
||||
return ...
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def full_cell():
|
||||
cell = cell()
|
||||
cell.make_full()
|
||||
return cell
|
||||
|
||||
This is a great source of confusion to new users, which will often call the fixture functions and request them from test functions interchangeably, which breaks the fixture resolution model.
|
||||
|
||||
In those cases just request the function directly in the dependent fixture:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.fixture
|
||||
def cell():
|
||||
return ...
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def full_cell(cell):
|
||||
cell.make_full()
|
||||
return cell
|
||||
|
||||
``Node.get_marker``
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.6
|
||||
|
||||
As part of a large :ref:`marker-revamp`, :meth:`_pytest.nodes.Node.get_marker` is deprecated. See
|
||||
:ref:`the documentation <update marker code>` on tips on how to update your code.
|
||||
|
||||
|
||||
record_xml_property
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.5
|
||||
|
||||
The ``record_xml_property`` fixture is now deprecated in favor of the more generic ``record_property``, which
|
||||
can be used by other consumers (for example ``pytest-html``) to obtain custom information about the test run.
|
||||
|
||||
This is just a matter of renaming the fixture as the API is the same:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_foo(record_xml_property):
|
||||
...
|
||||
|
||||
Change to:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def test_foo(record_property):
|
||||
...
|
||||
|
||||
pytest_plugins in non-top-level conftest files
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.5
|
||||
|
||||
Defining ``pytest_plugins`` is now deprecated in non-top-level conftest.py
|
||||
files because they will activate referenced plugins *globally*, which is surprising because for all other pytest
|
||||
features ``conftest.py`` files are only *active* for tests at or below it.
|
||||
|
||||
Metafunc.addcall
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.3
|
||||
|
||||
:meth:`_pytest.python.Metafunc.addcall` was a precursor to the current parametrized mechanism. Users should use
|
||||
:meth:`_pytest.python.Metafunc.parametrize` instead.
|
||||
|
||||
marks in ``pytest.mark.parametrize``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.2
|
||||
|
||||
Applying marks to values of a ``pytest.mark.parametrize`` call is now deprecated. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"a, b", [(3, 9), pytest.mark.xfail(reason="flaky")(6, 36), (10, 100)]
|
||||
)
|
||||
def test_foo(a, b):
|
||||
...
|
||||
|
||||
This code applies the ``pytest.mark.xfail(reason="flaky")`` mark to the ``(6, 36)`` value of the above parametrization
|
||||
call.
|
||||
|
||||
This was considered hard to read and understand, and also its implementation presented problems to the code preventing
|
||||
further internal improvements in the marks architecture.
|
||||
|
||||
To update the code, use ``pytest.param``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"a, b",
|
||||
[(3, 9), pytest.param((6, 36), marks=pytest.mark.xfail(reason="flaky")), (10, 100)],
|
||||
)
|
||||
def test_foo(a, b):
|
||||
...
|
||||
|
||||
|
||||
|
||||
Passing command-line string to ``pytest.main()``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.0
|
||||
|
||||
Passing a command-line string to ``pytest.main()`` is deprecated:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pytest.main("-v -s")
|
||||
|
||||
Pass a list instead:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pytest.main(["-v", "-s"])
|
||||
|
||||
|
||||
By passing a string, users expect that pytest will interpret that command-line using the shell rules they are working
|
||||
on (for example ``bash`` or ``Powershell``), but this is very hard/impossible to do in a portable way.
|
||||
|
||||
|
||||
``yield`` tests
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.0
|
||||
|
||||
pytest supports ``yield``-style tests, where a test function actually ``yield`` functions and values
|
||||
that are then turned into proper test methods. Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def check(x, y):
|
||||
assert x ** x == y
|
||||
|
||||
|
||||
def test_squared():
|
||||
yield check, 2, 4
|
||||
yield check, 3, 9
|
||||
|
||||
This would result into two actual test functions being generated.
|
||||
|
||||
This form of test function doesn't support fixtures properly, and users should switch to ``pytest.mark.parametrize``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.mark.parametrize("x, y", [(2, 4), (3, 9)])
|
||||
def test_squared():
|
||||
assert x ** x == y
|
||||
|
||||
|
||||
``pytest_funcarg__`` prefix
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.0
|
||||
|
||||
In very early pytest versions fixtures could be defined using the ``pytest_funcarg__`` prefix:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def pytest_funcarg__data():
|
||||
return SomeData()
|
||||
|
||||
Switch over to the ``@pytest.fixture`` decorator:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@pytest.fixture
|
||||
def data():
|
||||
return SomeData()
|
||||
|
||||
[pytest] section in setup.cfg files
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.0
|
||||
|
||||
``[pytest]`` sections in ``setup.cfg`` files should now be named ``[tool:pytest]``
|
||||
to avoid conflicts with other distutils commands.
|
||||
|
||||
Result log (``--result-log``)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. deprecated:: 3.0
|
||||
|
||||
The ``--resultlog`` command line option has been deprecated: it is little used
|
||||
and there are more modern and better alternatives, for example `pytest-tap <https://tappy.readthedocs.io/en/latest/>`_.
|
||||
|
||||
Removed Features
|
||||
----------------
|
||||
|
||||
As stated in our :ref:`backwards-compatibility` policy, deprecated features are removed only in major releases after
|
||||
an appropriate period of deprecation has passed.
|
||||
|
||||
|
||||
Reinterpretation mode (``--assert=reinterp``)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Removed in version 3.0.*
|
||||
|
||||
Reinterpretation mode has now been removed and only plain and rewrite
|
||||
mode are available, consequently the ``--assert=reinterp`` option is
|
||||
no longer available. This also means files imported from plugins or
|
||||
``conftest.py`` will not benefit from improved assertions by
|
||||
default, you should use ``pytest.register_assert_rewrite()`` to
|
||||
explicitly turn on assertion rewriting for those files.
|
||||
|
||||
Removed command-line options
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Removed in version 3.0.*
|
||||
|
||||
The following deprecated commandline options were removed:
|
||||
|
||||
* ``--genscript``: no longer supported;
|
||||
* ``--no-assert``: use ``--assert=plain`` instead;
|
||||
* ``--nomagic``: use ``--assert=plain`` instead;
|
||||
* ``--report``: use ``-r`` instead;
|
||||
|
||||
py.test-X* entry points
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
*Removed in version 3.0.*
|
||||
|
||||
Removed all ``py.test-X*`` entry points. The versioned, suffixed entry points
|
||||
were never documented and a leftover from a pre-virtualenv era. These entry
|
||||
points also created broken entry points in wheels, so removing them also
|
||||
removes a source of confusion for users.
|
||||
@@ -31,7 +31,7 @@ You can then restrict a test run to only run tests marked with ``webtest``::
|
||||
|
||||
$ pytest -v -m webtest
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items / 3 deselected
|
||||
@@ -44,7 +44,7 @@ Or the inverse, running all tests except the webtest ones::
|
||||
|
||||
$ pytest -v -m "not webtest"
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items / 1 deselected
|
||||
@@ -64,7 +64,7 @@ tests based on their module, class, method, or function name::
|
||||
|
||||
$ pytest -v test_server.py::TestClass::test_method
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 1 item
|
||||
@@ -77,7 +77,7 @@ You can also select on the class::
|
||||
|
||||
$ pytest -v test_server.py::TestClass
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 1 item
|
||||
@@ -90,7 +90,7 @@ Or select multiple nodes::
|
||||
|
||||
$ pytest -v test_server.py::TestClass test_server.py::test_send_http
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 2 items
|
||||
@@ -128,7 +128,7 @@ select tests based on their names::
|
||||
|
||||
$ pytest -v -k http # running with the above defined example module
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items / 3 deselected
|
||||
@@ -141,7 +141,7 @@ And you can also run all tests except the ones that match the keyword::
|
||||
|
||||
$ pytest -k "not send_http" -v
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items / 1 deselected
|
||||
@@ -156,7 +156,7 @@ Or to select "http" and "quick" tests::
|
||||
|
||||
$ pytest -k "http or quick" -v
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items / 2 deselected
|
||||
|
||||
@@ -59,7 +59,7 @@ consulted when reporting in ``verbose`` mode::
|
||||
|
||||
nonpython $ pytest -v
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
|
||||
collecting ... collected 2 items
|
||||
|
||||
@@ -357,7 +357,7 @@ which will add info only when run with "--v"::
|
||||
|
||||
$ pytest -v
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: .pytest_cache
|
||||
info1: did you know that ...
|
||||
did you?
|
||||
|
||||
@@ -171,6 +171,7 @@ to cause the decorated ``smtp_connection`` fixture function to only be invoked
|
||||
once per test *module* (the default is to invoke once per test *function*).
|
||||
Multiple test functions in a test module will thus
|
||||
each receive the same ``smtp_connection`` fixture instance, thus saving time.
|
||||
Possible values for ``scope`` are: ``function``, ``class``, ``module``, ``package`` or ``session``.
|
||||
|
||||
The next example puts the fixture function into a separate ``conftest.py`` file
|
||||
so that tests from multiple test modules in the directory can
|
||||
@@ -726,7 +727,7 @@ Running this test will *skip* the invocation of ``data_set`` with value ``2``::
|
||||
|
||||
$ pytest test_fixture_marks.py -v
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 3 items
|
||||
@@ -769,7 +770,7 @@ Here we declare an ``app`` fixture which receives the previously defined
|
||||
|
||||
$ pytest -v test_appsetup.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 2 items
|
||||
@@ -838,7 +839,7 @@ Let's run the tests in verbose mode and with looking at the print-output::
|
||||
|
||||
$ pytest -v -s test_module.py
|
||||
=========================== test session starts ============================
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
|
||||
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python
|
||||
cachedir: .pytest_cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 8 items
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Installation and Getting Started
|
||||
===================================
|
||||
|
||||
**Pythons**: Python 2.7, 3.4, 3.5, 3.6, Jython, PyPy-2.3
|
||||
**Pythons**: Python 2.7, 3.4, 3.5, 3.6, 3.7, Jython, PyPy-2.3
|
||||
|
||||
**Platforms**: Unix/Posix and Windows
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ should add ``--strict`` to ``addopts``:
|
||||
serial
|
||||
|
||||
|
||||
.. _marker-revamp:
|
||||
|
||||
Marker revamp and iteration
|
||||
---------------------------
|
||||
|
||||
|
||||
@@ -1256,15 +1256,25 @@ passed multiple times. The expected format is ``name=value``. For example::
|
||||
|
||||
One or more Glob-style file patterns determining which python files
|
||||
are considered as test modules. Search for multiple glob patterns by
|
||||
adding a space between patterns::
|
||||
adding a space between patterns:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pytest]
|
||||
python_files = test_*.py check_*.py example_*.py
|
||||
|
||||
By default, pytest will consider any file matching with ``test_*.py``
|
||||
and ``*_test.py`` globs as a test module.
|
||||
Or one per line:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pytest]
|
||||
python_files =
|
||||
test_*.py
|
||||
check_*.py
|
||||
example_*.py
|
||||
|
||||
By default, files matching ``test_*.py`` and ``*_test.py`` will be considered
|
||||
test modules.
|
||||
|
||||
|
||||
.. confval:: python_functions
|
||||
|
||||
@@ -255,7 +255,7 @@ Pytest supports the use of ``breakpoint()`` with the following behaviours:
|
||||
|
||||
- When ``breakpoint()`` is called and ``PYTHONBREAKPOINT`` is set to the default value, pytest will use the custom internal PDB trace UI instead of the system default ``Pdb``.
|
||||
- When tests are complete, the system will default back to the system ``Pdb`` trace UI.
|
||||
- If ``--pdb`` is called on execution of pytest, the custom internal Pdb trace UI is used on ``bothbreakpoint()`` and failed tests/unhandled exceptions.
|
||||
- If ``--pdb`` is called on execution of pytest, the custom internal Pdb trace UI is used on both ``breakpoint()`` and failed tests/unhandled exceptions.
|
||||
- If ``--pdbcls`` is used, the custom class debugger will be executed when a test fails (as expected within existing behaviour), but also when ``breakpoint()`` is called from within a test, the custom class debugger will be instantiated.
|
||||
|
||||
.. _durations:
|
||||
|
||||
@@ -330,6 +330,9 @@ You can also use it as a contextmanager::
|
||||
myobject.deprecated_method()
|
||||
|
||||
|
||||
|
||||
.. _internal-warnings:
|
||||
|
||||
Internal pytest warnings
|
||||
------------------------
|
||||
|
||||
@@ -363,9 +366,8 @@ defines an ``__init__`` constructor, as this prevents the class from being insta
|
||||
|
||||
These warnings might be filtered using the same builtin mechanisms used to filter other types of warnings.
|
||||
|
||||
Following our :ref:`backwards-compatibility`, deprecated features will be kept *at least* two minor releases. After that,
|
||||
they will changed so they by default raise errors instead of just warnings, so users can adapt to it on their own time
|
||||
if not having done so until now. In a later release the deprecated feature will be removed completely.
|
||||
Please read our :ref:`backwards-compatibility` to learn how we proceed about deprecating and eventually removing
|
||||
features.
|
||||
|
||||
The following warning types ares used by pytest and are part of the public API:
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
REM skip "coveralls" run in PRs or forks
|
||||
if "%TOXENV%" == "coveralls" (
|
||||
if not defined COVERALLS_REPO_TOKEN (
|
||||
echo skipping coveralls run because COVERALLS_REPO_TOKEN is not defined
|
||||
exit /b 0
|
||||
)
|
||||
)
|
||||
C:\Python36\python -m tox
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Used by .pre-commit-config.yaml"""
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(" ".join(sys.argv[1:]))
|
||||
sys.exit(1)
|
||||
10
scripts/prepare-coverage.bat
Normal file
10
scripts/prepare-coverage.bat
Normal file
@@ -0,0 +1,10 @@
|
||||
REM scripts called by AppVeyor to setup the environment variables to enable coverage
|
||||
if not defined PYTEST_NO_COVERAGE (
|
||||
set "COVERAGE_FILE=%CD%\.coverage"
|
||||
set "COVERAGE_PROCESS_START=%CD%\.coveragerc"
|
||||
set "_PYTEST_TOX_COVERAGE_RUN=coverage run -m"
|
||||
set "_PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess"
|
||||
echo Coverage setup completed
|
||||
) else (
|
||||
echo Skipping coverage setup, PYTEST_NO_COVERAGE is set
|
||||
)
|
||||
11
scripts/upload-coverage.bat
Normal file
11
scripts/upload-coverage.bat
Normal file
@@ -0,0 +1,11 @@
|
||||
REM script called by AppVeyor to combine and upload coverage information to codecov
|
||||
if not defined PYTEST_NO_COVERAGE (
|
||||
echo Prepare to upload coverage information
|
||||
C:\Python36\Scripts\pip install codecov
|
||||
C:\Python36\Scripts\coverage combine
|
||||
C:\Python36\Scripts\coverage xml --ignore-errors
|
||||
C:\Python36\Scripts\coverage report -m --ignore-errors
|
||||
C:\Python36\Scripts\codecov --required -X gcov pycov search -f coverage.xml --flags %TOXENV:-= % windows
|
||||
) else (
|
||||
echo Skipping coverage upload, PYTEST_NO_COVERAGE is set
|
||||
)
|
||||
@@ -8,6 +8,7 @@ import marshal
|
||||
import os
|
||||
import re
|
||||
import six
|
||||
import string
|
||||
import struct
|
||||
import sys
|
||||
import types
|
||||
@@ -16,7 +17,8 @@ import atomicwrites
|
||||
import py
|
||||
|
||||
from _pytest.assertion import util
|
||||
|
||||
from _pytest.compat import PurePath, spec_from_file_location
|
||||
from _pytest.paths import fnmatch_ex
|
||||
|
||||
# pytest caches rewritten pycs in __pycache__.
|
||||
if hasattr(imp, "get_tag"):
|
||||
@@ -45,14 +47,6 @@ else:
|
||||
return ast.Call(a, b, c, None, None)
|
||||
|
||||
|
||||
if sys.version_info >= (3, 4):
|
||||
from importlib.util import spec_from_file_location
|
||||
else:
|
||||
|
||||
def spec_from_file_location(*_, **__):
|
||||
return None
|
||||
|
||||
|
||||
class AssertionRewritingHook(object):
|
||||
"""PEP302 Import hook which rewrites asserts."""
|
||||
|
||||
@@ -198,14 +192,14 @@ class AssertionRewritingHook(object):
|
||||
return False
|
||||
|
||||
# For matching the name it must be as if it was a filename.
|
||||
parts[-1] = parts[-1] + ".py"
|
||||
fn_pypath = py.path.local(os.path.sep.join(parts))
|
||||
path = PurePath(os.path.sep.join(parts) + ".py")
|
||||
|
||||
for pat in self.fnpats:
|
||||
# if the pattern contains subdirectories ("tests/**.py" for example) we can't bail out based
|
||||
# on the name alone because we need to match against the full path
|
||||
if os.path.dirname(pat):
|
||||
return False
|
||||
if fn_pypath.fnmatch(pat):
|
||||
if fnmatch_ex(pat, path):
|
||||
return False
|
||||
|
||||
if self._is_marked_for_rewrite(name, state):
|
||||
@@ -275,17 +269,17 @@ class AssertionRewritingHook(object):
|
||||
)
|
||||
|
||||
def load_module(self, name):
|
||||
# If there is an existing module object named 'fullname' in
|
||||
# sys.modules, the loader must use that existing module. (Otherwise,
|
||||
# the reload() builtin will not work correctly.)
|
||||
if name in sys.modules:
|
||||
return sys.modules[name]
|
||||
|
||||
co, pyc = self.modules.pop(name)
|
||||
# I wish I could just call imp.load_compiled here, but __file__ has to
|
||||
# be set properly. In Python 3.2+, this all would be handled correctly
|
||||
# by load_compiled.
|
||||
mod = sys.modules[name] = imp.new_module(name)
|
||||
if name in sys.modules:
|
||||
# If there is an existing module object named 'fullname' in
|
||||
# sys.modules, the loader must use that existing module. (Otherwise,
|
||||
# the reload() builtin will not work correctly.)
|
||||
mod = sys.modules[name]
|
||||
else:
|
||||
# I wish I could just call imp.load_compiled here, but __file__ has to
|
||||
# be set properly. In Python 3.2+, this all would be handled correctly
|
||||
# by load_compiled.
|
||||
mod = sys.modules[name] = imp.new_module(name)
|
||||
try:
|
||||
mod.__file__ = co.co_filename
|
||||
# Normally, this attribute is 3.2+.
|
||||
@@ -473,10 +467,14 @@ def _saferepr(obj):
|
||||
|
||||
"""
|
||||
r = py.io.saferepr(obj)
|
||||
if isinstance(r, six.text_type):
|
||||
return r.replace(u"\n", u"\\n")
|
||||
else:
|
||||
return r.replace(b"\n", b"\\n")
|
||||
# only occurs in python2.x, repr must return text in python3+
|
||||
if isinstance(r, bytes):
|
||||
# Represent unprintable bytes as `\x##`
|
||||
r = u"".join(
|
||||
u"\\x{:x}".format(ord(c)) if c not in string.printable else c.decode()
|
||||
for c in r
|
||||
)
|
||||
return r.replace(u"\n", u"\\n")
|
||||
|
||||
|
||||
from _pytest.assertion.util import format_explanation as _format_explanation # noqa
|
||||
|
||||
@@ -115,15 +115,18 @@ class Cache(object):
|
||||
else:
|
||||
with f:
|
||||
json.dump(value, f, indent=2, sort_keys=True)
|
||||
self._ensure_readme()
|
||||
|
||||
def _ensure_readme(self):
|
||||
self._ensure_supporting_files()
|
||||
|
||||
def _ensure_supporting_files(self):
|
||||
"""Create supporting files in the cache dir that are not really part of the cache."""
|
||||
if self._cachedir.is_dir():
|
||||
readme_path = self._cachedir / "README.md"
|
||||
if not readme_path.is_file():
|
||||
readme_path.write_text(README_CONTENT)
|
||||
|
||||
msg = u"# created by pytest automatically, do not change\n*"
|
||||
self._cachedir.joinpath(".gitignore").write_text(msg, encoding="UTF-8")
|
||||
|
||||
|
||||
class LFPlugin(object):
|
||||
""" Plugin which implements the --lf (run last-failing) option """
|
||||
|
||||
@@ -23,7 +23,7 @@ except ImportError: # pragma: no cover
|
||||
# Only available in Python 3.4+ or as a backport
|
||||
enum = None
|
||||
|
||||
__all__ = ["Path"]
|
||||
__all__ = ["Path", "PurePath"]
|
||||
|
||||
_PY3 = sys.version_info > (3, 0)
|
||||
_PY2 = not _PY3
|
||||
@@ -42,9 +42,9 @@ PY36 = sys.version_info[:2] >= (3, 6)
|
||||
MODULE_NOT_FOUND_ERROR = "ModuleNotFoundError" if PY36 else "ImportError"
|
||||
|
||||
if PY36:
|
||||
from pathlib import Path
|
||||
from pathlib import Path, PurePath
|
||||
else:
|
||||
from pathlib2 import Path
|
||||
from pathlib2 import Path, PurePath
|
||||
|
||||
|
||||
if _PY3:
|
||||
@@ -56,6 +56,14 @@ else:
|
||||
from collections import Mapping, Sequence # noqa
|
||||
|
||||
|
||||
if sys.version_info >= (3, 4):
|
||||
from importlib.util import spec_from_file_location
|
||||
else:
|
||||
|
||||
def spec_from_file_location(*_, **__):
|
||||
return None
|
||||
|
||||
|
||||
def _format_args(func):
|
||||
return str(signature(func))
|
||||
|
||||
|
||||
@@ -351,6 +351,7 @@ class PytestPluginManager(PluginManager):
|
||||
else None
|
||||
)
|
||||
self._noconftest = namespace.noconftest
|
||||
self._using_pyargs = namespace.pyargs
|
||||
testpaths = namespace.file_or_dir
|
||||
foundanchor = False
|
||||
for path in testpaths:
|
||||
@@ -416,7 +417,11 @@ class PytestPluginManager(PluginManager):
|
||||
_ensure_removed_sysmodule(conftestpath.purebasename)
|
||||
try:
|
||||
mod = conftestpath.pyimport()
|
||||
if hasattr(mod, "pytest_plugins") and self._configured:
|
||||
if (
|
||||
hasattr(mod, "pytest_plugins")
|
||||
and self._configured
|
||||
and not self._using_pyargs
|
||||
):
|
||||
from _pytest.deprecated import (
|
||||
PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST
|
||||
)
|
||||
|
||||
@@ -562,6 +562,9 @@ def pytest_warning_captured(warning_message, when, item):
|
||||
* ``"runtest"``: during test execution.
|
||||
|
||||
:param pytest.Item|None item:
|
||||
**DEPRECATED**: This parameter is incompatible with ``pytest-xdist``, and will always receive ``None``
|
||||
in a future release.
|
||||
|
||||
The item being executed if ``when`` is ``"runtest"``, otherwise ``None``.
|
||||
"""
|
||||
|
||||
|
||||
@@ -445,8 +445,8 @@ class LoggingPlugin(object):
|
||||
try:
|
||||
yield # run test
|
||||
finally:
|
||||
del item.catch_log_handler
|
||||
if when == "teardown":
|
||||
del item.catch_log_handler
|
||||
del item.catch_log_handlers
|
||||
|
||||
if self.print_logs:
|
||||
|
||||
@@ -504,13 +504,14 @@ class Session(nodes.FSCollector):
|
||||
pkginit = parent.join("__init__.py")
|
||||
if pkginit.isfile():
|
||||
if pkginit in self._node_cache:
|
||||
root = self._node_cache[pkginit]
|
||||
root = self._node_cache[pkginit][0]
|
||||
else:
|
||||
col = root._collectfile(pkginit)
|
||||
if col:
|
||||
if isinstance(col[0], Package):
|
||||
root = col[0]
|
||||
self._node_cache[root.fspath] = root
|
||||
# always store a list in the cache, matchnodes expects it
|
||||
self._node_cache[root.fspath] = [root]
|
||||
|
||||
# If it's a directory argument, recurse and look for any Subpackages.
|
||||
# Let the Package collector deal with subnodes, don't collect here.
|
||||
@@ -530,8 +531,8 @@ class Session(nodes.FSCollector):
|
||||
if (type(x), x.fspath) in self._node_cache:
|
||||
yield self._node_cache[(type(x), x.fspath)]
|
||||
else:
|
||||
yield x
|
||||
self._node_cache[(type(x), x.fspath)] = x
|
||||
yield x
|
||||
else:
|
||||
assert argpath.check(file=1)
|
||||
|
||||
|
||||
@@ -66,7 +66,10 @@ python_keywords_allowed_list = ["or", "and", "not"]
|
||||
|
||||
def matchmark(colitem, markexpr):
|
||||
"""Tries to match on any marker names, attached to the given colitem."""
|
||||
return eval(markexpr, {}, MarkMapping.from_item(colitem))
|
||||
try:
|
||||
return eval(markexpr, {}, MarkMapping.from_item(colitem))
|
||||
except SyntaxError as e:
|
||||
raise SyntaxError(str(e) + "\nMarker expression must be valid Python!")
|
||||
|
||||
|
||||
def matchkeyword(colitem, keywordexpr):
|
||||
|
||||
@@ -4,9 +4,12 @@ from __future__ import absolute_import, division, print_function
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import warnings
|
||||
from contextlib import contextmanager
|
||||
|
||||
import six
|
||||
|
||||
import pytest
|
||||
from _pytest.fixtures import fixture
|
||||
|
||||
RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$")
|
||||
@@ -209,22 +212,41 @@ class MonkeyPatch(object):
|
||||
self._setitem.append((dic, name, dic.get(name, notset)))
|
||||
del dic[name]
|
||||
|
||||
def _warn_if_env_name_is_not_str(self, name):
|
||||
"""On Python 2, warn if the given environment variable name is not a native str (#4056)"""
|
||||
if six.PY2 and not isinstance(name, str):
|
||||
warnings.warn(
|
||||
pytest.PytestWarning(
|
||||
"Environment variable name {!r} should be str".format(name)
|
||||
)
|
||||
)
|
||||
|
||||
def setenv(self, name, value, prepend=None):
|
||||
""" Set environment variable ``name`` to ``value``. If ``prepend``
|
||||
is a character, read the current environment variable value
|
||||
and prepend the ``value`` adjoined with the ``prepend`` character."""
|
||||
value = str(value)
|
||||
if not isinstance(value, str):
|
||||
warnings.warn(
|
||||
pytest.PytestWarning(
|
||||
"Environment variable value {!r} should be str, converted to str implicitly".format(
|
||||
value
|
||||
)
|
||||
)
|
||||
)
|
||||
value = str(value)
|
||||
if prepend and name in os.environ:
|
||||
value = value + prepend + os.environ[name]
|
||||
self._warn_if_env_name_is_not_str(name)
|
||||
self.setitem(os.environ, name, value)
|
||||
|
||||
def delenv(self, name, raising=True):
|
||||
""" Delete ``name`` from the environment. Raise KeyError it does not
|
||||
exist.
|
||||
""" Delete ``name`` from the environment. Raise KeyError if it does
|
||||
not exist.
|
||||
|
||||
If ``raising`` is set to False, no exception will be raised if the
|
||||
environment variable is missing.
|
||||
"""
|
||||
self._warn_if_env_name_is_not_str(name)
|
||||
self.delitem(os.environ, name, raising=raising)
|
||||
|
||||
def syspath_prepend(self, path):
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
from .compat import Path
|
||||
from os.path import expanduser, expandvars, isabs
|
||||
from os.path import expanduser, expandvars, isabs, sep
|
||||
from posixpath import sep as posix_sep
|
||||
import fnmatch
|
||||
import sys
|
||||
|
||||
import six
|
||||
|
||||
from .compat import Path, PurePath
|
||||
|
||||
|
||||
def resolve_from_str(input, root):
|
||||
@@ -11,3 +17,36 @@ def resolve_from_str(input, root):
|
||||
return Path(input)
|
||||
else:
|
||||
return root.joinpath(input)
|
||||
|
||||
|
||||
def fnmatch_ex(pattern, path):
|
||||
"""FNMatcher port from py.path.common which works with PurePath() instances.
|
||||
|
||||
The difference between this algorithm and PurePath.match() is that the latter matches "**" glob expressions
|
||||
for each part of the path, while this algorithm uses the whole path instead.
|
||||
|
||||
For example:
|
||||
"tests/foo/bar/doc/test_foo.py" matches pattern "tests/**/doc/test*.py" with this algorithm, but not with
|
||||
PurePath.match().
|
||||
|
||||
This algorithm was ported to keep backward-compatibility with existing settings which assume paths match according
|
||||
this logic.
|
||||
|
||||
References:
|
||||
* https://bugs.python.org/issue29249
|
||||
* https://bugs.python.org/issue34731
|
||||
"""
|
||||
path = PurePath(path)
|
||||
iswin32 = sys.platform.startswith("win")
|
||||
|
||||
if iswin32 and sep not in pattern and posix_sep in pattern:
|
||||
# Running on Windows, the pattern has no Windows path separators,
|
||||
# and the pattern has one or more Posix path separators. Replace
|
||||
# the Posix path separators with the Windows path separator.
|
||||
pattern = pattern.replace(posix_sep, sep)
|
||||
|
||||
if sep not in pattern:
|
||||
name = path.name
|
||||
else:
|
||||
name = six.text_type(path)
|
||||
return fnmatch.fnmatch(name, pattern)
|
||||
|
||||
@@ -37,6 +37,7 @@ from _pytest.compat import (
|
||||
getlocation,
|
||||
enum,
|
||||
get_default_arg_names,
|
||||
getimfunc,
|
||||
)
|
||||
from _pytest.outcomes import fail
|
||||
from _pytest.mark.structures import (
|
||||
@@ -681,14 +682,12 @@ class Class(PyCollector):
|
||||
def setup(self):
|
||||
setup_class = _get_xunit_func(self.obj, "setup_class")
|
||||
if setup_class is not None:
|
||||
setup_class = getattr(setup_class, "im_func", setup_class)
|
||||
setup_class = getattr(setup_class, "__func__", setup_class)
|
||||
setup_class = getimfunc(setup_class)
|
||||
setup_class(self.obj)
|
||||
|
||||
fin_class = getattr(self.obj, "teardown_class", None)
|
||||
if fin_class is not None:
|
||||
fin_class = getattr(fin_class, "im_func", fin_class)
|
||||
fin_class = getattr(fin_class, "__func__", fin_class)
|
||||
fin_class = getimfunc(fin_class)
|
||||
self.addfinalizer(lambda: fin_class(self.obj))
|
||||
|
||||
|
||||
@@ -1142,13 +1141,18 @@ def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
|
||||
"""
|
||||
from _pytest.fixtures import scopes
|
||||
|
||||
indirect_as_list = isinstance(indirect, (list, tuple))
|
||||
all_arguments_are_fixtures = (
|
||||
indirect is True or indirect_as_list and len(indirect) == argnames
|
||||
)
|
||||
if isinstance(indirect, (list, tuple)):
|
||||
all_arguments_are_fixtures = len(indirect) == len(argnames)
|
||||
else:
|
||||
all_arguments_are_fixtures = bool(indirect)
|
||||
|
||||
if all_arguments_are_fixtures:
|
||||
fixturedefs = arg2fixturedefs or {}
|
||||
used_scopes = [fixturedef[0].scope for name, fixturedef in fixturedefs.items()]
|
||||
used_scopes = [
|
||||
fixturedef[0].scope
|
||||
for name, fixturedef in fixturedefs.items()
|
||||
if name in argnames
|
||||
]
|
||||
if used_scopes:
|
||||
# Takes the most narrow scope from used fixtures
|
||||
for scope in reversed(scopes):
|
||||
@@ -1433,7 +1437,7 @@ class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
|
||||
@property
|
||||
def function(self):
|
||||
"underlying python 'function' object"
|
||||
return getattr(self.obj, "im_func", self.obj)
|
||||
return getimfunc(self.obj)
|
||||
|
||||
def _getobj(self):
|
||||
name = self.name
|
||||
|
||||
@@ -212,6 +212,8 @@ class WarningsChecker(WarningsRecorder):
|
||||
def __exit__(self, *exc_info):
|
||||
super(WarningsChecker, self).__exit__(*exc_info)
|
||||
|
||||
__tracebackhide__ = True
|
||||
|
||||
# only check if we're not currently handling an exception
|
||||
if all(a is None for a in exc_info):
|
||||
if self.expected_warning is not None:
|
||||
|
||||
@@ -110,7 +110,7 @@ class TestReport(BaseReport):
|
||||
when,
|
||||
sections=(),
|
||||
duration=0,
|
||||
user_properties=(),
|
||||
user_properties=None,
|
||||
**extra
|
||||
):
|
||||
#: normalized collection node id
|
||||
@@ -136,7 +136,7 @@ class TestReport(BaseReport):
|
||||
|
||||
#: user properties is a list of tuples (name, value) that holds user
|
||||
#: defined properties of the test
|
||||
self.user_properties = user_properties
|
||||
self.user_properties = list(user_properties or [])
|
||||
|
||||
#: list of pairs ``(str, str)`` of extra information which needs to
|
||||
#: marshallable. Used by pytest to add captured text
|
||||
|
||||
@@ -745,9 +745,10 @@ class TerminalReporter(object):
|
||||
return
|
||||
self.write_sep("=", "PASSES")
|
||||
for rep in reports:
|
||||
msg = self._getfailureheadline(rep)
|
||||
self.write_sep("_", msg)
|
||||
self._outrep_summary(rep)
|
||||
if rep.sections:
|
||||
msg = self._getfailureheadline(rep)
|
||||
self.write_sep("_", msg)
|
||||
self._outrep_summary(rep)
|
||||
|
||||
def print_teardown_sections(self, rep):
|
||||
showcapture = self.config.option.showcapture
|
||||
|
||||
@@ -9,6 +9,7 @@ import _pytest._code
|
||||
from _pytest.config import hookimpl
|
||||
from _pytest.outcomes import fail, skip, xfail
|
||||
from _pytest.python import transfer_markers, Class, Module, Function
|
||||
from _pytest.compat import getimfunc
|
||||
|
||||
|
||||
def pytest_pycollect_makeitem(collector, name, obj):
|
||||
@@ -53,7 +54,7 @@ class UnitTestCase(Class):
|
||||
x = getattr(self.obj, name)
|
||||
if not getattr(x, "__test__", True):
|
||||
continue
|
||||
funcobj = getattr(x, "im_func", x)
|
||||
funcobj = getimfunc(x)
|
||||
transfer_markers(funcobj, cls, module)
|
||||
yield TestCaseFunction(name, parent=self, callobj=funcobj)
|
||||
foundsomething = True
|
||||
|
||||
@@ -577,7 +577,7 @@ class TestInvocationVariants(object):
|
||||
return what
|
||||
|
||||
empty_package = testdir.mkpydir("empty_package")
|
||||
monkeypatch.setenv("PYTHONPATH", join_pythonpath(empty_package))
|
||||
monkeypatch.setenv("PYTHONPATH", str(join_pythonpath(empty_package)))
|
||||
# the path which is not a package raises a warning on pypy;
|
||||
# no idea why only pypy and not normal python warn about it here
|
||||
with warnings.catch_warnings():
|
||||
@@ -586,7 +586,7 @@ class TestInvocationVariants(object):
|
||||
assert result.ret == 0
|
||||
result.stdout.fnmatch_lines(["*2 passed*"])
|
||||
|
||||
monkeypatch.setenv("PYTHONPATH", join_pythonpath(testdir))
|
||||
monkeypatch.setenv("PYTHONPATH", str(join_pythonpath(testdir)))
|
||||
result = testdir.runpytest("--pyargs", "tpkg.test_missing", syspathinsert=True)
|
||||
assert result.ret != 0
|
||||
result.stderr.fnmatch_lines(["*not*found*test_missing*"])
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
# coding: utf-8
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import sys
|
||||
|
||||
import _pytest._code
|
||||
import pytest
|
||||
import mock
|
||||
from test_excinfo import TWMock
|
||||
from six import text_type
|
||||
|
||||
from test_excinfo import TWMock
|
||||
|
||||
try:
|
||||
import mock
|
||||
except ImportError:
|
||||
import unittest.mock as mock
|
||||
|
||||
|
||||
def test_ne():
|
||||
code1 = _pytest._code.Code(compile('foo = "bar"', "", "exec"))
|
||||
|
||||
@@ -129,7 +129,7 @@ def test_source_strip_multiline():
|
||||
def test_syntaxerror_rerepresentation():
|
||||
ex = pytest.raises(SyntaxError, _pytest._code.compile, "xyz xyz")
|
||||
assert ex.value.lineno == 1
|
||||
assert ex.value.offset in (4, 7) # XXX pypy/jython versus cpython?
|
||||
assert ex.value.offset in (4, 5, 7) # XXX pypy/jython versus cpython?
|
||||
assert ex.value.text.strip(), "x x"
|
||||
|
||||
|
||||
|
||||
@@ -178,21 +178,12 @@ def test_pytest_catchlog_deprecated(testdir, plugin):
|
||||
def test_pytest_plugins_in_non_top_level_conftest_deprecated(testdir):
|
||||
from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST
|
||||
|
||||
subdirectory = testdir.tmpdir.join("subdirectory")
|
||||
subdirectory.mkdir()
|
||||
# create the inner conftest with makeconftest and then move it to the subdirectory
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
**{
|
||||
"subdirectory/conftest.py": """
|
||||
pytest_plugins=['capture']
|
||||
"""
|
||||
)
|
||||
testdir.tmpdir.join("conftest.py").move(subdirectory.join("conftest.py"))
|
||||
# make the top level conftest
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
import warnings
|
||||
warnings.filterwarnings('always', category=DeprecationWarning)
|
||||
"""
|
||||
}
|
||||
)
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
@@ -200,7 +191,7 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated(testdir):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
res = testdir.runpytest_subprocess()
|
||||
res = testdir.runpytest()
|
||||
assert res.ret == 0
|
||||
msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0]
|
||||
res.stdout.fnmatch_lines(
|
||||
@@ -210,6 +201,34 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated(testdir):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("use_pyargs", [True, False])
|
||||
def test_pytest_plugins_in_non_top_level_conftest_deprecated_pyargs(
|
||||
testdir, use_pyargs
|
||||
):
|
||||
"""When using --pyargs, do not emit the warning about non-top-level conftest warnings (#4039, #4044)"""
|
||||
from _pytest.deprecated import PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST
|
||||
|
||||
files = {
|
||||
"src/pkg/__init__.py": "",
|
||||
"src/pkg/conftest.py": "",
|
||||
"src/pkg/test_root.py": "def test(): pass",
|
||||
"src/pkg/sub/__init__.py": "",
|
||||
"src/pkg/sub/conftest.py": "pytest_plugins=['capture']",
|
||||
"src/pkg/sub/test_bar.py": "def test(): pass",
|
||||
}
|
||||
testdir.makepyfile(**files)
|
||||
testdir.syspathinsert(testdir.tmpdir.join("src"))
|
||||
|
||||
args = ("--pyargs", "pkg") if use_pyargs else ()
|
||||
res = testdir.runpytest(*args)
|
||||
assert res.ret == 0
|
||||
msg = str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0]
|
||||
if use_pyargs:
|
||||
assert msg not in res.stdout.str()
|
||||
else:
|
||||
res.stdout.fnmatch_lines("*{msg}*".format(msg=msg))
|
||||
|
||||
|
||||
def test_pytest_plugins_in_non_top_level_conftest_deprecated_no_top_level_conftest(
|
||||
testdir
|
||||
):
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
def test():
|
||||
pass
|
||||
@@ -1591,6 +1591,13 @@ def test_package_collection_infinite_recursion(testdir):
|
||||
result.stdout.fnmatch_lines("*1 passed*")
|
||||
|
||||
|
||||
def test_package_collection_init_given_as_argument(testdir):
|
||||
"""Regression test for #3749"""
|
||||
p = testdir.copy_example("collect/package_init_given_as_arg")
|
||||
result = testdir.runpytest(p / "pkg" / "__init__.py")
|
||||
result.stdout.fnmatch_lines("*1 passed*")
|
||||
|
||||
|
||||
def test_package_with_modules(testdir):
|
||||
"""
|
||||
.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
@@ -488,6 +489,10 @@ class TestRequestBasic(object):
|
||||
assert len(arg2fixturedefs) == 1
|
||||
assert arg2fixturedefs["something"][0].argname == "something"
|
||||
|
||||
@pytest.mark.skipif(
|
||||
hasattr(sys, "pypy_version_info"),
|
||||
reason="this method of test doesn't work on pypy",
|
||||
)
|
||||
def test_request_garbage(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
@@ -498,33 +503,32 @@ class TestRequestBasic(object):
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def something(request):
|
||||
# this method of test doesn't work on pypy
|
||||
if hasattr(sys, "pypy_version_info"):
|
||||
yield
|
||||
else:
|
||||
original = gc.get_debug()
|
||||
gc.set_debug(gc.DEBUG_SAVEALL)
|
||||
gc.collect()
|
||||
original = gc.get_debug()
|
||||
gc.set_debug(gc.DEBUG_SAVEALL)
|
||||
gc.collect()
|
||||
|
||||
yield
|
||||
yield
|
||||
|
||||
try:
|
||||
gc.collect()
|
||||
leaked_types = sum(1 for _ in gc.garbage
|
||||
if isinstance(_, PseudoFixtureDef))
|
||||
|
||||
# debug leaked types if the test fails
|
||||
print(leaked_types)
|
||||
|
||||
gc.garbage[:] = []
|
||||
|
||||
try:
|
||||
assert leaked_types == 0
|
||||
finally:
|
||||
gc.set_debug(original)
|
||||
assert leaked_types == 0
|
||||
finally:
|
||||
gc.set_debug(original)
|
||||
|
||||
def test_func():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=1)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines("* 1 passed in *")
|
||||
|
||||
def test_getfixturevalue_recursive(self, testdir):
|
||||
testdir.makeconftest(
|
||||
|
||||
@@ -132,6 +132,52 @@ class TestMetafunc(object):
|
||||
except ValueError as ve:
|
||||
assert "has an unsupported scope value 'doggy'" in str(ve)
|
||||
|
||||
def test_find_parametrized_scope(self):
|
||||
"""unittest for _find_parametrized_scope (#3941)"""
|
||||
from _pytest.python import _find_parametrized_scope
|
||||
|
||||
@attr.s
|
||||
class DummyFixtureDef(object):
|
||||
scope = attr.ib()
|
||||
|
||||
fixtures_defs = dict(
|
||||
session_fix=[DummyFixtureDef("session")],
|
||||
package_fix=[DummyFixtureDef("package")],
|
||||
module_fix=[DummyFixtureDef("module")],
|
||||
class_fix=[DummyFixtureDef("class")],
|
||||
func_fix=[DummyFixtureDef("function")],
|
||||
)
|
||||
|
||||
# use arguments to determine narrow scope; the cause of the bug is that it would look on all
|
||||
# fixture defs given to the method
|
||||
def find_scope(argnames, indirect):
|
||||
return _find_parametrized_scope(argnames, fixtures_defs, indirect=indirect)
|
||||
|
||||
assert find_scope(["func_fix"], indirect=True) == "function"
|
||||
assert find_scope(["class_fix"], indirect=True) == "class"
|
||||
assert find_scope(["module_fix"], indirect=True) == "module"
|
||||
assert find_scope(["package_fix"], indirect=True) == "package"
|
||||
assert find_scope(["session_fix"], indirect=True) == "session"
|
||||
|
||||
assert find_scope(["class_fix", "func_fix"], indirect=True) == "function"
|
||||
assert find_scope(["func_fix", "session_fix"], indirect=True) == "function"
|
||||
assert find_scope(["session_fix", "class_fix"], indirect=True) == "class"
|
||||
assert find_scope(["package_fix", "session_fix"], indirect=True) == "package"
|
||||
assert find_scope(["module_fix", "session_fix"], indirect=True) == "module"
|
||||
|
||||
# when indirect is False or is not for all scopes, always use function
|
||||
assert find_scope(["session_fix", "module_fix"], indirect=False) == "function"
|
||||
assert (
|
||||
find_scope(["session_fix", "module_fix"], indirect=["module_fix"])
|
||||
== "function"
|
||||
)
|
||||
assert (
|
||||
find_scope(
|
||||
["session_fix", "module_fix"], indirect=["session_fix", "module_fix"]
|
||||
)
|
||||
== "module"
|
||||
)
|
||||
|
||||
def test_parametrize_and_id(self):
|
||||
def func(x, y):
|
||||
pass
|
||||
@@ -211,9 +257,6 @@ class TestMetafunc(object):
|
||||
@hypothesis.settings(
|
||||
deadline=400.0
|
||||
) # very close to std deadline and CI boxes are not reliable in CPU power
|
||||
@pytest.mark.xfail(
|
||||
sys.platform.startswith("win32"), reason="flaky #3707", strict=False
|
||||
)
|
||||
def test_idval_hypothesis(self, value):
|
||||
from _pytest.python import _idval
|
||||
|
||||
@@ -799,7 +842,7 @@ class TestMetafuncFunctional(object):
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
# assumes that generate/provide runs in the same process
|
||||
import sys, pytest
|
||||
import sys, pytest, six
|
||||
def pytest_generate_tests(metafunc):
|
||||
metafunc.addcall(param=metafunc)
|
||||
|
||||
@@ -818,11 +861,7 @@ class TestMetafuncFunctional(object):
|
||||
def test_method(self, metafunc, pytestconfig):
|
||||
assert metafunc.config == pytestconfig
|
||||
assert metafunc.module.__name__ == __name__
|
||||
if sys.version_info > (3, 0):
|
||||
unbound = TestClass.test_method
|
||||
else:
|
||||
unbound = TestClass.test_method.im_func
|
||||
# XXX actually have an unbound test function here?
|
||||
unbound = six.get_unbound_function(TestClass.test_method)
|
||||
assert metafunc.function == unbound
|
||||
assert metafunc.cls == TestClass
|
||||
"""
|
||||
@@ -1390,6 +1429,39 @@ class TestMetafuncFunctionalAuto(object):
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(["* 3 passed *"])
|
||||
|
||||
def test_parametrize_some_arguments_auto_scope(self, testdir, monkeypatch):
|
||||
"""Integration test for (#3941)"""
|
||||
class_fix_setup = []
|
||||
monkeypatch.setattr(sys, "class_fix_setup", class_fix_setup, raising=False)
|
||||
func_fix_setup = []
|
||||
monkeypatch.setattr(sys, "func_fix_setup", func_fix_setup, raising=False)
|
||||
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
import sys
|
||||
|
||||
@pytest.fixture(scope='class', autouse=True)
|
||||
def class_fix(request):
|
||||
sys.class_fix_setup.append(request.param)
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def func_fix():
|
||||
sys.func_fix_setup.append(True)
|
||||
|
||||
@pytest.mark.parametrize('class_fix', [10, 20], indirect=True)
|
||||
class Test:
|
||||
def test_foo(self):
|
||||
pass
|
||||
def test_bar(self):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest_inprocess()
|
||||
result.stdout.fnmatch_lines(["* 4 passed in *"])
|
||||
assert func_fix_setup == [True] * 4
|
||||
assert class_fix_setup == [10, 20]
|
||||
|
||||
def test_parametrize_issue634(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import glob
|
||||
@@ -57,7 +58,7 @@ def getmsg(f, extra_ns=None, must_pass=False):
|
||||
except AssertionError:
|
||||
if must_pass:
|
||||
pytest.fail("shouldn't have raised")
|
||||
s = str(sys.exc_info()[1])
|
||||
s = six.text_type(sys.exc_info()[1])
|
||||
if not s.startswith("assert"):
|
||||
return "AssertionError: " + s
|
||||
return s
|
||||
@@ -608,6 +609,21 @@ class TestAssertionRewrite(object):
|
||||
|
||||
assert r"where 1 = \n{ \n~ \n}.a" in util._format_lines([getmsg(f)])[0]
|
||||
|
||||
def test_custom_repr_non_ascii(self):
|
||||
def f():
|
||||
class A(object):
|
||||
name = u"ä"
|
||||
|
||||
def __repr__(self):
|
||||
return self.name.encode("UTF-8") # only legal in python2
|
||||
|
||||
a = A()
|
||||
assert not a.name
|
||||
|
||||
msg = getmsg(f)
|
||||
assert "UnicodeDecodeError" not in msg
|
||||
assert "UnicodeEncodeError" not in msg
|
||||
|
||||
|
||||
class TestRewriteOnImport(object):
|
||||
def test_pycache_is_a_file(self, testdir):
|
||||
@@ -1034,6 +1050,48 @@ class TestAssertionRewriteHookDetails(object):
|
||||
result = testdir.runpytest("-s")
|
||||
result.stdout.fnmatch_lines(["* 1 passed*"])
|
||||
|
||||
def test_reload_reloads(self, testdir):
|
||||
"""Reloading a module after change picks up the change."""
|
||||
testdir.tmpdir.join("file.py").write(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
def reloaded():
|
||||
return False
|
||||
|
||||
def rewrite_self():
|
||||
with open(__file__, 'w') as self:
|
||||
self.write('def reloaded(): return True')
|
||||
"""
|
||||
)
|
||||
)
|
||||
testdir.tmpdir.join("pytest.ini").write(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
[pytest]
|
||||
python_files = *.py
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
testdir.makepyfile(
|
||||
test_fun="""
|
||||
import sys
|
||||
try:
|
||||
from imp import reload
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
def test_loader():
|
||||
import file
|
||||
assert not file.reloaded()
|
||||
file.rewrite_self()
|
||||
reload(file)
|
||||
assert file.reloaded()
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("-s")
|
||||
result.stdout.fnmatch_lines(["* 1 passed*"])
|
||||
|
||||
def test_get_data_support(self, testdir):
|
||||
"""Implement optional PEP302 api (#808).
|
||||
"""
|
||||
@@ -1232,3 +1290,27 @@ class TestEarlyRewriteBailout(object):
|
||||
hook.fnpats[:] = ["tests/**.py"]
|
||||
assert hook.find_module("file") is not None
|
||||
assert self.find_module_calls == ["file"]
|
||||
|
||||
@pytest.mark.skipif(
|
||||
sys.platform.startswith("win32"), reason="cannot remove cwd on Windows"
|
||||
)
|
||||
def test_cwd_changed(self, testdir):
|
||||
testdir.makepyfile(
|
||||
**{
|
||||
"test_bar.py": """
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
d = tempfile.mkdtemp()
|
||||
os.chdir(d)
|
||||
shutil.rmtree(d)
|
||||
""",
|
||||
"test_foo.py": """
|
||||
def test():
|
||||
pass
|
||||
""",
|
||||
}
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines("* 1 passed in *")
|
||||
|
||||
@@ -215,7 +215,7 @@ def test_cache_show(testdir):
|
||||
|
||||
class TestLastFailed(object):
|
||||
def test_lastfailed_usecase(self, testdir, monkeypatch):
|
||||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
|
||||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1")
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
def test_1():
|
||||
@@ -301,7 +301,7 @@ class TestLastFailed(object):
|
||||
assert "test_a.py" not in result.stdout.str()
|
||||
|
||||
def test_lastfailed_difference_invocations(self, testdir, monkeypatch):
|
||||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
|
||||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1")
|
||||
testdir.makepyfile(
|
||||
test_a="""\
|
||||
def test_a1():
|
||||
@@ -335,7 +335,7 @@ class TestLastFailed(object):
|
||||
result.stdout.fnmatch_lines(["*1 failed*1 desel*"])
|
||||
|
||||
def test_lastfailed_usecase_splice(self, testdir, monkeypatch):
|
||||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
|
||||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1")
|
||||
testdir.makepyfile(
|
||||
"""\
|
||||
def test_1():
|
||||
@@ -474,8 +474,8 @@ class TestLastFailed(object):
|
||||
)
|
||||
|
||||
def rlf(fail_import, fail_run):
|
||||
monkeypatch.setenv("FAILIMPORT", fail_import)
|
||||
monkeypatch.setenv("FAILTEST", fail_run)
|
||||
monkeypatch.setenv("FAILIMPORT", str(fail_import))
|
||||
monkeypatch.setenv("FAILTEST", str(fail_run))
|
||||
|
||||
testdir.runpytest("-q")
|
||||
config = testdir.parseconfigure()
|
||||
@@ -519,8 +519,8 @@ class TestLastFailed(object):
|
||||
)
|
||||
|
||||
def rlf(fail_import, fail_run, args=()):
|
||||
monkeypatch.setenv("FAILIMPORT", fail_import)
|
||||
monkeypatch.setenv("FAILTEST", fail_run)
|
||||
monkeypatch.setenv("FAILIMPORT", str(fail_import))
|
||||
monkeypatch.setenv("FAILTEST", str(fail_run))
|
||||
|
||||
result = testdir.runpytest("-q", "--lf", *args)
|
||||
config = testdir.parseconfigure()
|
||||
@@ -884,3 +884,14 @@ class TestReadme(object):
|
||||
)
|
||||
testdir.runpytest()
|
||||
assert self.check_readme(testdir) is True
|
||||
|
||||
|
||||
def test_gitignore(testdir):
|
||||
"""Ensure we automatically create .gitignore file in the pytest_cache directory (#3286)."""
|
||||
from _pytest.cacheprovider import Cache
|
||||
|
||||
config = testdir.parseconfig()
|
||||
cache = Cache.for_config(config)
|
||||
cache.set("foo", "bar")
|
||||
msg = "# created by pytest automatically, do not change\n*"
|
||||
assert cache._cachedir.joinpath(".gitignore").read_text(encoding="UTF-8") == msg
|
||||
|
||||
@@ -30,6 +30,7 @@ def conftest_setinitial(conftest, args, confcutdir=None):
|
||||
self.file_or_dir = args
|
||||
self.confcutdir = str(confcutdir)
|
||||
self.noconftest = False
|
||||
self.pyargs = False
|
||||
|
||||
conftest._set_initial_conftests(Namespace())
|
||||
|
||||
|
||||
@@ -850,7 +850,7 @@ def test_logxml_path_expansion(tmpdir, monkeypatch):
|
||||
assert xml_tilde.logfile == home_tilde
|
||||
|
||||
# this is here for when $HOME is not set correct
|
||||
monkeypatch.setenv("HOME", tmpdir)
|
||||
monkeypatch.setenv("HOME", str(tmpdir))
|
||||
home_var = os.path.normpath(os.path.expandvars("$HOME/test.xml"))
|
||||
|
||||
xml_var = LogXML("$HOME%stest.xml" % tmpdir.sep, None)
|
||||
|
||||
@@ -799,6 +799,18 @@ class TestFunctional(object):
|
||||
deselected_tests = dlist[0].items
|
||||
assert len(deselected_tests) == 2
|
||||
|
||||
def test_invalid_m_option(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_a():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("-m bogus/")
|
||||
result.stdout.fnmatch_lines(
|
||||
["INTERNALERROR> Marker expression must be valid Python!"]
|
||||
)
|
||||
|
||||
def test_keywords_at_node_level(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
|
||||
@@ -3,6 +3,8 @@ import os
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import six
|
||||
|
||||
import pytest
|
||||
from _pytest.monkeypatch import MonkeyPatch
|
||||
|
||||
@@ -163,7 +165,8 @@ def test_delitem():
|
||||
|
||||
def test_setenv():
|
||||
monkeypatch = MonkeyPatch()
|
||||
monkeypatch.setenv("XYZ123", 2)
|
||||
with pytest.warns(pytest.PytestWarning):
|
||||
monkeypatch.setenv("XYZ123", 2)
|
||||
import os
|
||||
|
||||
assert os.environ["XYZ123"] == "2"
|
||||
@@ -192,13 +195,49 @@ def test_delenv():
|
||||
del os.environ[name]
|
||||
|
||||
|
||||
class TestEnvironWarnings(object):
|
||||
"""
|
||||
os.environ keys and values should be native strings, otherwise it will cause problems with other modules (notably
|
||||
subprocess). On Python 2 os.environ accepts anything without complaining, while Python 3 does the right thing
|
||||
and raises an error.
|
||||
"""
|
||||
|
||||
VAR_NAME = u"PYTEST_INTERNAL_MY_VAR"
|
||||
|
||||
@pytest.mark.skipif(six.PY3, reason="Python 2 only test")
|
||||
def test_setenv_unicode_key(self, monkeypatch):
|
||||
with pytest.warns(
|
||||
pytest.PytestWarning,
|
||||
match="Environment variable name {!r} should be str".format(self.VAR_NAME),
|
||||
):
|
||||
monkeypatch.setenv(self.VAR_NAME, "2")
|
||||
|
||||
@pytest.mark.skipif(six.PY3, reason="Python 2 only test")
|
||||
def test_delenv_unicode_key(self, monkeypatch):
|
||||
with pytest.warns(
|
||||
pytest.PytestWarning,
|
||||
match="Environment variable name {!r} should be str".format(self.VAR_NAME),
|
||||
):
|
||||
monkeypatch.delenv(self.VAR_NAME, raising=False)
|
||||
|
||||
def test_setenv_non_str_warning(self, monkeypatch):
|
||||
value = 2
|
||||
msg = (
|
||||
"Environment variable value {!r} should be str, converted to str implicitly"
|
||||
)
|
||||
with pytest.warns(pytest.PytestWarning, match=msg.format(value)):
|
||||
monkeypatch.setenv(str(self.VAR_NAME), value)
|
||||
|
||||
|
||||
def test_setenv_prepend():
|
||||
import os
|
||||
|
||||
monkeypatch = MonkeyPatch()
|
||||
monkeypatch.setenv("XYZ123", 2, prepend="-")
|
||||
with pytest.warns(pytest.PytestWarning):
|
||||
monkeypatch.setenv("XYZ123", 2, prepend="-")
|
||||
assert os.environ["XYZ123"] == "2"
|
||||
monkeypatch.setenv("XYZ123", 3, prepend="-")
|
||||
with pytest.warns(pytest.PytestWarning):
|
||||
monkeypatch.setenv("XYZ123", 3, prepend="-")
|
||||
assert os.environ["XYZ123"] == "3-2"
|
||||
monkeypatch.undo()
|
||||
assert "XYZ123" not in os.environ
|
||||
|
||||
69
testing/test_paths.py
Normal file
69
testing/test_paths.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import sys
|
||||
|
||||
import py
|
||||
|
||||
import pytest
|
||||
|
||||
from _pytest.paths import fnmatch_ex
|
||||
|
||||
|
||||
class TestPort:
|
||||
"""Test that our port of py.common.FNMatcher (fnmatch_ex) produces the same results as the
|
||||
original py.path.local.fnmatch method.
|
||||
"""
|
||||
|
||||
@pytest.fixture(params=["pathlib", "py.path"])
|
||||
def match(self, request):
|
||||
if request.param == "py.path":
|
||||
|
||||
def match_(pattern, path):
|
||||
return py.path.local(path).fnmatch(pattern)
|
||||
|
||||
else:
|
||||
assert request.param == "pathlib"
|
||||
|
||||
def match_(pattern, path):
|
||||
return fnmatch_ex(pattern, path)
|
||||
|
||||
return match_
|
||||
|
||||
if sys.platform == "win32":
|
||||
drv1 = "c:"
|
||||
drv2 = "d:"
|
||||
else:
|
||||
drv1 = "/c"
|
||||
drv2 = "/d"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"pattern, path",
|
||||
[
|
||||
("*.py", "foo.py"),
|
||||
("*.py", "bar/foo.py"),
|
||||
("test_*.py", "foo/test_foo.py"),
|
||||
("tests/*.py", "tests/foo.py"),
|
||||
(drv1 + "/*.py", drv1 + "/foo.py"),
|
||||
(drv1 + "/foo/*.py", drv1 + "/foo/foo.py"),
|
||||
("tests/**/test*.py", "tests/foo/test_foo.py"),
|
||||
("tests/**/doc/test*.py", "tests/foo/bar/doc/test_foo.py"),
|
||||
("tests/**/doc/**/test*.py", "tests/foo/doc/bar/test_foo.py"),
|
||||
],
|
||||
)
|
||||
def test_matching(self, match, pattern, path):
|
||||
assert match(pattern, path)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"pattern, path",
|
||||
[
|
||||
("*.py", "foo.pyc"),
|
||||
("*.py", "foo/foo.pyc"),
|
||||
("tests/*.py", "foo/foo.py"),
|
||||
(drv1 + "/*.py", drv2 + "/foo.py"),
|
||||
(drv1 + "/foo/*.py", drv2 + "/foo/foo.py"),
|
||||
("tests/**/test*.py", "tests/foo.py"),
|
||||
("tests/**/test*.py", "foo/test_foo.py"),
|
||||
("tests/**/doc/test*.py", "tests/foo/bar/doc/foo.py"),
|
||||
("tests/**/doc/test*.py", "tests/foo/bar/test_foo.py"),
|
||||
],
|
||||
)
|
||||
def test_not_matching(self, match, pattern, path):
|
||||
assert not match(pattern, path)
|
||||
@@ -397,6 +397,24 @@ class TestPDB(object):
|
||||
child.read()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_with_caplog_on_pdb_invocation(self, testdir):
|
||||
p1 = testdir.makepyfile(
|
||||
"""
|
||||
def test_1(capsys, caplog):
|
||||
import logging
|
||||
logging.getLogger(__name__).warning("some_warning")
|
||||
assert 0
|
||||
"""
|
||||
)
|
||||
child = testdir.spawn_pytest("--pdb %s" % str(p1))
|
||||
child.send("caplog.record_tuples\n")
|
||||
child.expect_exact(
|
||||
"[('test_pdb_with_caplog_on_pdb_invocation', 30, 'some_warning')]"
|
||||
)
|
||||
child.sendeof()
|
||||
child.read()
|
||||
self.flush(child)
|
||||
|
||||
def test_set_trace_capturing_afterwards(self, testdir):
|
||||
p1 = testdir.makepyfile(
|
||||
"""
|
||||
|
||||
@@ -681,14 +681,22 @@ def test_pass_reporting_on_fail(testdir):
|
||||
def test_pass_output_reporting(testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_pass_output():
|
||||
def test_pass_has_output():
|
||||
print("Four score and seven years ago...")
|
||||
def test_pass_no_output():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
assert "Four score and seven years ago..." not in result.stdout.str()
|
||||
s = result.stdout.str()
|
||||
assert "test_pass_has_output" not in s
|
||||
assert "Four score and seven years ago..." not in s
|
||||
assert "test_pass_no_output" not in s
|
||||
result = testdir.runpytest("-rP")
|
||||
result.stdout.fnmatch_lines(["Four score and seven years ago..."])
|
||||
result.stdout.fnmatch_lines(
|
||||
["*test_pass_has_output*", "Four score and seven years ago..."]
|
||||
)
|
||||
assert "test_pass_no_output" not in result.stdout.str()
|
||||
|
||||
|
||||
def test_color_yes(testdir):
|
||||
|
||||
59
tox.ini
59
tox.ini
@@ -18,18 +18,20 @@ envlist =
|
||||
|
||||
[testenv]
|
||||
commands =
|
||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest --lsof -ra {env:_PYTEST_TEST_OPTS:} {posargs:testing}
|
||||
coverage: coverage report -m --skip-covered
|
||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest --lsof -ra {posargs:testing}
|
||||
coverage: coverage combine
|
||||
coverage: coverage report
|
||||
passenv = USER USERNAME
|
||||
setenv =
|
||||
# configuration if a user runs tox with a "coverage" factor, for example "tox -e py36-coverage"
|
||||
coverage: _PYTEST_TOX_COVERAGE_RUN=coverage run -m
|
||||
coverage: _PYTEST_TOX_EXTRA_DEP=coverage-enable-subprocess
|
||||
coverage: COVERAGE_FILE={toxinidir}/.coverage
|
||||
coverage: COVERAGE_PROCESS_START={toxinidir}/.coveragerc
|
||||
passenv = USER USERNAME
|
||||
deps =
|
||||
hypothesis>=3.56
|
||||
nose
|
||||
mock
|
||||
{py27,pypy}: mock
|
||||
requests
|
||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
||||
|
||||
@@ -37,31 +39,38 @@ deps =
|
||||
changedir = .
|
||||
deps =
|
||||
pytest-xdist>=1.13
|
||||
mock
|
||||
py27: mock
|
||||
nose
|
||||
passenv = USER USERNAME TRAVIS
|
||||
commands =
|
||||
pytest -n3 -ra --runpytest=subprocess {posargs:testing}
|
||||
pytest -n auto -ra --runpytest=subprocess {posargs:testing}
|
||||
|
||||
|
||||
[testenv:linting]
|
||||
skip_install = True
|
||||
basepython = python3.6
|
||||
deps = pre-commit
|
||||
deps = pre-commit>=1.11.0
|
||||
commands = pre-commit run --all-files --show-diff-on-failure
|
||||
|
||||
[testenv:py27-xdist]
|
||||
deps =
|
||||
pytest-xdist>=1.13
|
||||
mock
|
||||
{py27,pypy}: mock
|
||||
nose
|
||||
hypothesis>=3.56
|
||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
||||
changedir=testing
|
||||
passenv = USER USERNAME TRAVIS
|
||||
commands =
|
||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n8 -ra {posargs:.}
|
||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto -ra {posargs:testing}
|
||||
|
||||
[testenv:py36-xdist]
|
||||
deps = {[testenv:py27-xdist]deps}
|
||||
# NOTE: copied from above due to https://github.com/tox-dev/tox/issues/706.
|
||||
deps =
|
||||
pytest-xdist>=1.13
|
||||
{py27,pypy}: mock
|
||||
nose
|
||||
hypothesis>=3.56
|
||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
||||
commands = {[testenv:py27-xdist]commands}
|
||||
|
||||
[testenv:py27-pexpect]
|
||||
@@ -83,15 +92,16 @@ commands = {[testenv:py27-pexpect]commands}
|
||||
deps =
|
||||
pytest-xdist>=1.13
|
||||
hypothesis>=3.56
|
||||
mock
|
||||
py27: mock
|
||||
{env:_PYTEST_TOX_EXTRA_DEP:}
|
||||
distribute = true
|
||||
changedir=testing
|
||||
setenv =
|
||||
{[testenv]setenv}
|
||||
PYTHONDONTWRITEBYTECODE=1
|
||||
passenv = USER USERNAME TRAVIS
|
||||
commands =
|
||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n3 -ra {posargs:.}
|
||||
{env:_PYTEST_TOX_COVERAGE_RUN:} pytest -n auto -ra {posargs:.}
|
||||
|
||||
[testenv:py27-trial]
|
||||
deps =
|
||||
@@ -119,13 +129,11 @@ commands = {[testenv:py27-numpy]commands}
|
||||
setenv=
|
||||
{[testenv]setenv}
|
||||
_PYTEST_SETUP_SKIP_PLUGGY_DEP=1
|
||||
deps =
|
||||
{[testenv]deps}
|
||||
git+https://github.com/pytest-dev/pluggy.git@master
|
||||
# NOTE: using env instead of "{[testenv]deps}", because of https://github.com/tox-dev/tox/issues/706.
|
||||
_PYTEST_TOX_EXTRA_DEP=git+https://github.com/pytest-dev/pluggy.git@master
|
||||
|
||||
[testenv:py36-pluggymaster]
|
||||
setenv = {[testenv:py27-pluggymaster]setenv}
|
||||
deps = {[testenv:py27-pluggymaster]deps}
|
||||
|
||||
[testenv:docs]
|
||||
skipsdist = True
|
||||
@@ -177,21 +185,6 @@ commands =
|
||||
{envpython} create_executable.py
|
||||
{envpython} tox_run.py
|
||||
|
||||
|
||||
[testenv:coveralls]
|
||||
passenv = CI TRAVIS TRAVIS_* COVERALLS_REPO_TOKEN
|
||||
usedevelop = True
|
||||
changedir = .
|
||||
deps =
|
||||
{[testenv]deps}
|
||||
coveralls
|
||||
codecov
|
||||
commands =
|
||||
coverage run -m pytest testing
|
||||
coverage report -m
|
||||
coveralls
|
||||
codecov
|
||||
|
||||
[testenv:release]
|
||||
decription = do a release, required posarg of the version number
|
||||
basepython = python3.6
|
||||
@@ -201,7 +194,7 @@ passenv = *
|
||||
deps =
|
||||
colorama
|
||||
gitpython
|
||||
pre-commit
|
||||
pre-commit>=1.11.0
|
||||
towncrier
|
||||
wheel
|
||||
commands = python scripts/release.py {posargs}
|
||||
|
||||
Reference in New Issue
Block a user