Compare commits

...

283 Commits
3.6.3 ... 3.7.2

Author SHA1 Message Date
Bruno Oliveira
e0b088b52e Changelog tweaks 2018-08-16 19:32:41 -03:00
Bruno Oliveira
e5a3c870b4 Preparing release version 3.7.2 2018-08-16 22:29:00 +00:00
Ronny Pfannschmidt
7d4c4c66d4 Merge pull request #3805 from asottile/cause_cycles
Fix traceback reporting for exceptions with `__cause__` cycles.
2018-08-16 07:16:51 +02:00
Ronny Pfannschmidt
939a792c41 Merge pull request #3798 from jonozzz/fix-3751
Fix #3751
2018-08-16 06:52:16 +02:00
Anthony Sottile
17644ff285 Fix traceback reporting for exceptions with __cause__ cycles. 2018-08-15 18:15:07 -07:00
Bruno Oliveira
64faa41d06 Merge pull request #3802 from jonozzz/fix-3768
Fix test collection from packages mixed with directories. #3768 and #3789
2018-08-15 21:42:25 -03:00
Anthony Sottile
ca1bb9a3a1 Merge pull request #3815 from sankt-petersbug/fix-3671
Fix #3671 - `filterwarnings` Is an Unregistered Marker
2018-08-15 17:07:46 -07:00
Sankt Petersbug
78ef531420 corrected the position of myname 2018-08-14 20:33:55 -05:00
Sankt Petersbug
212ee450b7 simplified test function 2018-08-14 20:29:42 -05:00
Sankt Petersbug
c1c08852f9 lint checks 2018-08-14 19:54:51 -05:00
Sankt Petersbug
e06a077ac2 added changelog 2018-08-14 16:16:37 -05:00
Sankt Petersbug
cb77e65c97 updated AUTHORS 2018-08-14 16:16:25 -05:00
Sankt Petersbug
6367f0f5f1 fix filterwarnings mark not registered 2018-08-14 16:13:15 -05:00
Daniel Hahler
b88e09a697 Merge pull request #3548 from blueyed/fix-docs
tox: clean up docs target
2018-08-14 15:22:46 +02:00
Ronny Pfannschmidt
68bbd42213 Merge pull request #3795 from nicoddemus/changelog-3774
Add CHANGELOG for issue #3774, missing from PR #3780
2018-08-14 09:32:20 +02:00
Ronny Pfannschmidt
22ee2093b8 Merge pull request #3801 from nicoddemus/improve-warning-addoption
Improve warning messages when addoption is called with string as `type`
2018-08-14 09:30:17 +02:00
Anthony Sottile
87a99275fb Merge pull request #3807 from anowlcalledjosh/metafunc-config-doc
Unhide documentation for metafunc.config
2018-08-13 15:32:43 -07:00
Josh Holland
abbd7c30a4 Unhide documentation for metafunc.config
Fixes #3746.
2018-08-11 20:48:55 +01:00
Bruno Oliveira
abae60c8d0 Add CHANGELOG entries 2018-08-10 22:04:42 -03:00
turturica
e92893ed24 Add test for packages mixed with modules. 2018-08-10 17:29:30 -07:00
Bruno Oliveira
27b5435a40 Fix docs formatting and improve test a bit 2018-08-10 18:19:07 -03:00
turturica
50db718a6a Add a test description. 2018-08-10 13:57:29 -07:00
turturica
bfd0addaeb Fix test collection from packages mixed with directories. #3768 and #3789 2018-08-10 12:56:08 -07:00
Bruno Oliveira
be11d3e195 Improve warning messages when addoption is called with string as type
Encountered the warning myself and to me the message was not clear about
what should be done to fix the warning
2018-08-10 12:49:06 -03:00
turturica
266f05c4c4 Fix #3751 2018-08-09 18:28:22 -07:00
Bruno Oliveira
220288ac77 Add CHANGELOG for issue #3774, missing from PR #3780 2018-08-09 12:33:02 -03:00
Bruno Oliveira
4d8903fd0b Merge pull request #3780 from nicoddemus/mock-integration-fix
Fix issue where fixtures would lose the decorated functionality
2018-08-09 12:26:09 -03:00
Bruno Oliveira
67106f056b Use a custom holder class so we can be sure __pytest_wrapper__ was set by us 2018-08-09 09:22:00 -03:00
Ronny Pfannschmidt
5d3c5123f8 Merge pull request #3792 from decisio/pr-3788-fix-teardown-exception
Pr 3788 fix teardown exception
2018-08-09 06:53:22 +02:00
Bruno Oliveira
74d9f56d0f Improve CHANGELOG a bit 2018-08-08 21:24:14 -03:00
Wes Thomas
051db6a33d Trimming Trailing Whitespace 2018-08-08 18:18:18 -05:00
Wes Thomas
aa358433b0 Fix AttributeError bug in TestCaseFunction.teardown by creating TestCaseFunction._testcase as attribute of class with a None default. 2018-08-08 18:13:21 -05:00
Bruno Oliveira
e723069165 Merge pull request #3771 from nicoddemus/package-infinite-recursion-bug
Fix infinite recursion collection bug with pytest_ignore_collect hook
2018-08-06 10:09:31 -03:00
Bruno Oliveira
855fd17014 Merge pull request #3779 from nicoddemus/changelog-title
Add a changelog blurb and title, similar to tox
2018-08-05 18:03:57 -03:00
Bruno Oliveira
d11781920b Merge pull request #3782 from nicoddemus/pytest-non-functions
Only consider actual functions when collecting hooks
2018-08-05 09:07:01 -03:00
Bruno Oliveira
2c0d2eef40 Only consider actual functions when considering hooks
Fix #3775
2018-08-04 16:37:07 -03:00
Bruno Oliveira
ef8ec01e39 Fix issue where fixtures would lose the decorated functionality
Fix #3774
2018-08-04 15:14:00 -03:00
Bruno Oliveira
0a1c2a7ca1 Add a changelog blurb and title, similar to tox 2018-08-04 13:15:20 -03:00
Bruno Oliveira
fe0a76e1a6 Fix recursion bug if a pytest_ignore_collect returns False instead of None 2018-08-03 15:40:33 -03:00
Bruno Oliveira
dcafb8c48c Add example for package recursion bug 2018-08-03 15:40:33 -03:00
Bruno Oliveira
a76cc8f8c4 Add changelog for #3742
This was missing from PR #3751
2018-08-02 15:28:26 -03:00
Bruno Oliveira
4d2fa581e1 Merge pull request #3769 from nicoddemus/release-3.7.1
Preparing release version 3.7.1
2018-08-02 15:21:48 -03:00
Bruno Oliveira
f7a3f45a18 Preparing release version 3.7.1 2018-08-02 12:04:39 +00:00
Daniel Hahler
dff7b203f7 tox: clean up docs target 2018-08-02 13:07:03 +02:00
Anthony Sottile
4705fd2bbe Merge pull request #3765 from asottile/require_changelog_rst
Enforce that changelog files are .rst
2018-08-01 23:47:09 -07:00
Ronny Pfannschmidt
ca0476953e Merge pull request #3751 from nicoddemus/collect-file-bug
Workaround for #3742
2018-08-02 07:35:46 +02:00
Ronny Pfannschmidt
7e92930fa9 Merge pull request #3764 from asottile/fix_3763
Fix `TypeError` when the assertion message is `bytes` in python 3.
2018-08-02 07:19:51 +02:00
Ronny Pfannschmidt
33769d0328 Merge pull request #3754 from nicoddemus/fix-function-call-warning
Refactor direct fixture call warning to avoid incompatibility with plugins
2018-08-02 07:17:15 +02:00
Bruno Oliveira
5db2e6c7a1 Merge pull request #3761 from nicoddemus/numpy-recursion-error
Fix recursion in pytest.approx() with arrays in numpy<1.13
2018-08-01 23:40:30 -03:00
Bruno Oliveira
804fc4063a Merge pull request #3741 from kalekundert/approx_misc_tweaks
Miscellaneous improvements to approx()
2018-08-01 23:40:21 -03:00
Bruno Oliveira
82a2174867 Fix typo in CHANGELOG 2018-08-01 20:11:16 -03:00
Anthony Sottile
a80e031c62 Enforce that changelog files are .rst 2018-08-01 15:22:43 -07:00
Anthony Sottile
452e5c1cf0 Fix TypeError when the assertion message is bytes in python 3. 2018-08-01 15:09:25 -07:00
Bruno Oliveira
c6b11b9f62 Refactor direct fixture call warning to avoid incompatibility with plugins
This refactors the code so we have the real function object right during
collection. This avoids having to unwrap it later and lose attached information
such as "async" functions.

Fix #3747
2018-08-01 16:38:43 -03:00
Kale Kundert
b8255308d6 Make the infinite-recusrion fix more explicit.
So we remember what happened and don't accidentally regress in the
future.
2018-08-01 12:11:03 -07:00
Bruno Oliveira
a5c0fb7f6b Rename recursive_map -> _recursive_list_map as requested in review 2018-08-01 15:17:58 -03:00
Bruno Oliveira
f25683354e Merge pull request #3760 from RonnyPfannschmidt/fix-3757-pin-pathlib
fix #3757 by pinning to pathlib2 that supports __fspath__
2018-08-01 09:44:30 -03:00
Bruno Oliveira
7d13599ba1 Fix recursion in pytest.approx() with arrays in numpy<1.13 2018-08-01 08:04:09 -03:00
Bruno Oliveira
43664d7841 Use ids for parametrized values in test_expected_value_type_error 2018-08-01 07:34:08 -03:00
Bruno Oliveira
2a2f888909 Move recursive_map from local to free function 2018-08-01 07:30:40 -03:00
Bruno Oliveira
ad5ddaf55a Simplify is_numpy_array as suggested in review 2018-08-01 07:28:39 -03:00
Bruno Oliveira
4588130c1e Merge pull request #3756 from RonnyPfannschmidt/fix-3745
fix #3745 - display absolute cache_dir if necessary
2018-08-01 07:09:19 -03:00
Bruno Oliveira
5003bae0de Fix 'at' string for non-numeric messages in approx() 2018-08-01 07:07:37 -03:00
Bruno Oliveira
6e32a1f73d Use parametrize in repr test for nd arrays 2018-08-01 07:04:25 -03:00
Bruno Oliveira
611d254ed5 Improve error checking messages: add position and use pprint 2018-08-01 07:01:00 -03:00
Ronny Pfannschmidt
57a8f208bc fix #3757 by pinning to pathlib2 that supports __fspath__ 2018-08-01 11:45:39 +02:00
Ronny Pfannschmidt
fcdc1d867e fix #3745 - display absolute cache_dir if necessary 2018-08-01 08:25:37 +02:00
Bruno Oliveira
098dca3a9f Use {!r} for a few other messages as well 2018-07-31 21:14:51 -03:00
Bruno Oliveira
8e2ed76227 Create appropriate CHANGELOG entries 2018-07-31 21:11:26 -03:00
Bruno Oliveira
bf7c188cc0 Improve error message for invalid types passed to pytest.approx()
* Hide the internal traceback
* Use !r representation instead of !s (the default for {} formatting)
2018-07-31 21:08:24 -03:00
Bruno Oliveira
8c9efd8608 Only call _collectfile on package instances
As discussed in #3751, this feels like a hack, pushing it only so we can
see how it fares on CI and if there are better solutions out there
2018-07-31 19:06:30 -03:00
Bruno Oliveira
e1ad1a14af Add example script and failure for #3742 2018-07-31 17:50:55 -03:00
Kale Kundert
327fe4cfcc Update the changelog. 2018-07-31 11:40:02 -07:00
Kale Kundert
d02491931a Fix the unused import. 2018-07-31 11:33:46 -07:00
Kale Kundert
032db159c9 Let black reformat the code... 2018-07-31 11:23:23 -07:00
Kale Kundert
cd2085ee71 approx(): Detect type errors earlier. 2018-07-31 00:26:35 -07:00
Kale Kundert
ad305e71d7 Improve docstrings for Approx classes. 2018-07-30 23:26:57 -07:00
Kale Kundert
7d8688d54b Reflect dimension in approx repr for numpy arrays. 2018-07-30 23:23:17 -07:00
Bruno Oliveira
253419316c Merge pull request #3738 from nicoddemus/release-3.7.0
Release 3.7.0
2018-07-30 20:38:22 -03:00
Bruno Oliveira
997ef59306 Fix typos in CHANGELOG 2018-07-30 18:31:35 -03:00
Bruno Oliveira
60b1913ba2 Preparing release version 3.7.0 2018-07-30 20:14:42 +00:00
Bruno Oliveira
2c09930b6d Use proper quotes for python 3.7 on travis.yml 2018-07-30 20:13:17 +00:00
Bruno Oliveira
d461e931dd Use python 3.6 for regendoc 2018-07-30 20:12:52 +00:00
Bruno Oliveira
eada0b1fd7 Merge remote-tracking branch 'upstream/master' into release-3.7.0 2018-07-30 20:12:30 +00:00
Bruno Oliveira
150535b6c1 Merge pull request #3696 from abrammer/approx_numpy_tolerance_bugfix
bugfix in ApproxNumpy initialisation, use keywords for arguments to fix
2018-07-30 17:09:18 -03:00
Ronny Pfannschmidt
f1ec02cdcd Merge pull request #3733 from nicoddemus/py37
Test with Python 3.7 on Travis and AppVeyor
2018-07-30 20:02:03 +02:00
Ronny Pfannschmidt
9f5d73d44a Merge pull request #3735 from nicoddemus/deprecate-pytest-namespace
Deprecate pytest namespace
2018-07-30 19:55:45 +02:00
Bruno Oliveira
8609f8d25a Move warning definition to deprecated module 2018-07-30 14:25:29 -03:00
Bruno Oliveira
953a618102 Update CHANGELOG entry about pytest_namespace deprecation 2018-07-30 12:18:37 -03:00
Bruno Oliveira
cf6d8e7e53 Fix test and update warning in pytest_namespace docs 2018-07-30 12:16:42 -03:00
Bruno Oliveira
8af78f417f Merge pull request #3732 from nicoddemus/merge-master-into-features
Merge master into features
2018-07-30 07:26:19 -03:00
abrammer
535fd1f311 may as well include inf test while we're at it 2018-07-29 23:12:04 -04:00
abrammer
762eaf443a update changelog to include the addition of tests 2018-07-29 22:57:39 -04:00
abrammer
330640eb96 update tests to check tolerance args and expecing nan in numpy arrays 2018-07-29 22:47:38 -04:00
Bruno Oliveira
e3d412d1f4 Warn when implementations exist for pytest_namespace hook
This hook has been deprecated and will be removed in the future.

Fix #2639
2018-07-29 22:20:23 -03:00
Bruno Oliveira
6f9a12a8a3 Merge pull request #3486 from ammarnajjar/last-failed-no-failures_docs_correction
--last-failed-no-failures docs correction
2018-07-29 21:26:03 -03:00
Bruno Oliveira
0e47599572 Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-07-29 21:15:51 -03:00
Bruno Oliveira
c480223e88 Test with Python 3.7 on Travis and AppVeyor 2018-07-29 21:14:38 -03:00
Bruno Oliveira
0b522d40a7 Merge pull request #3734 from nicoddemus/fix-docs
Fix reference to _Result in docs
2018-07-29 21:14:09 -03:00
Bruno Oliveira
d900a6c8bd Merge pull request #3731 from asottile/pygrep_hooks
Use upstream rst-backticks hook
2018-07-29 20:52:28 -03:00
Bruno Oliveira
fe46fbb719 Fix reference to _Result in docs 2018-07-29 20:50:24 -03:00
Anthony Sottile
8d401cdb9d Use upstream rst-backticks hook 2018-07-29 15:46:06 -07:00
Bruno Oliveira
3f3f6f1be4 Merge pull request #3729 from nicoddemus/fix-changelog-titles
Fix "Pytest" to "pytest" in the CHANGELOG
2018-07-29 19:42:24 -03:00
Bruno Oliveira
b6da5cc54c Fix "Pytest" to "pytest" in the CHANGELOG
Now that we have fixed towncrier to render the proper title, seems fitting
to update the others
2018-07-28 12:57:18 -03:00
Bruno Oliveira
eaeeedc9c3 Merge pull request #3728 from nicoddemus/release-3.6.4
Release 3.6.4
2018-07-28 12:53:49 -03:00
Bruno Oliveira
317cd41215 Preparing release version 3.6.4 2018-07-28 12:59:04 +00:00
Bruno Oliveira
7f27512a48 Pin pluggy to <0.8 2018-07-28 09:46:35 -03:00
Kale Kundert
bf127a63b2 Need to iterate over the flattened array. 2018-07-27 11:24:42 -07:00
Bruno Oliveira
fe16f81da1 Merge pull request #3705 from nicoddemus/deprecate-call-fixture-func
Deprecate calling fixture functions directly
2018-07-27 15:09:09 -03:00
Bruno Oliveira
d0ba242c46 Implement change suggested by @kalekundert in PR 2018-07-27 15:07:20 -03:00
Bruno Oliveira
fe06be8590 Merge pull request #3723 from pytest-dev/nicoddemus-patch-1
Use "pytest" on the CHANGELOG
2018-07-27 10:23:11 -03:00
Bruno Oliveira
79b4ca92d8 Use "pytest" on the CHANGELOG 2018-07-27 09:21:18 -03:00
Bruno Oliveira
57b0c60cb4 Remove Testdir.run_example as recommended 2018-07-26 20:10:40 -03:00
Bruno Oliveira
6e57d123bb Mark test_idval_hypothesis as flaky on Windows (#3707) 2018-07-26 19:58:42 -03:00
Bruno Oliveira
011f88f7e7 Deprecate calling fixture functions directly
This will now issue a RemovedInPytest4Warning when the user calls
a fixture function directly, instead of requesting it from test
functions as is expected

Fix #3661
2018-07-26 19:58:42 -03:00
Bruno Oliveira
2eb9301ad5 Improve CHANGELOG 2018-07-25 08:09:31 -03:00
abrammer
f0db64ac2e drop the duplicate approx call
update test to include both np.array(actual) and np.array(expected)
2018-07-24 21:18:44 -04:00
abrammer
514ca6f4ad add test wrt #3695 checking numpy array tolerance args 2018-07-23 23:40:06 -04:00
Bruno Oliveira
b7419bd9bb Merge pull request #3710 from bmwiedemann/date
Do not claim copyright for future years
2018-07-23 11:14:39 -03:00
Bernhard M. Wiedemann
2e344d4d63 Do not claim copyright for future years
When building today's python-pytest-doc openSUSE package
in the year 2033, the documentation .html files state
Copyright 2015-2033 , holger krekel and pytest-dev team.

That cannot be correct, because nobody did anything copyright-worthy
for this file in 2033.

See also https://stackoverflow.com/questions/2390230/do-copyright-dates-need-to-be-updated

Additionally, this change makes the package build reproducible.
See https://reproducible-builds.org/ for why this is good.
2018-07-23 15:02:59 +02:00
Ronny Pfannschmidt
f8749eeb5c Merge pull request #3708 from nicoddemus/small-refactors
Small refactorings
2018-07-23 06:53:08 +02:00
Anthony Sottile
f76142508f Merge pull request #3694 from drewrisinger/patch-2
Fix mark.rst typos & grammar
2018-07-22 09:15:33 -07:00
Anthony Sottile
be2afb950a Merge pull request #3706 from ehershey/patch-1
typo - $PYTEST_ADDOTPS -> $PYTEST_ADDOPTS
2018-07-22 09:03:23 -07:00
E Hershey
19de1b7f29 typo - PYTEST_ADDOTPS -> PYTEST_ADDOPTS 2018-07-22 11:39:32 -04:00
Bruno Oliveira
f5165064ee Make yield_fixture just call fixture to do its work
Since fixture and yield_fixture are identical, they should call
the same code; as it was, the code inside them was already starting
to deviate.
2018-07-22 09:41:03 -03:00
Bruno Oliveira
c9a0881309 Isolate the code that resolves the fixturefunc to a separate function
pytest_fixture_setup was somewhat convoluted because it was trying
to do too many things.
2018-07-22 09:37:41 -03:00
Bruno Oliveira
5167933395 Move teardown code of yield fixtures to a partial to avoid leaks
As it were before, it was keeping a reference to fixturefunc and it
alive when an error occurred
2018-07-22 09:27:34 -03:00
Alan Brammer
75db608479 update changelog 2018-07-18 17:56:00 -04:00
Alan
7bff5866b1 bugfix in ApproxNumpy initialisation, use keywords for arguments now 2018-07-18 17:29:55 -04:00
Anthony Sottile
9720c3301a Merge pull request #3690 from drewrisinger/patch-1
Fix fixture.rst typos & grammar
2018-07-17 18:13:41 -07:00
Drew
254689ff83 Fix mark.rst typos & grammar
Fix minor typos
2018-07-17 11:19:40 -04:00
Drew
21f5222784 Fix fixture.rst spelling & grammar
Fix a few typos.
2018-07-17 10:53:57 -04:00
Bruno Oliveira
0bb29d5649 Merge pull request #3685 from nicoddemus/merge-master-into-features
Merge master into features
2018-07-15 16:53:39 -03:00
Bruno Oliveira
db33f03c15 Merge pull request #3681 from tadeoos/980-fix-truncated-locals-in-verbose
Fix truncated locals in verbose mode
2018-07-15 14:43:15 -03:00
Bruno Oliveira
ac9ceaacd8 Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2018-07-15 11:54:14 -03:00
Bruno Oliveira
771d3e8f4f Merge pull request #3684 from Vlad-Shcherbina/type_comments
Replace broken type annotations with type comments
2018-07-15 11:52:12 -03:00
Bruno Oliveira
82a11e6207 Merge pull request #3680 from nicoddemus/labels
Manage GH labels using the new 'labels' tool
2018-07-15 11:34:20 -03:00
Bruno Oliveira
a1c3df1889 Merge pull request #3683 from caramelomartins/master
[#3402] CITATION
2018-07-15 11:33:06 -03:00
Bruno Oliveira
a821af6b1c Merge pull request #3682 from nicoddemus/tox-release
Revamp the release script: drop invoke and use tox directly
2018-07-15 11:32:20 -03:00
Tadek Teleżyński
d2fe619120 Fix truncated locals in verbose mode 2018-07-15 11:30:26 -03:00
Vlad Shcherbina
58e77f58bd Replace broken type annotations with type comments
Fixes #3635.
2018-07-15 16:58:39 +03:00
Hugo Martins
6a4fa4f485 Fix more linting issues 2018-07-14 16:44:47 +01:00
Hugo Martins
5be03bff61 Add changelog information 2018-07-14 16:37:55 +01:00
Hugo Martins
e9fd038aae Fix linting issues 2018-07-14 16:35:33 +01:00
Hugo Martins
a8464a95ce Add CITATION
Relates to #3402
2018-07-14 16:32:29 +01:00
Bruno Oliveira
a0b0c37feb Revamp the release script: drop invoke and use tox directly
Following the lead from tox, use a simple Python script instead of depending
on ``invoke``.

Other changes:

* Some colors using ``colorama``.
* Run ``pre-commit`` before the final commit to ensure everything is neatly
  formatted.
* Drop generating local tag: legacy from the time we used ``devpi`` as staging
  area, currently we no longer use it, and we should push a
  tag from the last HEAD of the PR always to ensure it is correct.
2018-07-14 11:35:37 -03:00
Bruno Oliveira
35ffd29404 Manage GH labels using the new 'labels' tool 2018-07-14 10:12:52 -03:00
Bruno Oliveira
0565a7a4e1 Merge pull request #3679 from nicoddemus/parametrized-internal-refactor
Refactor parametrize() code for readability
2018-07-12 21:31:06 -03:00
Bruno Oliveira
6c3713226c Merge pull request #3642 from caramelomartins/master
Fixes # 3592 - Clarify Fixtures' Documentation
2018-07-11 23:31:51 -03:00
Bruno Oliveira
f6ceedd15b Merge pull request #3636 from RonnyPfannschmidt/fixturetest-examples
[RFC] Fixturetest examples - move test contents to use example scripts for contents
2018-07-11 23:29:33 -03:00
Bruno Oliveira
3e599dc149 Check that param sets match number of args during _for_parametrize
It makes sense to validate them during creation of the parameter set
2018-07-11 22:57:53 -03:00
Bruno Oliveira
54fbc6f6e1 Refactor parametrize() code for readability
Extract the parametrize() block of code into methods for better
readability
2018-07-11 21:29:21 -03:00
Bruno Oliveira
aa47b64e2a Improve CHANGELOG entry 2018-07-11 21:07:21 -03:00
Bruno Oliveira
251adbf644 Merge with upstream/master 2018-07-11 20:46:14 -03:00
Bruno Oliveira
2c4759ce57 Run regendocs 2018-07-11 20:37:50 -03:00
Bruno Oliveira
4dfe2eee94 Fix finalize call 2018-07-11 20:24:39 -03:00
Bruno Oliveira
5226c7fac3 Merge pull request #3665 from nicoddemus/changelog-tweaks
Small tweaks to the changelog entries
2018-07-10 21:12:50 -03:00
Bruno Oliveira
593b451373 Merge pull request #3670 from asottile/remove_unused_fix_lint
Remove unused fix-lint tox environment
2018-07-10 19:08:31 -03:00
Anthony Sottile
898544e147 Merge pull request #3669 from asottile/yesqa
Remove some extraneous `# noqa` comments
2018-07-10 14:53:08 -07:00
Anthony Sottile
61301d934e Remove some extraneous # noqa comments
This was partially automated with https://github.com/asottile/yesqa

_with a few caveats_:
- it was run under python2 (chosen arbitrarily, when run under python3 other
  things were changed)
- I used `git checkout -p` to revert the removal of `noqa` comments from
  `cmp()` lines.
2018-07-08 17:05:52 -07:00
Anthony Sottile
af0059079c Remove unused fix-lint tox environment 2018-07-08 17:05:01 -07:00
Bruno Oliveira
9ef7878cbc Merge pull request #3668 from asottile/remove_obsolete_future_imports
Remove obsolete __future__ imports
2018-07-08 13:41:59 -03:00
Anthony Sottile
4ae93a7a07 Remove obsolete __future__ imports 2018-07-08 08:37:02 -07:00
Bruno Oliveira
d4faa4056b Merge pull request #3667 from asottile/avoid_sys_path_pollution_742
Use -mpytest when invoking pytest in pytester
2018-07-08 11:51:16 -03:00
Anthony Sottile
42bbb4fa8a Use -mpytest when invoking pytest in pytester 2018-07-07 17:18:44 -07:00
Bruno Oliveira
12c5b6104c Merge pull request #3666 from nicoddemus/deploy-on-tags
Skip deploy stage entirely unless we have a tag
2018-07-07 20:40:14 -03:00
Bruno Oliveira
aa9d1ad2eb Merge pull request #3663 from nicoddemus/usefixtures-reference
Add reference docs for pytest.mark.usefixtures
2018-07-07 12:12:31 -03:00
Bruno Oliveira
49e82a4be8 Skip deploy stage entirely unless we have a tag
Borrowed from https://github.com/tox-dev/tox/pull/877
2018-07-07 12:12:07 -03:00
Bruno Oliveira
803302e70c Fix end-of-line in 2220.bugfix.rst 2018-07-07 11:13:48 -03:00
Bruno Oliveira
05f1d0d3ef Update README for CHANGELOG about using multiple paragraphs 2018-07-07 11:07:13 -03:00
Bruno Oliveira
1cd62f8c38 Update CHANGELOG template to put issue links at the start of entries
This allows us to use the new multi-line entries available with
towncrier 18.6.0
2018-07-07 11:02:33 -03:00
Bruno Oliveira
a522fc745a Small tweaks to the changelog entries 2018-07-07 10:43:37 -03:00
Bruno Oliveira
303133f013 Merge pull request #3647 from jeffreyrack/3610-add-trace-option
3610 add trace option
2018-07-07 10:05:34 -03:00
Bruno Oliveira
d26a596072 Add a warning about usefixtures mark not working in fixtures
Fix #1014
2018-07-07 10:01:10 -03:00
Bruno Oliveira
f359b50fe5 Adjust copyright in README 2018-07-06 21:03:27 -03:00
Bruno Oliveira
18b2fc11ad Dummy change 2018-07-06 20:57:30 -03:00
Bruno Oliveira
d7b722e2ae Add reference docs for pytest.mark.usefixtures 2018-07-06 20:55:42 -03:00
Ronny Pfannschmidt
1e94ac784f Merge pull request #3389 from jonozzz/features
Add package scoped fixtures #2283
2018-07-06 11:42:36 +02:00
Bruno Oliveira
027d2336b8 Add minimal docs for package-scoped fixtures (experimental) 2018-07-05 21:56:31 -03:00
Bruno Oliveira
3c19370cec Merge remote-tracking branch 'upstream/features' into jonozzz/features 2018-07-05 18:15:17 -03:00
Bruno Oliveira
7e8f7bfa16 Merge pull request #3660 from asottile/code_highlight_fix
Correct code blocks in docs
2018-07-05 07:17:03 -03:00
Bruno Oliveira
3f5e06ecc4 Merge pull request #3659 from nicoddemus/merge-master-into-features
Merge master into features
2018-07-04 21:11:28 -03:00
Anthony Sottile
50f030d233 Correct code blocks in docs 2018-07-04 15:16:34 -07:00
Bruno Oliveira
7696d5371a Merge remote-tracking branch 'upstream/master' into features 2018-07-04 18:49:35 -03:00
Bruno Oliveira
b64c26674d Merge pull request #3656 from nicoddemus/release-3.6.3
Release 3.6.3
2018-07-04 18:45:45 -03:00
Bruno Oliveira
63b25304c3 Run pre-commit fixers 2018-07-03 22:20:29 -03:00
Hugo Martins
8232fd1a2d Fix remaining "smtp" references 2018-07-03 23:24:59 +01:00
Hugo Martins
f52a5d3be2 Merge remote-tracking branch 'upstream/master' 2018-07-03 23:11:35 +01:00
Jeffrey Rackauckas
067de257e1 Fix test_pdb.py with pexpect 2018-07-02 21:03:21 -07:00
Jeffrey Rackauckas
4a925ef5e9 Fixing bug in test. 2018-07-02 20:29:59 -07:00
Jeffrey Rackauckas
4afb8c428b Fix python 2 issues 2018-07-02 20:20:42 -07:00
Jeffrey Rackauckas
2f1a2cf07f Fixing --trace test. 2018-07-02 19:55:08 -07:00
Jeffrey Rackauckas
6cc4fe2412 Fixing bad indentation 2018-07-02 19:53:46 -07:00
Jeffrey Rackauckas
10a8691eca Add support for yielded functions. 2018-07-02 19:46:26 -07:00
Jeffrey Rackauckas
b75320ba95 Fix --trace option with yield tests. 2018-07-02 19:08:41 -07:00
Jeffrey Rackauckas
bc268a58d1 Adding needed newline 2018-07-01 20:22:50 -07:00
Jeffrey Rackauckas
0b70477930 Fix linting issues. 2018-07-01 20:18:00 -07:00
Jeffrey Rackauckas
8801162275 Fixing tabbing in usage.rst. 2018-07-01 18:54:04 -07:00
Jeffrey Rackauckas
a604a71185 Fixing usage.rst title. 2018-07-01 18:53:18 -07:00
Jeffrey Rackauckas
66fa6bb42e Fix flake8 issues. 2018-07-01 18:50:57 -07:00
Jeffrey Rackauckas
713d32c4da Adding documentation for the --trace option. 2018-07-01 18:49:39 -07:00
Jeffrey Rackauckas
57198d477b Adding changelog entry for the --trace option. 2018-07-01 12:14:35 -07:00
Jeffrey Rackauckas
533f4cc10c Fix test to pass 2018-06-30 21:36:27 -07:00
Jeffrey Rackauckas
a46b94950c Properly set immediately_break value 2018-06-30 21:32:25 -07:00
Jeffrey Rackauckas
952bbefaac Add initial test. 2018-06-30 18:26:58 -07:00
Jeffrey Rackauckas
54d3cd587d Adding the --trace option. 2018-06-30 18:09:06 -07:00
Hugo Martins
f7c929c932 Fix linting errors in docs/fixtures.rst 2018-06-30 14:34:19 +01:00
Hugo Martins
b48f1d378b Clarify examples in fixtures' documentation 2018-06-30 02:01:49 +01:00
Ronny Pfannschmidt
0fd86ec8a8 move some fill fixture acceptance tests contents to the examples script folder 2018-06-29 10:58:33 +02:00
Ronny Pfannschmidt
4ae7e9788c fix quotes in scope order test 2018-06-29 07:13:18 +02:00
Ronny Pfannschmidt
5582ad0445 remove use of formatting in test_func_closure_module_auto
this makes it apparent that pytester should supply some kind of variable support
2018-06-29 07:07:03 +02:00
Ronny Pfannschmidt
982b614010 remove format calls for most fixture tests 2018-06-29 07:07:03 +02:00
Ronny Pfannschmidt
7845ab4bc3 remove test file formatting from TestContextManagerFixtureFuncs 2018-06-29 07:07:03 +02:00
Bruno Oliveira
8680dfc939 Merge pull request #3629 from egnartsms/issue-2220-param-breaks-dep
Make test parametrization override indirect fixtures
2018-06-28 21:43:21 -03:00
Serhii Mozghovyi
76ac670f7d Add changelog description 2018-06-28 23:42:18 +03:00
Bruno Oliveira
7b47dfb744 Merge pull request #3634 from RonnyPfannschmidt/merge-from-master
Merge from master
2018-06-28 14:05:18 -03:00
Ronny Pfannschmidt
3c73d6298a merge from master to features 2018-06-28 17:32:41 +02:00
Serhii Mozghovyi
c220fb235a Minor fix (code improvement) 2018-06-28 14:53:06 +03:00
Serhii Mozghovyi
1dc5e97ac2 Make test parametrization override indirect fixtures 2018-06-28 14:32:29 +03:00
Bruno Oliveira
e9371a58a0 Merge pull request #3622 from RonnyPfannschmidt/builtin-serialize
move report classes to own file to prepare for serialisazion
2018-06-27 15:17:09 -03:00
Bruno Oliveira
ea379ba10f Merge pull request #3623 from RonnyPfannschmidt/pytester-runexamples
Pytester runexamples
2018-06-27 09:13:29 -03:00
Ronny Pfannschmidt
17e01993d9 regendoc and invocation fixes 2018-06-27 08:28:21 +02:00
Ronny Pfannschmidt
8a6345515b regendoc 2018-06-27 06:52:54 +02:00
Ronny Pfannschmidt
581d49635e add docs and changelog 2018-06-27 06:52:36 +02:00
Ronny Pfannschmidt
e860ff7299 port some acceptance tests over to copy_example 2018-06-26 22:59:40 +02:00
Ronny Pfannschmidt
0672bc633f enable pytester to run examples copied from the cwd 2018-06-26 22:48:33 +02:00
Ronny Pfannschmidt
2dfb52f7e0 fix rebase artifacts 2018-06-26 22:10:26 +02:00
Ronny Pfannschmidt
cc6eb9f83c move test reports to own file 2018-06-26 22:09:15 +02:00
Ronny Pfannschmidt
6b239263da Merge pull request #3620 from RonnyPfannschmidt/merge-from-master
Merge from master
2018-06-26 21:56:09 +02:00
Ronny Pfannschmidt
89e0a3ec27 merge from master to features 2018-06-26 17:01:05 +02:00
Bruno Oliveira
5b186cd609 Merge pull request #3594 from pytest-dev/interal-pathlib
[WIP] port cache plugin internals to pathlib
2018-06-25 11:09:36 -03:00
Ronny Pfannschmidt
5a156b3645 disable pypy on windows until scandir works for it 2018-06-24 22:54:26 +02:00
Ronny Pfannschmidt
95f00de0df use paths for config.cache.get key 2018-06-23 00:14:06 +02:00
Ronny Pfannschmidt
c4c666cbc4 use Pathlib instead of path splitting 2018-06-23 00:07:57 +02:00
Ronny Pfannschmidt
ee30bf45c9 rebase onto readme addition 2018-06-23 00:03:10 +02:00
Ronny Pfannschmidt
603df1ea1c whops, its supported starting with python 3.6, not python 3.5 2018-06-22 23:56:22 +02:00
Ronny Pfannschmidt
abbf73ad1a use pathlib2 up to python3.4 - damn the stdlib 2018-06-22 23:56:22 +02:00
Ronny Pfannschmidt
1226cdab47 fix warnings and json dumping of cacheprovider 2018-06-22 23:56:22 +02:00
Ronny Pfannschmidt
ab80e0fba0 sort compat flake8 mess correctly 2018-06-22 23:56:22 +02:00
Ronny Pfannschmidt
fb992a0c81 reorder attr.ib specs 2018-06-22 23:56:22 +02:00
Ronny Pfannschmidt
23581d44bd add missed file 2018-06-22 23:56:22 +02:00
Ronny Pfannschmidt
c7eb53317b port cache plugin internals to pathlib
warning logging got broken by detanglement from config
2018-06-22 23:56:22 +02:00
Bruno Oliveira
de98939ebf Merge pull request #3608 from avirlrma/features
add reamde for .pytest_cache
2018-06-21 21:05:23 -03:00
Bruno Oliveira
0d3914b626 Remove extra '\' left at the end of a line in cache's readme contents 2018-06-21 20:12:50 -03:00
Bruno Oliveira
eb94bce3e2 Change 3519 to trivial 2018-06-21 20:11:22 -03:00
Bruno Oliveira
b897008887 Improve CHANGELOG grammar 2018-06-21 20:09:32 -03:00
avirlrma
998d540b73 fixed changelog entry 2018-06-21 17:56:26 +05:30
avirlrma
c672bfa32e added changelog entry
moved cache readme tests to test_cacheprovider.py
2018-06-21 17:43:10 +05:30
avirlrma
8f1d8ac970 fixed linting errors
ran black
removed unused imports and variables
2018-06-21 15:15:55 +05:30
avirlrma
53d4710c62 added tests for .pytest_cache README
Helper class to check if readme exists in .pytest_cache directory
 Tests to check for readme when tests pass and when they fail
2018-06-21 14:25:00 +05:30
avirlrma
31f089db6a add reamde for .pytest_cache
method - `ensure_readme()`
2018-06-21 13:14:58 +05:30
Bruno Oliveira
9d60cf25c0 Merge pull request #2207 from RonnyPfannschmidt/fix/519
add example scripts for issue #519
2018-06-15 15:05:18 -03:00
Bruno Oliveira
9e32b6ae48 Small typo and grammar fix 2018-06-15 15:05:00 -03:00
Ronny Pfannschmidt
99402cf1c0 add a readme to the example scripts 2018-06-15 20:02:01 +02:00
Ronny Pfannschmidt
3ac2ae3c8c black 2018-06-15 18:13:45 +02:00
Ronny Pfannschmidt
ea906056fa add the actually expected fixtureorder for #519 2018-06-15 18:04:24 +02:00
Ronny Pfannschmidt
c081c5ee23 add example scripts for issue #519 2018-06-15 18:04:24 +02:00
Bruno Oliveira
3dcdaab103 Merge pull request #3585 from wcooley/feature/3579-caplog-messages
Add `messages` property to `caplog` fixture.
2018-06-15 12:41:33 -03:00
Wil Cooley
3615977608 Add messages property to caplog fixture. 2018-06-14 12:22:33 -07:00
Bruno Oliveira
94c41bec64 Merge pull request #3576 from RonnyPfannschmidt/addmarker-api
fix addmarker - extract mark from markdecorator
2018-06-13 18:36:40 -03:00
Ronny Pfannschmidt
8d072205e9 fix whitespace 2018-06-13 22:00:22 +02:00
Bruno Oliveira
b5102d03a6 Fix add_marker docs 2018-06-13 14:57:10 -03:00
Ronny Pfannschmidt
791bb3502c changelog 2018-06-13 17:29:42 +02:00
Ronny Pfannschmidt
eb0c6a8287 fix addmarker - extract mark from markdecorator 2018-06-13 17:27:00 +02:00
Ammar Najjar
9ddd573774 Issue #3295: add changelog doc entry for adding a missing argument in
the examples
2018-05-17 23:11:37 +02:00
Ammar Najjar
f99da9058f Issue #3295: Correct the usage of --last-failed-no-failures documentation
- add the missing --last-failed argument in the presented examples, for they are missleading and lead to think that the missing argument is not needed.
2018-05-17 23:07:55 +02:00
turturica
7d0dba18de Removed _CompatProperty("Package") 2018-04-27 10:23:15 -07:00
turturica
6fc7f07a80 Workaround for py36-xdist failure. 2018-04-26 23:05:03 -07:00
turturica
05b5b64379 Merge remote-tracking branch 'upstream/features' into features 2018-04-25 21:20:39 -07:00
turturica
229c8e551d Fix parametrized fixtures reordering. 2018-04-25 18:44:54 -07:00
turturica
d483b401ee Merge remote-tracking branch 'upstream/features' into features 2018-04-24 13:45:10 -07:00
turturica
acacf75f49 Added another package-scoped fixture test.
Changed existing complex tests to use package fixtures.
2018-04-24 13:32:58 -07:00
turturica
f8350c6304 Fix an issue that popped up only on Windows. 2018-04-21 19:51:33 -07:00
turturica
fedc78522b Build a stack of all previous packages instead of just the one closest to the initial argument(s).
Address #3358 by caching nodes in a session dict.
2018-04-21 18:39:42 -07:00
turturica
b0474398ec Fix a formatting error. 2018-04-20 18:18:44 -07:00
turturica
dc90c9108f Collapse all parent nested package fixtures when pointing to a sub-node.
Example:
Given this hierarchy: p1.s1.s2.s3
I want to run pytest p1/s1/s2/s3/foo.py

If there are any package fixtures defined at p1..s2 levels, they should also be executed.
2018-04-20 17:15:09 -07:00
turturica
69031d0033 Forgot one file from previous commit. 2018-04-20 15:54:21 -07:00
turturica
e44d4e6508 Merge remote-tracking branch 'upstream/features' into features 2018-04-20 15:21:24 -07:00
turturica
c416b1d935 Don't stop at the first package when looking up package-scoped fixtures.
Example:
package1.subpackage1
package1.subpackage2

package1's setup/teardown were executed again when exiting subpackage1 and entering subpackage2.
2018-04-20 15:04:58 -07:00
turturica
7d923c389e Merge remote-tracking branch 'upstream/features' into features 2018-04-18 00:11:03 -07:00
turturica
c02e8d8b0d Fix test collection when tests are passed as IDs at the command line. Note this is still broken due to #3358. 2018-04-16 11:44:05 -07:00
turturica
35df2cdbee Fix linting error. 2018-04-11 15:45:43 -07:00
turturica
2b1410895e Add package scoped fixtures #2283 2018-04-11 15:39:42 -07:00
129 changed files with 2870 additions and 1089 deletions

149
.github/labels.toml vendored Normal file
View File

@@ -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"

View File

@@ -25,6 +25,10 @@ repos:
rev: v1.2.0
hooks:
- id: pyupgrade
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.0.0
hooks:
- id: rst-backticks
- repo: local
hooks:
- id: rst
@@ -34,3 +38,8 @@ repos:
language: python
additional_dependencies: [pygments, restructuredtext_lint]
python_version: python3.6
- id: changelogs-rst
name: changelog files must end in .rst
entry: ./scripts/fail
language: script
files: 'changelog/.*(?<!\.rst)$'

View File

@@ -3,7 +3,8 @@ language: python
stages:
- linting
- test
- deploy
- name: deploy
if: repo = pytest-dev/pytest AND tag IS present
python:
- '3.6'
install:
@@ -40,7 +41,9 @@ jobs:
- env: TOXENV=py36-freeze
python: '3.6'
- env: TOXENV=py37
python: 'nightly'
python: '3.7'
sudo: required
dist: xenial
- stage: deploy
python: '3.6'

View File

@@ -89,6 +89,7 @@ Hugo van Kemenade
Hui Wang (coldnight)
Ian Bicking
Ian Lesperance
Ionuț Turturică
Jaap Broekhuizen
Jan Balster
Janne Vanhala
@@ -181,7 +182,9 @@ Russel Winder
Ryan Wooden
Samuel Dion-Girardeau
Samuele Pedroni
Sankt Petersbug
Segev Finer
Serhii Mozghovyi
Simon Gomizelj
Skylar Downes
Srinivas Reddy Thatiparthy
@@ -190,6 +193,7 @@ Stefan Zimmermann
Stefano Taschini
Steffen Allner
Stephan Obermann
Tadek Teleżyński
Tarcisio Fischer
Tareq Alayan
Ted Xiao
@@ -206,6 +210,7 @@ Victor Uriarte
Vidar T. Fauske
Vitaly Lashmanov
Vlad Dragos
Wil Cooley
William Lee
Wouter van Ackooy
Xuan Luong

View File

@@ -1,3 +1,13 @@
=================
Changelog history
=================
Versions follow `Semantic Versioning <https://semver.org/>`_ (``<major>.<minor>.<patch>``).
Backward incompatible (breaking) changes will only be introduced in major versions
with advance notice in the **Deprecations** section of releases.
..
You should *NOT* be adding new change log entries to this file, this
file is managed by towncrier. You *may* edit previous change logs to
@@ -8,7 +18,160 @@
.. towncrier release notes start
Pytest 3.6.3 (2018-07-04)
pytest 3.7.2 (2018-08-16)
=========================
Bug Fixes
---------
- `#3671 <https://github.com/pytest-dev/pytest/issues/3671>`_: Fix ``filterwarnings`` not being registered as a builtin mark.
- `#3768 <https://github.com/pytest-dev/pytest/issues/3768>`_, `#3789 <https://github.com/pytest-dev/pytest/issues/3789>`_: Fix test collection from packages mixed with normal directories.
- `#3771 <https://github.com/pytest-dev/pytest/issues/3771>`_: Fix infinite recursion during collection if a ``pytest_ignore_collect`` hook returns ``False`` instead of ``None``.
- `#3774 <https://github.com/pytest-dev/pytest/issues/3774>`_: Fix bug where decorated fixtures would lose functionality (for example ``@mock.patch``).
- `#3775 <https://github.com/pytest-dev/pytest/issues/3775>`_: Fix bug where importing modules or other objects with prefix ``pytest_`` prefix would raise a ``PluginValidationError``.
- `#3788 <https://github.com/pytest-dev/pytest/issues/3788>`_: Fix ``AttributeError`` during teardown of ``TestCase`` subclasses which raise an exception during ``__init__``.
- `#3804 <https://github.com/pytest-dev/pytest/issues/3804>`_: Fix traceback reporting for exceptions with ``__cause__`` cycles.
Improved Documentation
----------------------
- `#3746 <https://github.com/pytest-dev/pytest/issues/3746>`_: Add documentation for ``metafunc.config`` that had been mistakenly hidden.
pytest 3.7.1 (2018-08-02)
=========================
Bug Fixes
---------
- `#3473 <https://github.com/pytest-dev/pytest/issues/3473>`_: Raise immediately if ``approx()`` is given an expected value of a type it doesn't understand (e.g. strings, nested dicts, etc.).
- `#3712 <https://github.com/pytest-dev/pytest/issues/3712>`_: Correctly represent the dimensions of an numpy array when calling ``repr()`` on ``approx()``.
- `#3742 <https://github.com/pytest-dev/pytest/issues/3742>`_: Fix incompatibility with third party plugins during collection, which produced the error ``object has no attribute '_collectfile'``.
- `#3745 <https://github.com/pytest-dev/pytest/issues/3745>`_: Display the absolute path if ``cache_dir`` is not relative to the ``rootdir`` instead of failing.
- `#3747 <https://github.com/pytest-dev/pytest/issues/3747>`_: Fix compatibility problem with plugins and the warning code issued by fixture functions when they are called directly.
- `#3748 <https://github.com/pytest-dev/pytest/issues/3748>`_: Fix infinite recursion in ``pytest.approx`` with arrays in ``numpy<1.13``.
- `#3757 <https://github.com/pytest-dev/pytest/issues/3757>`_: Pin pathlib2 to ``>=2.2.0`` as we require ``__fspath__`` support.
- `#3763 <https://github.com/pytest-dev/pytest/issues/3763>`_: Fix ``TypeError`` when the assertion message is ``bytes`` in python 3.
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.
- `#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.
Features
--------
- `#2283 <https://github.com/pytest-dev/pytest/issues/2283>`_: New ``package`` fixture scope: fixtures are finalized when the last test of a *package* finishes. This feature is considered **experimental**, so use it sparingly.
- `#3576 <https://github.com/pytest-dev/pytest/issues/3576>`_: ``Node.add_marker`` now supports an ``append=True/False`` parameter to determine whether the mark comes last (default) or first.
- `#3579 <https://github.com/pytest-dev/pytest/issues/3579>`_: Fixture ``caplog`` now has a ``messages`` property, providing convenient access to the format-interpolated log messages without the extra data provided by the formatter/handler.
- `#3610 <https://github.com/pytest-dev/pytest/issues/3610>`_: New ``--trace`` option to enter the debugger at the start of a test.
- `#3623 <https://github.com/pytest-dev/pytest/issues/3623>`_: Introduce ``pytester.copy_example`` as helper to do acceptance tests against examples from the project.
Bug Fixes
---------
- `#2220 <https://github.com/pytest-dev/pytest/issues/2220>`_: Fix a bug where fixtures overridden by direct parameters (for example parametrization) were being instantiated even if they were not being used by a test.
- `#3695 <https://github.com/pytest-dev/pytest/issues/3695>`_: Fix ``ApproxNumpy`` initialisation argument mixup, ``abs`` and ``rel`` tolerances were flipped causing strange comparsion results.
Add tests to check ``abs`` and ``rel`` tolerances for ``np.array`` and test for expecting ``nan`` with ``np.array()``
- `#980 <https://github.com/pytest-dev/pytest/issues/980>`_: Fix truncated locals output in verbose mode.
Improved Documentation
----------------------
- `#3295 <https://github.com/pytest-dev/pytest/issues/3295>`_: Correct the usage documentation of ``--last-failed-no-failures`` by adding the missing ``--last-failed`` argument in the presented examples, because they are misleading and lead to think that the missing argument is not needed.
Trivial/Internal Changes
------------------------
- `#3519 <https://github.com/pytest-dev/pytest/issues/3519>`_: Now a ``README.md`` file is created in ``.pytest_cache`` to make it clear why the directory exists.
pytest 3.6.4 (2018-07-28)
=========================
Bug Fixes
---------
- Invoke pytest using ``-mpytest`` so ``sys.path`` does not get polluted by packages installed in ``site-packages``. (`#742 <https://github.com/pytest-dev/pytest/issues/742>`_)
Improved Documentation
----------------------
- Use ``smtp_connection`` instead of ``smtp`` in fixtures documentation to avoid possible confusion. (`#3592 <https://github.com/pytest-dev/pytest/issues/3592>`_)
Trivial/Internal Changes
------------------------
- Remove obsolete ``__future__`` imports. (`#2319 <https://github.com/pytest-dev/pytest/issues/2319>`_)
- Add CITATION to provide information on how to formally cite pytest. (`#3402 <https://github.com/pytest-dev/pytest/issues/3402>`_)
- Replace broken type annotations with type comments. (`#3635 <https://github.com/pytest-dev/pytest/issues/3635>`_)
- Pin ``pluggy`` to ``<0.8``. (`#3727 <https://github.com/pytest-dev/pytest/issues/3727>`_)
pytest 3.6.3 (2018-07-04)
=========================
Bug Fixes
@@ -54,7 +217,7 @@ Trivial/Internal Changes
<https://github.com/pytest-dev/pytest/issues/3653>`_)
Pytest 3.6.2 (2018-06-20)
pytest 3.6.2 (2018-06-20)
=========================
Bug Fixes
@@ -72,7 +235,7 @@ Bug Fixes
raises an exception. (`#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>`_)
@@ -100,7 +263,7 @@ Trivial/Internal Changes
<https://github.com/pytest-dev/pytest/issues/3567>`_)
Pytest 3.6.1 (2018-06-05)
pytest 3.6.1 (2018-06-05)
=========================
Bug Fixes
@@ -144,7 +307,7 @@ Trivial/Internal Changes
<https://github.com/pytest-dev/pytest/issues/3529>`_)
Pytest 3.6.0 (2018-05-23)
pytest 3.6.0 (2018-05-23)
=========================
Features
@@ -230,7 +393,7 @@ Trivial/Internal Changes
3.7 or newer. (`#3497 <https://github.com/pytest-dev/pytest/issues/3497>`_)
Pytest 3.5.1 (2018-04-23)
pytest 3.5.1 (2018-04-23)
=========================
@@ -282,7 +445,7 @@ Trivial/Internal Changes
<https://github.com/pytest-dev/pytest/issues/3398>`_)
Pytest 3.5.0 (2018-03-21)
pytest 3.5.0 (2018-03-21)
=========================
Deprecations and Removals
@@ -345,7 +508,7 @@ Features
``pytest_runtest_logfinish`` hooks when live logs are enabled. (`#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>`_)
- Add command line option ``--deselect`` to allow deselection of individual
@@ -434,7 +597,7 @@ Trivial/Internal Changes
<https://github.com/pytest-dev/pytest/issues/3308>`_)
Pytest 3.4.2 (2018-03-04)
pytest 3.4.2 (2018-03-04)
=========================
Bug Fixes
@@ -471,7 +634,7 @@ Trivial/Internal Changes
<https://github.com/pytest-dev/pytest/issues/3259>`_)
Pytest 3.4.1 (2018-02-20)
pytest 3.4.1 (2018-02-20)
=========================
Bug Fixes
@@ -532,7 +695,7 @@ Trivial/Internal Changes
<https://github.com/pytest-dev/pytest/issues/985>`_)
Pytest 3.4.0 (2018-01-30)
pytest 3.4.0 (2018-01-30)
=========================
Deprecations and Removals
@@ -664,7 +827,7 @@ Trivial/Internal Changes
<https://github.com/pytest-dev/pytest/issues/3129>`_)
Pytest 3.3.2 (2017-12-25)
pytest 3.3.2 (2017-12-25)
=========================
Bug Fixes
@@ -697,11 +860,11 @@ Trivial/Internal Changes
- Code cleanup. (`#3015 <https://github.com/pytest-dev/pytest/issues/3015>`_,
`#3021 <https://github.com/pytest-dev/pytest/issues/3021>`_)
- Clean up code by replacing imports and references of `_ast` to `ast`. (`#3018
<https://github.com/pytest-dev/pytest/issues/3018>`_)
- Clean up code by replacing imports and references of ``_ast`` to ``ast``.
(`#3018 <https://github.com/pytest-dev/pytest/issues/3018>`_)
Pytest 3.3.1 (2017-12-05)
pytest 3.3.1 (2017-12-05)
=========================
Bug Fixes
@@ -743,13 +906,13 @@ Trivial/Internal Changes
<https://github.com/pytest-dev/pytest/issues/2949>`_)
Pytest 3.3.0 (2017-11-23)
pytest 3.3.0 (2017-11-23)
=========================
Deprecations and Removals
-------------------------
- Pytest no longer supports Python **2.6** and **3.3**. Those Python versions
- pytest no longer supports Python **2.6** and **3.3**. Those Python versions
are EOL for some time now and incur maintenance and compatibility costs on
the pytest core team, and following up with the rest of the community we
decided that they will no longer be supported starting on this version. Users
@@ -803,7 +966,7 @@ Features
- Match ``warns`` signature to ``raises`` by adding ``match`` keyword. (`#2708
<https://github.com/pytest-dev/pytest/issues/2708>`_)
- Pytest now captures and displays output from the standard ``logging`` module.
- pytest now captures and displays output from the standard ``logging`` module.
The user can control the logging level to be captured by specifying options
in ``pytest.ini``, the command line and also during individual tests using
markers. Also, a ``caplog`` fixture is available that enables users to test
@@ -868,7 +1031,7 @@ Bug Fixes
avoids a number of potential problems. (`#2751
<https://github.com/pytest-dev/pytest/issues/2751>`_)
- Pytest no longer complains about warnings with unicode messages being
- pytest no longer complains about warnings with unicode messages being
non-ascii compatible even for ascii-compatible messages. As a result of this,
warnings with unicode messages are converted first to an ascii representation
for safety. (`#2809 <https://github.com/pytest-dev/pytest/issues/2809>`_)
@@ -920,7 +1083,7 @@ Trivial/Internal Changes
<https://github.com/pytest-dev/pytest/issues/2922>`_)
Pytest 3.2.5 (2017-11-15)
pytest 3.2.5 (2017-11-15)
=========================
Bug Fixes
@@ -931,7 +1094,7 @@ Bug Fixes
<https://github.com/pytest-dev/pytest/issues/2926>`_)
Pytest 3.2.4 (2017-11-13)
pytest 3.2.4 (2017-11-13)
=========================
Bug Fixes
@@ -980,7 +1143,7 @@ Improved Documentation
<https://github.com/pytest-dev/pytest/issues/911>`_)
Pytest 3.2.3 (2017-10-03)
pytest 3.2.3 (2017-10-03)
=========================
Bug Fixes
@@ -1020,13 +1183,13 @@ Trivial/Internal Changes
(`#2765 <https://github.com/pytest-dev/pytest/issues/2765>`_)
Pytest 3.2.2 (2017-09-06)
pytest 3.2.2 (2017-09-06)
=========================
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>`_)
- Allow tests declared as ``@staticmethod`` to use fixtures. (`#2699
@@ -1048,10 +1211,10 @@ Improved Documentation
``pytest.mark.MARKER_NAME.__call__`` (`#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
user error when acessing `pytest.config` before the argument parsing. (`#2653
<https://github.com/pytest-dev/pytest/issues/2653>`_)
user error when acessing ``pytest.config`` before the argument parsing.
(`#2653 <https://github.com/pytest-dev/pytest/issues/2653>`_)
Trivial/Internal Changes
@@ -1067,7 +1230,7 @@ Trivial/Internal Changes
<https://github.com/pytest-dev/pytest/issues/2739>`_)
Pytest 3.2.1 (2017-08-08)
pytest 3.2.1 (2017-08-08)
=========================
Bug Fixes
@@ -1097,7 +1260,7 @@ Improved Documentation
<https://github.com/pytest-dev/pytest/issues/2626>`_)
Pytest 3.2.0 (2017-07-30)
pytest 3.2.0 (2017-07-30)
=========================
Deprecations and Removals
@@ -1129,7 +1292,7 @@ Features
from parent classes or modules. (`#2516 <https://github.com/pytest-
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-
dev/pytest/issues/2518>`_)
@@ -1263,7 +1426,7 @@ Trivial/Internal Changes
<https://github.com/pytest-dev/pytest/issues/2620>`_)
Pytest 3.1.3 (2017-07-03)
pytest 3.1.3 (2017-07-03)
=========================
Bug Fixes
@@ -1309,7 +1472,7 @@ Trivial/Internal Changes
(`#2499 <https://github.com/pytest-dev/pytest/issues/2499>`_)
Pytest 3.1.2 (2017-06-08)
pytest 3.1.2 (2017-06-08)
=========================
Bug Fixes
@@ -1341,7 +1504,7 @@ Improved Documentation
and improve overall flow of the ``skipping`` docs. (#810)
Pytest 3.1.1 (2017-05-30)
pytest 3.1.1 (2017-05-30)
=========================
Bug Fixes

16
CITATION Normal file
View File

@@ -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},
}

View File

@@ -18,19 +18,11 @@ taking a lot of time to make a new one.
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
#. 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"
$ tox -e release -- <VERSION>
This will generate a commit with all the changes ready for pushing.
#. Open a PR for this branch targeting ``master``.

View File

@@ -3,6 +3,7 @@
:align: center
:alt: pytest
------
.. 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
-------
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.

View File

@@ -14,7 +14,8 @@ environment:
- TOXENV: "py34"
- TOXENV: "py35"
- TOXENV: "py36"
- TOXENV: "pypy"
- TOXENV: "py37"
# - TOXENV: "pypy" reenable when we are able to provide a scandir wheel or build scandir
- TOXENV: "py27-pexpect"
- TOXENV: "py27-xdist"
- TOXENV: "py27-trial"

View File

@@ -26,7 +26,7 @@ changelog using that instead.
If you are not sure what issue type to use, don't hesitate to ask in your PR.
Note that the ``towncrier`` tool will automatically
reflow your text, so it will work best if you stick to a single paragraph, but multiple sentences and links are OK
and encouraged. You can install ``towncrier`` and then run ``towncrier --draft``
``towncrier`` preserves multiple paragraphs and formatting (code blocks, lists, and so on), but for entries
other than ``features`` it is usually better to stick to a single paragraph to keep it concise. You can install
``towncrier`` and then run ``towncrier --draft``
if you want to get a preview of how your change will look in the final release notes.

View File

@@ -14,7 +14,7 @@
{% if definitions[category]['showcontent'] %}
{% for text, values in sections[section][category]|dictsort(by='value') %}
{% set issue_joiner = joiner(', ') %}
- {{ text }}{% if category != 'vendor' %} ({% for value in values|sort %}{{ issue_joiner() }}`{{ value }} <https://github.com/pytest-dev/pytest/issues/{{ value[1:] }}>`_{% endfor %}){% endif %}
- {% for value in values|sort %}{{ issue_joiner() }}`{{ value }} <https://github.com/pytest-dev/pytest/issues/{{ value[1:] }}>`_{% endfor %}: {{ text }}
{% endfor %}

View File

@@ -6,6 +6,10 @@ Release announcements
:maxdepth: 2
release-3.7.2
release-3.7.1
release-3.7.0
release-3.6.4
release-3.6.3
release-3.6.2
release-3.6.1

View File

@@ -124,7 +124,7 @@ The py.test Development Team
Thanks `@biern`_ for the PR.
* 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.
* Fix (`#1422`_): junit record_xml_property doesn't allow multiple records

View File

@@ -0,0 +1,24 @@
pytest-3.6.4
=======================================
pytest 3.6.4 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 http://doc.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Anthony Sottile
* Bernhard M. Wiedemann
* Bruno Oliveira
* Drew
* E Hershey
* Hugo Martins
* Vlad Shcherbina
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,41 @@
pytest-3.7.0
=======================================
The pytest team is proud to announce the 3.7.0 release!
pytest is a mature Python testing tool with more than a 2000 tests
against itself, passing on many different interpreters and platforms.
This release contains a number of bugs fixes and improvements, so users are encouraged
to take a look at the CHANGELOG:
http://doc.pytest.org/en/latest/changelog.html
For complete documentation, please visit:
http://docs.pytest.org
As usual, you can upgrade from pypi via:
pip install -U pytest
Thanks to all who contributed to this release, among them:
* Alan
* Alan Brammer
* Ammar Najjar
* Anthony Sottile
* Bruno Oliveira
* Jeffrey Rackauckas
* Kale Kundert
* Ronny Pfannschmidt
* Serhii Mozghovyi
* Tadek Teleżyński
* Wil Cooley
* abrammer
* avirlrma
* turturica
Happy testing,
The Pytest Development Team

View File

@@ -0,0 +1,21 @@
pytest-3.7.1
=======================================
pytest 3.7.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 http://doc.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Anthony Sottile
* Bruno Oliveira
* Kale Kundert
* Ronny Pfannschmidt
Happy testing,
The pytest Development Team

View File

@@ -0,0 +1,25 @@
pytest-3.7.2
=======================================
pytest 3.7.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 http://doc.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Anthony Sottile
* Bruno Oliveira
* Daniel Hahler
* Josh Holland
* Ronny Pfannschmidt
* Sankt Petersbug
* Wes Thomas
* turturica
Happy testing,
The pytest Development Team

View File

@@ -162,8 +162,8 @@ When no tests failed in the last run, or when no cached ``lastfailed`` data was
found, ``pytest`` can be configured either to run all of the tests or no tests,
using the ``--last-failed-no-failures`` option, which takes one of the following values::
pytest --last-failed-no-failures all # run all tests (default behavior)
pytest --last-failed-no-failures none # run no tests and exit
pytest --last-failed --last-failed-no-failures all # run all tests (default behavior)
pytest --last-failed --last-failed-no-failures none # run no tests and exit
The new config.cache object
--------------------------------

View File

@@ -1,7 +1,4 @@
.. _changelog:
Changelog history
=================================
.. include:: ../../CHANGELOG.rst

View File

@@ -65,7 +65,7 @@ master_doc = "contents"
# General information about the project.
project = u"pytest"
year = datetime.datetime.utcnow().year
copyright = u"2015{} , holger krekel and pytest-dev team".format(year)
copyright = u"20152018 , holger krekel and pytest-dev team"
# The language for content autogenerated by Sphinx. Refer to documentation

View File

@@ -139,7 +139,7 @@ line options while the environment is in use::
Here's how the command-line is built in the presence of ``addopts`` or the environment variable::
<pytest.ini:addopts> $PYTEST_ADDOTPS <extra command-line arguments>
<pytest.ini:addopts> $PYTEST_ADDOPTS <extra command-line arguments>
So if the user executes in the command-line::

View File

@@ -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.
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
~~~~~~~~~~~~~~~~

View File

@@ -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.5
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
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.5
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
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.5
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
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.5
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
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.5
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
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.5
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
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.5
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
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.5
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
cachedir: .pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items / 2 deselected
@@ -200,6 +200,8 @@ You can ask which markers exist for your test suite - the list includes our just
$ pytest --markers
@pytest.mark.webtest: mark a test as a webtest.
@pytest.mark.filterwarnings(warning): add a warning filter to the given test. see http://pytest.org/latest/warnings.html#pytest-mark-filterwarnings
@pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test.
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
@@ -299,10 +301,10 @@ Skip and xfail marks can also be applied in this way, see :ref:`skip/xfail with
.. note::
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
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`:
@@ -374,6 +376,8 @@ The ``--markers`` option always gives you a list of available markers::
$ pytest --markers
@pytest.mark.env(name): mark test to run only on named environment
@pytest.mark.filterwarnings(warning): add a warning filter to the given test. see http://pytest.org/latest/warnings.html#pytest-mark-filterwarnings
@pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test.
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html

View File

@@ -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.5
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
cachedir: .pytest_cache
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
collecting ... collected 2 items
@@ -84,8 +84,9 @@ interesting to just look at the collection tree::
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
collected 2 items
<YamlFile 'test_simple.yml'>
<YamlItem 'hello'>
<YamlItem 'ok'>
<Package '$REGENDOC_TMPDIR/nonpython'>
<YamlFile 'test_simple.yml'>
<YamlItem 'hello'>
<YamlItem 'ok'>
======================= no tests ran in 0.12 seconds =======================

View File

@@ -411,8 +411,10 @@ is to be run with different sets of arguments for its three arguments:
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize)::
. $ pytest -rs -q multipython.py
........................... [100%]
27 passed in 0.12 seconds
...sss...sssssssss...sss... [100%]
========================= short test summary info ==========================
SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:28: 'python3.4' not found
12 passed, 15 skipped in 0.12 seconds
Indirect parametrization of optional implementations/imports
--------------------------------------------------------------------

View File

@@ -363,7 +363,7 @@ get on the terminal - we are working on that)::
> int(s)
E ValueError: invalid literal for int() with base 10: 'qwe'
<0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python_api.py:635>:1: ValueError
<0-codegen $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/python_api.py:682>:1: ValueError
______________________ TestRaises.test_raises_doesnt _______________________
self = <failure_demo.TestRaises object at 0xdeadbeef>

View File

@@ -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.5
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
cachedir: .pytest_cache
info1: did you know that ...
did you?
@@ -416,7 +416,7 @@ Now we can profile which test functions execute the slowest::
========================= slowest 3 test durations =========================
0.30s call test_some_are_slow.py::test_funcslow2
0.20s call test_some_are_slow.py::test_funcslow1
0.13s call test_some_are_slow.py::test_funcfast
0.10s call test_some_are_slow.py::test_funcfast
========================= 3 passed in 0.12 seconds =========================
incremental testing - test steps

View File

@@ -55,18 +55,18 @@ using it::
import pytest
@pytest.fixture
def smtp():
def smtp_connection():
import smtplib
return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
def test_ehlo(smtp):
response, msg = smtp.ehlo()
def test_ehlo(smtp_connection):
response, msg = smtp_connection.ehlo()
assert response == 250
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>`
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
=========================== test session starts ============================
@@ -79,10 +79,10 @@ marked ``smtp`` fixture function. Running the test looks like this::
================================= FAILURES =================================
________________________________ test_ehlo _________________________________
smtp = <smtplib.SMTP object at 0xdeadbeef>
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
def test_ehlo(smtp):
response, msg = smtp.ehlo()
def test_ehlo(smtp_connection):
response, msg = smtp_connection.ehlo()
assert response == 250
> assert 0 # for demo purposes
E assert 0
@@ -91,18 +91,18 @@ marked ``smtp`` fixture function. Running the test looks like this::
========================= 1 failed in 0.12 seconds =========================
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
the exact protocol used by ``pytest`` to call the test function this way:
1. pytest :ref:`finds <test discovery>` the ``test_ehlo`` because
of the ``test_`` prefix. The test function needs a function argument
named ``smtp``. A matching fixture function is discovered by
looking for a fixture-marked function named ``smtp``.
named ``smtp_connection``. A matching fixture function is discovered by
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.
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
can add a ``scope="module"`` parameter to the
:py:func:`@pytest.fixture <_pytest.python.fixture>` invocation
to cause the decorated ``smtp`` fixture function to only be invoked once
per test *module* (the default is to invoke once per test *function*).
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`` 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
so that tests from multiple test modules in the directory can
@@ -181,23 +181,24 @@ access the fixture function::
import smtplib
@pytest.fixture(scope="module")
def smtp():
def smtp_connection():
return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
The name of the fixture again is ``smtp`` and you can access its result by
listing the name ``smtp`` as an input parameter in any test or fixture
function (in or below the directory where ``conftest.py`` is located)::
The name of the fixture again is ``smtp_connection`` and you can access its
result by listing the name ``smtp_connection`` as an input parameter in any
test or fixture function (in or below the directory where ``conftest.py`` is
located)::
# content of test_module.py
def test_ehlo(smtp):
response, msg = smtp.ehlo()
def test_ehlo(smtp_connection):
response, msg = smtp_connection.ehlo()
assert response == 250
assert b"smtp.gmail.com" in msg
assert 0 # for demo purposes
def test_noop(smtp):
response, msg = smtp.noop()
def test_noop(smtp_connection):
response, msg = smtp_connection.noop()
assert response == 250
assert 0 # for demo purposes
@@ -215,10 +216,10 @@ inspect what is going on and can now run the tests::
================================= FAILURES =================================
________________________________ test_ehlo _________________________________
smtp = <smtplib.SMTP object at 0xdeadbeef>
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
def test_ehlo(smtp):
response, msg = smtp.ehlo()
def test_ehlo(smtp_connection):
response, msg = smtp_connection.ehlo()
assert response == 250
assert b"smtp.gmail.com" in msg
> 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_noop _________________________________
smtp = <smtplib.SMTP object at 0xdeadbeef>
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
def test_noop(smtp):
response, msg = smtp.noop()
def test_noop(smtp_connection):
response, msg = smtp_connection.noop()
assert response == 250
> assert 0 # for demo purposes
E assert 0
@@ -239,18 +240,18 @@ inspect what is going on and can now run the tests::
========================= 2 failed in 0.12 seconds =========================
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
test functions because pytest shows the incoming argument values in the
traceback. As a result, the two test functions using ``smtp`` run as
quick as a single one because they reuse the same instance.
that the same (module-scoped) ``smtp_connection`` object was passed into the
two test functions because pytest shows the incoming argument values in the
traceback. As a result, the two test functions using ``smtp_connection`` run
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:
.. code-block:: python
@pytest.fixture(scope="session")
def smtp():
def smtp_connection():
# the returned fixture value will be shared for
# all tests needing it
...
@@ -258,6 +259,22 @@ instance, you can simply declare it:
Finally, the ``class`` scope will invoke the fixture once per test *class*.
``package`` scope (experimental)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 3.7
In pytest 3.7 the ``package`` scope has been introduced. Package-scoped fixtures
are finalized when the last test of a *package* finishes.
.. warning::
This functionality is considered **experimental** and may be removed in future
versions if hidden corner-cases or serious problems with this functionality
are discovered after it gets more usage in the wild.
Use this new feature sparingly and please make sure to report any issues you find.
Higher-scoped fixtures are instantiated first
---------------------------------------------
@@ -323,11 +340,11 @@ the code after the *yield* statement serves as the teardown code:
@pytest.fixture(scope="module")
def smtp():
smtp = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
yield smtp # provide the fixture value
def smtp_connection():
smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
yield smtp_connection # provide the fixture value
print("teardown smtp")
smtp.close()
smtp_connection.close()
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
@@ -340,7 +357,7 @@ Let's execute it::
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
function with ``scope='function'`` then fixture setup and cleanup would
occur around each single test. In either case the test
@@ -358,13 +375,13 @@ Note that we can also seamlessly use the ``yield`` syntax with ``with`` statemen
@pytest.fixture(scope="module")
def smtp():
with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp:
yield smtp # provide the fixture value
def smtp_connection():
with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection:
yield smtp_connection # provide the fixture value
The ``smtp`` connection will be closed after the test finished execution
because the ``smtp`` object automatically closes when
The ``smtp_connection`` connection will be closed after the test finished
execution because the ``smtp_connection`` object automatically closes when
the ``with`` statement ends.
Note that if an exception happens during the *setup* code (before the ``yield`` keyword), the
@@ -374,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
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
@@ -384,15 +401,15 @@ Here's the ``smtp`` fixture changed to use ``addfinalizer`` for cleanup:
@pytest.fixture(scope="module")
def smtp(request):
smtp = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
def smtp_connection(request):
smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
def fin():
print("teardown smtp")
smtp.close()
print("teardown smtp_connection")
smtp_connection.close()
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
@@ -425,7 +442,7 @@ Fixtures can introspect the requesting test context
Fixture functions can accept the :py:class:`request <FixtureRequest>` object
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::
# content of conftest.py
@@ -433,12 +450,12 @@ read an optional server URL from the test module which uses our fixture::
import smtplib
@pytest.fixture(scope="module")
def smtp(request):
def smtp_connection(request):
server = getattr(request.module, "smtpserver", "smtp.gmail.com")
smtp = smtplib.SMTP(server, 587, timeout=5)
yield smtp
print ("finalizing %s (%s)" % (smtp, server))
smtp.close()
smtp_connection = smtplib.SMTP(server, 587, timeout=5)
yield smtp_connection
print ("finalizing %s (%s)" % (smtp_connection, server))
smtp_connection.close()
We use the ``request.module`` attribute to optionally obtain an
``smtpserver`` attribute from the test module. If we just execute
@@ -456,8 +473,8 @@ server URL in its module namespace::
smtpserver = "mail.python.org" # will be read by smtp fixture
def test_showhelo(smtp):
assert 0, smtp.helo()
def test_showhelo(smtp_connection):
assert 0, smtp_connection.helo()
Running it::
@@ -466,13 +483,13 @@ Running it::
================================= FAILURES =================================
______________________________ 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 assert 0
------------------------- Captured stdout teardown -------------------------
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.
.. _`fixture-factory`:
@@ -535,13 +552,13 @@ Parametrizing fixtures
Fixture functions can be parametrized in which case they will be called
multiple times, each time executing the set of dependent tests, i. e. the
tests that depend on this fixture. Test functions do usually not need
tests that depend on this fixture. Test functions usually do not need
to be aware of their re-running. Fixture parametrization helps to
write exhaustive functional tests for components which themselves can be
configured in multiple ways.
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
through the special :py:class:`request <FixtureRequest>` object::
@@ -551,11 +568,11 @@ through the special :py:class:`request <FixtureRequest>` object::
@pytest.fixture(scope="module",
params=["smtp.gmail.com", "mail.python.org"])
def smtp(request):
smtp = smtplib.SMTP(request.param, 587, timeout=5)
yield smtp
print ("finalizing %s" % smtp)
smtp.close()
def smtp_connection(request):
smtp_connection = smtplib.SMTP(request.param, 587, timeout=5)
yield smtp_connection
print("finalizing %s" % smtp_connection)
smtp_connection.close()
The main change is the declaration of ``params`` with
:py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values
@@ -568,10 +585,10 @@ So let's just do another run::
================================= FAILURES =================================
________________________ test_ehlo[smtp.gmail.com] _________________________
smtp = <smtplib.SMTP object at 0xdeadbeef>
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
def test_ehlo(smtp):
response, msg = smtp.ehlo()
def test_ehlo(smtp_connection):
response, msg = smtp_connection.ehlo()
assert response == 250
assert b"smtp.gmail.com" in msg
> assert 0 # for demo purposes
@@ -580,10 +597,10 @@ So let's just do another run::
test_module.py:6: AssertionError
________________________ test_noop[smtp.gmail.com] _________________________
smtp = <smtplib.SMTP object at 0xdeadbeef>
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
def test_noop(smtp):
response, msg = smtp.noop()
def test_noop(smtp_connection):
response, msg = smtp_connection.noop()
assert response == 250
> assert 0 # for demo purposes
E assert 0
@@ -591,10 +608,10 @@ So let's just do another run::
test_module.py:11: AssertionError
________________________ test_ehlo[mail.python.org] ________________________
smtp = <smtplib.SMTP object at 0xdeadbeef>
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
def test_ehlo(smtp):
response, msg = smtp.ehlo()
def test_ehlo(smtp_connection):
response, msg = smtp_connection.ehlo()
assert response == 250
> 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'
@@ -604,10 +621,10 @@ So let's just do another run::
finalizing <smtplib.SMTP object at 0xdeadbeef>
________________________ test_noop[mail.python.org] ________________________
smtp = <smtplib.SMTP object at 0xdeadbeef>
smtp_connection = <smtplib.SMTP object at 0xdeadbeef>
def test_noop(smtp):
response, msg = smtp.noop()
def test_noop(smtp_connection):
response, msg = smtp_connection.noop()
assert response == 250
> assert 0 # for demo purposes
E assert 0
@@ -618,7 +635,7 @@ So let's just do another run::
4 failed in 0.12 seconds
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
different server string is expected than what arrived.
@@ -709,7 +726,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.5
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
cachedir: .pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 3 items
@@ -730,46 +747,46 @@ can use other fixtures themselves. This contributes to a modular design
of your fixtures and allows re-use of framework-specific fixtures across
many projects. As a simple example, we can extend the previous example
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
import pytest
class App(object):
def __init__(self, smtp):
self.smtp = smtp
def __init__(self, smtp_connection):
self.smtp_connection = smtp_connection
@pytest.fixture(scope="module")
def app(smtp):
return App(smtp)
def app(smtp_connection):
return App(smtp_connection)
def test_smtp_exists(app):
assert app.smtp
def test_smtp_connection_exists(app):
assert app.smtp_connection
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
=========================== 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.5
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
cachedir: .pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 2 items
test_appsetup.py::test_smtp_exists[smtp.gmail.com] PASSED [ 50%]
test_appsetup.py::test_smtp_exists[mail.python.org] PASSED [100%]
test_appsetup.py::test_smtp_connection_exists[smtp.gmail.com] PASSED [ 50%]
test_appsetup.py::test_smtp_connection_exists[mail.python.org] PASSED [100%]
========================= 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
need for the ``app`` fixture to be aware of the ``smtp`` parametrization
as pytest will fully analyse the fixture dependency graph.
need for the ``app`` fixture to be aware of the ``smtp_connection``
parametrization because pytest will fully analyse the fixture dependency graph.
Note, that the ``app`` fixture has a scope of ``module`` and uses a
module-scoped ``smtp`` fixture. The example would still work if ``smtp``
was cached on a ``session`` scope: it is fine for fixtures to use
module-scoped ``smtp_connection`` fixture. The example would still work if
``smtp_connection`` was cached on a ``session`` scope: it is fine for fixtures to use
"broader" scoped fixtures but not the other way round:
A session-scoped fixture could not use a module-scoped one in a
meaningful way.
@@ -788,7 +805,7 @@ first execute with one instance and then finalizers are called
before the next fixture instance is created. Among other things,
this eases testing of applications which create and use global state.
The following example uses two parametrized fixture, one of which is
The following example uses two parametrized fixtures, one of which is
scoped on a per-module basis, and all the functions perform ``print`` calls
to show the setup/teardown flow::
@@ -821,7 +838,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.5
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python3.6
cachedir: .pytest_cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 8 items
@@ -943,7 +960,7 @@ a generic feature of the mark mechanism:
Note that the assigned variable *must* be called ``pytestmark``, assigning e.g.
``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:
.. code-block:: ini
@@ -953,6 +970,22 @@ into an ini-file:
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 fixtures`:

View File

@@ -27,7 +27,7 @@ Install ``pytest``
2. Check that you installed the correct version::
$ pytest --version
This is pytest version 3.x.y, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py
This is pytest version 3.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py
.. _`simpletest`:

View File

@@ -187,7 +187,7 @@ You can then install your package in "editable" mode::
pip install -e .
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.
Once you are done with your work and want to make sure that your actual

View File

@@ -52,22 +52,20 @@ should add ``--strict`` to ``addopts``:
serial
.. `marker-iteration`
Marker revamp and iteration
---------------------------
.. versionadded:: 3.6
pytest's marker implementation traditionally worked by simply updating the ``__dict__`` attribute of functions to add markers, in a cumulative manner. As a result of the this, markers would unintendely be passed along class hierarchies in surprising ways plus the API for retriving them was inconsistent, as markers from parameterization would be stored differently than markers applied using the ``@pytest.mark`` decorator and markers added via ``node.add_marker``.
pytest's marker implementation traditionally worked by simply updating the ``__dict__`` attribute of functions to cumulatively add markers. As a result, markers would unintentionally be passed along class hierarchies in surprising ways. Further, the API for retrieving them was inconsistent, as markers from parameterization would be stored differently than markers applied using the ``@pytest.mark`` decorator and markers added via ``node.add_marker``.
This state of things made it technically next to impossible to use data from markers correctly without having a deep understanding of the internals, leading to subtle and hard to understand bugs in more advanced usages.
Depending on how a marker got declared/changed one would get either a ``MarkerInfo`` which might contain markers from sibling classes,
``MarkDecorators`` when marks came from parameterization or from a ``node.add_marker`` call, discarding prior marks. Also ``MarkerInfo`` acts like a single mark, when it in fact represents a merged view on multiple marks with the same name.
On top of that markers where not accessible the same way for modules, classes, and functions/methods,
in fact, markers where only accessible in functions, even if they where declared on classes/modules.
On top of that markers were not accessible the same way for modules, classes, and functions/methods.
In fact, markers were only accessible in functions, even if they were declared on classes/modules.
A new API to access markers has been introduced in pytest 3.6 in order to solve the problems with the initial design, providing :func:`_pytest.nodes.Node.iter_markers` method to iterate over markers in a consistent manner and reworking the internals, which solved great deal of problems with the initial design.
@@ -85,7 +83,7 @@ In general there are two scenarios on how markers should be handled:
1. Marks overwrite each other. Order matters but you only want to think of your mark as a single item. E.g.
``log_level('info')`` at a module level can be overwritten by ``log_level('debug')`` for a specific test.
In this case replace use ``Node.get_closest_marker(name)``:
In this case, use ``Node.get_closest_marker(name)``:
.. code-block:: python
@@ -99,7 +97,7 @@ In general there are two scenarios on how markers should be handled:
if marker:
level = marker.args[0]
2. Marks compose additive. E.g. ``skipif(condition)`` marks means you just want to evaluate all of them,
2. Marks compose in an additive manner. E.g. ``skipif(condition)`` marks mean you just want to evaluate all of them,
order doesn't even matter. You probably want to think of your marks as a set here.
In this case iterate over each mark and handle their ``*args`` and ``**kwargs`` individually.
@@ -129,27 +127,27 @@ Here is a non-exhaustive list of issues fixed by the new implementation:
* Marks don't pick up nested classes (`#199 <https://github.com/pytest-dev/pytest/issues/199>`_).
* markers stains on all related classes (`#568 <https://github.com/pytest-dev/pytest/issues/568>`_).
* Markers stain on all related classes (`#568 <https://github.com/pytest-dev/pytest/issues/568>`_).
* combining marks - args and kwargs calculation (`#2897 <https://github.com/pytest-dev/pytest/issues/2897>`_).
* Combining marks - args and kwargs calculation (`#2897 <https://github.com/pytest-dev/pytest/issues/2897>`_).
* ``request.node.get_marker('name')`` returns ``None`` for markers applied in classes (`#902 <https://github.com/pytest-dev/pytest/issues/902>`_).
* marks applied in parametrize are stored as markdecorator (`#2400 <https://github.com/pytest-dev/pytest/issues/2400>`_).
* Marks applied in parametrize are stored as markdecorator (`#2400 <https://github.com/pytest-dev/pytest/issues/2400>`_).
* fix marker interaction in a backward incompatible way (`#1670 <https://github.com/pytest-dev/pytest/issues/1670>`_).
* Fix marker interaction in a backward incompatible way (`#1670 <https://github.com/pytest-dev/pytest/issues/1670>`_).
* Refactor marks to get rid of the current "marks transfer" mechanism (`#2363 <https://github.com/pytest-dev/pytest/issues/2363>`_).
* Introduce FunctionDefinition node, use it in generate_tests (`#2522 <https://github.com/pytest-dev/pytest/issues/2522>`_).
* remove named marker attributes and collect markers in items (`#891 <https://github.com/pytest-dev/pytest/issues/891>`_).
* Remove named marker attributes and collect markers in items (`#891 <https://github.com/pytest-dev/pytest/issues/891>`_).
* skipif mark from parametrize hides module level skipif mark (`#1540 <https://github.com/pytest-dev/pytest/issues/1540>`_).
* skipif + parametrize not skipping tests (`#1296 <https://github.com/pytest-dev/pytest/issues/1296>`_).
* marker transfer incompatible with inheritance (`#535 <https://github.com/pytest-dev/pytest/issues/535>`_).
* Marker transfer incompatible with inheritance (`#535 <https://github.com/pytest-dev/pytest/issues/535>`_).
More details can be found in the `original PR <https://github.com/pytest-dev/pytest/pull/3317>`_.

View File

@@ -161,6 +161,25 @@ Skip a test function if a condition is ``True``.
: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
@@ -768,7 +787,7 @@ TestReport
_Result
~~~~~~~
.. autoclass:: pluggy._Result
.. autoclass:: pluggy.callers._Result
:members:
Special Variables

View File

@@ -171,6 +171,18 @@ for example::
>>> sys.last_value
AssertionError('assert result == "ok"',)
.. _trace-option:
Dropping to PDB_ (Python Debugger) at the start of a test
----------------------------------------------------------
``pytest`` allows one to drop into the PDB_ prompt immediately at the start of each test via a command line option::
pytest --trace
This will invoke the Python debugger at the start of every test.
.. _breakpoints:
Setting breakpoints

View File

@@ -386,11 +386,52 @@ return a result object, with which we can assert the tests' outcomes.
result.assert_outcomes(passed=4)
additionally it is possible to copy examples for a example folder before running pytest on it
.. code:: ini
# content of pytest.ini
[pytest]
pytester_example_dir = .
.. code:: python
# content of test_example.py
def test_plugin(testdir):
testdir.copy_example("test_example.py")
testdir.runpytest("-k", "test_example")
def test_example():
pass
.. code::
$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 2 items
test_example.py .. [100%]
============================= warnings summary =============================
test_example.py::test_plugin
$REGENDOC_TMPDIR/test_example.py:4: PytestExerimentalApiWarning: testdir.copy_example is an experimental api that may change over time
testdir.copy_example("test_example.py")
-- Docs: http://doc.pytest.org/en/latest/warnings.html
=================== 2 passed, 1 warnings in 0.12 seconds ===================
For more information about the result object that ``runpytest()`` returns, and
the methods that it provides please check out the :py:class:`RunResult
<_pytest.pytester.RunResult>` documentation.
.. _`writinghooks`:
Writing hook functions

View File

@@ -10,6 +10,7 @@ package = "pytest"
package_dir = "src"
filename = "CHANGELOG.rst"
directory = "changelog/"
title_format = "pytest {version} ({project_date})"
template = "changelog/_template.rst"
[[tool.towncrier.type]]

7
scripts/fail Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env python
"""Used by .pre-commit-config.yaml"""
import sys
if __name__ == "__main__":
print(" ".join(sys.argv[1:]))
sys.exit(1)

View File

@@ -1,14 +1,13 @@
"""
Invoke development tasks.
"""
import argparse
from colorama import init, Fore
from pathlib import Path
from subprocess import check_output, check_call
import invoke
from subprocess import check_output, check_call, call
@invoke.task(help={"version": "version being released"})
def announce(ctx, version):
def announce(version):
"""Generates a new release announcement entry in the docs."""
# Get our list of authors
stdout = check_output(["git", "describe", "--abbrev=0", "--tags"])
@@ -38,7 +37,7 @@ def announce(ctx, version):
"../doc/en/announce/release-{}.rst".format(version)
)
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
index_path = Path(__file__).parent.joinpath("../doc/en/announce/index.rst")
@@ -50,69 +49,63 @@ def announce(ctx, version):
if line != new_line:
lines.insert(index, new_line)
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:
print(
"[generate.announce] Skip {} (already contains release)".format(
index_path.name
)
f"{Fore.CYAN}[generate.announce] {Fore.RESET}Skip {index_path.name} (already contains release)"
)
break
check_call(["git", "add", str(target)])
@invoke.task()
def regen(ctx):
def regen():
"""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"])
@invoke.task()
def make_tag(ctx, version):
"""Create a new, local tag for the release, only if the repository is clean."""
from git import Repo
repo = Repo(".")
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)
def fix_formatting():
"""Runs pre-commit in all files to ensure they are formatted correctly"""
print(
f"{Fore.CYAN}[generate.fix linting] {Fore.RESET}Fixing formatting using pre-commit"
)
call(["pre-commit", "run", "--all-files"])
@invoke.task(help={"version": "version being released"})
def pre_release(ctx, version):
def pre_release(version):
"""Generates new docs, release announcements and creates a local tag."""
announce(ctx, version)
regen(ctx)
changelog(ctx, version, write_out=True)
announce(version)
regen()
changelog(version, write_out=True)
fix_formatting()
msg = "Preparing release version {}".format(version)
check_call(["git", "commit", "-a", "-m", msg])
make_tag(ctx, version)
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(
help={
"version": "version being released",
"write_out": "write changes to the actual changelog",
}
)
def changelog(ctx, version, write_out=False):
def changelog(version, write_out=False):
if write_out:
addopts = []
else:
addopts = ["--draft"]
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()

View File

@@ -69,19 +69,23 @@ def main():
# if _PYTEST_SETUP_SKIP_PLUGGY_DEP is set, skip installing pluggy;
# used by tox.ini to test with pluggy master
if "_PYTEST_SETUP_SKIP_PLUGGY_DEP" not in os.environ:
install_requires.append("pluggy>=0.5,<0.7")
install_requires.append("pluggy>=0.7")
environment_marker_support_level = get_environment_marker_support_level()
if environment_marker_support_level >= 2:
install_requires.append('funcsigs;python_version<"3.0"')
install_requires.append('pathlib2>=2.2.0;python_version<"3.6"')
install_requires.append('colorama;sys_platform=="win32"')
elif environment_marker_support_level == 1:
extras_require[':python_version<"3.0"'] = ["funcsigs"]
extras_require[':python_version<"3.6"'] = ["pathlib2>=2.2.0"]
extras_require[':sys_platform=="win32"'] = ["colorama"]
else:
if sys.platform == "win32":
install_requires.append("colorama")
if sys.version_info < (3, 0):
install_requires.append("funcsigs")
if sys.version_info < (3, 6):
install_requires.append("pathlib2>=2.2.0")
setup(
name="pytest",

View File

@@ -1,5 +1,6 @@
from __future__ import absolute_import, division, print_function
import inspect
import pprint
import sys
import traceback
from inspect import CO_VARARGS, CO_VARKEYWORDS
@@ -448,6 +449,7 @@ class ExceptionInfo(object):
abspath=False,
tbfilter=True,
funcargs=False,
truncate_locals=True,
):
""" return str()able representation of this exception info.
showlocals: show locals per traceback entry
@@ -472,6 +474,7 @@ class ExceptionInfo(object):
abspath=abspath,
tbfilter=tbfilter,
funcargs=funcargs,
truncate_locals=truncate_locals,
)
return fmt.repr_excinfo(self)
@@ -511,6 +514,7 @@ class FormattedExcinfo(object):
abspath = attr.ib(default=True)
tbfilter = attr.ib(default=True)
funcargs = attr.ib(default=False)
truncate_locals = attr.ib(default=True)
astcache = attr.ib(default=attr.Factory(dict), init=False, repr=False)
def _getindent(self, source):
@@ -593,7 +597,10 @@ class FormattedExcinfo(object):
# This formatting could all be handled by the
# _repr() function, which is only reprlib.Repr in
# disguise, so is very configurable.
str_repr = self._saferepr(value)
if self.truncate_locals:
str_repr = self._saferepr(value)
else:
str_repr = pprint.pformat(value)
# if len(str_repr) < 70 or not isinstance(value,
# (list, tuple, dict)):
lines.append("%-10s = %s" % (name, str_repr))
@@ -712,7 +719,9 @@ class FormattedExcinfo(object):
repr_chain = []
e = excinfo.value
descr = None
while e is not None:
seen = set()
while e is not None and id(e) not in seen:
seen.add(id(e))
if excinfo:
reprtraceback = self.repr_traceback(excinfo)
reprcrash = excinfo._getreprcrash()

View File

@@ -1,4 +1,4 @@
from __future__ import absolute_import, division, generators, print_function
from __future__ import absolute_import, division, print_function
import ast
from ast import PyCF_ONLY_AST as _AST_FLAG
@@ -152,12 +152,7 @@ class Source(object):
return "\n".join(self.lines)
def compile(
self,
filename=None,
mode="exec",
flag=generators.compiler_flag,
dont_inherit=0,
_genframe=None,
self, filename=None, mode="exec", flag=0, dont_inherit=0, _genframe=None
):
""" return compiled code object. if filename is None
invent an artificial filename which displays
@@ -201,9 +196,7 @@ class Source(object):
#
def compile_(
source, filename=None, mode="exec", flags=generators.compiler_flag, dont_inherit=0
):
def compile_(source, filename=None, mode="exec", flags=0, dont_inherit=0):
""" compile the given source to a raw code object,
and maintain an internal cache which allows later
retrieval of the source code for the code object

View File

@@ -425,20 +425,18 @@ def _format_assertmsg(obj):
# contains a newline it gets escaped, however if an object has a
# .__repr__() which contains newlines it does not get escaped.
# However in either case we want to preserve the newline.
if isinstance(obj, six.text_type) or isinstance(obj, six.binary_type):
s = obj
is_repr = False
else:
s = py.io.saferepr(obj)
is_repr = True
if isinstance(s, six.text_type):
t = six.text_type
else:
t = six.binary_type
s = s.replace(t("\n"), t("\n~")).replace(t("%"), t("%%"))
if is_repr:
s = s.replace(t("\\n"), t("\n~"))
return s
replaces = [(u"\n", u"\n~"), (u"%", u"%%")]
if not isinstance(obj, six.string_types):
obj = py.io.saferepr(obj)
replaces.append((u"\\n", u"\n~"))
if isinstance(obj, bytes):
replaces = [(r1.encode(), r2.encode()) for r1, r2 in replaces]
for r1, r2 in replaces:
obj = obj.replace(r1, r2)
return obj
def _should_repr_global_name(obj):

View File

@@ -5,38 +5,50 @@ the name cache was not chosen to ensure pluggy automatically
ignores the external pytest-cache
"""
from __future__ import absolute_import, division, print_function
from collections import OrderedDict
import py
import six
import attr
import pytest
import json
import os
from os.path import sep as _sep, altsep as _altsep
import shutil
from . import paths
from .compat import _PY2 as PY2, Path
README_CONTENT = u"""\
# pytest cache directory #
This directory contains data from the pytest's cache plugin,
which provides the `--lf` and `--ff` options, as well as the `cache` fixture.
**Do not** commit this to version control.
See [the docs](https://docs.pytest.org/en/latest/cache.html) for more information.
"""
@attr.s
class Cache(object):
def __init__(self, config):
self.config = config
self._cachedir = Cache.cache_dir_from_config(config)
self.trace = config.trace.root.get("cache")
if config.getoption("cacheclear"):
self.trace("clearing cachedir")
if self._cachedir.check():
self._cachedir.remove()
self._cachedir.mkdir()
_cachedir = attr.ib(repr=False)
_warn = attr.ib(repr=False)
@classmethod
def for_config(cls, config):
cachedir = cls.cache_dir_from_config(config)
if config.getoption("cacheclear") and cachedir.exists():
shutil.rmtree(str(cachedir))
cachedir.mkdir()
return cls(cachedir, config.warn)
@staticmethod
def cache_dir_from_config(config):
cache_dir = config.getini("cache_dir")
cache_dir = os.path.expanduser(cache_dir)
cache_dir = os.path.expandvars(cache_dir)
if os.path.isabs(cache_dir):
return py.path.local(cache_dir)
else:
return config.rootdir.join(cache_dir)
return paths.resolve_from_str(config.getini("cache_dir"), config.rootdir)
def warn(self, fmt, **args):
self._warn(code="I9", message=fmt.format(**args) if args else fmt)
def makedir(self, name):
""" return a directory path object with the given name. If the
@@ -48,12 +60,15 @@ class Cache(object):
Make sure the name contains your plugin or application
identifiers to prevent clashes with other cache users.
"""
if _sep in name or _altsep is not None and _altsep in name:
name = Path(name)
if len(name.parts) > 1:
raise ValueError("name is not allowed to contain path separators")
return self._cachedir.ensure_dir("d", name)
res = self._cachedir.joinpath("d", name)
res.mkdir(exist_ok=True, parents=True)
return py.path.local(res)
def _getvaluepath(self, key):
return self._cachedir.join("v", *key.split("/"))
return self._cachedir.joinpath("v", Path(key))
def get(self, key, default):
""" return cached value for the given key. If no value
@@ -67,13 +82,11 @@ class Cache(object):
"""
path = self._getvaluepath(key)
if path.check():
try:
with path.open("r") as f:
return json.load(f)
except ValueError:
self.trace("cache-invalid at %s" % (path,))
return default
try:
with path.open("r") as f:
return json.load(f)
except (ValueError, IOError, OSError):
return default
def set(self, key, value):
""" save value for the given key.
@@ -86,22 +99,25 @@ class Cache(object):
"""
path = self._getvaluepath(key)
try:
path.dirpath().ensure_dir()
except (py.error.EEXIST, py.error.EACCES):
self.config.warn(
code="I9", message="could not create cache path %s" % (path,)
)
path.parent.mkdir(exist_ok=True, parents=True)
except (IOError, OSError):
self.warn("could not create cache path {path}", path=path)
return
try:
f = path.open("w")
except py.error.ENOTDIR:
self.config.warn(
code="I9", message="cache could not write path %s" % (path,)
)
f = path.open("wb" if PY2 else "w")
except (IOError, OSError):
self.warn("cache could not write path {path}", path=path)
else:
with f:
self.trace("cache-write %s: %r" % (key, value))
json.dump(value, f, indent=2, sort_keys=True)
self._ensure_readme()
def _ensure_readme(self):
if self._cachedir.is_dir():
readme_path = self._cachedir / "README.md"
if not readme_path.is_file():
readme_path.write_text(README_CONTENT)
class LFPlugin(object):
@@ -273,7 +289,7 @@ def pytest_cmdline_main(config):
@pytest.hookimpl(tryfirst=True)
def pytest_configure(config):
config.cache = Cache(config)
config.cache = Cache.for_config(config)
config.pluginmanager.register(LFPlugin(config), "lfplugin")
config.pluginmanager.register(NFPlugin(config), "nfplugin")
@@ -296,41 +312,47 @@ def cache(request):
def pytest_report_header(config):
if config.option.verbose:
relpath = py.path.local().bestrelpath(config.cache._cachedir)
return "cachedir: %s" % relpath
cachedir = config.cache._cachedir
# TODO: evaluate generating upward relative paths
# starting with .., ../.. if sensible
try:
displaypath = cachedir.relative_to(config.rootdir)
except ValueError:
displaypath = cachedir
return "cachedir: {}".format(displaypath)
def cacheshow(config, session):
from pprint import pprint
from pprint import pformat
tw = py.io.TerminalWriter()
tw.line("cachedir: " + str(config.cache._cachedir))
if not config.cache._cachedir.check():
if not config.cache._cachedir.is_dir():
tw.line("cache is empty")
return 0
dummy = object()
basedir = config.cache._cachedir
vdir = basedir.join("v")
vdir = basedir / "v"
tw.sep("-", "cache values")
for valpath in sorted(vdir.visit(lambda x: x.isfile())):
key = valpath.relto(vdir).replace(valpath.sep, "/")
for valpath in sorted(x for x in vdir.rglob("*") if x.is_file()):
key = valpath.relative_to(vdir)
val = config.cache.get(key, dummy)
if val is dummy:
tw.line("%s contains unreadable content, " "will be ignored" % key)
else:
tw.line("%s contains:" % key)
stream = py.io.TextIO()
pprint(val, stream=stream)
for line in stream.getvalue().splitlines():
for line in pformat(val).splitlines():
tw.line(" " + line)
ddir = basedir.join("d")
if ddir.isdir() and ddir.listdir():
ddir = basedir / "d"
if ddir.is_dir():
contents = sorted(ddir.rglob("*"))
tw.sep("-", "cache directories")
for p in sorted(basedir.join("d").visit()):
for p in contents:
# if p.check(dir=1):
# print("%s/" % p.relto(basedir))
if p.isfile():
key = p.relto(basedir)
tw.line("%s is a file of length %d" % (key, p.size()))
if p.is_file():
key = p.relative_to(basedir)
tw.line("{} is a file of length {:d}".format(key, p.stat().st_size))
return 0

View File

@@ -22,6 +22,7 @@ except ImportError: # pragma: no cover
# Only available in Python 3.4+ or as a backport
enum = None
__all__ = ["Path"]
_PY3 = sys.version_info > (3, 0)
_PY2 = not _PY3
@@ -32,7 +33,6 @@ if _PY3:
else:
from funcsigs import signature, Parameter as Parameter
NoneType = type(None)
NOTSET = object()
@@ -40,9 +40,15 @@ PY35 = sys.version_info[:2] >= (3, 5)
PY36 = sys.version_info[:2] >= (3, 6)
MODULE_NOT_FOUND_ERROR = "ModuleNotFoundError" if PY36 else "ImportError"
if PY36:
from pathlib import Path
else:
from pathlib2 import Path
if _PY3:
from collections.abc import MutableMapping as MappingMixin # noqa
from collections.abc import Mapping, Sequence # noqa
from collections.abc import MutableMapping as MappingMixin
from collections.abc import Mapping, Sequence
else:
# those raise DeprecationWarnings in Python >=3.7
from collections import MutableMapping as MappingMixin # noqa
@@ -78,6 +84,7 @@ def iscoroutinefunction(func):
def getlocation(function, curdir):
function = get_real_func(function)
fn = py.path.local(inspect.getfile(function))
lineno = function.__code__.co_firstlineno
if fn.relto(curdir):
@@ -221,12 +228,31 @@ else:
return val.encode("unicode-escape")
class _PytestWrapper(object):
"""Dummy wrapper around a function object for internal use only.
Used to correctly unwrap the underlying function object
when we are creating fixtures, because we wrap the function object ourselves with a decorator
to issue warnings when the fixture function is called directly.
"""
def __init__(self, obj):
self.obj = obj
def get_real_func(obj):
""" gets the real function object of the (possibly) wrapped object by
functools.wraps or functools.partial.
"""
start_obj = obj
for i in range(100):
# __pytest_wrapped__ is set by @pytest.fixture when wrapping the fixture function
# to trigger a warning if it gets called directly instead of by pytest: we don't
# want to unwrap further than this otherwise we lose useful wrappings like @mock.patch (#3774)
new_obj = getattr(obj, "__pytest_wrapped__", None)
if isinstance(new_obj, _PytestWrapper):
obj = new_obj.obj
break
new_obj = getattr(obj, "__wrapped__", None)
if new_obj is None:
break
@@ -242,6 +268,21 @@ def get_real_func(obj):
return obj
def get_real_method(obj, holder):
"""
Attempts to obtain the real function object that might be wrapping ``obj``, while at the same time
returning a bound method to ``holder`` if the original object was a bound method.
"""
try:
is_method = hasattr(obj, "__func__")
obj = get_real_func(obj)
except Exception:
return obj
if is_method and hasattr(obj, "__get__") and callable(obj.__get__):
obj = obj.__get__(holder)
return obj
def getfslineno(obj):
# xxx let decorators etc specify a sane ordering
obj = get_real_func(obj)

View File

@@ -1,6 +1,7 @@
""" command line options, ini-file and conftest.py processing. """
from __future__ import absolute_import, division, print_function
import argparse
import inspect
import shlex
import traceback
import types
@@ -71,7 +72,7 @@ def main(args=None, plugins=None):
return 4
class cmdline(object): # NOQA compatibility namespace
class cmdline(object): # compatibility namespace
main = staticmethod(main)
@@ -252,6 +253,10 @@ class PytestPluginManager(PluginManager):
method = getattr(plugin, name)
opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name)
# consider only actual functions for hooks (#3775)
if not inspect.isroutine(method):
return
# collect unmarked hooks as long as they have the `pytest_' prefix
if opts is None and name.startswith("pytest_"):
opts = {}

View File

@@ -174,23 +174,23 @@ class Argument(object):
if isinstance(typ, six.string_types):
if typ == "choice":
warnings.warn(
"type argument to addoption() is a string %r."
" For parsearg this is optional and when supplied"
" should be a type."
"`type` argument to addoption() is the string %r."
" For choices this is optional and can be omitted, "
" but when supplied should be a type (for example `str` or `int`)."
" (options: %s)" % (typ, names),
DeprecationWarning,
stacklevel=3,
stacklevel=4,
)
# argparse expects a type here take it from
# the type of the first element
attrs["type"] = type(attrs["choices"][0])
else:
warnings.warn(
"type argument to addoption() is a string %r."
" For parsearg this should be a type."
"`type` argument to addoption() is the string %r, "
" but when supplied should be a type (for example `str` or `int`)."
" (options: %s)" % (typ, names),
DeprecationWarning,
stacklevel=3,
stacklevel=4,
)
attrs["type"] = Argument._typ_map[typ]
# used in test_parseopt -> test_parse_defaultgetter

View File

@@ -5,6 +5,8 @@ import sys
import os
from doctest import UnexpectedException
from _pytest.config import hookimpl
try:
from builtins import breakpoint # noqa
@@ -28,6 +30,12 @@ def pytest_addoption(parser):
help="start a custom interactive Python debugger on errors. "
"For example: --pdbcls=IPython.terminal.debugger:TerminalPdb",
)
group._addoption(
"--trace",
dest="trace",
action="store_true",
help="Immediately break when running each test.",
)
def pytest_configure(config):
@@ -38,6 +46,8 @@ def pytest_configure(config):
else:
pdb_cls = pdb.Pdb
if config.getvalue("trace"):
config.pluginmanager.register(PdbTrace(), "pdbtrace")
if config.getvalue("usepdb"):
config.pluginmanager.register(PdbInvoke(), "pdbinvoke")
@@ -71,7 +81,7 @@ class pytestPDB(object):
_pdb_cls = pdb.Pdb
@classmethod
def set_trace(cls):
def set_trace(cls, set_break=True):
""" invoke PDB set_trace debugging, dropping any IO capturing. """
import _pytest.config
@@ -84,7 +94,8 @@ class pytestPDB(object):
tw.line()
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
cls._pluginmanager.hook.pytest_enter_pdb(config=cls._config)
cls._pdb_cls().set_trace(frame)
if set_break:
cls._pdb_cls().set_trace(frame)
class PdbInvoke(object):
@@ -104,6 +115,30 @@ class PdbInvoke(object):
post_mortem(tb)
class PdbTrace(object):
@hookimpl(hookwrapper=True)
def pytest_pyfunc_call(self, pyfuncitem):
_test_pytest_function(pyfuncitem)
yield
def _test_pytest_function(pyfuncitem):
pytestPDB.set_trace(set_break=False)
testfunction = pyfuncitem.obj
pyfuncitem.obj = pdb.runcall
if pyfuncitem._isyieldedfunction():
arg_list = list(pyfuncitem._args)
arg_list.insert(0, testfunction)
pyfuncitem._args = tuple(arg_list)
else:
if "func" in pyfuncitem._fixtureinfo.argnames:
raise ValueError("--trace can't be used with a fixture named func!")
pyfuncitem.funcargs["func"] = testfunction
new_list = list(pyfuncitem._fixtureinfo.argnames)
new_list.append("func")
pyfuncitem._fixtureinfo.argnames = tuple(new_list)
def _enter_pdb(node, excinfo, rep):
# XXX we re-use the TerminalReporter's terminalwriter
# because this seems to avoid some encoding related troubles

View File

@@ -22,6 +22,12 @@ FUNCARG_PREFIX = (
"Please remove the prefix and use the @pytest.fixture decorator instead."
)
FIXTURE_FUNCTION_CALL = (
"Fixture {name} called directly. Fixtures are not meant to be called directly, "
"are created automatically when test functions request them as parameters. "
"See https://docs.pytest.org/en/latest/fixture.html for more information."
)
CFG_PYTEST_SECTION = (
"[pytest] section in {filename} files is deprecated, use [tool:pytest] instead."
)
@@ -65,3 +71,7 @@ PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST = RemovedInPytest4Warning(
"because it affects the entire directory tree in a non-explicit way.\n"
"Please move it to the top level conftest file instead."
)
PYTEST_NAMESPACE = RemovedInPytest4Warning(
"pytest_namespace is deprecated and will be removed soon"
)

View File

@@ -0,0 +1,13 @@
class PytestExerimentalApiWarning(FutureWarning):
"warning category used to denote experiments in pytest"
@classmethod
def simple(cls, apiname):
return cls(
"{apiname} is an experimental api that may change over time".format(
apiname=apiname
)
)
PYTESTER_COPY_EXAMPLE = PytestExerimentalApiWarning.simple("testdir.copy_example")

View File

@@ -2,9 +2,12 @@ from __future__ import absolute_import, division, print_function
import functools
import inspect
import os
import sys
import warnings
from collections import OrderedDict, deque, defaultdict
import six
from more_itertools import flatten
import attr
@@ -27,7 +30,10 @@ from _pytest.compat import (
getfuncargnames,
safe_getattr,
FuncargnamesCompatAttr,
get_real_method,
_PytestWrapper,
)
from _pytest.deprecated import FIXTURE_FUNCTION_CALL, RemovedInPytest4Warning
from _pytest.outcomes import fail, TEST_OUTCOME
FIXTURE_MSG = 'fixtures cannot have "pytest_funcarg__" prefix and be decorated with @pytest.fixture:\n{}'
@@ -45,6 +51,7 @@ def pytest_sessionstart(session):
scopename2class.update(
{
"package": _pytest.python.Package,
"class": _pytest.python.Class,
"module": _pytest.python.Module,
"function": _pytest.nodes.Item,
@@ -58,6 +65,7 @@ scopename2class = {}
scope2props = dict(session=())
scope2props["package"] = ("fspath",)
scope2props["module"] = ("fspath", "module")
scope2props["class"] = scope2props["module"] + ("cls",)
scope2props["instance"] = scope2props["class"] + ("instance",)
@@ -80,6 +88,21 @@ def scopeproperty(name=None, doc=None):
return decoratescope
def get_scope_package(node, fixturedef):
import pytest
cls = pytest.Package
current = node
fixture_package_name = os.path.join(fixturedef.baseid, "__init__.py")
while current and (
type(current) is not cls or fixture_package_name != current.nodeid
):
current = current.parent
if current is None:
return node.session
return current
def get_scope_node(node, scope):
cls = scopename2class.get(scope)
if cls is None:
@@ -173,9 +196,11 @@ def get_parametrized_fixture_keys(item, scopenum):
continue
if scopenum == 0: # session
key = (argname, param_index)
elif scopenum == 1: # module
elif scopenum == 1: # package
key = (argname, param_index, item.fspath.dirpath())
elif scopenum == 2: # module
key = (argname, param_index, item.fspath)
elif scopenum == 2: # class
elif scopenum == 3: # class
key = (argname, param_index, item.fspath, item.cls)
yield key
@@ -274,11 +299,43 @@ def get_direct_param_fixture_func(request):
return request.param
@attr.s(slots=True)
class FuncFixtureInfo(object):
def __init__(self, argnames, names_closure, name2fixturedefs):
self.argnames = argnames
self.names_closure = names_closure
self.name2fixturedefs = name2fixturedefs
# original function argument names
argnames = attr.ib(type=tuple)
# argnames that function immediately requires. These include argnames +
# fixture names specified via usefixtures and via autouse=True in fixture
# definitions.
initialnames = attr.ib(type=tuple)
names_closure = attr.ib(type="List[str]")
name2fixturedefs = attr.ib(type="List[str, List[FixtureDef]]")
def prune_dependency_tree(self):
"""Recompute names_closure from initialnames and name2fixturedefs
Can only reduce names_closure, which means that the new closure will
always be a subset of the old one. The order is preserved.
This method is needed because direct parametrization may shadow some
of the fixtures that were included in the originally built dependency
tree. In this way the dependency tree can get pruned, and the closure
of argnames may get reduced.
"""
closure = set()
working_set = set(self.initialnames)
while working_set:
argname = working_set.pop()
# argname may be smth not included in the original names_closure,
# in which case we ignore it. This currently happens with pseudo
# FixtureDefs which wrap 'get_direct_param_fixture_func(request)'.
# So they introduce the new dependency 'request' which might have
# been missing in the original tree (closure).
if argname not in closure and argname in self.names_closure:
closure.add(argname)
if argname in self.name2fixturedefs:
working_set.update(self.name2fixturedefs[argname][-1].argnames)
self.names_closure[:] = sorted(closure, key=self.names_closure.index)
class FixtureRequest(FuncargnamesCompatAttr):
@@ -580,7 +637,10 @@ class FixtureRequest(FuncargnamesCompatAttr):
if scope == "function":
# this might also be a non-function Item despite its attribute name
return self._pyfuncitem
node = get_scope_node(self._pyfuncitem, scope)
if scope == "package":
node = get_scope_package(self._pyfuncitem, self._fixturedef)
else:
node = get_scope_node(self._pyfuncitem, scope)
if node is None and scope == "class":
# fallback to function item itself
node = self._pyfuncitem
@@ -624,7 +684,7 @@ class ScopeMismatchError(Exception):
"""
scopes = "session module class function".split()
scopes = "session package module class function".split()
scopenum_function = scopes.index("function")
@@ -734,23 +794,26 @@ def call_fixture_func(fixturefunc, request, kwargs):
if yieldctx:
it = fixturefunc(**kwargs)
res = next(it)
def teardown():
try:
next(it)
except StopIteration:
pass
else:
fail_fixturefunc(
fixturefunc, "yield_fixture function has more than one 'yield'"
)
request.addfinalizer(teardown)
finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, it)
request.addfinalizer(finalizer)
else:
res = fixturefunc(**kwargs)
return res
def _teardown_yield_fixture(fixturefunc, it):
"""Executes the teardown of a fixture function by advancing the iterator after the
yield and ensure the iteration ends (if not it means there is more than one yield in the function)"""
try:
next(it)
except StopIteration:
pass
else:
fail_fixturefunc(
fixturefunc, "yield_fixture function has more than one 'yield'"
)
class FixtureDef(object):
""" A container for a factory definition. """
@@ -841,15 +904,10 @@ class FixtureDef(object):
)
def pytest_fixture_setup(fixturedef, request):
""" Execution of fixture setup. """
kwargs = {}
for argname in fixturedef.argnames:
fixdef = request._get_active_fixturedef(argname)
result, arg_cache_key, exc = fixdef.cached_result
request._check_scope(argname, request.scope, fixdef.scope)
kwargs[argname] = result
def resolve_fixture_function(fixturedef, request):
"""Gets the actual callable that can be called to obtain the fixture value, dealing with unittest-specific
instances and bound methods.
"""
fixturefunc = fixturedef.func
if fixturedef.unittest:
if request.instance is not None:
@@ -863,6 +921,19 @@ def pytest_fixture_setup(fixturedef, request):
fixturefunc = getimfunc(fixturedef.func)
if fixturefunc != fixturedef.func:
fixturefunc = fixturefunc.__get__(request.instance)
return fixturefunc
def pytest_fixture_setup(fixturedef, request):
""" Execution of fixture setup. """
kwargs = {}
for argname in fixturedef.argnames:
fixdef = request._get_active_fixturedef(argname)
result, arg_cache_key, exc = fixdef.cached_result
request._check_scope(argname, request.scope, fixdef.scope)
kwargs[argname] = result
fixturefunc = resolve_fixture_function(fixturedef, request)
my_cache_key = request.param_index
try:
result = call_fixture_func(fixturefunc, request, kwargs)
@@ -881,6 +952,41 @@ def _ensure_immutable_ids(ids):
return tuple(ids)
def wrap_function_to_warning_if_called_directly(function, fixture_marker):
"""Wrap the given fixture function so we can issue warnings about it being called directly, instead of
used as an argument in a test function.
"""
is_yield_function = is_generator(function)
msg = FIXTURE_FUNCTION_CALL.format(name=fixture_marker.name or function.__name__)
warning = RemovedInPytest4Warning(msg)
if is_yield_function:
@functools.wraps(function)
def result(*args, **kwargs):
__tracebackhide__ = True
warnings.warn(warning, stacklevel=3)
for x in function(*args, **kwargs):
yield x
else:
@functools.wraps(function)
def result(*args, **kwargs):
__tracebackhide__ = True
warnings.warn(warning, stacklevel=3)
return function(*args, **kwargs)
if six.PY2:
result.__wrapped__ = function
# keep reference to the original function in our own custom attribute so we don't unwrap
# further than this point and lose useful wrappings like @mock.patch (#3774)
result.__pytest_wrapped__ = _PytestWrapper(function)
return result
@attr.s(frozen=True)
class FixtureFunctionMarker(object):
scope = attr.ib()
@@ -898,6 +1004,8 @@ class FixtureFunctionMarker(object):
"fixture is being applied more than once to the same function"
)
function = wrap_function_to_warning_if_called_directly(function, self)
function._pytestfixturefunction = self
return function
@@ -905,16 +1013,27 @@ class FixtureFunctionMarker(object):
def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
"""Decorator to mark a fixture factory function.
This decorator can be used (with or without parameters) to define a
fixture function. The name of the fixture function can later be
referenced to cause its invocation ahead of running tests: test
modules or classes can use the pytest.mark.usefixtures(fixturename)
marker. Test functions can directly use fixture names as input
This decorator can be used, with or without parameters, to define a
fixture function.
The name of the fixture function can later be referenced to cause its
invocation ahead of running tests: test
modules or classes can use the ``pytest.mark.usefixtures(fixturename)``
marker.
Test functions can directly use fixture names as input
arguments in which case the fixture instance returned from the fixture
function will be injected.
Fixtures can provide their values to test functions using ``return`` or ``yield``
statements. When using ``yield`` the code block after the ``yield`` statement is executed
as teardown code regardless of the test outcome, and must yield exactly once.
:arg scope: the scope for which this fixture is shared, one of
"function" (default), "class", "module" or "session".
``"function"`` (default), ``"class"``, ``"module"``,
``"package"`` or ``"session"``.
``"package"`` is considered **experimental** at this time.
:arg params: an optional list of parameters which will cause multiple
invocations of the fixture function and all of the tests
@@ -935,10 +1054,6 @@ def fixture(scope="function", params=None, autouse=False, ids=None, name=None):
to resolve this is to name the decorated function
``fixture_<fixturename>`` and then use
``@pytest.fixture(name='<fixturename>')``.
Fixtures can optionally provide their values to test functions using a ``yield`` statement,
instead of ``return``. In this case, the code block after the ``yield`` statement is executed
as teardown code regardless of the test outcome. A fixture function must yield exactly once.
"""
if callable(scope) and params is None and autouse is False:
# direct decoration
@@ -954,13 +1069,7 @@ def yield_fixture(scope="function", params=None, autouse=False, ids=None, name=N
.. deprecated:: 3.0
Use :py:func:`pytest.fixture` directly instead.
"""
if callable(scope) and params is None and not autouse:
# direct decoration
return FixtureFunctionMarker("function", params, autouse, ids=ids, name=name)(
scope
)
else:
return FixtureFunctionMarker(scope, params, autouse, ids=ids, name=name)
return fixture(scope=scope, params=params, autouse=autouse, ids=ids, name=name)
defaultfuncargprefixmarker = fixture()
@@ -1033,11 +1142,12 @@ class FixtureManager(object):
usefixtures = flatten(
mark.args for mark in node.iter_markers(name="usefixtures")
)
initialnames = argnames
initialnames = tuple(usefixtures) + initialnames
initialnames = tuple(usefixtures) + argnames
fm = node.session._fixturemanager
names_closure, arg2fixturedefs = fm.getfixtureclosure(initialnames, node)
return FuncFixtureInfo(argnames, names_closure, arg2fixturedefs)
initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure(
initialnames, node
)
return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs)
def pytest_plugin_registered(self, plugin):
nodeid = None
@@ -1085,6 +1195,12 @@ class FixtureManager(object):
fixturenames_closure.append(arg)
merge(fixturenames)
# at this point, fixturenames_closure contains what we call "initialnames",
# which is a set of fixturenames the function immediately requests. We
# need to return it as well, so save this.
initialnames = tuple(fixturenames_closure)
arg2fixturedefs = {}
lastlen = -1
while lastlen != len(fixturenames_closure):
@@ -1106,7 +1222,7 @@ class FixtureManager(object):
return fixturedefs[-1].scopenum
fixturenames_closure.sort(key=sort_by_scope)
return fixturenames_closure, arg2fixturedefs
return initialnames, fixturenames_closure, arg2fixturedefs
def pytest_generate_tests(self, metafunc):
for argname in metafunc.fixturenames:
@@ -1155,9 +1271,9 @@ class FixtureManager(object):
# The attribute can be an arbitrary descriptor, so the attribute
# access below can raise. safe_getatt() ignores such exceptions.
obj = safe_getattr(holderobj, name, None)
marker = getfixturemarker(obj)
# fixture functions have a pytest_funcarg__ prefix (pre-2.3 style)
# or are "@pytest.fixture" marked
marker = getfixturemarker(obj)
if marker is None:
if not name.startswith(self._argprefix):
continue
@@ -1179,6 +1295,15 @@ class FixtureManager(object):
name = marker.name
assert not name.startswith(self._argprefix), FIXTURE_MSG.format(name)
# during fixture definition we wrap the original fixture function
# to issue a warning if called directly, so here we unwrap it in order to not emit the warning
# when pytest itself calls the fixture function
if six.PY2 and unittest:
# hack on Python 2 because of the unbound methods
obj = get_real_func(obj)
else:
obj = get_real_method(obj, holderobj)
fixture_def = FixtureDef(
self,
nodeid,

View File

@@ -1,6 +1,8 @@
""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
from pluggy import HookspecMarker
from .deprecated import PYTEST_NAMESPACE
hookspec = HookspecMarker("pytest")
@@ -22,10 +24,9 @@ def pytest_addhooks(pluginmanager):
"""
@hookspec(historic=True)
@hookspec(historic=True, warn_on_impl=PYTEST_NAMESPACE)
def pytest_namespace():
"""
(**Deprecated**) this hook causes direct monkeypatching on pytest, its use is strongly discouraged
return dict of name->object to be made globally available in
the pytest namespace.
@@ -33,6 +34,19 @@ def pytest_namespace():
.. note::
This hook is incompatible with ``hookwrapper=True``.
.. warning::
This hook has been **deprecated** and will be removed in pytest 4.0.
Plugins whose users depend on the current namespace functionality should prepare to migrate to a
namespace they actually own.
To support the migration its suggested to trigger ``DeprecationWarnings`` for objects they put into the
pytest namespace.
An stopgap measure to avoid the warning is to monkeypatch the ``pytest`` module, but just as the
``pytest_namespace`` hook this should be seen as a temporary measure to be removed in future versions after
an appropriate transition period.
"""

View File

@@ -270,6 +270,22 @@ class LogCaptureFixture(object):
"""
return [(r.name, r.levelno, r.getMessage()) for r in self.records]
@property
def messages(self):
"""Returns a list of format-interpolated log messages.
Unlike 'records', which contains the format string and parameters for interpolation, log messages in this list
are all interpolated.
Unlike 'text', which contains the output from the handler, log messages in this list are unadorned with
levels, timestamps, etc, making exact comparisions more reliable.
Note that traceback or stack info (from :func:`logging.exception` or the `exc_info` or `stack_info` arguments
to the logging functions) is not included, as this is added by the formatter in the handler.
.. versionadded:: 3.7
"""
return [r.getMessage() for r in self.records]
def clear(self):
"""Reset the list of log records and the captured log text."""
self.handler.reset()

View File

@@ -383,6 +383,8 @@ class Session(nodes.FSCollector):
self.trace = config.trace.root.get("collection")
self._norecursepatterns = config.getini("norecursedirs")
self.startdir = py.path.local()
# Keep track of any collected nodes in here, so we don't duplicate fixtures
self._node_cache = {}
self.config.pluginmanager.register(self, name="session")
@@ -480,19 +482,65 @@ class Session(nodes.FSCollector):
self.trace.root.indent -= 1
def _collect(self, arg):
from _pytest.python import Package
names = self._parsearg(arg)
path = names.pop(0)
if path.check(dir=1):
argpath = names.pop(0)
paths = []
root = self
# Start with a Session root, and delve to argpath item (dir or file)
# and stack all Packages found on the way.
# No point in finding packages when collecting doctests
if not self.config.option.doctestmodules:
for parent in argpath.parts():
pm = self.config.pluginmanager
if pm._confcutdir and pm._confcutdir.relto(parent):
continue
if parent.isdir():
pkginit = parent.join("__init__.py")
if pkginit.isfile():
if pkginit in self._node_cache:
root = self._node_cache[pkginit]
else:
col = root._collectfile(pkginit)
if col:
if isinstance(col[0], Package):
root = col[0]
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.
if argpath.check(dir=1):
assert not names, "invalid arg %r" % (arg,)
for path in path.visit(
for path in argpath.visit(
fil=lambda x: x.check(file=1), rec=self._recurse, bf=True, sort=True
):
for x in self._collectfile(path):
yield x
pkginit = path.dirpath().join("__init__.py")
if pkginit.exists() and not any(x in pkginit.parts() for x in paths):
for x in root._collectfile(pkginit):
yield x
paths.append(x.fspath.dirpath())
if not any(x in path.parts() for x in paths):
for x in root._collectfile(path):
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
else:
assert path.check(file=1)
for x in self.matchnodes(self._collectfile(path), names):
yield x
assert argpath.check(file=1)
if argpath in self._node_cache:
col = self._node_cache[argpath]
else:
col = root._collectfile(argpath)
if col:
self._node_cache[argpath] = col
for y in self.matchnodes(col, names):
yield y
def _collectfile(self, path):
ihook = self.gethookproxy(path)
@@ -577,7 +625,11 @@ class Session(nodes.FSCollector):
resultnodes.append(node)
continue
assert isinstance(node, nodes.Collector)
rep = collect_one_node(node)
if node.nodeid in self._node_cache:
rep = self._node_cache[node.nodeid]
else:
rep = collect_one_node(node)
self._node_cache[node.nodeid] = rep
if rep.passed:
has_matched = False
for x in rep.result:

View File

@@ -111,7 +111,19 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
]
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)
parameters.append(
ParameterSet(values=(NOTSET,) * len(argnames), marks=[mark], id=None)
@@ -124,9 +136,9 @@ class Mark(object):
#: name of the mark
name = attr.ib(type=str)
#: positional arguments of the mark decorator
args = attr.ib(type="List[object]")
args = attr.ib() # type: List[object]
#: 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):
"""

View File

@@ -173,10 +173,13 @@ class Node(object):
chain.reverse()
return chain
def add_marker(self, marker):
def add_marker(self, marker, append=True):
"""dynamically add a marker object to the node.
:type marker: str or pytest.mark.*
:type marker: ``str`` or ``pytest.mark.*`` object
:param marker:
``append=True`` whether to append the marker,
if ``False`` insert at position ``0``.
"""
from _pytest.mark import MarkDecorator, MARK_GEN
@@ -185,7 +188,10 @@ class Node(object):
elif not isinstance(marker, MarkDecorator):
raise ValueError("is not a string or pytest.mark.* Marker")
self.keywords[marker.name] = marker
self.own_markers.append(marker.mark)
if append:
self.own_markers.append(marker.mark)
else:
self.own_markers.insert(0, marker.mark)
def iter_markers(self, name=None):
"""
@@ -281,6 +287,11 @@ class Node(object):
else:
style = "long"
if self.config.option.verbose > 1:
truncate_locals = False
else:
truncate_locals = True
try:
os.getcwd()
abspath = False
@@ -293,6 +304,7 @@ class Node(object):
showlocals=self.config.option.showlocals,
style=style,
tbfilter=tbfilter,
truncate_locals=truncate_locals,
)
repr_failure = _repr_failure_py
@@ -352,7 +364,7 @@ class FSCollector(Collector):
if not nodeid:
nodeid = _check_initialpaths_for_relpath(session, fspath)
if os.sep != SEP:
if nodeid and os.sep != SEP:
nodeid = nodeid.replace(os.sep, SEP)
super(FSCollector, self).__init__(

13
src/_pytest/paths.py Normal file
View File

@@ -0,0 +1,13 @@
from .compat import Path
from os.path import expanduser, expandvars, isabs
def resolve_from_str(input, root):
assert not isinstance(input, Path), "would break on py2"
root = Path(root)
input = expanduser(input)
input = expandvars(input)
if isabs(input):
return Path(input)
else:
return root.joinpath(input)

View File

@@ -21,12 +21,7 @@ import py
import pytest
from _pytest.main import Session, EXIT_OK
from _pytest.assertion.rewrite import AssertionRewritingHook
PYTEST_FULLPATH = os.path.abspath(pytest.__file__.rstrip("oc")).replace(
"$py.class", ".py"
)
from _pytest.compat import Path
IGNORE_PAM = [ # filenames added when obtaining details about the current user
u"/var/lib/sss/mc/passwd"
@@ -53,6 +48,10 @@ def pytest_addoption(parser):
),
)
parser.addini(
"pytester_example_dir", help="directory to take the pytester example files from"
)
def pytest_configure(config):
if config.getvalue("lsof"):
@@ -628,6 +627,48 @@ class Testdir(object):
p.ensure("__init__.py")
return p
def copy_example(self, name=None):
from . import experiments
import warnings
warnings.warn(experiments.PYTESTER_COPY_EXAMPLE, stacklevel=2)
example_dir = self.request.config.getini("pytester_example_dir")
if example_dir is None:
raise ValueError("pytester_example_dir is unset, can't copy examples")
example_dir = self.request.config.rootdir.join(example_dir)
for extra_element in self.request.node.iter_markers("pytester_example_path"):
assert extra_element.args
example_dir = example_dir.join(*extra_element.args)
if name is None:
func_name = self.request.function.__name__
maybe_dir = example_dir / func_name
maybe_file = example_dir / (func_name + ".py")
if maybe_dir.isdir():
example_path = maybe_dir
elif maybe_file.isfile():
example_path = maybe_file
else:
raise LookupError(
"{} cant be found as module or package in {}".format(
func_name, example_dir.bestrelpath(self.request.confg.rootdir)
)
)
else:
example_path = example_dir.join(name)
if example_path.isdir() and not example_path.join("__init__.py").isfile():
example_path.copy(self.tmpdir)
return self.tmpdir
elif example_path.isfile():
result = self.tmpdir.join(example_path.basename)
example_path.copy(result)
return result
else:
raise LookupError("example is not found as a file or directory")
Session = Session
def getnode(self, config, arg):
@@ -934,14 +975,16 @@ class Testdir(object):
same directory to ensure it is a package
"""
kw = {self.request.function.__name__: Source(source).strip()}
path = self.makepyfile(**kw)
if isinstance(source, Path):
path = self.tmpdir.join(str(source))
assert not withinit, "not supported for paths"
else:
kw = {self.request.function.__name__: Source(source).strip()}
path = self.makepyfile(**kw)
if withinit:
self.makepyfile(__init__="#")
self.config = config = self.parseconfigure(path, *configargs)
node = self.getnode(config, path)
return node
return self.getnode(config, path)
def collect_by_name(self, modcol, name):
"""Return the collection node for name from the module collection.
@@ -1029,9 +1072,7 @@ class Testdir(object):
print("couldn't print to %s because of encoding" % (fp,))
def _getpytestargs(self):
# we cannot use `(sys.executable, script)` because on Windows the
# script is e.g. `pytest.exe`
return (sys.executable, PYTEST_FULLPATH) # noqa
return (sys.executable, "-mpytest")
def runpython(self, script):
"""Run a python script using sys.executable as interpreter.

View File

@@ -8,11 +8,11 @@ import os
import collections
import warnings
from textwrap import dedent
from itertools import count
import py
import six
from _pytest.main import FSHookProxy
from _pytest.mark import MarkerError
from _pytest.config import hookimpl
@@ -201,7 +201,7 @@ def pytest_collect_file(path, parent):
ext = path.ext
if ext == ".py":
if not parent.session.isinitpath(path):
for pat in parent.config.getini("python_files"):
for pat in parent.config.getini("python_files") + ["__init__.py"]:
if path.fnmatch(pat):
break
else:
@@ -211,6 +211,8 @@ def pytest_collect_file(path, parent):
def pytest_pycollect_makemodule(path, parent):
if path.basename == "__init__.py":
return Package(path, parent)
return Module(path, parent)
@@ -439,6 +441,11 @@ class PyCollector(PyobjMixin, nodes.Collector):
# add funcargs() as fixturedefs to fixtureinfo.arg2fixturedefs
fixtures.add_funcarg_pseudo_fixture_def(self, metafunc, fm)
# add_funcarg_pseudo_fixture_def may have shadowed some fixtures
# with direct parametrization, so make sure we update what the
# function really needs.
fixtureinfo.prune_dependency_tree()
for callspec in metafunc._calls:
subname = "%s[%s]" % (name, callspec.id)
yield Function(
@@ -526,6 +533,76 @@ class Module(nodes.File, PyCollector):
self.addfinalizer(teardown_module)
class Package(Module):
def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None):
session = parent.session
nodes.FSCollector.__init__(
self, fspath, parent=parent, config=config, session=session, nodeid=nodeid
)
self.name = fspath.dirname
self.trace = session.trace
self._norecursepatterns = session._norecursepatterns
self.fspath = fspath
def _recurse(self, path):
ihook = self.gethookproxy(path.dirpath())
if ihook.pytest_ignore_collect(path=path, config=self.config):
return False
for pat in self._norecursepatterns:
if path.check(fnmatch=pat):
return False
ihook = self.gethookproxy(path)
ihook.pytest_collect_directory(path=path, parent=self)
return True
def gethookproxy(self, fspath):
# check if we have the common case of running
# hooks with all conftest.py filesall conftest.py
pm = self.config.pluginmanager
my_conftestmodules = pm._getconftestmodules(fspath)
remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
if remove_mods:
# one or more conftests are not in use at this fspath
proxy = FSHookProxy(fspath, pm, remove_mods)
else:
# all plugis are active for this fspath
proxy = self.config.hook
return proxy
def _collectfile(self, path):
ihook = self.gethookproxy(path)
if not self.isinitpath(path):
if ihook.pytest_ignore_collect(path=path, config=self.config):
return ()
return ihook.pytest_collect_file(path=path, parent=self)
def isinitpath(self, path):
return path in self.session._initialpaths
def collect(self):
# XXX: HACK!
# Before starting to collect any files from this package we need
# to cleanup the duplicate paths added by the session's collect().
# Proper fix is to not track these as duplicates in the first place.
for path in list(self.session.config.pluginmanager._duplicatepaths):
# if path.parts()[:len(self.fspath.dirpath().parts())] == self.fspath.dirpath().parts():
if path.dirname.startswith(self.name):
self.session.config.pluginmanager._duplicatepaths.remove(path)
this_path = self.fspath.dirpath()
pkg_prefix = None
for path in this_path.visit(rec=self._recurse, bf=True, sort=True):
# we will visit our own __init__.py file, in which case we skip it
if path.basename == "__init__.py" and path.dirpath() == this_path:
continue
if pkg_prefix and pkg_prefix in path.parts():
continue
for x in self._collectfile(path):
yield x
if isinstance(x, Package):
pkg_prefix = path.dirpath()
def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):
"""
Return a callable to perform xunit-style setup or teardown if
@@ -801,12 +878,13 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
"""
def __init__(self, definition, fixtureinfo, config, cls=None, module=None):
#: access to the :class:`_pytest.config.Config` object for the test session
assert (
isinstance(definition, FunctionDefinition)
or type(definition).__name__ == "DefinitionMock"
)
self.definition = definition
#: access to the :class:`_pytest.config.Config` object for the test session
self.config = config
#: the module object where the test function is defined in.
@@ -865,46 +943,54 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
"""
from _pytest.fixtures import scope2index
from _pytest.mark import ParameterSet
from py.io import saferepr
argnames, parameters = ParameterSet._for_parametrize(
argnames, argvalues, self.function, self.config
)
del argvalues
default_arg_names = set(get_default_arg_names(self.function))
if scope is None:
scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect)
scopenum = scope2index(scope, descr="call to {}".format(self.parametrize))
valtypes = {}
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))
self._validate_if_using_arg_names(argnames, indirect)
arg_values_types = self._resolve_arg_value_types(argnames, indirect)
ids = self._resolve_arg_ids(argnames, ids, parameters)
scopenum = scope2index(scope, descr="call to {}".format(self.parametrize))
# create the new calls: if we are parametrize() multiple times (by applying the decorator
# more than once) then we accumulate those calls generating the cartesian product
# of all calls
newcalls = []
for callspec in self._calls or [CallSpec2(self)]:
for param_index, (param_id, param_set) in enumerate(zip(ids, parameters)):
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
if callable(ids):
idfn = ids
@@ -921,29 +1007,57 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
msg % (saferepr(id_value), type(id_value).__name__)
)
ids = idmaker(argnames, parameters, idfn, ids, self.config)
newcalls = []
for callspec in self._calls or [CallSpec2(self)]:
elements = zip(ids, parameters, count())
for a_id, param, param_index in elements:
if len(param.values) != len(argnames):
return ids
def _resolve_arg_value_types(self, argnames, indirect):
"""Resolves if each parametrized argument must be considered a parameter to a fixture or a "funcarg"
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(
'In "parametrize" the number of values ({}) must be '
"equal to the number of names ({})".format(
param.values, argnames
)
"indirect given to %r: fixture %r doesn't exist"
% (self.function, arg)
)
newcallspec = callspec.copy()
newcallspec.setmulti2(
valtypes,
argnames,
param.values,
a_id,
param.marks,
scopenum,
param_index,
)
newcalls.append(newcallspec)
self._calls = newcalls
valtypes[arg] = "params"
return valtypes
def _validate_if_using_arg_names(self, argnames, indirect):
"""
Check if all argnames are being used, by default values, or directly/indirectly.
:param List[str] argnames: list of argument names passed to ``parametrize()``.
:param indirect: same ``indirect`` parameter of ``parametrize()``.
:raise ValueError: if validation fails.
"""
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):
""" Add a new call to the underlying test function during the collection phase of a test run.

View File

@@ -1,5 +1,8 @@
import math
import pprint
import sys
from numbers import Number
from decimal import Decimal
import py
from six.moves import zip, filterfalse
@@ -30,6 +33,15 @@ def _cmp_raises_type_error(self, other):
)
def _non_numeric_type_error(value, at):
at_str = " at {}".format(at) if at else ""
return TypeError(
"cannot make approximate comparisons to non-numeric values: {!r} {}".format(
value, at_str
)
)
# builtin pytest.approx helper
@@ -39,15 +51,17 @@ class ApproxBase(object):
or sequences of numbers.
"""
# Tell numpy to use our `__eq__` operator instead of its
# Tell numpy to use our `__eq__` operator instead of its.
__array_ufunc__ = None
__array_priority__ = 100
def __init__(self, expected, rel=None, abs=None, nan_ok=False):
__tracebackhide__ = True
self.expected = expected
self.abs = abs
self.rel = rel
self.nan_ok = nan_ok
self._check_type()
def __repr__(self):
raise NotImplementedError
@@ -75,21 +89,32 @@ class ApproxBase(object):
"""
raise NotImplementedError
def _check_type(self):
"""
Raise a TypeError if the expected value is not a valid type.
"""
# This is only a concern if the expected value is a sequence. In every
# other case, the approx() function ensures that the expected value has
# a numeric type. For this reason, the default is to do nothing. The
# classes that deal with sequences should reimplement this method to
# raise if there are any non-numeric elements in the sequence.
pass
def _recursive_list_map(f, x):
if isinstance(x, list):
return list(_recursive_list_map(f, xi) for xi in x)
else:
return f(x)
class ApproxNumpy(ApproxBase):
"""
Perform approximate comparisons for numpy arrays.
Perform approximate comparisons where the expected value is numpy array.
"""
def __repr__(self):
# It might be nice to rewrite this function to account for the
# shape of the array...
import numpy as np
list_scalars = []
for x in np.ndindex(self.expected.shape):
list_scalars.append(self._approx_scalar(np.asscalar(self.expected[x])))
list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist())
return "approx({!r})".format(list_scalars)
if sys.version_info[0] == 2:
@@ -128,8 +153,8 @@ class ApproxNumpy(ApproxBase):
class ApproxMapping(ApproxBase):
"""
Perform approximate comparisons for mappings where the values are numbers
(the keys can be anything).
Perform approximate comparisons where the expected value is a mapping with
numeric values (the keys can be anything).
"""
def __repr__(self):
@@ -147,10 +172,20 @@ class ApproxMapping(ApproxBase):
for k in self.expected.keys():
yield actual[k], self.expected[k]
def _check_type(self):
__tracebackhide__ = True
for key, value in self.expected.items():
if isinstance(value, type(self.expected)):
msg = "pytest.approx() does not support nested dictionaries: key={!r} value={!r}\n full mapping={}"
raise TypeError(msg.format(key, value, pprint.pformat(self.expected)))
elif not isinstance(value, Number):
raise _non_numeric_type_error(self.expected, at="key={!r}".format(key))
class ApproxSequence(ApproxBase):
"""
Perform approximate comparisons for sequences of numbers.
Perform approximate comparisons where the expected value is a sequence of
numbers.
"""
def __repr__(self):
@@ -169,10 +204,21 @@ class ApproxSequence(ApproxBase):
def _yield_comparisons(self, actual):
return zip(actual, self.expected)
def _check_type(self):
__tracebackhide__ = True
for index, x in enumerate(self.expected):
if isinstance(x, type(self.expected)):
msg = "pytest.approx() does not support nested data structures: {!r} at index {}\n full sequence: {}"
raise TypeError(msg.format(x, index, pprint.pformat(self.expected)))
elif not isinstance(x, Number):
raise _non_numeric_type_error(
self.expected, at="index {}".format(index)
)
class ApproxScalar(ApproxBase):
"""
Perform approximate comparisons for single numbers only.
Perform approximate comparisons where the expected value is a single number.
"""
DEFAULT_ABSOLUTE_TOLERANCE = 1e-12
@@ -211,7 +257,9 @@ class ApproxScalar(ApproxBase):
the pre-specified tolerance.
"""
if _is_numpy_array(actual):
return ApproxNumpy(actual, self.abs, self.rel, self.nan_ok) == self.expected
# Call ``__eq__()`` manually to prevent infinite-recursion with
# numpy<1.13. See #3748.
return all(self.__eq__(a) for a in actual.flat)
# Short-circuit exact equality.
if actual == self.expected:
@@ -286,7 +334,9 @@ class ApproxScalar(ApproxBase):
class ApproxDecimal(ApproxScalar):
from decimal import Decimal
"""
Perform approximate comparisons where the expected value is a decimal.
"""
DEFAULT_ABSOLUTE_TOLERANCE = Decimal("1e-12")
DEFAULT_RELATIVE_TOLERANCE = Decimal("1e-6")
@@ -445,32 +495,35 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
__ https://docs.python.org/3/reference/datamodel.html#object.__ge__
"""
from decimal import Decimal
# Delegate the comparison to a class that knows how to deal with the type
# of the expected value (e.g. int, float, list, dict, numpy.array, etc).
#
# This architecture is really driven by the need to support numpy arrays.
# The only way to override `==` for arrays without requiring that approx be
# the left operand is to inherit the approx object from `numpy.ndarray`.
# But that can't be a general solution, because it requires (1) numpy to be
# installed and (2) the expected value to be a numpy array. So the general
# solution is to delegate each type of expected value to a different class.
# The primary responsibility of these classes is to implement ``__eq__()``
# and ``__repr__()``. The former is used to actually check if some
# "actual" value is equivalent to the given expected value within the
# allowed tolerance. The latter is used to show the user the expected
# value and tolerance, in the case that a test failed.
#
# This has the advantage that it made it easy to support mapping types
# (i.e. dict). The old code accepted mapping types, but would only compare
# their keys, which is probably not what most people would expect.
# The actual logic for making approximate comparisons can be found in
# ApproxScalar, which is used to compare individual numbers. All of the
# other Approx classes eventually delegate to this class. The ApproxBase
# class provides some convenient methods and overloads, but isn't really
# essential.
if _is_numpy_array(expected):
cls = ApproxNumpy
__tracebackhide__ = True
if isinstance(expected, Decimal):
cls = ApproxDecimal
elif isinstance(expected, Number):
cls = ApproxScalar
elif isinstance(expected, Mapping):
cls = ApproxMapping
elif isinstance(expected, Sequence) and not isinstance(expected, STRING_TYPES):
cls = ApproxSequence
elif isinstance(expected, Decimal):
cls = ApproxDecimal
elif _is_numpy_array(expected):
cls = ApproxNumpy
else:
cls = ApproxScalar
raise _non_numeric_type_error(expected, at=None)
return cls(expected, rel, abs, nan_ok)
@@ -480,17 +533,11 @@ def _is_numpy_array(obj):
Return true if the given object is a numpy array. Make a special effort to
avoid importing numpy unless it's really necessary.
"""
import inspect
for cls in inspect.getmro(type(obj)):
if cls.__module__ == "numpy":
try:
import numpy as np
return isinstance(obj, np.ndarray)
except ImportError:
pass
import sys
np = sys.modules.get("numpy")
if np is not None:
return isinstance(obj, np.ndarray)
return False

196
src/_pytest/reports.py Normal file
View File

@@ -0,0 +1,196 @@
import py
from _pytest._code.code import TerminalRepr
def getslaveinfoline(node):
try:
return node._slaveinfocache
except AttributeError:
d = node.slaveinfo
ver = "%s.%s.%s" % d["version_info"][:3]
node._slaveinfocache = s = "[%s] %s -- Python %s %s" % (
d["id"],
d["sysplatform"],
ver,
d["executable"],
)
return s
class BaseReport(object):
def __init__(self, **kw):
self.__dict__.update(kw)
def toterminal(self, out):
if hasattr(self, "node"):
out.line(getslaveinfoline(self.node))
longrepr = self.longrepr
if longrepr is None:
return
if hasattr(longrepr, "toterminal"):
longrepr.toterminal(out)
else:
try:
out.line(longrepr)
except UnicodeEncodeError:
out.line("<unprintable longrepr>")
def get_sections(self, prefix):
for name, content in self.sections:
if name.startswith(prefix):
yield prefix, content
@property
def longreprtext(self):
"""
Read-only property that returns the full string representation
of ``longrepr``.
.. versionadded:: 3.0
"""
tw = py.io.TerminalWriter(stringio=True)
tw.hasmarkup = False
self.toterminal(tw)
exc = tw.stringio.getvalue()
return exc.strip()
@property
def caplog(self):
"""Return captured log lines, if log capturing is enabled
.. versionadded:: 3.5
"""
return "\n".join(
content for (prefix, content) in self.get_sections("Captured log")
)
@property
def capstdout(self):
"""Return captured text from stdout, if capturing is enabled
.. versionadded:: 3.0
"""
return "".join(
content for (prefix, content) in self.get_sections("Captured stdout")
)
@property
def capstderr(self):
"""Return captured text from stderr, if capturing is enabled
.. versionadded:: 3.0
"""
return "".join(
content for (prefix, content) in self.get_sections("Captured stderr")
)
passed = property(lambda x: x.outcome == "passed")
failed = property(lambda x: x.outcome == "failed")
skipped = property(lambda x: x.outcome == "skipped")
@property
def fspath(self):
return self.nodeid.split("::")[0]
class TestReport(BaseReport):
""" Basic test report object (also used for setup and teardown calls if
they fail).
"""
def __init__(
self,
nodeid,
location,
keywords,
outcome,
longrepr,
when,
sections=(),
duration=0,
user_properties=(),
**extra
):
#: normalized collection node id
self.nodeid = nodeid
#: a (filesystempath, lineno, domaininfo) tuple indicating the
#: actual location of a test item - it might be different from the
#: collected one e.g. if a method is inherited from a different module.
self.location = location
#: a name -> value dictionary containing all keywords and
#: markers associated with a test invocation.
self.keywords = keywords
#: test outcome, always one of "passed", "failed", "skipped".
self.outcome = outcome
#: None or a failure representation.
self.longrepr = longrepr
#: one of 'setup', 'call', 'teardown' to indicate runtest phase.
self.when = when
#: user properties is a list of tuples (name, value) that holds user
#: defined properties of the test
self.user_properties = user_properties
#: list of pairs ``(str, str)`` of extra information which needs to
#: marshallable. Used by pytest to add captured text
#: from ``stdout`` and ``stderr``, but may be used by other plugins
#: to add arbitrary information to reports.
self.sections = list(sections)
#: time it took to run just the test
self.duration = duration
self.__dict__.update(extra)
def __repr__(self):
return "<TestReport %r when=%r outcome=%r>" % (
self.nodeid,
self.when,
self.outcome,
)
class TeardownErrorReport(BaseReport):
outcome = "failed"
when = "teardown"
def __init__(self, longrepr, **extra):
self.longrepr = longrepr
self.sections = []
self.__dict__.update(extra)
class CollectReport(BaseReport):
def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra):
self.nodeid = nodeid
self.outcome = outcome
self.longrepr = longrepr
self.result = result or []
self.sections = list(sections)
self.__dict__.update(extra)
@property
def location(self):
return (self.fspath, None, self.fspath)
def __repr__(self):
return "<CollectReport %r lenresult=%s outcome=%r>" % (
self.nodeid,
len(self.result),
self.outcome,
)
class CollectErrorRepr(TerminalRepr):
def __init__(self, msg):
self.longrepr = msg
def toterminal(self, out):
out.line(self.longrepr, red=True)

View File

@@ -7,9 +7,11 @@ import sys
from time import time
import py
from _pytest._code.code import TerminalRepr, ExceptionInfo
from _pytest._code.code import ExceptionInfo
from _pytest.outcomes import skip, Skipped, TEST_OUTCOME
from .reports import TestReport, CollectReport, CollectErrorRepr
#
# pytest plugin hooks
@@ -215,99 +217,6 @@ class CallInfo(object):
return "<CallInfo when=%r %s>" % (self.when, status)
def getslaveinfoline(node):
try:
return node._slaveinfocache
except AttributeError:
d = node.slaveinfo
ver = "%s.%s.%s" % d["version_info"][:3]
node._slaveinfocache = s = "[%s] %s -- Python %s %s" % (
d["id"],
d["sysplatform"],
ver,
d["executable"],
)
return s
class BaseReport(object):
def __init__(self, **kw):
self.__dict__.update(kw)
def toterminal(self, out):
if hasattr(self, "node"):
out.line(getslaveinfoline(self.node))
longrepr = self.longrepr
if longrepr is None:
return
if hasattr(longrepr, "toterminal"):
longrepr.toterminal(out)
else:
try:
out.line(longrepr)
except UnicodeEncodeError:
out.line("<unprintable longrepr>")
def get_sections(self, prefix):
for name, content in self.sections:
if name.startswith(prefix):
yield prefix, content
@property
def longreprtext(self):
"""
Read-only property that returns the full string representation
of ``longrepr``.
.. versionadded:: 3.0
"""
tw = py.io.TerminalWriter(stringio=True)
tw.hasmarkup = False
self.toterminal(tw)
exc = tw.stringio.getvalue()
return exc.strip()
@property
def caplog(self):
"""Return captured log lines, if log capturing is enabled
.. versionadded:: 3.5
"""
return "\n".join(
content for (prefix, content) in self.get_sections("Captured log")
)
@property
def capstdout(self):
"""Return captured text from stdout, if capturing is enabled
.. versionadded:: 3.0
"""
return "".join(
content for (prefix, content) in self.get_sections("Captured stdout")
)
@property
def capstderr(self):
"""Return captured text from stderr, if capturing is enabled
.. versionadded:: 3.0
"""
return "".join(
content for (prefix, content) in self.get_sections("Captured stderr")
)
passed = property(lambda x: x.outcome == "passed")
failed = property(lambda x: x.outcome == "failed")
skipped = property(lambda x: x.outcome == "skipped")
@property
def fspath(self):
return self.nodeid.split("::")[0]
def pytest_runtest_makereport(item, call):
when = call.when
duration = call.stop - call.start
@@ -348,78 +257,6 @@ def pytest_runtest_makereport(item, call):
)
class TestReport(BaseReport):
""" Basic test report object (also used for setup and teardown calls if
they fail).
"""
def __init__(
self,
nodeid,
location,
keywords,
outcome,
longrepr,
when,
sections=(),
duration=0,
user_properties=(),
**extra
):
#: normalized collection node id
self.nodeid = nodeid
#: a (filesystempath, lineno, domaininfo) tuple indicating the
#: actual location of a test item - it might be different from the
#: collected one e.g. if a method is inherited from a different module.
self.location = location
#: a name -> value dictionary containing all keywords and
#: markers associated with a test invocation.
self.keywords = keywords
#: test outcome, always one of "passed", "failed", "skipped".
self.outcome = outcome
#: None or a failure representation.
self.longrepr = longrepr
#: one of 'setup', 'call', 'teardown' to indicate runtest phase.
self.when = when
#: user properties is a list of tuples (name, value) that holds user
#: defined properties of the test
self.user_properties = user_properties
#: list of pairs ``(str, str)`` of extra information which needs to
#: marshallable. Used by pytest to add captured text
#: from ``stdout`` and ``stderr``, but may be used by other plugins
#: to add arbitrary information to reports.
self.sections = list(sections)
#: time it took to run just the test
self.duration = duration
self.__dict__.update(extra)
def __repr__(self):
return "<TestReport %r when=%r outcome=%r>" % (
self.nodeid,
self.when,
self.outcome,
)
class TeardownErrorReport(BaseReport):
outcome = "failed"
when = "teardown"
def __init__(self, longrepr, **extra):
self.longrepr = longrepr
self.sections = []
self.__dict__.update(extra)
def pytest_make_collect_report(collector):
call = CallInfo(lambda: list(collector.collect()), "collect")
longrepr = None
@@ -446,35 +283,6 @@ def pytest_make_collect_report(collector):
return rep
class CollectReport(BaseReport):
def __init__(self, nodeid, outcome, longrepr, result, sections=(), **extra):
self.nodeid = nodeid
self.outcome = outcome
self.longrepr = longrepr
self.result = result or []
self.sections = list(sections)
self.__dict__.update(extra)
@property
def location(self):
return (self.fspath, None, self.fspath)
def __repr__(self):
return "<CollectReport %r lenresult=%s outcome=%r>" % (
self.nodeid,
len(self.result),
self.outcome,
)
class CollectErrorRepr(TerminalRepr):
def __init__(self, msg):
self.longrepr = msg
def toterminal(self, out):
out.line(self.longrepr, red=True)
class SetupState(object):
""" shared state for setting up/tearing down test items or collectors. """

View File

@@ -69,6 +69,7 @@ class UnitTestCase(Class):
class TestCaseFunction(Function):
nofuncargs = True
_excinfo = None
_testcase = None
def setup(self):
self._testcase = self.parent.obj(self.name)

View File

@@ -49,6 +49,14 @@ def pytest_addoption(parser):
)
def pytest_configure(config):
config.addinivalue_line(
"markers",
"filterwarnings(warning): add a warning filter to the given test. "
"see http://pytest.org/latest/warnings.html#pytest-mark-filterwarnings ",
)
@contextmanager
def catch_warnings_for_item(item):
"""

View File

@@ -18,7 +18,7 @@ from _pytest.mark import MARK_GEN as mark, param
from _pytest.main import Session
from _pytest.nodes import Item, Collector, File
from _pytest.fixtures import fillfixtures as _fillfuncargs
from _pytest.python import Module, Class, Instance, Function, Generator
from _pytest.python import Package, Module, Class, Instance, Function, Generator
from _pytest.python_api import approx, raises
@@ -50,6 +50,7 @@ __all__ = [
"Item",
"File",
"Collector",
"Package",
"Session",
"Module",
"Class",

View File

@@ -1,10 +0,0 @@
"""
Invoke tasks to help with pytest development and release process.
"""
import invoke
from . import generate
ns = invoke.Collection(generate)

View File

@@ -1,6 +0,0 @@
-e .
gitpython
invoke
towncrier
tox
wheel

View File

@@ -14,13 +14,7 @@ from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
class TestGeneralUsage(object):
def test_config_error(self, testdir):
testdir.makeconftest(
"""
def pytest_configure(config):
import pytest
raise pytest.UsageError("hello")
"""
)
testdir.copy_example("conftest_usageerror/conftest.py")
result = testdir.runpytest(testdir.tmpdir)
assert result.ret != 0
result.stderr.fnmatch_lines(["*ERROR: hello"])
@@ -170,18 +164,7 @@ class TestGeneralUsage(object):
result.stdout.fnmatch_lines(["*1 skip*"])
def test_issue88_initial_file_multinodes(self, testdir):
testdir.makeconftest(
"""
import pytest
class MyFile(pytest.File):
def collect(self):
return [MyItem("hello", parent=self)]
def pytest_collect_file(path, parent):
return MyFile(path, parent)
class MyItem(pytest.Item):
pass
"""
)
testdir.copy_example("issue88_initial_file_multinodes")
p = testdir.makepyfile("def test_hello(): pass")
result = testdir.runpytest(p, "--collect-only")
result.stdout.fnmatch_lines(["*MyFile*test_issue88*", "*Module*test_issue88*"])
@@ -1061,3 +1044,10 @@ def test_frame_leak_on_failing_test(testdir):
)
result = testdir.runpytest_subprocess()
result.stdout.fnmatch_lines(["*1 failed, 1 passed in*"])
def test_fixture_mock_integration(testdir):
"""Test that decorators applied to fixture are left working (#3774)"""
p = testdir.copy_example("acceptance/fixture_mock_integration.py")
result = testdir.runpytest(p)
result.stdout.fnmatch_lines("*1 passed*")

View File

@@ -4,6 +4,7 @@ from __future__ import absolute_import, division, print_function
import operator
import os
import sys
import textwrap
import _pytest
import py
import pytest
@@ -579,6 +580,18 @@ raise ValueError()
assert reprlocals.lines[2] == "y = 5"
assert reprlocals.lines[3] == "z = 7"
def test_repr_local_truncated(self):
loc = {"l": [i for i in range(10)]}
p = FormattedExcinfo(showlocals=True)
truncated_reprlocals = p.repr_locals(loc)
assert truncated_reprlocals.lines
assert truncated_reprlocals.lines[0] == "l = [0, 1, 2, 3, 4, 5, ...]"
q = FormattedExcinfo(showlocals=True, truncate_locals=False)
full_reprlocals = q.repr_locals(loc)
assert full_reprlocals.lines
assert full_reprlocals.lines[0] == "l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]"
def test_repr_tracebackentry_lines(self, importasmod):
mod = importasmod(
"""
@@ -1253,6 +1266,50 @@ raise ValueError()
]
)
@pytest.mark.skipif("sys.version_info[0] < 3")
def test_exc_chain_repr_cycle(self, importasmod):
mod = importasmod(
"""
class Err(Exception):
pass
def fail():
return 0 / 0
def reraise():
try:
fail()
except ZeroDivisionError as e:
raise Err() from e
def unreraise():
try:
reraise()
except Err as e:
raise e.__cause__
"""
)
excinfo = pytest.raises(ZeroDivisionError, mod.unreraise)
r = excinfo.getrepr(style="short")
tw = TWMock()
r.toterminal(tw)
out = "\n".join(line for line in tw.lines if isinstance(line, str))
expected_out = textwrap.dedent(
"""\
:13: in unreraise
reraise()
:10: in reraise
raise Err() from e
E test_exc_chain_repr_cycle0.mod.Err
During handling of the above exception, another exception occurred:
:15: in unreraise
raise e.__cause__
:8: in reraise
fail()
:5: in fail
return 0 / 0
E ZeroDivisionError: division by zero"""
)
assert out == expected_out
@pytest.mark.parametrize("style", ["short", "long"])
@pytest.mark.parametrize("encoding", [None, "utf8", "utf16"])

View File

@@ -744,3 +744,19 @@ something
'''"""
result = getstatement(1, source)
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

View File

@@ -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

View File

@@ -1,4 +1,6 @@
from __future__ import absolute_import, division, print_function
import pytest
@@ -263,3 +265,14 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated_no_false_positives(
str(PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST).splitlines()[0]
not in res.stderr.str()
)
def test_call_fixture_function_deprecated():
"""Check if a warning is raised if a fixture function is called directly (#3661)"""
@pytest.fixture
def fix():
return 1
with pytest.deprecated_call():
assert fix() == 1

View File

@@ -0,0 +1,9 @@
Example test scripts
=====================
The files in this folder are not direct tests, but rather example test suites that demonstrate certain issues/behaviours.
In the future we will move part of the content of the acceptance tests here in order to have directly testable code instead of writing out things and then running them in nested pytest sessions/subprocesses.
This will aid debugging and comprehension.

View File

@@ -0,0 +1,17 @@
"""Reproduces issue #3774"""
import mock
import pytest
config = {"mykey": "ORIGINAL"}
@pytest.fixture(scope="function")
@mock.patch.dict(config, {"mykey": "MOCKED"})
def my_fixture():
return config["mykey"]
def test_foobar(my_fixture):
assert my_fixture == "MOCKED"

View File

@@ -0,0 +1,2 @@
def pytest_ignore_collect(path):
return False

View File

@@ -0,0 +1,2 @@
def test():
pass

View File

@@ -0,0 +1,2 @@
class pytest_something(object):
pass

View File

@@ -0,0 +1,2 @@
def test_foo():
pass

View File

@@ -0,0 +1,4 @@
def pytest_configure(config):
import pytest
raise pytest.UsageError("hello")

View File

@@ -0,0 +1,10 @@
import pytest
class CustomItem(pytest.Item, pytest.File):
def runtest(self):
pass
def pytest_collect_file(path, parent):
return CustomItem(path, parent)

View File

@@ -0,0 +1,2 @@
def test():
pass

View File

@@ -0,0 +1,7 @@
import pytest
@pytest.fixture
def arg1(request):
with pytest.raises(Exception):
request.getfixturevalue("arg2")

View File

@@ -0,0 +1,7 @@
import pytest
@pytest.fixture
def arg2(request):
pytest.raises(Exception, "request.getfixturevalue('arg1')")

View File

@@ -0,0 +1,6 @@
import pytest
@pytest.fixture
def spam():
return "spam"

View File

@@ -0,0 +1,6 @@
import pytest
@pytest.fixture
def spam(spam):
return spam * 2

View File

@@ -0,0 +1,2 @@
def test_spam(spam):
assert spam == "spamspam"

View File

@@ -0,0 +1,6 @@
import pytest
@pytest.fixture
def spam():
return "spam"

View File

@@ -0,0 +1,10 @@
import pytest
@pytest.fixture
def spam(spam):
return spam * 2
def test_spam(spam):
assert spam == "spamspam"

View File

@@ -0,0 +1,15 @@
import pytest
@pytest.fixture
def spam():
return "spam"
class TestSpam(object):
@pytest.fixture
def spam(self, spam):
return spam * 2
def test_spam(self, spam):
assert spam == "spamspam"

View File

@@ -0,0 +1,16 @@
import pytest
@pytest.fixture
def some(request):
return request.function.__name__
@pytest.fixture
def other(request):
return 42
def test_func(some, other):
pass

View File

@@ -0,0 +1,11 @@
import pytest
class TestClass(object):
@pytest.fixture
def something(self, request):
return request.instance
def test_method(self, something):
assert something is self

View File

@@ -0,0 +1,15 @@
import pytest
@pytest.fixture
def something(request):
return request.function.__name__
class TestClass(object):
def test_method(self, something):
assert something == "test_method"
def test_func(something):
assert something == "test_func"

View File

@@ -0,0 +1,10 @@
import pytest
@pytest.fixture
def xyzsomething(request):
return 42
def test_func(some):
pass

Some files were not shown because too many files have changed in this diff Show More