Merge remote-tracking branch 'upstream/master' into merge-master-into-features
This commit is contained in:
commit
ac9ceaacd8
|
@ -0,0 +1,149 @@
|
||||||
|
["os: cygwin"]
|
||||||
|
color = "006b75"
|
||||||
|
description = "cygwin platform-specific problem"
|
||||||
|
name = "os: cygwin"
|
||||||
|
|
||||||
|
["os: linux"]
|
||||||
|
color = "1d76db"
|
||||||
|
description = "linux platform-specific problem"
|
||||||
|
name = "os: linux"
|
||||||
|
|
||||||
|
["os: mac"]
|
||||||
|
color = "bfdadc"
|
||||||
|
description = "mac platform-specific problem"
|
||||||
|
name = "os: mac"
|
||||||
|
|
||||||
|
["os: windows"]
|
||||||
|
color = "fbca04"
|
||||||
|
description = "windows platform-specific problem"
|
||||||
|
name = "os: windows"
|
||||||
|
|
||||||
|
["plugin: argcomplete"]
|
||||||
|
color = "d4c5f9"
|
||||||
|
description = "related to the argcomplete builtin plugin"
|
||||||
|
name = "plugin: argcomplete"
|
||||||
|
|
||||||
|
["plugin: cache"]
|
||||||
|
color = "c7def8"
|
||||||
|
description = "related to the cache builtin plugin"
|
||||||
|
name = "plugin: cache"
|
||||||
|
|
||||||
|
["plugin: capture"]
|
||||||
|
color = "1d76db"
|
||||||
|
description = "related to the capture builtin plugin"
|
||||||
|
name = "plugin: capture"
|
||||||
|
|
||||||
|
["plugin: debugging"]
|
||||||
|
color = "dd52a8"
|
||||||
|
description = "related to the debugging builtin plugin"
|
||||||
|
name = "plugin: debugging"
|
||||||
|
|
||||||
|
["plugin: doctests"]
|
||||||
|
color = "fad8c7"
|
||||||
|
description = "related to the doctests builtin plugin"
|
||||||
|
name = "plugin: doctests"
|
||||||
|
|
||||||
|
["plugin: junitxml"]
|
||||||
|
color = "c5def5"
|
||||||
|
description = "related to the junitxml builtin plugin"
|
||||||
|
name = "plugin: junitxml"
|
||||||
|
|
||||||
|
["plugin: logging"]
|
||||||
|
color = "ff5432"
|
||||||
|
description = "related to the logging builtin plugin"
|
||||||
|
name = "plugin: logging"
|
||||||
|
|
||||||
|
["plugin: monkeypatch"]
|
||||||
|
color = "0e8a16"
|
||||||
|
description = "related to the monkeypatch builtin plugin"
|
||||||
|
name = "plugin: monkeypatch"
|
||||||
|
|
||||||
|
["plugin: nose"]
|
||||||
|
color = "bfdadc"
|
||||||
|
description = "related to the nose integration builtin plugin"
|
||||||
|
name = "plugin: nose"
|
||||||
|
|
||||||
|
["plugin: pastebin"]
|
||||||
|
color = "bfd4f2"
|
||||||
|
description = "related to the pastebin builtin plugin"
|
||||||
|
name = "plugin: pastebin"
|
||||||
|
|
||||||
|
["plugin: pytester"]
|
||||||
|
color = "c5def5"
|
||||||
|
description = "related to the pytester builtin plugin"
|
||||||
|
name = "plugin: pytester"
|
||||||
|
|
||||||
|
["plugin: tmpdir"]
|
||||||
|
color = "bfd4f2"
|
||||||
|
description = "related to the tmpdir builtin plugin"
|
||||||
|
name = "plugin: tmpdir"
|
||||||
|
|
||||||
|
["plugin: unittest"]
|
||||||
|
color = "006b75"
|
||||||
|
description = "related to the unittest integration builtin plugin"
|
||||||
|
name = "plugin: unittest"
|
||||||
|
|
||||||
|
["plugin: warnings"]
|
||||||
|
color = "fef2c0"
|
||||||
|
description = "related to the warnings builtin plugin"
|
||||||
|
name = "plugin: warnings"
|
||||||
|
|
||||||
|
["plugin: xdist"]
|
||||||
|
color = "5319e7"
|
||||||
|
description = "related to the xdist external plugin"
|
||||||
|
name = "plugin: xdist"
|
||||||
|
|
||||||
|
["status: critical"]
|
||||||
|
color = "e11d21"
|
||||||
|
description = "grave problem or usability issue that affects lots of users"
|
||||||
|
name = "status: critical"
|
||||||
|
|
||||||
|
["status: easy"]
|
||||||
|
color = "bfe5bf"
|
||||||
|
description = "easy issue that is friendly to new contributor"
|
||||||
|
name = "status: easy"
|
||||||
|
|
||||||
|
["status: help wanted"]
|
||||||
|
color = "159818"
|
||||||
|
description = "developers would like help from experts on this topic"
|
||||||
|
name = "status: help wanted"
|
||||||
|
|
||||||
|
["status: needs information"]
|
||||||
|
color = "5319e7"
|
||||||
|
description = "reporter needs to provide more information; can be closed after 2 or more weeks of inactivity"
|
||||||
|
name = "status: needs information"
|
||||||
|
|
||||||
|
["topic: collection"]
|
||||||
|
color = "006b75"
|
||||||
|
description = "related to the collection phase"
|
||||||
|
name = "topic: collection"
|
||||||
|
|
||||||
|
["topic: config"]
|
||||||
|
color = "006b75"
|
||||||
|
description = "related to config handling, argument parsing and config file"
|
||||||
|
name = "topic: config"
|
||||||
|
|
||||||
|
["topic: fixtures"]
|
||||||
|
color = "5319e7"
|
||||||
|
description = "anything involving fixtures directly or indirectly"
|
||||||
|
name = "topic: fixtures"
|
||||||
|
|
||||||
|
["topic: marks"]
|
||||||
|
color = "b60205"
|
||||||
|
description = "related to marks, either the general marks or builtin"
|
||||||
|
name = "topic: marks"
|
||||||
|
|
||||||
|
["topic: parametrize"]
|
||||||
|
color = "fbca04"
|
||||||
|
description = "related to @pytest.mark.parametrize"
|
||||||
|
name = "topic: parametrize"
|
||||||
|
|
||||||
|
["topic: reporting"]
|
||||||
|
color = "fef2c0"
|
||||||
|
description = "related to terminal output and user-facing messages and errors"
|
||||||
|
name = "topic: reporting"
|
||||||
|
|
||||||
|
["topic: rewrite"]
|
||||||
|
color = "0e8a16"
|
||||||
|
description = "related to the assertion rewrite mechanism"
|
||||||
|
name = "topic: rewrite"
|
|
@ -34,3 +34,8 @@ repos:
|
||||||
language: python
|
language: python
|
||||||
additional_dependencies: [pygments, restructuredtext_lint]
|
additional_dependencies: [pygments, restructuredtext_lint]
|
||||||
python_version: python3.6
|
python_version: python3.6
|
||||||
|
- id: rst-backticks
|
||||||
|
name: rst ``code`` is two backticks
|
||||||
|
entry: ' `[^`]+[^_]`([^_]|$)'
|
||||||
|
language: pygrep
|
||||||
|
types: [rst]
|
||||||
|
|
|
@ -3,7 +3,8 @@ language: python
|
||||||
stages:
|
stages:
|
||||||
- linting
|
- linting
|
||||||
- test
|
- test
|
||||||
- deploy
|
- name: deploy
|
||||||
|
if: repo = pytest-dev/pytest AND tag IS present
|
||||||
python:
|
python:
|
||||||
- '3.6'
|
- '3.6'
|
||||||
install:
|
install:
|
||||||
|
|
|
@ -72,7 +72,7 @@ Bug Fixes
|
||||||
raises an exception. (`#3569
|
raises an exception. (`#3569
|
||||||
<https://github.com/pytest-dev/pytest/issues/3569>`_)
|
<https://github.com/pytest-dev/pytest/issues/3569>`_)
|
||||||
|
|
||||||
- Fix encoding error with `print` statements in doctests (`#3583
|
- Fix encoding error with ``print`` statements in doctests (`#3583
|
||||||
<https://github.com/pytest-dev/pytest/issues/3583>`_)
|
<https://github.com/pytest-dev/pytest/issues/3583>`_)
|
||||||
|
|
||||||
|
|
||||||
|
@ -345,7 +345,7 @@ Features
|
||||||
``pytest_runtest_logfinish`` hooks when live logs are enabled. (`#3189
|
``pytest_runtest_logfinish`` hooks when live logs are enabled. (`#3189
|
||||||
<https://github.com/pytest-dev/pytest/issues/3189>`_)
|
<https://github.com/pytest-dev/pytest/issues/3189>`_)
|
||||||
|
|
||||||
- Passing `--log-cli-level` in the command-line now automatically activates
|
- Passing ``--log-cli-level`` in the command-line now automatically activates
|
||||||
live logging. (`#3190 <https://github.com/pytest-dev/pytest/issues/3190>`_)
|
live logging. (`#3190 <https://github.com/pytest-dev/pytest/issues/3190>`_)
|
||||||
|
|
||||||
- Add command line option ``--deselect`` to allow deselection of individual
|
- Add command line option ``--deselect`` to allow deselection of individual
|
||||||
|
@ -697,8 +697,8 @@ Trivial/Internal Changes
|
||||||
- Code cleanup. (`#3015 <https://github.com/pytest-dev/pytest/issues/3015>`_,
|
- Code cleanup. (`#3015 <https://github.com/pytest-dev/pytest/issues/3015>`_,
|
||||||
`#3021 <https://github.com/pytest-dev/pytest/issues/3021>`_)
|
`#3021 <https://github.com/pytest-dev/pytest/issues/3021>`_)
|
||||||
|
|
||||||
- Clean up code by replacing imports and references of `_ast` to `ast`. (`#3018
|
- Clean up code by replacing imports and references of ``_ast`` to ``ast``.
|
||||||
<https://github.com/pytest-dev/pytest/issues/3018>`_)
|
(`#3018 <https://github.com/pytest-dev/pytest/issues/3018>`_)
|
||||||
|
|
||||||
|
|
||||||
Pytest 3.3.1 (2017-12-05)
|
Pytest 3.3.1 (2017-12-05)
|
||||||
|
@ -1026,7 +1026,7 @@ Pytest 3.2.2 (2017-09-06)
|
||||||
Bug Fixes
|
Bug Fixes
|
||||||
---------
|
---------
|
||||||
|
|
||||||
- Calling the deprecated `request.getfuncargvalue()` now shows the source of
|
- Calling the deprecated ``request.getfuncargvalue()`` now shows the source of
|
||||||
the call. (`#2681 <https://github.com/pytest-dev/pytest/issues/2681>`_)
|
the call. (`#2681 <https://github.com/pytest-dev/pytest/issues/2681>`_)
|
||||||
|
|
||||||
- Allow tests declared as ``@staticmethod`` to use fixtures. (`#2699
|
- Allow tests declared as ``@staticmethod`` to use fixtures. (`#2699
|
||||||
|
@ -1048,10 +1048,10 @@ Improved Documentation
|
||||||
``pytest.mark.MARKER_NAME.__call__`` (`#2604
|
``pytest.mark.MARKER_NAME.__call__`` (`#2604
|
||||||
<https://github.com/pytest-dev/pytest/issues/2604>`_)
|
<https://github.com/pytest-dev/pytest/issues/2604>`_)
|
||||||
|
|
||||||
- In one of the simple examples, use `pytest_collection_modifyitems()` to skip
|
- In one of the simple examples, use ``pytest_collection_modifyitems()`` to skip
|
||||||
tests based on a command-line option, allowing its sharing while preventing a
|
tests based on a command-line option, allowing its sharing while preventing a
|
||||||
user error when acessing `pytest.config` before the argument parsing. (`#2653
|
user error when acessing ``pytest.config`` before the argument parsing.
|
||||||
<https://github.com/pytest-dev/pytest/issues/2653>`_)
|
(`#2653 <https://github.com/pytest-dev/pytest/issues/2653>`_)
|
||||||
|
|
||||||
|
|
||||||
Trivial/Internal Changes
|
Trivial/Internal Changes
|
||||||
|
@ -1129,7 +1129,7 @@ Features
|
||||||
from parent classes or modules. (`#2516 <https://github.com/pytest-
|
from parent classes or modules. (`#2516 <https://github.com/pytest-
|
||||||
dev/pytest/issues/2516>`_)
|
dev/pytest/issues/2516>`_)
|
||||||
|
|
||||||
- Collection ignores local virtualenvs by default; `--collect-in-virtualenv`
|
- Collection ignores local virtualenvs by default; ``--collect-in-virtualenv``
|
||||||
overrides this behavior. (`#2518 <https://github.com/pytest-
|
overrides this behavior. (`#2518 <https://github.com/pytest-
|
||||||
dev/pytest/issues/2518>`_)
|
dev/pytest/issues/2518>`_)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
NOTE: Change "x.y" by the version you use. If you are unsure about which version
|
||||||
|
you are using run: `pip show pytest`.
|
||||||
|
|
||||||
|
Text:
|
||||||
|
|
||||||
|
[pytest] pytest x.y, 2004
|
||||||
|
Krekel et al., https://github.com/pytest-dev/pytest
|
||||||
|
|
||||||
|
BibTeX:
|
||||||
|
|
||||||
|
@misc{pytestx.y,
|
||||||
|
title = {pytest x.y},
|
||||||
|
author = {Krekel, Holger and Oliveira, Bruno and Pfannschmidt, Ronny and Bruynooghe, Floris and Laugher, Brianna and Bruhin, Florian},
|
||||||
|
year = {2004},
|
||||||
|
url = {https://github.com/pytest-dev/pytest},
|
||||||
|
}
|
|
@ -18,19 +18,11 @@ taking a lot of time to make a new one.
|
||||||
|
|
||||||
Ensure your are in a clean work tree.
|
Ensure your are in a clean work tree.
|
||||||
|
|
||||||
#. Install development dependencies in a virtual environment with::
|
#. Using ``tox``, generate docs, changelog, announcements::
|
||||||
|
|
||||||
$ pip3 install -U -r tasks/requirements.txt
|
$ tox -e release -- <VERSION>
|
||||||
|
|
||||||
#. Generate docs, changelog, announcements, and a **local** tag::
|
|
||||||
|
|
||||||
$ invoke generate.pre-release <VERSION>
|
|
||||||
|
|
||||||
#. Execute pre-commit on all files to ensure the docs are conformant and commit your results::
|
|
||||||
|
|
||||||
$ pre-commit run --all-files
|
|
||||||
$ git commit -am "Fix files with pre-commit"
|
|
||||||
|
|
||||||
|
This will generate a commit with all the changes ready for pushing.
|
||||||
|
|
||||||
#. Open a PR for this branch targeting ``master``.
|
#. Open a PR for this branch targeting ``master``.
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
:align: center
|
:align: center
|
||||||
:alt: pytest
|
:alt: pytest
|
||||||
|
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
.. image:: https://img.shields.io/pypi/v/pytest.svg
|
.. image:: https://img.shields.io/pypi/v/pytest.svg
|
||||||
|
@ -109,7 +110,7 @@ Consult the `Changelog <http://docs.pytest.org/en/latest/changelog.html>`__ page
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Copyright Holger Krekel and others, 2004-2017.
|
Copyright Holger Krekel and others, 2004-2018.
|
||||||
|
|
||||||
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
|
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Remove obsolete ``__future__`` imports.
|
|
@ -0,0 +1 @@
|
||||||
|
Add CITATION to provide information on how to formally cite pytest.
|
|
@ -0,0 +1 @@
|
||||||
|
Use ``smtp_connection`` instead of ``smtp`` in fixtures documentation to avoid possible confusion.
|
|
@ -0,0 +1 @@
|
||||||
|
Replace broken type annotations with type comments.
|
|
@ -0,0 +1 @@
|
||||||
|
Invoke pytest using ``-mpytest`` so ``sys.path`` does not get polluted by packages installed in ``site-packages``.
|
|
@ -124,7 +124,7 @@ The py.test Development Team
|
||||||
Thanks `@biern`_ for the PR.
|
Thanks `@biern`_ for the PR.
|
||||||
|
|
||||||
* Fix `traceback style docs`_ to describe all of the available options
|
* Fix `traceback style docs`_ to describe all of the available options
|
||||||
(auto/long/short/line/native/no), with `auto` being the default since v2.6.
|
(auto/long/short/line/native/no), with ``auto`` being the default since v2.6.
|
||||||
Thanks `@hackebrot`_ for the PR.
|
Thanks `@hackebrot`_ for the PR.
|
||||||
|
|
||||||
* Fix (`#1422`_): junit record_xml_property doesn't allow multiple records
|
* Fix (`#1422`_): junit record_xml_property doesn't allow multiple records
|
||||||
|
|
|
@ -39,6 +39,11 @@ avoid creating labels just for the sake of creating them.
|
||||||
|
|
||||||
Each label should include a description in the GitHub's interface stating its purpose.
|
Each label should include a description in the GitHub's interface stating its purpose.
|
||||||
|
|
||||||
|
Labels are managed using `labels <https://github.com/hackebrot/labels>`_. All the labels in the repository
|
||||||
|
are kept in ``.github/labels.toml``, so any changes should be via PRs to that file.
|
||||||
|
After a PR is accepted and merged, one of the maintainers must manually synchronize the labels file with the
|
||||||
|
GitHub repository.
|
||||||
|
|
||||||
Temporary labels
|
Temporary labels
|
||||||
~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -299,10 +299,10 @@ Skip and xfail marks can also be applied in this way, see :ref:`skip/xfail with
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
If the data you are parametrizing happen to be single callables, you need to be careful
|
If the data you are parametrizing happen to be single callables, you need to be careful
|
||||||
when marking these items. `pytest.mark.xfail(my_func)` won't work because it's also the
|
when marking these items. ``pytest.mark.xfail(my_func)`` won't work because it's also the
|
||||||
signature of a function being decorated. To resolve this ambiguity, you need to pass a
|
signature of a function being decorated. To resolve this ambiguity, you need to pass a
|
||||||
reason argument:
|
reason argument:
|
||||||
`pytest.mark.xfail(func_bar, reason="Issue#7")`.
|
``pytest.mark.xfail(func_bar, reason="Issue#7")``.
|
||||||
|
|
||||||
|
|
||||||
.. _`adding a custom marker from a plugin`:
|
.. _`adding a custom marker from a plugin`:
|
||||||
|
|
|
@ -55,18 +55,18 @@ using it::
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def smtp():
|
def smtp_connection():
|
||||||
import smtplib
|
import smtplib
|
||||||
return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
||||||
|
|
||||||
def test_ehlo(smtp):
|
def test_ehlo(smtp_connection):
|
||||||
response, msg = smtp.ehlo()
|
response, msg = smtp_connection.ehlo()
|
||||||
assert response == 250
|
assert response == 250
|
||||||
assert 0 # for demo purposes
|
assert 0 # for demo purposes
|
||||||
|
|
||||||
Here, the ``test_ehlo`` needs the ``smtp`` fixture value. pytest
|
Here, the ``test_ehlo`` needs the ``smtp_connection`` fixture value. pytest
|
||||||
will discover and call the :py:func:`@pytest.fixture <_pytest.python.fixture>`
|
will discover and call the :py:func:`@pytest.fixture <_pytest.python.fixture>`
|
||||||
marked ``smtp`` fixture function. Running the test looks like this::
|
marked ``smtp_connection`` fixture function. Running the test looks like this::
|
||||||
|
|
||||||
$ pytest test_smtpsimple.py
|
$ pytest test_smtpsimple.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
|
@ -79,10 +79,10 @@ marked ``smtp`` fixture function. Running the test looks like this::
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
________________________________ test_ehlo _________________________________
|
________________________________ test_ehlo _________________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP object at 0xdeadbeef>
|
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
|
||||||
|
|
||||||
def test_ehlo(smtp):
|
def test_ehlo(smtp_connection):
|
||||||
response, msg = smtp.ehlo()
|
response, msg = smtp_connection.ehlo()
|
||||||
assert response == 250
|
assert response == 250
|
||||||
> assert 0 # for demo purposes
|
> assert 0 # for demo purposes
|
||||||
E assert 0
|
E assert 0
|
||||||
|
@ -91,18 +91,18 @@ marked ``smtp`` fixture function. Running the test looks like this::
|
||||||
========================= 1 failed in 0.12 seconds =========================
|
========================= 1 failed in 0.12 seconds =========================
|
||||||
|
|
||||||
In the failure traceback we see that the test function was called with a
|
In the failure traceback we see that the test function was called with a
|
||||||
``smtp`` argument, the ``smtplib.SMTP()`` instance created by the fixture
|
``smtp_connection`` argument, the ``smtplib.SMTP()`` instance created by the fixture
|
||||||
function. The test function fails on our deliberate ``assert 0``. Here is
|
function. The test function fails on our deliberate ``assert 0``. Here is
|
||||||
the exact protocol used by ``pytest`` to call the test function this way:
|
the exact protocol used by ``pytest`` to call the test function this way:
|
||||||
|
|
||||||
1. pytest :ref:`finds <test discovery>` the ``test_ehlo`` because
|
1. pytest :ref:`finds <test discovery>` the ``test_ehlo`` because
|
||||||
of the ``test_`` prefix. The test function needs a function argument
|
of the ``test_`` prefix. The test function needs a function argument
|
||||||
named ``smtp``. A matching fixture function is discovered by
|
named ``smtp_connection``. A matching fixture function is discovered by
|
||||||
looking for a fixture-marked function named ``smtp``.
|
looking for a fixture-marked function named ``smtp_connection``.
|
||||||
|
|
||||||
2. ``smtp()`` is called to create an instance.
|
2. ``smtp_connection()`` is called to create an instance.
|
||||||
|
|
||||||
3. ``test_ehlo(<SMTP instance>)`` is called and fails in the last
|
3. ``test_ehlo(<smtp_connection instance>)`` is called and fails in the last
|
||||||
line of the test function.
|
line of the test function.
|
||||||
|
|
||||||
Note that if you misspell a function argument or want
|
Note that if you misspell a function argument or want
|
||||||
|
@ -167,10 +167,10 @@ Fixtures requiring network access depend on connectivity and are
|
||||||
usually time-expensive to create. Extending the previous example, we
|
usually time-expensive to create. Extending the previous example, we
|
||||||
can add a ``scope="module"`` parameter to the
|
can add a ``scope="module"`` parameter to the
|
||||||
:py:func:`@pytest.fixture <_pytest.python.fixture>` invocation
|
:py:func:`@pytest.fixture <_pytest.python.fixture>` invocation
|
||||||
to cause the decorated ``smtp`` fixture function to only be invoked once
|
to cause the decorated ``smtp_connection`` fixture function to only be invoked
|
||||||
per test *module* (the default is to invoke once per test *function*).
|
once per test *module* (the default is to invoke once per test *function*).
|
||||||
Multiple test functions in a test module will thus
|
Multiple test functions in a test module will thus
|
||||||
each receive the same ``smtp`` fixture instance, thus saving time.
|
each receive the same ``smtp_connection`` fixture instance, thus saving time.
|
||||||
|
|
||||||
The next example puts the fixture function into a separate ``conftest.py`` file
|
The next example puts the fixture function into a separate ``conftest.py`` file
|
||||||
so that tests from multiple test modules in the directory can
|
so that tests from multiple test modules in the directory can
|
||||||
|
@ -181,23 +181,24 @@ access the fixture function::
|
||||||
import smtplib
|
import smtplib
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def smtp():
|
def smtp_connection():
|
||||||
return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
||||||
|
|
||||||
The name of the fixture again is ``smtp`` and you can access its result by
|
The name of the fixture again is ``smtp_connection`` and you can access its
|
||||||
listing the name ``smtp`` as an input parameter in any test or fixture
|
result by listing the name ``smtp_connection`` as an input parameter in any
|
||||||
function (in or below the directory where ``conftest.py`` is located)::
|
test or fixture function (in or below the directory where ``conftest.py`` is
|
||||||
|
located)::
|
||||||
|
|
||||||
# content of test_module.py
|
# content of test_module.py
|
||||||
|
|
||||||
def test_ehlo(smtp):
|
def test_ehlo(smtp_connection):
|
||||||
response, msg = smtp.ehlo()
|
response, msg = smtp_connection.ehlo()
|
||||||
assert response == 250
|
assert response == 250
|
||||||
assert b"smtp.gmail.com" in msg
|
assert b"smtp.gmail.com" in msg
|
||||||
assert 0 # for demo purposes
|
assert 0 # for demo purposes
|
||||||
|
|
||||||
def test_noop(smtp):
|
def test_noop(smtp_connection):
|
||||||
response, msg = smtp.noop()
|
response, msg = smtp_connection.noop()
|
||||||
assert response == 250
|
assert response == 250
|
||||||
assert 0 # for demo purposes
|
assert 0 # for demo purposes
|
||||||
|
|
||||||
|
@ -215,10 +216,10 @@ inspect what is going on and can now run the tests::
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
________________________________ test_ehlo _________________________________
|
________________________________ test_ehlo _________________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP object at 0xdeadbeef>
|
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
|
||||||
|
|
||||||
def test_ehlo(smtp):
|
def test_ehlo(smtp_connection):
|
||||||
response, msg = smtp.ehlo()
|
response, msg = smtp_connection.ehlo()
|
||||||
assert response == 250
|
assert response == 250
|
||||||
assert b"smtp.gmail.com" in msg
|
assert b"smtp.gmail.com" in msg
|
||||||
> assert 0 # for demo purposes
|
> assert 0 # for demo purposes
|
||||||
|
@ -227,10 +228,10 @@ inspect what is going on and can now run the tests::
|
||||||
test_module.py:6: AssertionError
|
test_module.py:6: AssertionError
|
||||||
________________________________ test_noop _________________________________
|
________________________________ test_noop _________________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP object at 0xdeadbeef>
|
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
|
||||||
|
|
||||||
def test_noop(smtp):
|
def test_noop(smtp_connection):
|
||||||
response, msg = smtp.noop()
|
response, msg = smtp_connection.noop()
|
||||||
assert response == 250
|
assert response == 250
|
||||||
> assert 0 # for demo purposes
|
> assert 0 # for demo purposes
|
||||||
E assert 0
|
E assert 0
|
||||||
|
@ -239,18 +240,18 @@ inspect what is going on and can now run the tests::
|
||||||
========================= 2 failed in 0.12 seconds =========================
|
========================= 2 failed in 0.12 seconds =========================
|
||||||
|
|
||||||
You see the two ``assert 0`` failing and more importantly you can also see
|
You see the two ``assert 0`` failing and more importantly you can also see
|
||||||
that the same (module-scoped) ``smtp`` object was passed into the two
|
that the same (module-scoped) ``smtp_connection`` object was passed into the
|
||||||
test functions because pytest shows the incoming argument values in the
|
two test functions because pytest shows the incoming argument values in the
|
||||||
traceback. As a result, the two test functions using ``smtp`` run as
|
traceback. As a result, the two test functions using ``smtp_connection`` run
|
||||||
quick as a single one because they reuse the same instance.
|
as quick as a single one because they reuse the same instance.
|
||||||
|
|
||||||
If you decide that you rather want to have a session-scoped ``smtp``
|
If you decide that you rather want to have a session-scoped ``smtp_connection``
|
||||||
instance, you can simply declare it:
|
instance, you can simply declare it:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def smtp():
|
def smtp_connection():
|
||||||
# the returned fixture value will be shared for
|
# the returned fixture value will be shared for
|
||||||
# all tests needing it
|
# all tests needing it
|
||||||
...
|
...
|
||||||
|
@ -339,11 +340,11 @@ the code after the *yield* statement serves as the teardown code:
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def smtp():
|
def smtp_connection():
|
||||||
smtp = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
||||||
yield smtp # provide the fixture value
|
yield smtp_connection # provide the fixture value
|
||||||
print("teardown smtp")
|
print("teardown smtp")
|
||||||
smtp.close()
|
smtp_connection.close()
|
||||||
|
|
||||||
The ``print`` and ``smtp.close()`` statements will execute when the last test in
|
The ``print`` and ``smtp.close()`` statements will execute when the last test in
|
||||||
the module has finished execution, regardless of the exception status of the
|
the module has finished execution, regardless of the exception status of the
|
||||||
|
@ -356,7 +357,7 @@ Let's execute it::
|
||||||
|
|
||||||
2 failed in 0.12 seconds
|
2 failed in 0.12 seconds
|
||||||
|
|
||||||
We see that the ``smtp`` instance is finalized after the two
|
We see that the ``smtp_connection`` instance is finalized after the two
|
||||||
tests finished execution. Note that if we decorated our fixture
|
tests finished execution. Note that if we decorated our fixture
|
||||||
function with ``scope='function'`` then fixture setup and cleanup would
|
function with ``scope='function'`` then fixture setup and cleanup would
|
||||||
occur around each single test. In either case the test
|
occur around each single test. In either case the test
|
||||||
|
@ -374,13 +375,13 @@ Note that we can also seamlessly use the ``yield`` syntax with ``with`` statemen
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def smtp():
|
def smtp_connection():
|
||||||
with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp:
|
with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection:
|
||||||
yield smtp # provide the fixture value
|
yield smtp_connection # provide the fixture value
|
||||||
|
|
||||||
|
|
||||||
The ``smtp`` connection will be closed after the test finished execution
|
The ``smtp_connection`` connection will be closed after the test finished
|
||||||
because the ``smtp`` object automatically closes when
|
execution because the ``smtp_connection`` object automatically closes when
|
||||||
the ``with`` statement ends.
|
the ``with`` statement ends.
|
||||||
|
|
||||||
Note that if an exception happens during the *setup* code (before the ``yield`` keyword), the
|
Note that if an exception happens during the *setup* code (before the ``yield`` keyword), the
|
||||||
|
@ -390,7 +391,7 @@ An alternative option for executing *teardown* code is to
|
||||||
make use of the ``addfinalizer`` method of the `request-context`_ object to register
|
make use of the ``addfinalizer`` method of the `request-context`_ object to register
|
||||||
finalization functions.
|
finalization functions.
|
||||||
|
|
||||||
Here's the ``smtp`` fixture changed to use ``addfinalizer`` for cleanup:
|
Here's the ``smtp_connection`` fixture changed to use ``addfinalizer`` for cleanup:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
@ -400,15 +401,15 @@ Here's the ``smtp`` fixture changed to use ``addfinalizer`` for cleanup:
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def smtp(request):
|
def smtp_connection(request):
|
||||||
smtp = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
|
||||||
|
|
||||||
def fin():
|
def fin():
|
||||||
print("teardown smtp")
|
print("teardown smtp_connection")
|
||||||
smtp.close()
|
smtp_connection.close()
|
||||||
|
|
||||||
request.addfinalizer(fin)
|
request.addfinalizer(fin)
|
||||||
return smtp # provide the fixture value
|
return smtp_connection # provide the fixture value
|
||||||
|
|
||||||
|
|
||||||
Both ``yield`` and ``addfinalizer`` methods work similarly by calling their code after the test
|
Both ``yield`` and ``addfinalizer`` methods work similarly by calling their code after the test
|
||||||
|
@ -441,7 +442,7 @@ Fixtures can introspect the requesting test context
|
||||||
|
|
||||||
Fixture functions can accept the :py:class:`request <FixtureRequest>` object
|
Fixture functions can accept the :py:class:`request <FixtureRequest>` object
|
||||||
to introspect the "requesting" test function, class or module context.
|
to introspect the "requesting" test function, class or module context.
|
||||||
Further extending the previous ``smtp`` fixture example, let's
|
Further extending the previous ``smtp_connection`` fixture example, let's
|
||||||
read an optional server URL from the test module which uses our fixture::
|
read an optional server URL from the test module which uses our fixture::
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
|
@ -449,12 +450,12 @@ read an optional server URL from the test module which uses our fixture::
|
||||||
import smtplib
|
import smtplib
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def smtp(request):
|
def smtp_connection(request):
|
||||||
server = getattr(request.module, "smtpserver", "smtp.gmail.com")
|
server = getattr(request.module, "smtpserver", "smtp.gmail.com")
|
||||||
smtp = smtplib.SMTP(server, 587, timeout=5)
|
smtp_connection = smtplib.SMTP(server, 587, timeout=5)
|
||||||
yield smtp
|
yield smtp_connection
|
||||||
print ("finalizing %s (%s)" % (smtp, server))
|
print ("finalizing %s (%s)" % (smtp_connection, server))
|
||||||
smtp.close()
|
smtp_connection.close()
|
||||||
|
|
||||||
We use the ``request.module`` attribute to optionally obtain an
|
We use the ``request.module`` attribute to optionally obtain an
|
||||||
``smtpserver`` attribute from the test module. If we just execute
|
``smtpserver`` attribute from the test module. If we just execute
|
||||||
|
@ -472,8 +473,8 @@ server URL in its module namespace::
|
||||||
|
|
||||||
smtpserver = "mail.python.org" # will be read by smtp fixture
|
smtpserver = "mail.python.org" # will be read by smtp fixture
|
||||||
|
|
||||||
def test_showhelo(smtp):
|
def test_showhelo(smtp_connection):
|
||||||
assert 0, smtp.helo()
|
assert 0, smtp_connection.helo()
|
||||||
|
|
||||||
Running it::
|
Running it::
|
||||||
|
|
||||||
|
@ -482,13 +483,13 @@ Running it::
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
______________________________ test_showhelo _______________________________
|
______________________________ test_showhelo _______________________________
|
||||||
test_anothersmtp.py:5: in test_showhelo
|
test_anothersmtp.py:5: in test_showhelo
|
||||||
assert 0, smtp.helo()
|
assert 0, smtp_connection.helo()
|
||||||
E AssertionError: (250, b'mail.python.org')
|
E AssertionError: (250, b'mail.python.org')
|
||||||
E assert 0
|
E assert 0
|
||||||
------------------------- Captured stdout teardown -------------------------
|
------------------------- Captured stdout teardown -------------------------
|
||||||
finalizing <smtplib.SMTP object at 0xdeadbeef> (mail.python.org)
|
finalizing <smtplib.SMTP object at 0xdeadbeef> (mail.python.org)
|
||||||
|
|
||||||
voila! The ``smtp`` fixture function picked up our mail server name
|
voila! The ``smtp_connection`` fixture function picked up our mail server name
|
||||||
from the module namespace.
|
from the module namespace.
|
||||||
|
|
||||||
.. _`fixture-factory`:
|
.. _`fixture-factory`:
|
||||||
|
@ -557,7 +558,7 @@ write exhaustive functional tests for components which themselves can be
|
||||||
configured in multiple ways.
|
configured in multiple ways.
|
||||||
|
|
||||||
Extending the previous example, we can flag the fixture to create two
|
Extending the previous example, we can flag the fixture to create two
|
||||||
``smtp`` fixture instances which will cause all tests using the fixture
|
``smtp_connection`` fixture instances which will cause all tests using the fixture
|
||||||
to run twice. The fixture function gets access to each parameter
|
to run twice. The fixture function gets access to each parameter
|
||||||
through the special :py:class:`request <FixtureRequest>` object::
|
through the special :py:class:`request <FixtureRequest>` object::
|
||||||
|
|
||||||
|
@ -567,11 +568,11 @@ through the special :py:class:`request <FixtureRequest>` object::
|
||||||
|
|
||||||
@pytest.fixture(scope="module",
|
@pytest.fixture(scope="module",
|
||||||
params=["smtp.gmail.com", "mail.python.org"])
|
params=["smtp.gmail.com", "mail.python.org"])
|
||||||
def smtp(request):
|
def smtp_connection(request):
|
||||||
smtp = smtplib.SMTP(request.param, 587, timeout=5)
|
smtp_connection = smtplib.SMTP(request.param, 587, timeout=5)
|
||||||
yield smtp
|
yield smtp_connection
|
||||||
print ("finalizing %s" % smtp)
|
print("finalizing %s" % smtp_connection)
|
||||||
smtp.close()
|
smtp_connection.close()
|
||||||
|
|
||||||
The main change is the declaration of ``params`` with
|
The main change is the declaration of ``params`` with
|
||||||
:py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values
|
:py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values
|
||||||
|
@ -584,10 +585,10 @@ So let's just do another run::
|
||||||
================================= FAILURES =================================
|
================================= FAILURES =================================
|
||||||
________________________ test_ehlo[smtp.gmail.com] _________________________
|
________________________ test_ehlo[smtp.gmail.com] _________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP object at 0xdeadbeef>
|
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
|
||||||
|
|
||||||
def test_ehlo(smtp):
|
def test_ehlo(smtp_connection):
|
||||||
response, msg = smtp.ehlo()
|
response, msg = smtp_connection.ehlo()
|
||||||
assert response == 250
|
assert response == 250
|
||||||
assert b"smtp.gmail.com" in msg
|
assert b"smtp.gmail.com" in msg
|
||||||
> assert 0 # for demo purposes
|
> assert 0 # for demo purposes
|
||||||
|
@ -596,10 +597,10 @@ So let's just do another run::
|
||||||
test_module.py:6: AssertionError
|
test_module.py:6: AssertionError
|
||||||
________________________ test_noop[smtp.gmail.com] _________________________
|
________________________ test_noop[smtp.gmail.com] _________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP object at 0xdeadbeef>
|
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
|
||||||
|
|
||||||
def test_noop(smtp):
|
def test_noop(smtp_connection):
|
||||||
response, msg = smtp.noop()
|
response, msg = smtp_connection.noop()
|
||||||
assert response == 250
|
assert response == 250
|
||||||
> assert 0 # for demo purposes
|
> assert 0 # for demo purposes
|
||||||
E assert 0
|
E assert 0
|
||||||
|
@ -607,10 +608,10 @@ So let's just do another run::
|
||||||
test_module.py:11: AssertionError
|
test_module.py:11: AssertionError
|
||||||
________________________ test_ehlo[mail.python.org] ________________________
|
________________________ test_ehlo[mail.python.org] ________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP object at 0xdeadbeef>
|
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
|
||||||
|
|
||||||
def test_ehlo(smtp):
|
def test_ehlo(smtp_connection):
|
||||||
response, msg = smtp.ehlo()
|
response, msg = smtp_connection.ehlo()
|
||||||
assert response == 250
|
assert response == 250
|
||||||
> assert b"smtp.gmail.com" in msg
|
> assert b"smtp.gmail.com" in msg
|
||||||
E AssertionError: assert b'smtp.gmail.com' in b'mail.python.org\nPIPELINING\nSIZE 51200000\nETRN\nSTARTTLS\nAUTH DIGEST-MD5 NTLM CRAM-MD5\nENHANCEDSTATUSCODES\n8BITMIME\nDSN\nSMTPUTF8'
|
E AssertionError: assert b'smtp.gmail.com' in b'mail.python.org\nPIPELINING\nSIZE 51200000\nETRN\nSTARTTLS\nAUTH DIGEST-MD5 NTLM CRAM-MD5\nENHANCEDSTATUSCODES\n8BITMIME\nDSN\nSMTPUTF8'
|
||||||
|
@ -620,10 +621,10 @@ So let's just do another run::
|
||||||
finalizing <smtplib.SMTP object at 0xdeadbeef>
|
finalizing <smtplib.SMTP object at 0xdeadbeef>
|
||||||
________________________ test_noop[mail.python.org] ________________________
|
________________________ test_noop[mail.python.org] ________________________
|
||||||
|
|
||||||
smtp = <smtplib.SMTP object at 0xdeadbeef>
|
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
|
||||||
|
|
||||||
def test_noop(smtp):
|
def test_noop(smtp_connection):
|
||||||
response, msg = smtp.noop()
|
response, msg = smtp_connection.noop()
|
||||||
assert response == 250
|
assert response == 250
|
||||||
> assert 0 # for demo purposes
|
> assert 0 # for demo purposes
|
||||||
E assert 0
|
E assert 0
|
||||||
|
@ -634,7 +635,7 @@ So let's just do another run::
|
||||||
4 failed in 0.12 seconds
|
4 failed in 0.12 seconds
|
||||||
|
|
||||||
We see that our two test functions each ran twice, against the different
|
We see that our two test functions each ran twice, against the different
|
||||||
``smtp`` instances. Note also, that with the ``mail.python.org``
|
``smtp_connection`` instances. Note also, that with the ``mail.python.org``
|
||||||
connection the second test fails in ``test_ehlo`` because a
|
connection the second test fails in ``test_ehlo`` because a
|
||||||
different server string is expected than what arrived.
|
different server string is expected than what arrived.
|
||||||
|
|
||||||
|
@ -746,25 +747,25 @@ can use other fixtures themselves. This contributes to a modular design
|
||||||
of your fixtures and allows re-use of framework-specific fixtures across
|
of your fixtures and allows re-use of framework-specific fixtures across
|
||||||
many projects. As a simple example, we can extend the previous example
|
many projects. As a simple example, we can extend the previous example
|
||||||
and instantiate an object ``app`` where we stick the already defined
|
and instantiate an object ``app`` where we stick the already defined
|
||||||
``smtp`` resource into it::
|
``smtp_connection`` resource into it::
|
||||||
|
|
||||||
# content of test_appsetup.py
|
# content of test_appsetup.py
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
class App(object):
|
class App(object):
|
||||||
def __init__(self, smtp):
|
def __init__(self, smtp_connection):
|
||||||
self.smtp = smtp
|
self.smtp_connection = smtp_connection
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def app(smtp):
|
def app(smtp_connection):
|
||||||
return App(smtp)
|
return App(smtp_connection)
|
||||||
|
|
||||||
def test_smtp_exists(app):
|
def test_smtp_connection_exists(app):
|
||||||
assert app.smtp
|
assert app.smtp_connection
|
||||||
|
|
||||||
Here we declare an ``app`` fixture which receives the previously defined
|
Here we declare an ``app`` fixture which receives the previously defined
|
||||||
``smtp`` fixture and instantiates an ``App`` object with it. Let's run it::
|
``smtp_connection`` fixture and instantiates an ``App`` object with it. Let's run it::
|
||||||
|
|
||||||
$ pytest -v test_appsetup.py
|
$ pytest -v test_appsetup.py
|
||||||
=========================== test session starts ============================
|
=========================== test session starts ============================
|
||||||
|
@ -773,19 +774,19 @@ Here we declare an ``app`` fixture which receives the previously defined
|
||||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||||
collecting ... collected 2 items
|
collecting ... collected 2 items
|
||||||
|
|
||||||
test_appsetup.py::test_smtp_exists[smtp.gmail.com] PASSED [ 50%]
|
test_appsetup.py::test_smtp_connection_exists[smtp.gmail.com] PASSED [ 50%]
|
||||||
test_appsetup.py::test_smtp_exists[mail.python.org] PASSED [100%]
|
test_appsetup.py::test_smtp_connection_exists[mail.python.org] PASSED [100%]
|
||||||
|
|
||||||
========================= 2 passed in 0.12 seconds =========================
|
========================= 2 passed in 0.12 seconds =========================
|
||||||
|
|
||||||
Due to the parametrization of ``smtp`` the test will run twice with two
|
Due to the parametrization of ``smtp_connection`` the test will run twice with two
|
||||||
different ``App`` instances and respective smtp servers. There is no
|
different ``App`` instances and respective smtp servers. There is no
|
||||||
need for the ``app`` fixture to be aware of the ``smtp`` parametrization
|
need for the ``app`` fixture to be aware of the ``smtp_connection``
|
||||||
as pytest will fully analyse the fixture dependency graph.
|
parametrization as pytest will fully analyse the fixture dependency graph.
|
||||||
|
|
||||||
Note, that the ``app`` fixture has a scope of ``module`` and uses a
|
Note, that the ``app`` fixture has a scope of ``module`` and uses a
|
||||||
module-scoped ``smtp`` fixture. The example would still work if ``smtp``
|
module-scoped ``smtp_connection`` fixture. The example would still work if
|
||||||
was cached on a ``session`` scope: it is fine for fixtures to use
|
``smtp_connection`` was cached on a ``session`` scope: it is fine for fixtures to use
|
||||||
"broader" scoped fixtures but not the other way round:
|
"broader" scoped fixtures but not the other way round:
|
||||||
A session-scoped fixture could not use a module-scoped one in a
|
A session-scoped fixture could not use a module-scoped one in a
|
||||||
meaningful way.
|
meaningful way.
|
||||||
|
@ -959,7 +960,7 @@ a generic feature of the mark mechanism:
|
||||||
Note that the assigned variable *must* be called ``pytestmark``, assigning e.g.
|
Note that the assigned variable *must* be called ``pytestmark``, assigning e.g.
|
||||||
``foomark`` will not activate the fixtures.
|
``foomark`` will not activate the fixtures.
|
||||||
|
|
||||||
Lastly you can put fixtures required by all tests in your project
|
It is also possible to put fixtures required by all tests in your project
|
||||||
into an ini-file:
|
into an ini-file:
|
||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
|
@ -969,6 +970,22 @@ into an ini-file:
|
||||||
usefixtures = cleandir
|
usefixtures = cleandir
|
||||||
|
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
Note this mark has no effect in **fixture functions**. For example,
|
||||||
|
this **will not work as expected**:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("my_other_fixture")
|
||||||
|
@pytest.fixture
|
||||||
|
def my_fixture_that_sadly_wont_use_my_other_fixture():
|
||||||
|
...
|
||||||
|
|
||||||
|
Currently this will not generate any error or warning, but this is intended
|
||||||
|
to be handled by `#3664 <https://github.com/pytest-dev/pytest/issues/3664>`_.
|
||||||
|
|
||||||
|
|
||||||
.. _`autouse`:
|
.. _`autouse`:
|
||||||
.. _`autouse fixtures`:
|
.. _`autouse fixtures`:
|
||||||
|
|
||||||
|
|
|
@ -187,7 +187,7 @@ You can then install your package in "editable" mode::
|
||||||
pip install -e .
|
pip install -e .
|
||||||
|
|
||||||
which lets you change your source code (both tests and application) and rerun tests at will.
|
which lets you change your source code (both tests and application) and rerun tests at will.
|
||||||
This is similar to running `python setup.py develop` or `conda develop` in that it installs
|
This is similar to running ``python setup.py develop`` or ``conda develop`` in that it installs
|
||||||
your package using a symlink to your development code.
|
your package using a symlink to your development code.
|
||||||
|
|
||||||
Once you are done with your work and want to make sure that your actual
|
Once you are done with your work and want to make sure that your actual
|
||||||
|
|
|
@ -52,8 +52,6 @@ should add ``--strict`` to ``addopts``:
|
||||||
serial
|
serial
|
||||||
|
|
||||||
|
|
||||||
.. `marker-iteration`
|
|
||||||
|
|
||||||
Marker revamp and iteration
|
Marker revamp and iteration
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
|
|
@ -161,6 +161,25 @@ Skip a test function if a condition is ``True``.
|
||||||
:keyword str reason: Reason why the test function is being skipped.
|
:keyword str reason: Reason why the test function is being skipped.
|
||||||
|
|
||||||
|
|
||||||
|
.. _`pytest.mark.usefixtures ref`:
|
||||||
|
|
||||||
|
pytest.mark.usefixtures
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
**Tutorial**: :ref:`usefixtures`.
|
||||||
|
|
||||||
|
Mark a test function as using the given fixture names.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
This mark can be used with *test functions* only, having no affect when applied
|
||||||
|
to a **fixture** function.
|
||||||
|
|
||||||
|
.. py:function:: pytest.mark.usefixtures(*names)
|
||||||
|
|
||||||
|
:param args: the names of the fixture to use, as strings
|
||||||
|
|
||||||
|
|
||||||
.. _`pytest.mark.xfail ref`:
|
.. _`pytest.mark.xfail ref`:
|
||||||
|
|
||||||
pytest.mark.xfail
|
pytest.mark.xfail
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
"""
|
"""
|
||||||
Invoke development tasks.
|
Invoke development tasks.
|
||||||
"""
|
"""
|
||||||
|
import argparse
|
||||||
|
from colorama import init, Fore
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from subprocess import check_output, check_call
|
from subprocess import check_output, check_call, call
|
||||||
|
|
||||||
import invoke
|
|
||||||
|
|
||||||
|
|
||||||
@invoke.task(help={"version": "version being released"})
|
def announce(version):
|
||||||
def announce(ctx, version):
|
|
||||||
"""Generates a new release announcement entry in the docs."""
|
"""Generates a new release announcement entry in the docs."""
|
||||||
# Get our list of authors
|
# Get our list of authors
|
||||||
stdout = check_output(["git", "describe", "--abbrev=0", "--tags"])
|
stdout = check_output(["git", "describe", "--abbrev=0", "--tags"])
|
||||||
|
@ -38,7 +37,7 @@ def announce(ctx, version):
|
||||||
"../doc/en/announce/release-{}.rst".format(version)
|
"../doc/en/announce/release-{}.rst".format(version)
|
||||||
)
|
)
|
||||||
target.write_text(text, encoding="UTF-8")
|
target.write_text(text, encoding="UTF-8")
|
||||||
print("[generate.announce] Generated {}".format(target.name))
|
print(f"{Fore.CYAN}[generate.announce] {Fore.RESET}Generated {target.name}")
|
||||||
|
|
||||||
# Update index with the new release entry
|
# Update index with the new release entry
|
||||||
index_path = Path(__file__).parent.joinpath("../doc/en/announce/index.rst")
|
index_path = Path(__file__).parent.joinpath("../doc/en/announce/index.rst")
|
||||||
|
@ -50,69 +49,63 @@ def announce(ctx, version):
|
||||||
if line != new_line:
|
if line != new_line:
|
||||||
lines.insert(index, new_line)
|
lines.insert(index, new_line)
|
||||||
index_path.write_text("\n".join(lines) + "\n", encoding="UTF-8")
|
index_path.write_text("\n".join(lines) + "\n", encoding="UTF-8")
|
||||||
print("[generate.announce] Updated {}".format(index_path.name))
|
print(
|
||||||
|
f"{Fore.CYAN}[generate.announce] {Fore.RESET}Updated {index_path.name}"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
print(
|
print(
|
||||||
"[generate.announce] Skip {} (already contains release)".format(
|
f"{Fore.CYAN}[generate.announce] {Fore.RESET}Skip {index_path.name} (already contains release)"
|
||||||
index_path.name
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
check_call(["git", "add", str(target)])
|
check_call(["git", "add", str(target)])
|
||||||
|
|
||||||
|
|
||||||
@invoke.task()
|
def regen():
|
||||||
def regen(ctx):
|
|
||||||
"""Call regendoc tool to update examples and pytest output in the docs."""
|
"""Call regendoc tool to update examples and pytest output in the docs."""
|
||||||
print("[generate.regen] Updating docs")
|
print(f"{Fore.CYAN}[generate.regen] {Fore.RESET}Updating docs")
|
||||||
check_call(["tox", "-e", "regen"])
|
check_call(["tox", "-e", "regen"])
|
||||||
|
|
||||||
|
|
||||||
@invoke.task()
|
def fix_formatting():
|
||||||
def make_tag(ctx, version):
|
"""Runs pre-commit in all files to ensure they are formatted correctly"""
|
||||||
"""Create a new, local tag for the release, only if the repository is clean."""
|
print(
|
||||||
from git import Repo
|
f"{Fore.CYAN}[generate.fix linting] {Fore.RESET}Fixing formatting using pre-commit"
|
||||||
|
)
|
||||||
repo = Repo(".")
|
call(["pre-commit", "run", "--all-files"])
|
||||||
if repo.is_dirty():
|
|
||||||
print("Current repository is dirty. Please commit any changes and try again.")
|
|
||||||
raise invoke.Exit(code=2)
|
|
||||||
|
|
||||||
tag_names = [x.name for x in repo.tags]
|
|
||||||
if version in tag_names:
|
|
||||||
print("[generate.make_tag] Delete existing tag {}".format(version))
|
|
||||||
repo.delete_tag(version)
|
|
||||||
|
|
||||||
print("[generate.make_tag] Create tag {}".format(version))
|
|
||||||
repo.create_tag(version)
|
|
||||||
|
|
||||||
|
|
||||||
@invoke.task(help={"version": "version being released"})
|
def pre_release(version):
|
||||||
def pre_release(ctx, version):
|
|
||||||
"""Generates new docs, release announcements and creates a local tag."""
|
"""Generates new docs, release announcements and creates a local tag."""
|
||||||
announce(ctx, version)
|
announce(version)
|
||||||
regen(ctx)
|
regen()
|
||||||
changelog(ctx, version, write_out=True)
|
changelog(version, write_out=True)
|
||||||
|
fix_formatting()
|
||||||
|
|
||||||
msg = "Preparing release version {}".format(version)
|
msg = "Preparing release version {}".format(version)
|
||||||
check_call(["git", "commit", "-a", "-m", msg])
|
check_call(["git", "commit", "-a", "-m", msg])
|
||||||
|
|
||||||
make_tag(ctx, version)
|
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print("[generate.pre_release] Please push your branch and open a PR.")
|
print(f"{Fore.CYAN}[generate.pre_release] {Fore.GREEN}All done!")
|
||||||
|
print()
|
||||||
|
print(f"Please push your branch and open a PR.")
|
||||||
|
|
||||||
|
|
||||||
@invoke.task(
|
def changelog(version, write_out=False):
|
||||||
help={
|
|
||||||
"version": "version being released",
|
|
||||||
"write_out": "write changes to the actual changelog",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
def changelog(ctx, version, write_out=False):
|
|
||||||
if write_out:
|
if write_out:
|
||||||
addopts = []
|
addopts = []
|
||||||
else:
|
else:
|
||||||
addopts = ["--draft"]
|
addopts = ["--draft"]
|
||||||
check_call(["towncrier", "--yes", "--version", version] + addopts)
|
check_call(["towncrier", "--yes", "--version", version] + addopts)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
init(autoreset=True)
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("version", help="Release version")
|
||||||
|
options = parser.parse_args()
|
||||||
|
pre_release(options.version)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -1,4 +1,4 @@
|
||||||
from __future__ import absolute_import, division, generators, print_function
|
from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
import ast
|
import ast
|
||||||
from ast import PyCF_ONLY_AST as _AST_FLAG
|
from ast import PyCF_ONLY_AST as _AST_FLAG
|
||||||
|
@ -152,12 +152,7 @@ class Source(object):
|
||||||
return "\n".join(self.lines)
|
return "\n".join(self.lines)
|
||||||
|
|
||||||
def compile(
|
def compile(
|
||||||
self,
|
self, filename=None, mode="exec", flag=0, dont_inherit=0, _genframe=None
|
||||||
filename=None,
|
|
||||||
mode="exec",
|
|
||||||
flag=generators.compiler_flag,
|
|
||||||
dont_inherit=0,
|
|
||||||
_genframe=None,
|
|
||||||
):
|
):
|
||||||
""" return compiled code object. if filename is None
|
""" return compiled code object. if filename is None
|
||||||
invent an artificial filename which displays
|
invent an artificial filename which displays
|
||||||
|
@ -201,9 +196,7 @@ class Source(object):
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
def compile_(
|
def compile_(source, filename=None, mode="exec", flags=0, dont_inherit=0):
|
||||||
source, filename=None, mode="exec", flags=generators.compiler_flag, dont_inherit=0
|
|
||||||
):
|
|
||||||
""" compile the given source to a raw code object,
|
""" compile the given source to a raw code object,
|
||||||
and maintain an internal cache which allows later
|
and maintain an internal cache which allows later
|
||||||
retrieval of the source code for the code object
|
retrieval of the source code for the code object
|
||||||
|
|
|
@ -47,8 +47,8 @@ else:
|
||||||
|
|
||||||
|
|
||||||
if _PY3:
|
if _PY3:
|
||||||
from collections.abc import MutableMapping as MappingMixin # noqa
|
from collections.abc import MutableMapping as MappingMixin
|
||||||
from collections.abc import Mapping, Sequence # noqa
|
from collections.abc import Mapping, Sequence
|
||||||
else:
|
else:
|
||||||
# those raise DeprecationWarnings in Python >=3.7
|
# those raise DeprecationWarnings in Python >=3.7
|
||||||
from collections import MutableMapping as MappingMixin # noqa
|
from collections import MutableMapping as MappingMixin # noqa
|
||||||
|
|
|
@ -71,7 +71,7 @@ def main(args=None, plugins=None):
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
|
|
||||||
class cmdline(object): # NOQA compatibility namespace
|
class cmdline(object): # compatibility namespace
|
||||||
main = staticmethod(main)
|
main = staticmethod(main)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,19 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
|
||||||
]
|
]
|
||||||
del argvalues
|
del argvalues
|
||||||
|
|
||||||
if not parameters:
|
if parameters:
|
||||||
|
# check all parameter sets have the correct number of values
|
||||||
|
for param in parameters:
|
||||||
|
if len(param.values) != len(argnames):
|
||||||
|
raise ValueError(
|
||||||
|
'In "parametrize" the number of values ({}) must be '
|
||||||
|
"equal to the number of names ({})".format(
|
||||||
|
param.values, argnames
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# empty parameter set (likely computed at runtime): create a single
|
||||||
|
# parameter set with NOSET values, with the "empty parameter set" mark applied to it
|
||||||
mark = get_empty_parameterset_mark(config, argnames, func)
|
mark = get_empty_parameterset_mark(config, argnames, func)
|
||||||
parameters.append(
|
parameters.append(
|
||||||
ParameterSet(values=(NOTSET,) * len(argnames), marks=[mark], id=None)
|
ParameterSet(values=(NOTSET,) * len(argnames), marks=[mark], id=None)
|
||||||
|
@ -124,9 +136,9 @@ class Mark(object):
|
||||||
#: name of the mark
|
#: name of the mark
|
||||||
name = attr.ib(type=str)
|
name = attr.ib(type=str)
|
||||||
#: positional arguments of the mark decorator
|
#: positional arguments of the mark decorator
|
||||||
args = attr.ib(type="List[object]")
|
args = attr.ib() # type: List[object]
|
||||||
#: keyword arguments of the mark decorator
|
#: keyword arguments of the mark decorator
|
||||||
kwargs = attr.ib(type="Dict[str, object]")
|
kwargs = attr.ib() # type: Dict[str, object]
|
||||||
|
|
||||||
def combined_with(self, other):
|
def combined_with(self, other):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -23,11 +23,6 @@ from _pytest.main import Session, EXIT_OK
|
||||||
from _pytest.assertion.rewrite import AssertionRewritingHook
|
from _pytest.assertion.rewrite import AssertionRewritingHook
|
||||||
from _pytest.compat import Path
|
from _pytest.compat import Path
|
||||||
|
|
||||||
PYTEST_FULLPATH = os.path.abspath(pytest.__file__.rstrip("oc")).replace(
|
|
||||||
"$py.class", ".py"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
IGNORE_PAM = [ # filenames added when obtaining details about the current user
|
IGNORE_PAM = [ # filenames added when obtaining details about the current user
|
||||||
u"/var/lib/sss/mc/passwd"
|
u"/var/lib/sss/mc/passwd"
|
||||||
]
|
]
|
||||||
|
@ -1077,9 +1072,7 @@ class Testdir(object):
|
||||||
print("couldn't print to %s because of encoding" % (fp,))
|
print("couldn't print to %s because of encoding" % (fp,))
|
||||||
|
|
||||||
def _getpytestargs(self):
|
def _getpytestargs(self):
|
||||||
# we cannot use `(sys.executable, script)` because on Windows the
|
return (sys.executable, "-mpytest")
|
||||||
# script is e.g. `pytest.exe`
|
|
||||||
return (sys.executable, PYTEST_FULLPATH) # noqa
|
|
||||||
|
|
||||||
def runpython(self, script):
|
def runpython(self, script):
|
||||||
"""Run a python script using sys.executable as interpreter.
|
"""Run a python script using sys.executable as interpreter.
|
||||||
|
|
|
@ -8,7 +8,6 @@ import os
|
||||||
import collections
|
import collections
|
||||||
import warnings
|
import warnings
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
from itertools import count
|
|
||||||
|
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
@ -945,46 +944,54 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
||||||
"""
|
"""
|
||||||
from _pytest.fixtures import scope2index
|
from _pytest.fixtures import scope2index
|
||||||
from _pytest.mark import ParameterSet
|
from _pytest.mark import ParameterSet
|
||||||
from py.io import saferepr
|
|
||||||
|
|
||||||
argnames, parameters = ParameterSet._for_parametrize(
|
argnames, parameters = ParameterSet._for_parametrize(
|
||||||
argnames, argvalues, self.function, self.config
|
argnames, argvalues, self.function, self.config
|
||||||
)
|
)
|
||||||
del argvalues
|
del argvalues
|
||||||
default_arg_names = set(get_default_arg_names(self.function))
|
|
||||||
|
|
||||||
if scope is None:
|
if scope is None:
|
||||||
scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect)
|
scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect)
|
||||||
|
|
||||||
scopenum = scope2index(scope, descr="call to {}".format(self.parametrize))
|
self._validate_if_using_arg_names(argnames, indirect)
|
||||||
valtypes = {}
|
|
||||||
for arg in argnames:
|
arg_values_types = self._resolve_arg_value_types(argnames, indirect)
|
||||||
if arg not in self.fixturenames:
|
|
||||||
if arg in default_arg_names:
|
ids = self._resolve_arg_ids(argnames, ids, parameters)
|
||||||
raise ValueError(
|
|
||||||
"%r already takes an argument %r with a default value"
|
scopenum = scope2index(scope, descr="call to {}".format(self.parametrize))
|
||||||
% (self.function, arg)
|
|
||||||
)
|
# create the new calls: if we are parametrize() multiple times (by applying the decorator
|
||||||
else:
|
# more than once) then we accumulate those calls generating the cartesian product
|
||||||
if isinstance(indirect, (tuple, list)):
|
# of all calls
|
||||||
name = "fixture" if arg in indirect else "argument"
|
newcalls = []
|
||||||
else:
|
for callspec in self._calls or [CallSpec2(self)]:
|
||||||
name = "fixture" if indirect else "argument"
|
for param_index, (param_id, param_set) in enumerate(zip(ids, parameters)):
|
||||||
raise ValueError("%r uses no %s %r" % (self.function, name, arg))
|
newcallspec = callspec.copy()
|
||||||
|
newcallspec.setmulti2(
|
||||||
|
arg_values_types,
|
||||||
|
argnames,
|
||||||
|
param_set.values,
|
||||||
|
param_id,
|
||||||
|
param_set.marks,
|
||||||
|
scopenum,
|
||||||
|
param_index,
|
||||||
|
)
|
||||||
|
newcalls.append(newcallspec)
|
||||||
|
self._calls = newcalls
|
||||||
|
|
||||||
|
def _resolve_arg_ids(self, argnames, ids, parameters):
|
||||||
|
"""Resolves the actual ids for the given argnames, based on the ``ids`` parameter given
|
||||||
|
to ``parametrize``.
|
||||||
|
|
||||||
|
:param List[str] argnames: list of argument names passed to ``parametrize()``.
|
||||||
|
:param ids: the ids parameter of the parametrized call (see docs).
|
||||||
|
:param List[ParameterSet] parameters: the list of parameter values, same size as ``argnames``.
|
||||||
|
:rtype: List[str]
|
||||||
|
:return: the list of ids for each argname given
|
||||||
|
"""
|
||||||
|
from py.io import saferepr
|
||||||
|
|
||||||
if indirect is True:
|
|
||||||
valtypes = dict.fromkeys(argnames, "params")
|
|
||||||
elif indirect is False:
|
|
||||||
valtypes = dict.fromkeys(argnames, "funcargs")
|
|
||||||
elif isinstance(indirect, (tuple, list)):
|
|
||||||
valtypes = dict.fromkeys(argnames, "funcargs")
|
|
||||||
for arg in indirect:
|
|
||||||
if arg not in argnames:
|
|
||||||
raise ValueError(
|
|
||||||
"indirect given to %r: fixture %r doesn't exist"
|
|
||||||
% (self.function, arg)
|
|
||||||
)
|
|
||||||
valtypes[arg] = "params"
|
|
||||||
idfn = None
|
idfn = None
|
||||||
if callable(ids):
|
if callable(ids):
|
||||||
idfn = ids
|
idfn = ids
|
||||||
|
@ -1001,29 +1008,57 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
||||||
msg % (saferepr(id_value), type(id_value).__name__)
|
msg % (saferepr(id_value), type(id_value).__name__)
|
||||||
)
|
)
|
||||||
ids = idmaker(argnames, parameters, idfn, ids, self.config)
|
ids = idmaker(argnames, parameters, idfn, ids, self.config)
|
||||||
newcalls = []
|
return ids
|
||||||
for callspec in self._calls or [CallSpec2(self)]:
|
|
||||||
elements = zip(ids, parameters, count())
|
def _resolve_arg_value_types(self, argnames, indirect):
|
||||||
for a_id, param, param_index in elements:
|
"""Resolves if each parametrized argument must be considered a parameter to a fixture or a "funcarg"
|
||||||
if len(param.values) != len(argnames):
|
to the function, based on the ``indirect`` parameter of the parametrized() call.
|
||||||
|
|
||||||
|
:param List[str] argnames: list of argument names passed to ``parametrize()``.
|
||||||
|
:param indirect: same ``indirect`` parameter of ``parametrize()``.
|
||||||
|
:rtype: Dict[str, str]
|
||||||
|
A dict mapping each arg name to either:
|
||||||
|
* "params" if the argname should be the parameter of a fixture of the same name.
|
||||||
|
* "funcargs" if the argname should be a parameter to the parametrized test function.
|
||||||
|
"""
|
||||||
|
valtypes = {}
|
||||||
|
if indirect is True:
|
||||||
|
valtypes = dict.fromkeys(argnames, "params")
|
||||||
|
elif indirect is False:
|
||||||
|
valtypes = dict.fromkeys(argnames, "funcargs")
|
||||||
|
elif isinstance(indirect, (tuple, list)):
|
||||||
|
valtypes = dict.fromkeys(argnames, "funcargs")
|
||||||
|
for arg in indirect:
|
||||||
|
if arg not in argnames:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
'In "parametrize" the number of values ({}) must be '
|
"indirect given to %r: fixture %r doesn't exist"
|
||||||
"equal to the number of names ({})".format(
|
% (self.function, arg)
|
||||||
param.values, argnames
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
newcallspec = callspec.copy()
|
valtypes[arg] = "params"
|
||||||
newcallspec.setmulti2(
|
return valtypes
|
||||||
valtypes,
|
|
||||||
argnames,
|
def _validate_if_using_arg_names(self, argnames, indirect):
|
||||||
param.values,
|
"""
|
||||||
a_id,
|
Check if all argnames are being used, by default values, or directly/indirectly.
|
||||||
param.marks,
|
|
||||||
scopenum,
|
:param List[str] argnames: list of argument names passed to ``parametrize()``.
|
||||||
param_index,
|
:param indirect: same ``indirect`` parameter of ``parametrize()``.
|
||||||
)
|
:raise ValueError: if validation fails.
|
||||||
newcalls.append(newcallspec)
|
"""
|
||||||
self._calls = newcalls
|
default_arg_names = set(get_default_arg_names(self.function))
|
||||||
|
for arg in argnames:
|
||||||
|
if arg not in self.fixturenames:
|
||||||
|
if arg in default_arg_names:
|
||||||
|
raise ValueError(
|
||||||
|
"%r already takes an argument %r with a default value"
|
||||||
|
% (self.function, arg)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if isinstance(indirect, (tuple, list)):
|
||||||
|
name = "fixture" if arg in indirect else "argument"
|
||||||
|
else:
|
||||||
|
name = "fixture" if indirect else "argument"
|
||||||
|
raise ValueError("%r uses no %s %r" % (self.function, name, arg))
|
||||||
|
|
||||||
def addcall(self, funcargs=None, id=NOTSET, param=NOTSET):
|
def addcall(self, funcargs=None, id=NOTSET, param=NOTSET):
|
||||||
""" Add a new call to the underlying test function during the collection phase of a test run.
|
""" Add a new call to the underlying test function during the collection phase of a test run.
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
"""
|
|
||||||
Invoke tasks to help with pytest development and release process.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import invoke
|
|
||||||
|
|
||||||
from . import generate
|
|
||||||
|
|
||||||
|
|
||||||
ns = invoke.Collection(generate)
|
|
|
@ -1,6 +0,0 @@
|
||||||
-e .
|
|
||||||
gitpython
|
|
||||||
invoke
|
|
||||||
towncrier
|
|
||||||
tox
|
|
||||||
wheel
|
|
|
@ -744,3 +744,19 @@ something
|
||||||
'''"""
|
'''"""
|
||||||
result = getstatement(1, source)
|
result = getstatement(1, source)
|
||||||
assert str(result) == "'''\n'''"
|
assert str(result) == "'''\n'''"
|
||||||
|
|
||||||
|
|
||||||
|
def test_getstartingblock_multiline():
|
||||||
|
class A(object):
|
||||||
|
def __init__(self, *args):
|
||||||
|
frame = sys._getframe(1)
|
||||||
|
self.source = _pytest._code.Frame(frame).statement
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
|
x = A('x',
|
||||||
|
'y'
|
||||||
|
,
|
||||||
|
'z')
|
||||||
|
# fmt: on
|
||||||
|
values = [i for i in x.source.lines if i.strip()]
|
||||||
|
assert len(values) == 4
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
# flake8: noqa
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import _pytest._code
|
|
||||||
|
|
||||||
|
|
||||||
def test_getstartingblock_multiline():
|
|
||||||
"""
|
|
||||||
This test was originally found in test_source.py, but it depends on the weird
|
|
||||||
formatting of the ``x = A`` construct seen here and our autopep8 tool can only exclude entire
|
|
||||||
files (it does not support excluding lines/blocks using the traditional #noqa comment yet,
|
|
||||||
see hhatto/autopep8#307). It was considered better to just move this single test to its own
|
|
||||||
file and exclude it from autopep8 than try to complicate things.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class A(object):
|
|
||||||
def __init__(self, *args):
|
|
||||||
frame = sys._getframe(1)
|
|
||||||
self.source = _pytest._code.Frame(frame).statement
|
|
||||||
|
|
||||||
# fmt: off
|
|
||||||
x = A('x',
|
|
||||||
'y'
|
|
||||||
,
|
|
||||||
'z')
|
|
||||||
# fmt: on
|
|
||||||
values = [i for i in x.source.lines if i.strip()]
|
|
||||||
assert len(values) == 4
|
|
|
@ -33,8 +33,7 @@ class TestRaises(object):
|
||||||
def test_raises_as_contextmanager(self, testdir):
|
def test_raises_as_contextmanager(self, testdir):
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
from __future__ import with_statement
|
import pytest
|
||||||
import py, pytest
|
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
|
|
||||||
def test_simple():
|
def test_simple():
|
||||||
|
|
|
@ -97,7 +97,7 @@ class TestAssertionRewrite(object):
|
||||||
assert imp.lineno == 2
|
assert imp.lineno == 2
|
||||||
assert imp.col_offset == 0
|
assert imp.col_offset == 0
|
||||||
assert isinstance(m.body[2], ast.Assign)
|
assert isinstance(m.body[2], ast.Assign)
|
||||||
s = """from __future__ import with_statement\nother_stuff"""
|
s = """from __future__ import division\nother_stuff"""
|
||||||
m = rewrite(s)
|
m = rewrite(s)
|
||||||
assert isinstance(m.body[0], ast.ImportFrom)
|
assert isinstance(m.body[0], ast.ImportFrom)
|
||||||
for imp in m.body[1:3]:
|
for imp in m.body[1:3]:
|
||||||
|
@ -105,7 +105,7 @@ class TestAssertionRewrite(object):
|
||||||
assert imp.lineno == 2
|
assert imp.lineno == 2
|
||||||
assert imp.col_offset == 0
|
assert imp.col_offset == 0
|
||||||
assert isinstance(m.body[3], ast.Expr)
|
assert isinstance(m.body[3], ast.Expr)
|
||||||
s = """'doc string'\nfrom __future__ import with_statement"""
|
s = """'doc string'\nfrom __future__ import division"""
|
||||||
m = rewrite(s)
|
m = rewrite(s)
|
||||||
adjust_body_for_new_docstring_in_module_node(m)
|
adjust_body_for_new_docstring_in_module_node(m)
|
||||||
assert isinstance(m.body[0], ast.ImportFrom)
|
assert isinstance(m.body[0], ast.ImportFrom)
|
||||||
|
@ -113,7 +113,7 @@ class TestAssertionRewrite(object):
|
||||||
assert isinstance(imp, ast.Import)
|
assert isinstance(imp, ast.Import)
|
||||||
assert imp.lineno == 2
|
assert imp.lineno == 2
|
||||||
assert imp.col_offset == 0
|
assert imp.col_offset == 0
|
||||||
s = """'doc string'\nfrom __future__ import with_statement\nother"""
|
s = """'doc string'\nfrom __future__ import division\nother"""
|
||||||
m = rewrite(s)
|
m = rewrite(s)
|
||||||
adjust_body_for_new_docstring_in_module_node(m)
|
adjust_body_for_new_docstring_in_module_node(m)
|
||||||
assert isinstance(m.body[0], ast.ImportFrom)
|
assert isinstance(m.body[0], ast.ImportFrom)
|
||||||
|
@ -941,7 +941,7 @@ class TestAssertionRewriteHookDetails(object):
|
||||||
e = IOError()
|
e = IOError()
|
||||||
e.errno = 10
|
e.errno = 10
|
||||||
raise e
|
raise e
|
||||||
yield # noqa
|
yield
|
||||||
|
|
||||||
monkeypatch.setattr(atomicwrites, "atomic_write", atomic_write_failed)
|
monkeypatch.setattr(atomicwrites, "atomic_write", atomic_write_failed)
|
||||||
assert not _write_pyc(state, [1], source_path.stat(), pycpath)
|
assert not _write_pyc(state, [1], source_path.stat(), pycpath)
|
||||||
|
|
|
@ -2,7 +2,6 @@ from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
# note: py.io capture tests where copied from
|
# note: py.io capture tests where copied from
|
||||||
# pylib 1.4.20.dev2 (rev 13d9af95547e)
|
# pylib 1.4.20.dev2 (rev 13d9af95547e)
|
||||||
from __future__ import with_statement
|
|
||||||
import pickle
|
import pickle
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
|
@ -364,10 +364,7 @@ class TestSysPathsSnapshot(object):
|
||||||
original = list(sys_path)
|
original = list(sys_path)
|
||||||
original_other = list(getattr(sys, other_path_type))
|
original_other = list(getattr(sys, other_path_type))
|
||||||
snapshot = SysPathsSnapshot()
|
snapshot = SysPathsSnapshot()
|
||||||
transformation = {
|
transformation = {"source": (0, 1, 2, 3, 4, 5), "target": (6, 2, 9, 7, 5, 8)}
|
||||||
"source": (0, 1, 2, 3, 4, 5),
|
|
||||||
"target": (6, 2, 9, 7, 5, 8),
|
|
||||||
} # noqa: E201
|
|
||||||
assert sys_path == [self.path(x) for x in transformation["source"]]
|
assert sys_path == [self.path(x) for x in transformation["source"]]
|
||||||
sys_path[1] = self.path(6)
|
sys_path[1] = self.path(6)
|
||||||
sys_path[3] = self.path(7)
|
sys_path[3] = self.path(7)
|
||||||
|
@ -394,3 +391,8 @@ class TestSysPathsSnapshot(object):
|
||||||
assert getattr(sys, path_type) == original_data
|
assert getattr(sys, path_type) == original_data
|
||||||
assert getattr(sys, other_path_type) is original_other
|
assert getattr(sys, other_path_type) is original_other
|
||||||
assert getattr(sys, other_path_type) == original_other_data
|
assert getattr(sys, other_path_type) == original_other_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_testdir_subprocess(testdir):
|
||||||
|
testfile = testdir.makepyfile("def test_one(): pass")
|
||||||
|
assert testdir.runpytest_subprocess(testfile).ret == 0
|
||||||
|
|
22
tox.ini
22
tox.ini
|
@ -151,14 +151,6 @@ commands =
|
||||||
rm -rf /tmp/doc-exec*
|
rm -rf /tmp/doc-exec*
|
||||||
make regen
|
make regen
|
||||||
|
|
||||||
[testenv:fix-lint]
|
|
||||||
skipsdist = True
|
|
||||||
usedevelop = True
|
|
||||||
deps =
|
|
||||||
autopep8
|
|
||||||
commands =
|
|
||||||
autopep8 --in-place -r --max-line-length=120 --exclude=test_source_multiline_block.py _pytest testing setup.py pytest.py
|
|
||||||
|
|
||||||
[testenv:jython]
|
[testenv:jython]
|
||||||
changedir = testing
|
changedir = testing
|
||||||
commands =
|
commands =
|
||||||
|
@ -184,6 +176,20 @@ commands =
|
||||||
coverage report -m
|
coverage report -m
|
||||||
coveralls
|
coveralls
|
||||||
|
|
||||||
|
[testenv:release]
|
||||||
|
decription = do a release, required posarg of the version number
|
||||||
|
basepython = python3.6
|
||||||
|
skipsdist = True
|
||||||
|
usedevelop = True
|
||||||
|
passenv = *
|
||||||
|
deps =
|
||||||
|
colorama
|
||||||
|
gitpython
|
||||||
|
pre-commit
|
||||||
|
towncrier
|
||||||
|
wheel
|
||||||
|
commands = python scripts/release.py {posargs}
|
||||||
|
|
||||||
[pytest]
|
[pytest]
|
||||||
minversion = 2.0
|
minversion = 2.0
|
||||||
plugins = pytester
|
plugins = pytester
|
||||||
|
|
Loading…
Reference in New Issue