Compare commits

...

181 Commits
3.7.0 ... 3.7.3

Author SHA1 Message Date
Bruno Oliveira
70bdacf01a Fix collection example docs 2018-08-26 12:58:47 +00:00
Bruno Oliveira
b69f853acb Tweak CHANGELOG for 3.7.3 2018-08-26 09:46:46 -03:00
Bruno Oliveira
c31018d9bc Preparing release version 3.7.3 2018-08-26 12:43:43 +00:00
Bruno Oliveira
7ae23901d3 Merge pull request #3881 from GandalfSaxe/patch-2
Code block: :: missing and 4 spaces instead of 5
2018-08-26 09:22:30 -03:00
Bruno Oliveira
4d19b94347 Merge pull request #3877 from blueyed/codecov-master
tox: coveralls: also report to codecov
2018-08-26 09:19:03 -03:00
Bruno Oliveira
c15b537e3d Merge pull request #3878 from asottile/skip_install_linting
Use skip_install for testing tox env
2018-08-26 09:17:40 -03:00
Bruno Oliveira
2577a6ce8a Merge pull request #3873 from nicoddemus/sys-path-fix
Remove dangerous sys.path manipulations in test_pluginmanager
2018-08-26 08:53:57 -03:00
Bruno Oliveira
dd5f5ca4cb Merge pull request #3872 from nicoddemus/tests-in-init-files
Collect tests from __init__.py files if they match 'python_files'
2018-08-26 08:50:17 -03:00
Gandalf Saxe
508774742e Code block: :: missing and 4 spaces instead of 5
I just noticed the newly committed code block doesn't format as a code block without `::` in the paragraph before. Perhaps doesn't make a difference, but also corrected 5 spaces to 4 which seems standard.
2018-08-26 11:54:08 +02:00
Daniel Hahler
d3f5324386 tox: coveralls: also report to codecov
This is meant to get base coverage on master for codecov.
2018-08-26 02:14:22 +02:00
Anthony Sottile
3da88d794f Use skip_install for testing tox env 2018-08-25 16:48:01 -07:00
Bruno Oliveira
71b4995775 Merge pull request #3874 from blueyed/improve-pre-commit
Travis: use TOXENV=linting for linting stage
2018-08-25 20:14:27 -03:00
Bruno Oliveira
b0541e9d31 Correctly restore sys.path in test and remove dead code in test_pytester
The code in test_pytester has been refactored into a class right
above the dead code, and the code has been left there by mistake
apparently.
2018-08-25 18:17:52 -03:00
Daniel Hahler
415fcb912b Travis: use TOXENV=linting for linting stage
This will run it with `--show-diff-on-failure` then, and helps to keep
it in line / in a central place.
2018-08-25 23:14:09 +02:00
Bruno Oliveira
f872fcb5d0 Remove dangerous sys.path manipulations in test_pluginmanager
Noticed these while working in something else
2018-08-25 17:33:29 -03:00
Bruno Oliveira
de6f2c0336 Collect tests from __init__.py files if they match 'python_files'
Fix #3773
2018-08-25 11:18:52 -03:00
Bruno Oliveira
be4b359c74 Merge pull request #3861 from jonozzz/fix-3854
Fix #3854
2018-08-25 10:44:08 -03:00
Bruno Oliveira
72a58bbafe Merge pull request #3871 from schmamps/cmdclass
Correct cmdclass for doc: Good Integration Practices
2018-08-24 22:09:35 -03:00
turturica
c336449729 Make linting happy. Argh. 2018-08-24 18:05:35 -07:00
turturica
1e4ecda884 Fix the package fixture ordering in Windows. 2018-08-24 18:01:38 -07:00
turturica
8cf0e46bbf test_package_ordering: Collect *.py, but keep a mix of case for filenames. The test doesn't make sense for Windows, because of its case-insensitivity. 2018-08-24 16:23:50 -07:00
Bruno Oliveira
f0226e9329 Fix test_package_ordering on Windows 2018-08-24 20:15:33 -03:00
turturica
dce8df45d5 Added changelog items. 2018-08-24 15:51:42 -07:00
Andrew Champion
f6948597e4 add to changelog 2018-08-24 12:29:18 -07:00
turturica
e3df1031ca Add encoding: utf8 for python 2.7 2018-08-24 12:26:18 -07:00
Andrew Champion
14ffadf004 correct cmdclass 2018-08-24 12:07:22 -07:00
turturica
459b040d21 Fix dedent after merge. 2018-08-24 11:54:04 -07:00
turturica
3396225f74 Merge branch 'master' of github.com:pytest-dev/pytest into fix-3854 2018-08-24 11:47:24 -07:00
Bruno Oliveira
c82906105c Merge pull request #3865 from GandalfSaxe/patch-1
Move information on `pip install -e` to the top
2018-08-24 07:28:51 -03:00
Anthony Sottile
4c14740798 Merge pull request #3868 from asottile/bytes_py26_plus
Use `bytes` directly instead of `binary_type`
2018-08-23 23:16:59 -07:00
turturica
72e6482994 Make linting happy. 2018-08-23 22:58:36 -07:00
turturica
5f8b50c094 Address #3796 and add a test for it. 2018-08-23 22:48:44 -07:00
Anthony Sottile
99e31f6fb1 Use bytes directly instead of binary_type
`bytes` is an alias for `str` in python2.6+
2018-08-23 18:55:21 -07:00
Anthony Sottile
f2e35c8c4f Merge pull request #3859 from asottile/pyupgrade_1_4
Some pyupgrade 1.4.x changes
2018-08-23 18:32:53 -07:00
Bruno Oliveira
40b4fe64af Fix linting 2018-08-23 22:11:17 -03:00
Bruno Oliveira
d10d59c013 Merge pull request #3858 from mimi1vx/test_mock
Use unittest.mock if is only aviable
2018-08-23 19:02:39 -03:00
wim glenn
d54aa8ce13 Merge pull request #3848 from wimglenn/pytester_unicode_bugfixes
fixed a bunch of unicode bugs in pytester.py
2018-08-23 13:45:49 -05:00
Anthony Sottile
52fa8c98bb Merge pull request #3864 from asottile/source_to_dedent
Replace Source with dedent where possible
2018-08-23 10:45:11 -07:00
Gandalf Saxe
3f336869e2 Move information on pip install -e to the top
Should fix complaints in #2421.
2018-08-23 18:07:28 +02:00
Anthony Sottile
85482d575e Replace Source with dedent where possible 2018-08-23 09:06:17 -07:00
Bruno Oliveira
6f7365509d Merge pull request #3860 from asottile/purge_more_py
Purge more usage of `py` module
2018-08-23 06:05:32 -03:00
Anthony Sottile
7099ea9bb0 py.builtin._reraise -> six.reraise 2018-08-22 23:00:58 -07:00
Anthony Sottile
dccac69d82 py.builtin.text -> six.text_type 2018-08-22 23:00:06 -07:00
Anthony Sottile
c2cd337886 py.builtin.exec_ => six.exec_ 2018-08-22 23:00:06 -07:00
Anthony Sottile
0fc4a806e5 py.builtins._totext -> string literals or six.text_type 2018-08-22 23:00:04 -07:00
turturica
4d3c1ab4f0 Fixes #3854 2018-08-22 21:42:59 -07:00
turturica
e4f76f6350 Merge branch 'master' of github.com:pytest-dev/pytest into fix-3854 2018-08-22 20:36:52 -07:00
Anthony Sottile
0d65783dce Fix unicode errors when changing to .format(...) 2018-08-22 19:00:43 -07:00
Anthony Sottile
8bb8b91357 pyupgrade 1.4: tests 2018-08-22 18:47:21 -07:00
Bruno Oliveira
8804c7333a Fix CHANGELOG formatting 2018-08-22 20:06:13 -03:00
Bruno Oliveira
17eec5b97e Merge pull request #3856 from jennirinker/master
Resolving Issue #3824
2018-08-22 19:03:55 -03:00
Ondřej Súkup
cd07c4d4ff Use unittest.mock if is only aviable
from Python 3.3 is mock part of python standard library in unittest namespace
2018-08-22 23:49:40 +02:00
wim glenn
917b99e438 More unicode whack-a-mole
It seems pytest's very comprehensive CI sniffed out a few other places with similar bugs.  Ideally we should find all the places where args are not stringy and solve it at the source, but who knows how many people are relying on the implicit string conversion.  See [here](https://github.com/pytest-dev/pytest/blob/master/src/_pytest/config/__init__.py#L160-L166) for one such problem area (args with a single py.path.local instance is converted here, but a list or tuple containing some are not).
2018-08-22 13:40:21 -05:00
wim glenn
b08e156b79 strip trailing whitespace 2018-08-22 11:27:36 -05:00
wim glenn
8e2c7b4979 Add a failing testcase for PR #3848 2018-08-22 11:00:51 -05:00
Bruno Oliveira
5a7aa123ea Improve docs formatting 2018-08-22 11:22:30 -03:00
Jennifer Rinker
a12eadd9ef resolving Issue #3824 - expanding docs 2018-08-22 15:37:35 +02:00
Bruno Oliveira
2137e2b15b Merge pull request #3846 from nicoddemus/issue-3843
Fix collection error when tests is specified with --doctest-modules
2018-08-22 08:17:07 -03:00
wim glenn
89446af51e fixed a bunch of unicode bugs in pytester.py 2018-08-22 01:30:23 -05:00
Ronny Pfannschmidt
3b521bedf8 Merge pull request #3841 from sankt-petersbug/fix-3816
Fix '--show-capture=no' capture teardown logs
2018-08-22 07:16:53 +02:00
Ronny Pfannschmidt
5b2c8fa007 Merge pull request #3845 from natanlao/patch-1
Remove warning about #568 from documentation
2018-08-22 07:12:40 +02:00
Bruno Oliveira
eb8d145195 Add link to issue in the CHANGELOG entry 2018-08-21 21:08:21 -03:00
Natan Lao
80bea79512 Add changelog entry 2018-08-21 17:04:56 -07:00
Bruno Oliveira
07a560ff24 Fix collection error when tests is specified with --doctest-modules
The problem was that _matchnodes would receive two items: [DoctestModule, Module]. It would then collect the first one, *cache it*, and fail to match against the name in the command line. Next, it would reuse the cached item (DoctestModule) instead of collecting the Module which would eventually find the "test" name on it.

Added the type of the node to the cache key to avoid this problem, although I'm not a big fan of caches that have different key types.

Fix #3843
2018-08-21 21:02:46 -03:00
Natan Lao
717775a1c6 Remove warning about #568 from documentation
The documentation (https://docs.pytest.org/en/latest/skipping.html) references
issue #568, which has since been fixed.
2018-08-21 16:57:33 -07:00
Bruno Oliveira
672f4bb5aa Improve CHANGELOG 2018-08-21 20:19:48 -03:00
Bruno Oliveira
f1079a8222 Merge pull request #3832 from Sup3rGeo/bugfix/capsys-with-cli-logging
Bugfix/capsys with cli logging (again)
2018-08-21 20:12:31 -03:00
Sankt Petersbug
223eef6261 Fix '--show-capture=no' capture teardown logs
Add a check before printing teardown logs.

'print_teardown_sections' method does not check '--show-capture' option
value, and teardown logs are always printed.

Resolves: #3816
2018-08-20 15:01:21 -05:00
Bruno Oliveira
43657f252f Merge pull request #3830 from nicoddemus/capfd-fixture-capture
Fixtures during teardown can use capsys and capfd to get output from tests
2018-08-20 15:32:45 -03:00
Victor
70ebab3537 Renamed snap_global_capture to read_global_capture. 2018-08-20 17:48:14 +02:00
Ronny Pfannschmidt
d3bdfc704b Merge pull request #3839 from asottile/more_flexible
Use more flexible `language_version: python3`
2018-08-20 16:13:31 +02:00
Anthony Sottile
4de247cfa0 Use more flexible language_version: python3 2018-08-20 06:27:35 -07:00
Victor
d611b03589 Parametrized tests for capfd as well. Separated global capture test. 2018-08-20 12:23:59 +02:00
Anthony Sottile
308d789d92 Merge pull request #3835 from wimglenn/issue3833
more autodocs for pytester
2018-08-20 00:30:28 -07:00
wim glenn
e4bea9068b end of line for this file, perhaps? 2018-08-19 23:39:10 -05:00
wim glenn
e620798d33 more autodocs for pytester 2018-08-19 23:21:45 -05:00
victor
7ea4992f16 Fixed linting. 2018-08-19 15:46:02 +02:00
victor
0564b52c0e Fixed integration with other modules/tests 2018-08-19 15:26:57 +02:00
victor
8b2c91836b Fixed activation and used just runtest_protocol hook 2018-08-19 14:30:50 +02:00
victor
9e382e8d29 Fixed test. 2018-08-19 14:29:57 +02:00
victor
2255892d65 Improved test to cover more cases. 2018-08-19 13:44:12 +02:00
victor
7d9b198f73 Refactoring: Separated suspend from snapping (stopped always snapping when suspending - solves bug but still missing tests), reorganized functions and context managers. 2018-08-19 02:32:36 +02:00
Bruno Oliveira
f4c5994d27 Fixtures during teardown can use capsys and capfd to get output from tests
Fix #3033
2018-08-18 14:46:24 -03:00
Tyler Richard
c24c7e75e2 Added regression test for capfd in a fixture 2018-08-18 14:11:04 -03:00
Tyler Richard
273670b2a2 Fixes capfd so data is available after teardown. 2018-08-18 14:11:04 -03:00
Bruno Oliveira
28aff051ab Merge pull request #3822 from Sup3rGeo/bugfix/capsys-with-cli-logging
Bugfix/capsys with cli logging
2018-08-18 14:06:27 -03:00
Bruno Oliveira
29975e5b37 Merge pull request #3827 from Vlad-Shcherbina/funcfixtureinfo-type-hints
Replace broken type annotations with type comments
2018-08-18 12:54:47 -03:00
Bruno Oliveira
5cf7d1dba2 "suspend" method of capture fixture private
Also change the context-manager to global_and_fixture_disabled to
better convey its meaning
2018-08-18 11:38:08 -03:00
Bruno Oliveira
2fe824b8c4 Merge pull request #3821 from nicoddemus/release-3.7.2
Preparing release version 3.7.2
2018-08-18 11:18:18 -03:00
Bruno Oliveira
f674217c43 Moved dummy_context_manager to compat module 2018-08-18 11:15:58 -03:00
Bruno Oliveira
9f7345d663 Avoid leaving a reference to the last item on CaptureManager 2018-08-18 11:08:03 -03:00
victor
eb2d074530 Black changes. 2018-08-18 14:27:09 +02:00
victor
9fa7745795 Refactor, tests passing. 2018-08-18 13:40:08 +02:00
victor
14db2f91ba Fixed global not called if no capsys fixture. Using now capsys context manager as well. 2018-08-18 12:16:47 +02:00
Vlad Shcherbina
c3e494f6cf Replace broken type annotations with type comments
Fixes #3826.
2018-08-18 01:05:30 +03:00
Victor
090f67a980 Refactored implementation and updated tests. 2018-08-17 13:41:26 +02:00
Victor
3059bfb1b3 Update test with another problem. 2018-08-17 13:00:27 +02:00
Victor
e391c47ed8 Update capture suspend test for logging. 2018-08-17 00:44:15 +02:00
Victor
f66764e1c0 Added changelog and updated AUTHORS. 2018-08-17 00:33:56 +02:00
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
Victor
2b71cb9c38 Added activation/deactivation of capture fixture in logging emit. 2018-08-17 00:26:12 +02:00
Victor
da9d814da4 Added test. 2018-08-17 00:20:51 +02: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
turturica
d0bd01beca Collect any tests from a package's __init__.py 2018-08-09 18:06:38 -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
88 changed files with 2004 additions and 1035 deletions

View File

@@ -5,13 +5,13 @@ repos:
hooks:
- id: black
args: [--safe, --quiet]
language_version: python3.6
language_version: python3
- repo: https://github.com/asottile/blacken-docs
rev: v0.2.0
hooks:
- id: blacken-docs
additional_dependencies: [black==18.6b4]
language_version: python3.6
language_version: python3
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v1.3.0
hooks:
@@ -37,4 +37,8 @@ repos:
files: ^(CHANGELOG.rst|HOWTORELEASE.rst|README.rst|changelog/.*)$
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

@@ -62,12 +62,7 @@ jobs:
repo: pytest-dev/pytest
- stage: linting
python: '3.6'
env:
install:
- pip install pre-commit
- pre-commit install-hooks
script:
- pre-commit run --all-files
env: TOXENV=linting
script: tox --recreate

View File

@@ -98,6 +98,7 @@ Javier Domingo Cansino
Javier Romero
Jeff Rackauckas
Jeff Widman
Jenni Rinker
John Eddie Ayson
John Towler
Jon Sonesen
@@ -182,6 +183,7 @@ Russel Winder
Ryan Wooden
Samuel Dion-Girardeau
Samuele Pedroni
Sankt Petersbug
Segev Finer
Serhii Mozghovyi
Simon Gomizelj
@@ -205,6 +207,7 @@ Trevor Bekolay
Tyler Goodlet
Tzu-ping Chung
Vasily Kuznetsov
Victor Maryama
Victor Uriarte
Vidar T. Fauske
Vitaly Lashmanov

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,6 +18,123 @@
.. towncrier release notes start
pytest 3.7.3 (2018-08-26)
=========================
Bug Fixes
---------
- `#3033 <https://github.com/pytest-dev/pytest/issues/3033>`_: Fixtures during teardown can again use ``capsys`` and ``cafd`` to inspect output captured during tests.
- `#3773 <https://github.com/pytest-dev/pytest/issues/3773>`_: Fix collection of tests from ``__init__.py`` files if they match the ``python_files`` configuration option.
- `#3796 <https://github.com/pytest-dev/pytest/issues/3796>`_: Fix issue where teardown of fixtures of consecutive sub-packages were executed once, at the end of the outer
package.
- `#3816 <https://github.com/pytest-dev/pytest/issues/3816>`_: Fix bug where ``--show-capture=no`` option would still show logs printed during fixture teardown.
- `#3819 <https://github.com/pytest-dev/pytest/issues/3819>`_: Fix ``stdout/stderr`` not getting captured when real-time cli logging is active.
- `#3843 <https://github.com/pytest-dev/pytest/issues/3843>`_: Fix collection error when specifying test functions directly in the command line using ``test.py::test`` syntax together with ``--doctest-modules``.
- `#3848 <https://github.com/pytest-dev/pytest/issues/3848>`_: Fix bugs where unicode arguments could not be passed to ``testdir.runpytest`` on Python 2.
- `#3854 <https://github.com/pytest-dev/pytest/issues/3854>`_: Fix double collection of tests within packages when the filename starts with a capital letter.
Improved Documentation
----------------------
- `#3824 <https://github.com/pytest-dev/pytest/issues/3824>`_: Added example for multiple glob pattern matches in ``python_files``.
- `#3833 <https://github.com/pytest-dev/pytest/issues/3833>`_: Added missing docs for ``pytester.Testdir``.
- `#3870 <https://github.com/pytest-dev/pytest/issues/3870>`_: Correct documentation for setuptools integration.
Trivial/Internal Changes
------------------------
- `#3826 <https://github.com/pytest-dev/pytest/issues/3826>`_: Replace broken type annotations with type comments.
- `#3845 <https://github.com/pytest-dev/pytest/issues/3845>`_: Remove a reference to issue `#568 <https://github.com/pytest-dev/pytest/issues/568>`_ from the documentation, which has since been
fixed.
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)
=========================

View File

@@ -1,4 +1,4 @@
import py
import six
for i in range(1000):
py.builtin.exec_("def test_func_%d(): pass" % i)
six.exec_("def test_func_%d(): pass" % i)

View File

@@ -6,6 +6,9 @@ Release announcements
:maxdepth: 2
release-3.7.3
release-3.7.2
release-3.7.1
release-3.7.0
release-3.6.4
release-3.6.3

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

@@ -0,0 +1,33 @@
pytest-3.7.3
=======================================
pytest 3.7.3 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:
* Andrew Champion
* Anthony Sottile
* Bruno Oliveira
* Daniel Hahler
* Gandalf Saxe
* Jennifer Rinker
* Natan Lao
* Ondřej Súkup
* Ronny Pfannschmidt
* Sankt Petersbug
* Tyler Richard
* Victor
* Vlad Shcherbina
* turturica
* victor
* wim glenn
Happy testing,
The pytest Development Team

View File

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

View File

@@ -1,6 +1,6 @@
from pytest import raises
import _pytest._code
import py
import six
def otherfunc(a, b):
@@ -177,7 +177,7 @@ def test_dynamic_compile_shows_nicely():
name = "abc-123"
module = imp.new_module(name)
code = _pytest._code.compile(src, name, "exec")
py.builtin.exec_(code, module.__dict__)
six.exec_(code, module.__dict__)
sys.modules[name] = module
module.foo()

View File

@@ -10,4 +10,4 @@ def pytest_runtest_setup(item):
return
mod = item.getparent(pytest.Module).obj
if hasattr(mod, "hello"):
print("mod.hello %r" % (mod.hello,))
print("mod.hello {!r}".format(mod.hello))

View File

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

@@ -2,9 +2,10 @@
module containing a parametrized tests testing cross-python
serialization via the pickle module.
"""
import textwrap
import py
import pytest
import _pytest._code
pythonlist = ["python2.7", "python3.4", "python3.5"]
@@ -24,42 +25,44 @@ class Python(object):
def __init__(self, version, picklefile):
self.pythonpath = py.path.local.sysfind(version)
if not self.pythonpath:
pytest.skip("%r not found" % (version,))
pytest.skip("{!r} not found".format(version))
self.picklefile = picklefile
def dumps(self, obj):
dumpfile = self.picklefile.dirpath("dump.py")
dumpfile.write(
_pytest._code.Source(
"""
import pickle
f = open(%r, 'wb')
s = pickle.dump(%r, f, protocol=2)
f.close()
"""
% (str(self.picklefile), obj)
textwrap.dedent(
"""\
import pickle
f = open({!r}, 'wb')
s = pickle.dump({!r}, f, protocol=2)
f.close()
""".format(
str(self.picklefile), obj
)
)
)
py.process.cmdexec("%s %s" % (self.pythonpath, dumpfile))
py.process.cmdexec("{} {}".format(self.pythonpath, dumpfile))
def load_and_is_true(self, expression):
loadfile = self.picklefile.dirpath("load.py")
loadfile.write(
_pytest._code.Source(
"""
import pickle
f = open(%r, 'rb')
obj = pickle.load(f)
f.close()
res = eval(%r)
if not res:
raise SystemExit(1)
"""
% (str(self.picklefile), expression)
textwrap.dedent(
"""\
import pickle
f = open({!r}, 'rb')
obj = pickle.load(f)
f.close()
res = eval({!r})
if not res:
raise SystemExit(1)
""".format(
str(self.picklefile), expression
)
)
)
print(loadfile)
py.process.cmdexec("%s %s" % (self.pythonpath, loadfile))
py.process.cmdexec("{} {}".format(self.pythonpath, loadfile))
@pytest.mark.parametrize("obj", [42, {}, {1: 3}])

View File

@@ -413,7 +413,7 @@ Running it results in some skips if we don't have all the python interpreters in
. $ pytest -rs -q multipython.py
...sss...sssssssss...sss... [100%]
========================= short test summary info ==========================
SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:28: 'python3.4' not found
SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.4' not found
12 passed, 15 skipped in 0.12 seconds
Indirect parametrization of optional implementations/imports

View File

@@ -100,19 +100,21 @@ Changing naming conventions
You can configure different naming conventions by setting
the :confval:`python_files`, :confval:`python_classes` and
:confval:`python_functions` configuration options. Example::
:confval:`python_functions` configuration options.
Here is an example::
# content of pytest.ini
# Example 1: have pytest look for "check" instead of "test"
# can also be defined in tox.ini or setup.cfg file, although the section
# name in setup.cfg files should be "tool:pytest"
[pytest]
python_files=check_*.py
python_classes=Check
python_functions=*_check
python_files = check_*.py
python_classes = Check
python_functions = *_check
This would make ``pytest`` look for tests in files that match the ``check_*
.py`` glob-pattern, ``Check`` prefixes in classes, and functions and methods
that match ``*_check``. For example, if we have::
that match ``*_check``. For example, if we have::
# content of check_myapp.py
class CheckMyApp(object):
@@ -121,7 +123,7 @@ that match ``*_check``. For example, if we have::
def complex_check(self):
pass
then the test collection looks like this::
The test collection would look like this::
$ pytest --collect-only
=========================== test session starts ============================
@@ -136,11 +138,19 @@ then the test collection looks like this::
======================= no tests ran in 0.12 seconds =======================
You can check for multiple glob patterns by adding a space between the patterns::
# Example 2: have pytest look for files with "test" and "example"
# content of pytest.ini, tox.ini, or setup.cfg file (replace "pytest"
# with "tool:pytest" for setup.cfg)
[pytest]
python_files = test_*.py example_*.py
.. note::
the ``python_functions`` and ``python_classes`` options has no effect
for ``unittest.TestCase`` test discovery because pytest delegates
detection of test case methods to unittest code.
discovery of test case methods to unittest code.
Interpreting cmdline arguments as Python packages
-----------------------------------------------------

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.6/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>
@@ -423,7 +423,7 @@ get on the terminal - we are working on that)::
name = "abc-123"
module = imp.new_module(name)
code = _pytest._code.compile(src, name, "exec")
py.builtin.exec_(code, module.__dict__)
six.exec_(code, module.__dict__)
sys.modules[name] = module
> module.foo()

View File

@@ -4,6 +4,27 @@
Good Integration Practices
=================================================
Install package with pip
-------------------------------------------------
For development, we recommend to use virtualenv_ environments and pip_
for installing your application and any dependencies
as well as the ``pytest`` package itself. This ensures your code and
dependencies are isolated from the system Python installation.
First you need to place a ``setup.py`` file in the root of your package with the following minimum content::
from setuptools import setup, find_packages
setup(name="PACKAGENAME", packages=find_packages())
Where ``PACKAGENAME`` is the name of your package. You can then install your package in "editable" mode by running from the same directory::
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
your package using a symlink to your development code.
.. _`test discovery`:
.. _`Python test discovery`:
@@ -177,19 +198,6 @@ Note that this layout also works in conjunction with the ``src`` layout mentione
tox
------
For development, we recommend to use virtualenv_ environments and pip_
for installing your application and any dependencies
as well as the ``pytest`` package itself. This ensures your code and
dependencies are isolated from the system Python installation.
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
your package using a symlink to your development code.
Once you are done with your work and want to make sure that your actual
package passes all tests you may want to look into `tox`_, the
virtualenv test automation tool and its `pytest support
@@ -282,7 +290,7 @@ your own setuptools Test command for invoking pytest.
setup(
# ...,
tests_require=["pytest"],
cmdclass={"test": PyTest},
cmdclass={"pytest": PyTest},
)
Now if you run::

View File

@@ -460,7 +460,7 @@ To use it, include in your top-most ``conftest.py`` file::
.. autoclass:: Testdir()
:members: runpytest,runpytest_subprocess,runpytest_inprocess,makeconftest,makepyfile
:members:
.. autoclass:: RunResult()
:members:
@@ -1229,7 +1229,8 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: python_classes
One or more name prefixes or glob-style patterns determining which classes
are considered for test collection. By default, pytest will consider any
are considered for test collection. Search for multiple glob patterns by
adding a space between patterns. By default, pytest will consider any
class prefixed with ``Test`` as a test collection. Here is an example of how
to collect tests from classes that end in ``Suite``:
@@ -1246,15 +1247,23 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: python_files
One or more Glob-style file patterns determining which python files
are considered as test modules. By default, pytest will consider
any file matching with ``test_*.py`` and ``*_test.py`` globs as a test
module.
are considered as test modules. Search for multiple glob patterns by
adding a space between patterns::
.. code-block:: ini
[pytest]
python_files = test_*.py check_*.py example_*.py
By default, pytest will consider any file matching with ``test_*.py``
and ``*_test.py`` globs as a test module.
.. confval:: python_functions
One or more name prefixes or glob-patterns determining which test functions
and methods are considered tests. By default, pytest will consider any
and methods are considered tests. Search for multiple glob patterns by
adding a space between patterns. By default, pytest will consider any
function prefixed with ``test`` as a test. Here is an example of how
to collect test functions and methods that end in ``_test``:

View File

@@ -136,12 +136,6 @@ You can use the ``skipif`` marker (as any other marker) on classes::
If the condition is ``True``, this marker will produce a skip result for
each of the test methods of that class.
.. warning::
The use of ``skipif`` on classes that use inheritance is strongly
discouraged. `A Known bug <https://github.com/pytest-dev/pytest/issues/568>`_
in pytest's markers may cause unexpected behavior in super classes.
If you want to skip all test functions of a module, you may use
the ``pytestmark`` name on the global level:

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

@@ -73,11 +73,11 @@ def main():
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;python_version<"3.6"')
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"]
extras_require[':python_version<"3.6"'] = ["pathlib2>=2.2.0"]
extras_require[':sys_platform=="win32"'] = ["colorama"]
else:
if sys.platform == "win32":
@@ -85,7 +85,7 @@ def main():
if sys.version_info < (3, 0):
install_requires.append("funcsigs")
if sys.version_info < (3, 6):
install_requires.append("pathlib2")
install_requires.append("pathlib2>=2.2.0")
setup(
name="pytest",

View File

@@ -2,7 +2,7 @@
# CHANGES:
# - some_str is replaced, trying to create unicode strings
#
from __future__ import absolute_import, division, print_function
from __future__ import absolute_import, division, print_function, unicode_literals
import types
from six import text_type
@@ -51,17 +51,17 @@ def format_exception_only(etype, value):
pass
else:
filename = filename or "<string>"
lines.append(' File "%s", line %d\n' % (filename, lineno))
lines.append(' File "{}", line {}\n'.format(filename, lineno))
if badline is not None:
if isinstance(badline, bytes): # python 2 only
badline = badline.decode("utf-8", "replace")
lines.append(u" %s\n" % badline.strip())
lines.append(" {}\n".format(badline.strip()))
if offset is not None:
caretspace = badline.rstrip("\n")[:offset].lstrip()
# non-space whitespace (likes tabs) must be kept for alignment
caretspace = ((c.isspace() and c or " ") for c in caretspace)
# only three spaces to account for offset1 == pos 0
lines.append(" %s^\n" % "".join(caretspace))
lines.append(" {}^\n".format("".join(caretspace)))
value = msg
lines.append(_format_final_exc_line(stype, value))
@@ -72,9 +72,9 @@ def _format_final_exc_line(etype, value):
"""Return a list of a single line -- normal case for format_exception_only"""
valuestr = _some_str(value)
if value is None or not valuestr:
line = "%s\n" % etype
line = "{}\n".format(etype)
else:
line = "%s: %s\n" % (etype, valuestr)
line = "{}: {}\n".format(etype, valuestr)
return line
@@ -83,7 +83,7 @@ def _some_str(value):
return text_type(value)
except Exception:
try:
return str(value)
return bytes(value).decode("UTF-8", "replace")
except Exception:
pass
return "<unprintable %s object>" % type(value).__name__
return "<unprintable {} object>".format(type(value).__name__)

View File

@@ -11,6 +11,7 @@ from weakref import ref
from _pytest.compat import _PY2, _PY3, PY35, safe_str
from six import text_type
import py
import six
builtin_repr = repr
@@ -128,7 +129,7 @@ class Frame(object):
"""
f_locals = self.f_locals.copy()
f_locals.update(vars)
py.builtin.exec_(code, self.f_globals, f_locals)
six.exec_(code, self.f_globals, f_locals)
def repr(self, object):
""" return a 'safe' (non-recursive, one-line) string repr for 'object'
@@ -719,7 +720,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

@@ -223,7 +223,7 @@ class AssertionRewritingHook(object):
mod.__loader__ = self
# Normally, this attribute is 3.4+
mod.__spec__ = spec_from_file_location(name, co.co_filename, loader=self)
py.builtin.exec_(co, mod.__dict__)
six.exec_(co, mod.__dict__)
except: # noqa
if name in sys.modules:
del sys.modules[name]
@@ -402,12 +402,11 @@ def _saferepr(obj):
JSON reprs.
"""
repr = py.io.saferepr(obj)
if isinstance(repr, six.text_type):
t = six.text_type
r = py.io.saferepr(obj)
if isinstance(r, six.text_type):
return r.replace(u"\n", u"\\n")
else:
t = six.binary_type
return repr.replace(t("\n"), t("\\n"))
return r.replace(b"\n", b"\\n")
from _pytest.assertion.util import format_explanation as _format_explanation # noqa
@@ -425,20 +424,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):
@@ -448,10 +445,9 @@ def _should_repr_global_name(obj):
def _format_boolop(explanations, is_or):
explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")"
if isinstance(explanation, six.text_type):
t = six.text_type
return explanation.replace(u"%", u"%%")
else:
t = six.binary_type
return explanation.replace(t("%"), t("%%"))
return explanation.replace(b"%", b"%%")
def _call_reprcompare(ops, results, expls, each_obj):

View File

@@ -187,9 +187,9 @@ def _diff_text(left, right, verbose=False):
r = r.replace(r"\r", "\r")
return r
if isinstance(left, six.binary_type):
if isinstance(left, bytes):
left = escape_for_readable_diff(left)
if isinstance(right, six.binary_type):
if isinstance(right, bytes):
right = escape_for_readable_diff(right)
if not verbose:
i = 0 # just in case left or right has zero length

View File

@@ -312,8 +312,15 @@ def cache(request):
def pytest_report_header(config):
if config.option.verbose:
relpath = config.cache._cachedir.relative_to(config.rootdir)
return "cachedir: {}".format(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):

View File

@@ -16,7 +16,6 @@ import six
import pytest
from _pytest.compat import CaptureIO
patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"}
@@ -63,8 +62,9 @@ def pytest_load_initial_conftests(early_config, parser, args):
# finally trigger conftest loading but while capturing (issue93)
capman.start_global_capturing()
outcome = yield
out, err = capman.suspend_global_capture()
capman.suspend_global_capture()
if outcome.excinfo is not None:
out, err = capman.read_global_capture()
sys.stdout.write(out)
sys.stderr.write(err)
@@ -85,6 +85,7 @@ class CaptureManager(object):
def __init__(self, method):
self._method = method
self._global_capturing = None
self._current_item = None
def _getcapture(self, method):
if method == "fd":
@@ -96,6 +97,8 @@ class CaptureManager(object):
else:
raise ValueError("unknown capturing method: %r" % method)
# Global capturing control
def start_global_capturing(self):
assert self._global_capturing is None
self._global_capturing = self._getcapture(self._method)
@@ -110,16 +113,15 @@ class CaptureManager(object):
def resume_global_capture(self):
self._global_capturing.resume_capturing()
def suspend_global_capture(self, item=None, in_=False):
if item is not None:
self.deactivate_fixture(item)
def suspend_global_capture(self, in_=False):
cap = getattr(self, "_global_capturing", None)
if cap is not None:
try:
outerr = cap.readouterr()
finally:
cap.suspend_capturing(in_=in_)
return outerr
cap.suspend_capturing(in_=in_)
def read_global_capture(self):
return self._global_capturing.readouterr()
# Fixture Control (its just forwarding, think about removing this later)
def activate_fixture(self, item):
"""If the current item is using ``capsys`` or ``capfd``, activate them so they take precedence over
@@ -135,12 +137,53 @@ class CaptureManager(object):
if fixture is not None:
fixture.close()
def suspend_fixture(self, item):
fixture = getattr(item, "_capture_fixture", None)
if fixture is not None:
fixture._suspend()
def resume_fixture(self, item):
fixture = getattr(item, "_capture_fixture", None)
if fixture is not None:
fixture._resume()
# Helper context managers
@contextlib.contextmanager
def global_and_fixture_disabled(self):
"""Context manager to temporarily disables global and current fixture capturing."""
# Need to undo local capsys-et-al if exists before disabling global capture
self.suspend_fixture(self._current_item)
self.suspend_global_capture(in_=False)
try:
yield
finally:
self.resume_global_capture()
self.resume_fixture(self._current_item)
@contextlib.contextmanager
def item_capture(self, when, item):
self.resume_global_capture()
self.activate_fixture(item)
try:
yield
finally:
self.deactivate_fixture(item)
self.suspend_global_capture(in_=False)
out, err = self.read_global_capture()
item.add_report_section(when, "stdout", out)
item.add_report_section(when, "stderr", err)
# Hooks
@pytest.hookimpl(hookwrapper=True)
def pytest_make_collect_report(self, collector):
if isinstance(collector, pytest.File):
self.resume_global_capture()
outcome = yield
out, err = self.suspend_global_capture()
self.suspend_global_capture()
out, err = self.read_global_capture()
rep = outcome.get_result()
if out:
rep.sections.append(("Captured stdout", out))
@@ -150,29 +193,25 @@ class CaptureManager(object):
yield
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_setup(self, item):
self.resume_global_capture()
# no need to activate a capture fixture because they activate themselves during creation; this
# only makes sense when a fixture uses a capture fixture, otherwise the capture fixture will
# be activated during pytest_runtest_call
def pytest_runtest_protocol(self, item):
self._current_item = item
yield
self.suspend_capture_item(item, "setup")
self._current_item = None
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_setup(self, item):
with self.item_capture("setup", item):
yield
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(self, item):
self.resume_global_capture()
# it is important to activate this fixture during the call phase so it overwrites the "global"
# capture
self.activate_fixture(item)
yield
self.suspend_capture_item(item, "call")
with self.item_capture("call", item):
yield
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_teardown(self, item):
self.resume_global_capture()
self.activate_fixture(item)
yield
self.suspend_capture_item(item, "teardown")
with self.item_capture("teardown", item):
yield
@pytest.hookimpl(tryfirst=True)
def pytest_keyboard_interrupt(self, excinfo):
@@ -182,11 +221,6 @@ class CaptureManager(object):
def pytest_internalerror(self, excinfo):
self.stop_global_capturing()
def suspend_capture_item(self, item, when, in_=False):
out, err = self.suspend_global_capture(item, in_=in_)
item.add_report_section(when, "stdout", out)
item.add_report_section(when, "stderr", err)
capture_fixtures = {"capfd", "capfdbinary", "capsys", "capsysbinary"}
@@ -290,40 +324,54 @@ class CaptureFixture(object):
def __init__(self, captureclass, request):
self.captureclass = captureclass
self.request = request
self._capture = None
self._captured_out = self.captureclass.EMPTY_BUFFER
self._captured_err = self.captureclass.EMPTY_BUFFER
def _start(self):
self._capture = MultiCapture(
out=True, err=True, in_=False, Capture=self.captureclass
)
self._capture.start_capturing()
# Start if not started yet
if getattr(self, "_capture", None) is None:
self._capture = MultiCapture(
out=True, err=True, in_=False, Capture=self.captureclass
)
self._capture.start_capturing()
def close(self):
cap = self.__dict__.pop("_capture", None)
if cap is not None:
self._outerr = cap.pop_outerr_to_orig()
cap.stop_capturing()
if self._capture is not None:
out, err = self._capture.pop_outerr_to_orig()
self._captured_out += out
self._captured_err += err
self._capture.stop_capturing()
self._capture = None
def readouterr(self):
"""Read and return the captured output so far, resetting the internal buffer.
:return: captured content as a namedtuple with ``out`` and ``err`` string attributes
"""
try:
return self._capture.readouterr()
except AttributeError:
return self._outerr
captured_out, captured_err = self._captured_out, self._captured_err
if self._capture is not None:
out, err = self._capture.readouterr()
captured_out += out
captured_err += err
self._captured_out = self.captureclass.EMPTY_BUFFER
self._captured_err = self.captureclass.EMPTY_BUFFER
return CaptureResult(captured_out, captured_err)
def _suspend(self):
"""Suspends this fixture's own capturing temporarily."""
self._capture.suspend_capturing()
def _resume(self):
"""Resumes this fixture's own capturing temporarily."""
self._capture.resume_capturing()
@contextlib.contextmanager
def disabled(self):
"""Temporarily disables capture while inside the 'with' block."""
self._capture.suspend_capturing()
capmanager = self.request.config.pluginmanager.getplugin("capturemanager")
capmanager.suspend_global_capture(item=None, in_=False)
try:
with capmanager.global_and_fixture_disabled():
yield
finally:
capmanager.resume_global_capture()
self._capture.resume_capturing()
def safe_text_dupfile(f, mode, default_encoding="UTF8"):
@@ -440,6 +488,7 @@ class MultiCapture(object):
class NoCapture(object):
EMPTY_BUFFER = None
__init__ = start = done = suspend = resume = lambda *args: None
@@ -449,6 +498,8 @@ class FDCaptureBinary(object):
snap() produces `bytes`
"""
EMPTY_BUFFER = bytes()
def __init__(self, targetfd, tmpfile=None):
self.targetfd = targetfd
try:
@@ -522,6 +573,8 @@ class FDCapture(FDCaptureBinary):
snap() produces text
"""
EMPTY_BUFFER = str()
def snap(self):
res = FDCaptureBinary.snap(self)
enc = getattr(self.tmpfile, "encoding", None)
@@ -531,6 +584,9 @@ class FDCapture(FDCaptureBinary):
class SysCapture(object):
EMPTY_BUFFER = str()
def __init__(self, fd, tmpfile=None):
name = patchsysdict[fd]
self._old = getattr(sys, name)
@@ -568,6 +624,8 @@ class SysCapture(object):
class SysCaptureBinary(SysCapture):
EMPTY_BUFFER = bytes()
def snap(self):
res = self.tmpfile.buffer.getvalue()
self.tmpfile.seek(0)

View File

@@ -8,6 +8,7 @@ import functools
import inspect
import re
import sys
from contextlib import contextmanager
import py
@@ -151,6 +152,13 @@ def getfuncargnames(function, is_method=False, cls=None):
return arg_names
@contextmanager
def dummy_context_manager():
"""Context manager that does nothing, useful in situations where you might need an actual context manager or not
depending on some condition. Using this allow to keep the same code"""
yield
def get_default_arg_names(function):
# Note: this code intentionally mirrors the code at the beginning of getfuncargnames,
# to get the arguments which were excluded from its result because they had default values
@@ -228,12 +236,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
@@ -249,6 +276,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
@@ -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

@@ -2,6 +2,8 @@ import six
import warnings
import argparse
import py
FILE_OR_DIR = "file_or_dir"
@@ -70,7 +72,8 @@ class Parser(object):
self.optparser = self._getparser()
try_argcomplete(self.optparser)
return self.optparser.parse_args([str(x) for x in args], namespace=namespace)
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
return self.optparser.parse_args(args, namespace=namespace)
def _getparser(self):
from _pytest._argcomplete import filescompleter
@@ -106,7 +109,7 @@ class Parser(object):
the remaining arguments unknown at this point.
"""
optparser = self._getparser()
args = [str(x) for x in args]
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
return optparser.parse_known_args(args, namespace=namespace)
def addini(self, name, help, type=None, default=None):
@@ -174,23 +177,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

@@ -102,7 +102,8 @@ class PdbInvoke(object):
def pytest_exception_interact(self, node, call, report):
capman = node.config.pluginmanager.getplugin("capturemanager")
if capman:
out, err = capman.suspend_global_capture(in_=True)
capman.suspend_global_capture(in_=True)
out, err = capman.read_global_capture()
sys.stdout.write(out)
sys.stdout.write(err)
_enter_pdb(node, call.excinfo, report)

View File

@@ -2,7 +2,6 @@ from __future__ import absolute_import, division, print_function
import functools
import inspect
import os
import sys
import warnings
from collections import OrderedDict, deque, defaultdict
@@ -30,6 +29,8 @@ 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
@@ -91,7 +92,7 @@ def get_scope_package(node, fixturedef):
cls = pytest.Package
current = node
fixture_package_name = os.path.join(fixturedef.baseid, "__init__.py")
fixture_package_name = "%s/%s" % (fixturedef.baseid, "__init__.py")
while current and (
type(current) is not cls or fixture_package_name != current.nodeid
):
@@ -305,8 +306,8 @@ class FuncFixtureInfo(object):
# 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]]")
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
@@ -856,7 +857,7 @@ class FixtureDef(object):
if exceptions:
e = exceptions[0]
del exceptions # ensure we don't keep all frames alive because of the traceback
py.builtin._reraise(*e)
six.reraise(*e)
finally:
hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
@@ -883,7 +884,7 @@ class FixtureDef(object):
result, cache_key, err = cached_result
if my_cache_key == cache_key:
if err is not None:
py.builtin._reraise(*err)
six.reraise(*err)
else:
return result
# we have a previous but differently parametrized fixture instance
@@ -931,13 +932,6 @@ def pytest_fixture_setup(fixturedef, request):
request._check_scope(argname, request.scope, fixdef.scope)
kwargs[argname] = result
# if function has been defined with @pytest.fixture, we want to
# pass the special __being_called_by_pytest parameter so we don't raise a warning
# this is an ugly hack, see #3720 for an opportunity to improve this
defined_using_fixture_decorator = hasattr(fixturedef.func, "_pytestfixturefunction")
if defined_using_fixture_decorator:
kwargs["__being_called_by_pytest"] = True
fixturefunc = resolve_fixture_function(fixturedef, request)
my_cache_key = request.param_index
try:
@@ -960,9 +954,6 @@ def _ensure_immutable_ids(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.
The warning is emitted only in Python 3, because I didn't find a reliable way to make the wrapper function
keep the original signature, and we probably will drop Python 2 in Pytest 4 anyway.
"""
is_yield_function = is_generator(function)
msg = FIXTURE_FUNCTION_CALL.format(name=fixture_marker.name or function.__name__)
@@ -973,9 +964,7 @@ def wrap_function_to_warning_if_called_directly(function, fixture_marker):
@functools.wraps(function)
def result(*args, **kwargs):
__tracebackhide__ = True
__being_called_by_pytest = kwargs.pop("__being_called_by_pytest", False)
if not __being_called_by_pytest:
warnings.warn(warning, stacklevel=3)
warnings.warn(warning, stacklevel=3)
for x in function(*args, **kwargs):
yield x
@@ -984,14 +973,16 @@ def wrap_function_to_warning_if_called_directly(function, fixture_marker):
@functools.wraps(function)
def result(*args, **kwargs):
__tracebackhide__ = True
__being_called_by_pytest = kwargs.pop("__being_called_by_pytest", False)
if not __being_called_by_pytest:
warnings.warn(warning, stacklevel=3)
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
@@ -1279,9 +1270,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
@@ -1303,6 +1294,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

@@ -6,6 +6,7 @@ from contextlib import closing, contextmanager
import re
import six
from _pytest.compat import dummy_context_manager
from _pytest.config import create_terminal_writer
import pytest
import py
@@ -369,11 +370,6 @@ def pytest_configure(config):
config.pluginmanager.register(LoggingPlugin(config), "logging-plugin")
@contextmanager
def _dummy_context_manager():
yield
class LoggingPlugin(object):
"""Attaches to the logging module and captures log messages for each test.
"""
@@ -537,7 +533,7 @@ class LoggingPlugin(object):
log_cli_handler, formatter=log_cli_formatter, level=log_cli_level
)
else:
self.live_logs_context = _dummy_context_manager()
self.live_logs_context = dummy_context_manager()
class _LiveLoggingStreamHandler(logging.StreamHandler):
@@ -572,9 +568,12 @@ class _LiveLoggingStreamHandler(logging.StreamHandler):
self._test_outcome_written = False
def emit(self, record):
if self.capture_manager is not None:
self.capture_manager.suspend_global_capture()
try:
ctx_manager = (
self.capture_manager.global_and_fixture_disabled()
if self.capture_manager
else dummy_context_manager()
)
with ctx_manager:
if not self._first_record_emitted:
self.stream.write("\n")
self._first_record_emitted = True
@@ -586,6 +585,3 @@ class _LiveLoggingStreamHandler(logging.StreamHandler):
self.stream.section("live log " + self._when, sep="-", bold=True)
self._section_name_shown = True
logging.StreamHandler.emit(self, record)
finally:
if self.capture_manager is not None:
self.capture_manager.resume_global_capture()

View File

@@ -482,6 +482,8 @@ class Session(nodes.FSCollector):
self.trace.root.indent -= 1
def _collect(self, arg):
from _pytest.python import Package
names = self._parsearg(arg)
argpath = names.pop(0)
paths = []
@@ -504,7 +506,8 @@ class Session(nodes.FSCollector):
else:
col = root._collectfile(pkginit)
if col:
root = col[0]
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.
@@ -622,11 +625,12 @@ class Session(nodes.FSCollector):
resultnodes.append(node)
continue
assert isinstance(node, nodes.Collector)
if node.nodeid in self._node_cache:
rep = self._node_cache[node.nodeid]
key = (type(node), node.nodeid)
if key in self._node_cache:
rep = self._node_cache[key]
else:
rep = collect_one_node(node)
self._node_cache[node.nodeid] = rep
self._node_cache[key] = rep
if rep.passed:
has_matched = False
for x in rep.result:

View File

@@ -3,7 +3,6 @@ exception classes and constants handling test outcomes
as well as functions creating them
"""
from __future__ import absolute_import, division, print_function
import py
import sys
@@ -21,7 +20,7 @@ class OutcomeException(BaseException):
if self.msg:
val = self.msg
if isinstance(val, bytes):
val = py._builtin._totext(val, errors="replace")
val = val.decode("UTF-8", errors="replace")
return val
return "<%s instance>" % (self.__class__.__name__,)

View File

@@ -22,6 +22,7 @@ import pytest
from _pytest.main import Session, EXIT_OK
from _pytest.assertion.rewrite import AssertionRewritingHook
from _pytest.compat import Path
from _pytest.compat import safe_str
IGNORE_PAM = [ # filenames added when obtaining details about the current user
u"/var/lib/sss/mc/passwd"
@@ -34,7 +35,7 @@ def pytest_addoption(parser):
action="store_true",
dest="lsof",
default=False,
help=("run FD checks if lsof is available"),
help="run FD checks if lsof is available",
)
parser.addoption(
@@ -273,7 +274,7 @@ class HookRecorder(object):
del self.calls[i]
return call
lines = ["could not find call %r, in:" % (name,)]
lines.extend([" %s" % str(x) for x in self.calls])
lines.extend([" %s" % x for x in self.calls])
pytest.fail("\n".join(lines))
def getcall(self, name):
@@ -550,18 +551,22 @@ class Testdir(object):
return ret
def makefile(self, ext, *args, **kwargs):
"""Create a new file in the testdir.
r"""Create new file(s) in the testdir.
ext: The extension the file should use, including the dot, e.g. `.py`.
args: All args will be treated as strings and joined using newlines.
:param str ext: The extension the file(s) should use, including the dot, e.g. `.py`.
:param list[str] args: All args will be treated as strings and joined using newlines.
The result will be written as contents to the file. The name of the
file will be based on the test function requesting this fixture.
E.g. "testdir.makefile('.txt', 'line1', 'line2')"
kwargs: Each keyword is the name of a file, while the value of it will
:param kwargs: Each keyword is the name of a file, while the value of it will
be written as contents of the file.
E.g. "testdir.makefile('.ini', pytest='[pytest]\naddopts=-rs\n')"
Examples:
.. code-block:: python
testdir.makefile(".txt", "line1", "line2")
testdir.makefile(".ini", pytest="[pytest]\naddopts=-rs\n")
"""
return self._makefile(ext, args, kwargs)
@@ -667,7 +672,9 @@ class Testdir(object):
example_path.copy(result)
return result
else:
raise LookupError("example is not found as a file or directory")
raise LookupError(
'example "{}" is not found as a file or directory'.format(example_path)
)
Session = Session
@@ -881,14 +888,12 @@ class Testdir(object):
return self._runpytest_method(*args, **kwargs)
def _ensure_basetemp(self, args):
args = [str(x) for x in args]
args = list(args)
for x in args:
if str(x).startswith("--basetemp"):
# print("basedtemp exists: %s" %(args,))
if safe_str(x).startswith("--basetemp"):
break
else:
args.append("--basetemp=%s" % self.tmpdir.dirpath("basetemp"))
# print("added basetemp: %s" %(args,))
return args
def parseconfig(self, *args):
@@ -1014,7 +1019,7 @@ class Testdir(object):
"""
env = os.environ.copy()
env["PYTHONPATH"] = os.pathsep.join(
filter(None, [str(os.getcwd()), env.get("PYTHONPATH", "")])
filter(None, [os.getcwd(), env.get("PYTHONPATH", "")])
)
kw["env"] = env
@@ -1033,14 +1038,13 @@ class Testdir(object):
Returns a :py:class:`RunResult`.
"""
return self._run(*cmdargs)
def _run(self, *cmdargs):
cmdargs = [str(x) for x in cmdargs]
cmdargs = [
str(arg) if isinstance(arg, py.path.local) else arg for arg in cmdargs
]
p1 = self.tmpdir.join("stdout")
p2 = self.tmpdir.join("stderr")
print("running:", " ".join(cmdargs))
print(" in:", str(py.path.local()))
print("running:", *cmdargs)
print(" in:", py.path.local())
f1 = codecs.open(str(p1), "w", encoding="utf8")
f2 = codecs.open(str(p2), "w", encoding="utf8")
try:
@@ -1072,7 +1076,7 @@ class Testdir(object):
print("couldn't print to %s because of encoding" % (fp,))
def _getpytestargs(self):
return (sys.executable, "-mpytest")
return sys.executable, "-mpytest"
def runpython(self, script):
"""Run a python script using sys.executable as interpreter.

View File

@@ -201,33 +201,25 @@ 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") + ["__init__.py"]:
if path.fnmatch(pat):
break
else:
if not path_matches_patterns(
path, parent.config.getini("python_files") + ["__init__.py"]
):
return
ihook = parent.session.gethookproxy(path)
return ihook.pytest_pycollect_makemodule(path=path, parent=parent)
def path_matches_patterns(path, patterns):
"""Returns True if the given py.path.local matches one of the patterns in the list of globs given"""
return any(path.fnmatch(pattern) for pattern in patterns)
def pytest_pycollect_makemodule(path, parent):
if path.basename == "__init__.py":
return Package(path, parent)
return Module(path, parent)
def pytest_ignore_collect(path, config):
# Skip duplicate packages.
keepduplicates = config.getoption("keepduplicates")
if keepduplicates:
duplicate_paths = config.pluginmanager._duplicatepaths
if path.basename == "__init__.py":
if path in duplicate_paths:
return True
else:
duplicate_paths.add(path)
@hookimpl(hookwrapper=True)
def pytest_pycollect_makeitem(collector, name, obj):
outcome = yield
@@ -554,14 +546,12 @@ class Package(Module):
self.name = fspath.dirname
self.trace = session.trace
self._norecursepatterns = session._norecursepatterns
for path in list(session.config.pluginmanager._duplicatepaths):
if path.dirname == fspath.dirname and path != fspath:
session.config.pluginmanager._duplicatepaths.remove(path)
self.fspath = fspath
def _recurse(self, path):
ihook = self.gethookproxy(path.dirpath())
if ihook.pytest_ignore_collect(path=path, config=self.config):
return
return False
for pat in self._norecursepatterns:
if path.check(fnmatch=pat):
return False
@@ -594,15 +584,43 @@ class Package(Module):
return path in self.session._initialpaths
def collect(self):
path = self.fspath.dirpath()
pkg_prefix = None
for path in path.visit(fil=lambda x: 1, rec=self._recurse, bf=True, sort=True):
if pkg_prefix and pkg_prefix in path.parts():
# 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()
init_module = this_path.join("__init__.py")
if init_module.check(file=1) and path_matches_patterns(
init_module, self.config.getini("python_files")
):
yield Module(init_module, self)
pkg_prefixes = set()
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
skip = False
if path.basename == "__init__.py" and path.dirpath() == this_path:
continue
for pkg_prefix in pkg_prefixes:
if (
pkg_prefix in path.parts()
and pkg_prefix.join("__init__.py") != path
):
skip = True
if skip:
continue
if path.isdir() and path.join("__init__.py").check(file=1):
pkg_prefixes.add(path)
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):
@@ -743,7 +761,7 @@ class FunctionMixin(PyobjMixin):
def _repr_failure_py(self, excinfo, style="long"):
if excinfo.errisinstance(fail.Exception):
if not excinfo.value.pytrace:
return py._builtin._totext(excinfo.value)
return six.text_type(excinfo.value)
return super(FunctionMixin, self)._repr_failure_py(excinfo, style=style)
def repr_failure(self, excinfo, outerr=None):
@@ -880,12 +898,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.

View File

@@ -1,7 +1,10 @@
import math
import pprint
import sys
from numbers import Number
from decimal import Decimal
import py
import six
from six.moves import zip, filterfalse
from more_itertools.more import always_iterable
@@ -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 all(a == self for a in actual.flat)
# 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
@@ -633,8 +680,8 @@ def raises(expected_exception, *args, **kwargs):
# print "raises frame scope: %r" % frame.f_locals
try:
code = _pytest._code.Source(code).compile()
py.builtin.exec_(code, frame.f_globals, loc)
# XXX didn'T mean f_globals == f_locals something special?
six.exec_(code, frame.f_globals, loc)
# XXX didn't mean f_globals == f_locals something special?
# this is destroyed here ...
except expected_exception:
return _pytest._code.ExceptionInfo()

View File

@@ -4,11 +4,11 @@ from __future__ import absolute_import, division, print_function
import inspect
import _pytest._code
import py
import re
import sys
import warnings
import re
import six
from _pytest.fixtures import yield_fixture
from _pytest.outcomes import fail
@@ -130,7 +130,7 @@ def warns(expected_warning, *args, **kwargs):
with WarningsChecker(expected_warning, match_expr=match_expr):
code = _pytest._code.Source(code).compile()
py.builtin.exec_(code, frame.f_globals, loc)
six.exec_(code, frame.f_globals, loc)
else:
func = args[0]
with WarningsChecker(expected_warning, match_expr=match_expr):

View File

@@ -6,7 +6,7 @@ import os
import sys
from time import time
import py
import six
from _pytest._code.code import ExceptionInfo
from _pytest.outcomes import skip, Skipped, TEST_OUTCOME
@@ -317,7 +317,7 @@ class SetupState(object):
if exc is None:
exc = sys.exc_info()
if exc:
py.builtin._reraise(*exc)
six.reraise(*exc)
def _teardown_with_finalization(self, colitem):
self._callfinalizers(colitem)
@@ -352,7 +352,7 @@ class SetupState(object):
if exc is None:
exc = sys.exc_info()
if exc:
py.builtin._reraise(*exc)
six.reraise(*exc)
def prepare(self, colitem):
""" setup objects along the collector chain to the test-method
@@ -363,7 +363,7 @@ class SetupState(object):
# check if the last collection node has raised an error
for col in self.stack:
if hasattr(col, "_prepare_exc"):
py.builtin._reraise(*col._prepare_exc)
six.reraise(*col._prepare_exc)
for col in needed_collectors[len(self.stack) :]:
self.stack.append(col)
try:

View File

@@ -51,7 +51,8 @@ def _show_fixture_action(fixturedef, msg):
config = fixturedef._fixturemanager.config
capman = config.pluginmanager.getplugin("capturemanager")
if capman:
out, err = capman.suspend_global_capture()
capman.suspend_global_capture()
out, err = capman.read_global_capture()
tw = config.get_terminal_writer()
tw.line()

View File

@@ -706,7 +706,12 @@ class TerminalReporter(object):
self._outrep_summary(rep)
def print_teardown_sections(self, rep):
showcapture = self.config.option.showcapture
if showcapture == "no":
return
for secname, content in rep.sections:
if showcapture != "all" and showcapture not in secname:
continue
if "teardown" in secname:
self._tw.sep("-", secname)
if content[-1:] == "\n":

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

@@ -2,11 +2,11 @@
from __future__ import absolute_import, division, print_function
import os
import sys
import textwrap
import types
import six
import _pytest._code
import py
import pytest
from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
@@ -131,7 +131,7 @@ class TestGeneralUsage(object):
p2 = testdir.makefile(".pyc", "123")
result = testdir.runpytest(p1, p2)
assert result.ret
result.stderr.fnmatch_lines(["*ERROR: not found:*%s" % (p2.basename,)])
result.stderr.fnmatch_lines(["*ERROR: not found:*{}".format(p2.basename)])
def test_issue486_better_reporting_on_conftest_load_failure(self, testdir):
testdir.makepyfile("")
@@ -201,16 +201,16 @@ class TestGeneralUsage(object):
testdir.tmpdir.join("py").mksymlinkto(py._pydir)
p = testdir.tmpdir.join("main.py")
p.write(
_pytest._code.Source(
textwrap.dedent(
"""\
import sys, os
sys.path.insert(0, '')
import py
print(py.__file__)
print(py.__path__)
os.chdir(os.path.dirname(os.getcwd()))
print(py.log)
"""
import sys, os
sys.path.insert(0, '')
import py
print (py.__file__)
print (py.__path__)
os.chdir(os.path.dirname(os.getcwd()))
print (py.log)
"""
)
)
result = testdir.runpython(p)
@@ -453,7 +453,7 @@ class TestInvocationVariants(object):
@pytest.mark.xfail("sys.platform.startswith('java')")
def test_pydoc(self, testdir):
for name in ("py.test", "pytest"):
result = testdir.runpython_c("import %s;help(%s)" % (name, name))
result = testdir.runpython_c("import {};help({})".format(name, name))
assert result.ret == 0
s = result.stdout.str()
assert "MarkGenerator" in s
@@ -660,6 +660,16 @@ class TestInvocationVariants(object):
["*test_world.py::test_other*PASSED*", "*1 passed*"]
)
def test_invoke_test_and_doctestmodules(self, testdir):
p = testdir.makepyfile(
"""
def test():
pass
"""
)
result = testdir.runpytest(str(p) + "::test", "--doctest-modules")
result.stdout.fnmatch_lines(["*1 passed*"])
@pytest.mark.skipif(not hasattr(os, "symlink"), reason="requires symlinks")
def test_cmdline_python_package_symlink(self, testdir, monkeypatch):
"""
@@ -826,7 +836,7 @@ class TestDurations(object):
if ("test_%s" % x) in line and y in line:
break
else:
raise AssertionError("not found %s %s" % (x, y))
raise AssertionError("not found {} {}".format(x, y))
def test_with_deselected(self, testdir):
testdir.makepyfile(self.source)
@@ -1044,3 +1054,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

@@ -3,8 +3,8 @@ from __future__ import absolute_import, division, print_function
import sys
import _pytest._code
import py
import pytest
import mock
from test_excinfo import TWMock
from six import text_type
@@ -68,12 +68,8 @@ def test_getstatement_empty_fullsource():
f = func()
f = _pytest._code.Frame(f)
prop = f.code.__class__.fullsource
try:
f.code.__class__.fullsource = None
assert f.statement == _pytest._code.Source("")
finally:
f.code.__class__.fullsource = prop
with mock.patch.object(f.code.__class__, "fullsource", None):
assert f.statement == ""
def test_code_from_func():
@@ -83,7 +79,7 @@ def test_code_from_func():
def test_unicode_handling():
value = py.builtin._totext("\xc4\x85\xc4\x87\n", "utf-8").encode("utf8")
value = u"ąć".encode("UTF-8")
def f():
raise Exception(value)
@@ -96,7 +92,7 @@ def test_unicode_handling():
@pytest.mark.skipif(sys.version_info[0] >= 3, reason="python 2 only issue")
def test_unicode_handling_syntax_error():
value = py.builtin._totext("\xc4\x85\xc4\x87\n", "utf-8").encode("utf8")
value = u"ąć".encode("UTF-8")
def f():
raise SyntaxError("invalid syntax", (None, 1, 3, value))

View File

@@ -4,9 +4,11 @@ from __future__ import absolute_import, division, print_function
import operator
import os
import sys
import textwrap
import _pytest
import py
import pytest
import six
from _pytest._code.code import (
ExceptionInfo,
FormattedExcinfo,
@@ -147,7 +149,7 @@ class TestTraceback_f_g_h(object):
except somenoname:
pass
xyz()
"""
"""
)
try:
exec(source.compile())
@@ -250,7 +252,7 @@ class TestTraceback_f_g_h(object):
import sys
exc, val, tb = sys.exc_info()
py.builtin._reraise(exc, val, tb)
six.reraise(exc, val, tb)
def f(n):
try:
@@ -268,7 +270,7 @@ class TestTraceback_f_g_h(object):
decorator = pytest.importorskip("decorator").decorator
def log(f, *k, **kw):
print("%s %s" % (k, kw))
print("{} {}".format(k, kw))
f(*k, **kw)
log = decorator(log)
@@ -424,7 +426,7 @@ class TestFormattedExcinfo(object):
@pytest.fixture
def importasmod(self, request):
def importasmod(source):
source = _pytest._code.Source(source)
source = textwrap.dedent(source)
tmpdir = request.getfixturevalue("tmpdir")
modpath = tmpdir.join("mod.py")
tmpdir.ensure("__init__.py")
@@ -448,10 +450,10 @@ class TestFormattedExcinfo(object):
def test_repr_source(self):
pr = FormattedExcinfo()
source = _pytest._code.Source(
"""
"""\
def f(x):
pass
"""
"""
).strip()
pr.flow_marker = "|"
lines = pr.get_source(source, 0)
@@ -883,10 +885,10 @@ raise ValueError()
class MyRepr(TerminalRepr):
def toterminal(self, tw):
tw.line(py.builtin._totext("я", "utf-8"))
tw.line(u"я")
x = py.builtin._totext(MyRepr())
assert x == py.builtin._totext("я", "utf-8")
x = six.text_type(MyRepr())
assert x == u"я"
def test_toterminal_long(self, importasmod):
mod = importasmod(
@@ -1265,6 +1267,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

@@ -6,8 +6,8 @@ import inspect
import sys
import _pytest._code
import py
import pytest
import six
from _pytest._code import Source
from _pytest._code.source import ast
@@ -323,7 +323,7 @@ class TestSourceParsingAndCompiling(object):
def test_compile_and_getsource(self):
co = self.source.compile()
py.builtin.exec_(co, globals())
six.exec_(co, globals())
f(7)
excinfo = pytest.raises(AssertionError, "f(6)")
frame = excinfo.traceback[-1].frame
@@ -392,7 +392,7 @@ def test_getfuncsource_dynamic():
def g(): pass
"""
co = _pytest._code.compile(source)
py.builtin.exec_(co, globals())
six.exec_(co, globals())
assert str(_pytest._code.Source(f)).strip() == "def f():\n raise ValueError"
assert str(_pytest._code.Source(g)).strip() == "def g(): pass"

View File

@@ -267,7 +267,6 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated_no_false_positives(
)
# @pytest.mark.skipif(six.PY2, reason="We issue the warning in Python 3 only")
def test_call_fixture_function_deprecated():
"""Check if a warning is raised if a fixture function is called directly (#3661)"""

View File

@@ -0,0 +1,20 @@
"""Reproduces issue #3774"""
try:
import mock
except ImportError:
import unittest.mock as 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 @@
[pytest]
python_files = *.py

View File

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

View File

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

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

@@ -876,22 +876,18 @@ def test_live_logging_suspends_capture(has_capture_manager, request):
is installed.
"""
import logging
import contextlib
from functools import partial
from _pytest.capture import CaptureManager
from _pytest.logging import _LiveLoggingStreamHandler
class MockCaptureManager:
calls = []
def suspend_global_capture(self):
self.calls.append("suspend_global_capture")
def resume_global_capture(self):
self.calls.append("resume_global_capture")
# sanity check
assert CaptureManager.suspend_capture_item
assert CaptureManager.resume_global_capture
@contextlib.contextmanager
def global_and_fixture_disabled(self):
self.calls.append("enter disabled")
yield
self.calls.append("exit disabled")
class DummyTerminal(six.StringIO):
def section(self, *args, **kwargs):
@@ -908,10 +904,7 @@ def test_live_logging_suspends_capture(has_capture_manager, request):
logger.critical("some message")
if has_capture_manager:
assert MockCaptureManager.calls == [
"suspend_global_capture",
"resume_global_capture",
]
assert MockCaptureManager.calls == ["enter disabled", "exit disabled"]
else:
assert MockCaptureManager.calls == []
assert out_file.getvalue() == "\nsome message\n"

View File

@@ -59,17 +59,21 @@ class TestApprox(object):
),
)
def test_repr_0d_array(self, plus_minus):
@pytest.mark.parametrize(
"value, repr_string",
[
(5., "approx(5.0 {pm} 5.0e-06)"),
([5.], "approx([5.0 {pm} 5.0e-06])"),
([[5.]], "approx([[5.0 {pm} 5.0e-06]])"),
([[5., 6.]], "approx([[5.0 {pm} 5.0e-06, 6.0 {pm} 6.0e-06]])"),
([[5.], [6.]], "approx([[5.0 {pm} 5.0e-06], [6.0 {pm} 6.0e-06]])"),
],
)
def test_repr_nd_array(self, plus_minus, value, repr_string):
"""Make sure that arrays of all different dimensions are repr'd correctly."""
np = pytest.importorskip("numpy")
np_array = np.array(5.)
assert approx(np_array) == 5.0
string_expected = "approx([5.0 {} 5.0e-06])".format(plus_minus)
assert repr(approx(np_array)) == string_expected
np_array = np.array([5.])
assert approx(np_array) == 5.0
assert repr(approx(np_array)) == string_expected
np_array = np.array(value)
assert repr(approx(np_array)) == repr_string.format(pm=plus_minus)
def test_operator_overloading(self):
assert 1 == approx(1, rel=1e-6, abs=1e-12)
@@ -439,6 +443,21 @@ class TestApprox(object):
["*At index 0 diff: 3 != 4 * {}".format(expected), "=* 1 failed in *="]
)
@pytest.mark.parametrize(
"x",
[
pytest.param(None),
pytest.param("string"),
pytest.param(["string"], id="nested-str"),
pytest.param([[1]], id="nested-list"),
pytest.param({"key": "string"}, id="dict-with-string"),
pytest.param({"key": {"key": 1}}, id="nested-dict"),
],
)
def test_expected_value_type_error(self, x):
with pytest.raises(TypeError):
approx(x)
@pytest.mark.parametrize(
"op",
[

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import os
import sys
from textwrap import dedent
import textwrap
import _pytest._code
import pytest
@@ -47,13 +47,14 @@ class TestModule(object):
p = root2.join("test_x456.py")
monkeypatch.syspath_prepend(str(root1))
p.write(
dedent(
textwrap.dedent(
"""\
import x456
def test():
assert x456.__file__.startswith(%r)
"""
% str(root2)
import x456
def test():
assert x456.__file__.startswith({!r})
""".format(
str(root2)
)
)
)
with root2.as_cwd():
@@ -929,23 +930,23 @@ class TestConftestCustomization(object):
def test_customized_pymakemodule_issue205_subdir(self, testdir):
b = testdir.mkdir("a").mkdir("b")
b.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
import pytest
@pytest.hookimpl(hookwrapper=True)
def pytest_pycollect_makemodule():
outcome = yield
mod = outcome.get_result()
mod.obj.hello = "world"
"""
import pytest
@pytest.hookimpl(hookwrapper=True)
def pytest_pycollect_makemodule():
outcome = yield
mod = outcome.get_result()
mod.obj.hello = "world"
"""
)
)
b.join("test_module.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
def test_hello():
assert hello == "world"
"""
def test_hello():
assert hello == "world"
"""
)
)
reprec = testdir.inline_run()
@@ -954,31 +955,31 @@ class TestConftestCustomization(object):
def test_customized_pymakeitem(self, testdir):
b = testdir.mkdir("a").mkdir("b")
b.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
import pytest
@pytest.hookimpl(hookwrapper=True)
def pytest_pycollect_makeitem():
outcome = yield
if outcome.excinfo is None:
result = outcome.get_result()
if result:
for func in result:
func._some123 = "world"
"""
import pytest
@pytest.hookimpl(hookwrapper=True)
def pytest_pycollect_makeitem():
outcome = yield
if outcome.excinfo is None:
result = outcome.get_result()
if result:
for func in result:
func._some123 = "world"
"""
)
)
b.join("test_module.py").write(
_pytest._code.Source(
"""
import pytest
textwrap.dedent(
"""\
import pytest
@pytest.fixture()
def obj(request):
return request.node._some123
def test_hello(obj):
assert obj == "world"
"""
@pytest.fixture()
def obj(request):
return request.node._some123
def test_hello(obj):
assert obj == "world"
"""
)
)
reprec = testdir.inline_run()
@@ -1033,7 +1034,7 @@ class TestConftestCustomization(object):
)
testdir.makefile(
".narf",
"""
"""\
def test_something():
assert 1 + 1 == 2""",
)
@@ -1046,29 +1047,29 @@ def test_setup_only_available_in_subdir(testdir):
sub1 = testdir.mkpydir("sub1")
sub2 = testdir.mkpydir("sub2")
sub1.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
import pytest
def pytest_runtest_setup(item):
assert item.fspath.purebasename == "test_in_sub1"
def pytest_runtest_call(item):
assert item.fspath.purebasename == "test_in_sub1"
def pytest_runtest_teardown(item):
assert item.fspath.purebasename == "test_in_sub1"
"""
import pytest
def pytest_runtest_setup(item):
assert item.fspath.purebasename == "test_in_sub1"
def pytest_runtest_call(item):
assert item.fspath.purebasename == "test_in_sub1"
def pytest_runtest_teardown(item):
assert item.fspath.purebasename == "test_in_sub1"
"""
)
)
sub2.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
import pytest
def pytest_runtest_setup(item):
assert item.fspath.purebasename == "test_in_sub2"
def pytest_runtest_call(item):
assert item.fspath.purebasename == "test_in_sub2"
def pytest_runtest_teardown(item):
assert item.fspath.purebasename == "test_in_sub2"
"""
import pytest
def pytest_runtest_setup(item):
assert item.fspath.purebasename == "test_in_sub2"
def pytest_runtest_call(item):
assert item.fspath.purebasename == "test_in_sub2"
def pytest_runtest_teardown(item):
assert item.fspath.purebasename == "test_in_sub2"
"""
)
)
sub1.join("test_in_sub1.py").write("def test_1(): pass")
@@ -1547,12 +1548,12 @@ def test_skip_duplicates_by_default(testdir):
a = testdir.mkdir("a")
fh = a.join("test_a.py")
fh.write(
_pytest._code.Source(
textwrap.dedent(
"""\
import pytest
def test_real():
pass
"""
import pytest
def test_real():
pass
"""
)
)
result = testdir.runpytest(a.strpath, a.strpath)
@@ -1567,13 +1568,94 @@ def test_keep_duplicates(testdir):
a = testdir.mkdir("a")
fh = a.join("test_a.py")
fh.write(
_pytest._code.Source(
textwrap.dedent(
"""\
import pytest
def test_real():
pass
"""
import pytest
def test_real():
pass
"""
)
)
result = testdir.runpytest("--keep-duplicates", a.strpath, a.strpath)
result.stdout.fnmatch_lines(["*collected 2 item*"])
def test_package_collection_infinite_recursion(testdir):
testdir.copy_example("collect/package_infinite_recursion")
result = testdir.runpytest()
result.stdout.fnmatch_lines("*1 passed*")
def test_package_with_modules(testdir):
"""
.
└── root
├── __init__.py
├── sub1
│ ├── __init__.py
│ └── sub1_1
│ ├── __init__.py
│ └── test_in_sub1.py
└── sub2
└── test
└── test_in_sub2.py
"""
root = testdir.mkpydir("root")
sub1 = root.mkdir("sub1")
sub1.ensure("__init__.py")
sub1_test = sub1.mkdir("sub1_1")
sub1_test.ensure("__init__.py")
sub2 = root.mkdir("sub2")
sub2_test = sub2.mkdir("sub2")
sub1_test.join("test_in_sub1.py").write("def test_1(): pass")
sub2_test.join("test_in_sub2.py").write("def test_2(): pass")
# Execute from .
result = testdir.runpytest("-v", "-s")
result.assert_outcomes(passed=2)
# Execute from . with one argument "root"
result = testdir.runpytest("-v", "-s", "root")
result.assert_outcomes(passed=2)
# Chdir into package's root and execute with no args
root.chdir()
result = testdir.runpytest("-v", "-s")
result.assert_outcomes(passed=2)
def test_package_ordering(testdir):
"""
.
└── root
├── Test_root.py
├── __init__.py
├── sub1
│ ├── Test_sub1.py
│ └── __init__.py
└── sub2
└── test
└── test_sub2.py
"""
testdir.makeini(
"""
[pytest]
python_files=*.py
"""
)
root = testdir.mkpydir("root")
sub1 = root.mkdir("sub1")
sub1.ensure("__init__.py")
sub2 = root.mkdir("sub2")
sub2_test = sub2.mkdir("sub2")
root.join("Test_root.py").write("def test_1(): pass")
sub1.join("Test_sub1.py").write("def test_2(): pass")
sub2_test.join("test_sub2.py").write("def test_3(): pass")
# Execute from .
result = testdir.runpytest("-v", "-s")
result.assert_outcomes(passed=3)

View File

@@ -1,6 +1,6 @@
from textwrap import dedent
# -*- coding: utf-8 -*-
import textwrap
import _pytest._code
import pytest
from _pytest.pytester import get_public_names
from _pytest.fixtures import FixtureLookupError, FixtureRequest
@@ -208,23 +208,23 @@ class TestFillFixtures(object):
)
subdir = testdir.mkpydir("subdir")
subdir.join("conftest.py").write(
_pytest._code.Source(
"""
import pytest
textwrap.dedent(
"""\
import pytest
@pytest.fixture
def spam():
return 'spam'
"""
@pytest.fixture
def spam():
return 'spam'
"""
)
)
testfile = subdir.join("test_spam.py")
testfile.write(
_pytest._code.Source(
textwrap.dedent(
"""\
def test_spam(spam):
assert spam == "spam"
"""
def test_spam(spam):
assert spam == "spam"
"""
)
)
result = testdir.runpytest()
@@ -276,26 +276,26 @@ class TestFillFixtures(object):
)
subdir = testdir.mkpydir("subdir")
subdir.join("conftest.py").write(
_pytest._code.Source(
"""
import pytest
textwrap.dedent(
"""\
import pytest
@pytest.fixture(params=[1, 2, 3])
def spam(request):
return request.param
"""
@pytest.fixture(params=[1, 2, 3])
def spam(request):
return request.param
"""
)
)
testfile = subdir.join("test_spam.py")
testfile.write(
_pytest._code.Source(
"""
params = {'spam': 1}
textwrap.dedent(
"""\
params = {'spam': 1}
def test_spam(spam):
assert spam == params['spam']
params['spam'] += 1
"""
def test_spam(spam):
assert spam == params['spam']
params['spam'] += 1
"""
)
)
result = testdir.runpytest()
@@ -320,26 +320,26 @@ class TestFillFixtures(object):
)
subdir = testdir.mkpydir("subdir")
subdir.join("conftest.py").write(
_pytest._code.Source(
"""
import pytest
textwrap.dedent(
"""\
import pytest
@pytest.fixture(params=[1, 2, 3])
def spam(request):
return request.param
"""
@pytest.fixture(params=[1, 2, 3])
def spam(request):
return request.param
"""
)
)
testfile = subdir.join("test_spam.py")
testfile.write(
_pytest._code.Source(
"""
params = {'spam': 1}
textwrap.dedent(
"""\
params = {'spam': 1}
def test_spam(spam):
assert spam == params['spam']
params['spam'] += 1
"""
def test_spam(spam):
assert spam == params['spam']
params['spam'] += 1
"""
)
)
result = testdir.runpytest()
@@ -807,13 +807,13 @@ class TestRequestBasic(object):
# this tests that normalization of nodeids takes place
b = testdir.mkdir("tests").mkdir("unit")
b.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
import pytest
@pytest.fixture
def arg1():
pass
"""
import pytest
@pytest.fixture
def arg1():
pass
"""
)
)
p = b.join("test_module.py")
@@ -1470,10 +1470,9 @@ class TestFixtureManagerParseFactories(object):
print (faclist)
assert len(faclist) == 3
kwargs = {'__being_called_by_pytest': True}
assert faclist[0].func(item._request, **kwargs) == "conftest"
assert faclist[1].func(item._request, **kwargs) == "module"
assert faclist[2].func(item._request, **kwargs) == "class"
assert faclist[0].func(item._request) == "conftest"
assert faclist[1].func(item._request) == "module"
assert faclist[2].func(item._request) == "class"
"""
)
reprec = testdir.inline_run("-s")
@@ -1485,41 +1484,41 @@ class TestFixtureManagerParseFactories(object):
runner = testdir.mkdir("runner")
package = testdir.mkdir("package")
package.join("conftest.py").write(
dedent(
textwrap.dedent(
"""\
import pytest
@pytest.fixture
def one():
return 1
"""
"""
)
)
package.join("test_x.py").write(
dedent(
textwrap.dedent(
"""\
def test_x(one):
assert one == 1
"""
def test_x(one):
assert one == 1
"""
)
)
sub = package.mkdir("sub")
sub.join("__init__.py").ensure()
sub.join("conftest.py").write(
dedent(
textwrap.dedent(
"""\
import pytest
@pytest.fixture
def one():
return 2
"""
import pytest
@pytest.fixture
def one():
return 2
"""
)
)
sub.join("test_y.py").write(
dedent(
textwrap.dedent(
"""\
def test_x(one):
assert one == 2
"""
def test_x(one):
assert one == 2
"""
)
)
reprec = testdir.inline_run()
@@ -1536,44 +1535,44 @@ class TestFixtureManagerParseFactories(object):
)
package = testdir.mkdir("package")
package.join("__init__.py").write(
dedent(
textwrap.dedent(
"""\
from .. import values
def setup_module():
values.append("package")
def teardown_module():
values[:] = []
"""
from .. import values
def setup_module():
values.append("package")
def teardown_module():
values[:] = []
"""
)
)
package.join("test_x.py").write(
dedent(
textwrap.dedent(
"""\
from .. import values
def test_x():
assert values == ["package"]
"""
from .. import values
def test_x():
assert values == ["package"]
"""
)
)
package = testdir.mkdir("package2")
package.join("__init__.py").write(
dedent(
textwrap.dedent(
"""\
from .. import values
def setup_module():
values.append("package2")
def teardown_module():
values[:] = []
"""
from .. import values
def setup_module():
values.append("package2")
def teardown_module():
values[:] = []
"""
)
)
package.join("test_x.py").write(
dedent(
textwrap.dedent(
"""\
from .. import values
def test_x():
assert values == ["package2"]
"""
from .. import values
def test_x():
assert values == ["package2"]
"""
)
)
reprec = testdir.inline_run()
@@ -1588,37 +1587,42 @@ class TestFixtureManagerParseFactories(object):
package = testdir.mkdir("package")
package.join("__init__.py").write("")
package.join("conftest.py").write(
dedent(
textwrap.dedent(
"""\
import pytest
from .. import values
@pytest.fixture(scope="package")
def one():
values.append("package")
yield values
values.pop()
@pytest.fixture(scope="package", autouse=True)
def two():
values.append("package-auto")
yield values
values.pop()
"""
import pytest
from .. import values
@pytest.fixture(scope="package")
def one():
values.append("package")
yield values
values.pop()
@pytest.fixture(scope="package", autouse=True)
def two():
values.append("package-auto")
yield values
values.pop()
"""
)
)
package.join("test_x.py").write(
dedent(
textwrap.dedent(
"""\
from .. import values
def test_package_autouse():
assert values == ["package-auto"]
def test_package(one):
assert values == ["package-auto", "package"]
"""
from .. import values
def test_package_autouse():
assert values == ["package-auto"]
def test_package(one):
assert values == ["package-auto", "package"]
"""
)
)
reprec = testdir.inline_run()
reprec.assertoutcome(passed=2)
def test_collect_custom_items(self, testdir):
testdir.copy_example("fixtures/custom_item")
result = testdir.runpytest("foo")
result.stdout.fnmatch_lines("*passed*")
class TestAutouseDiscovery(object):
@pytest.fixture
@@ -1800,24 +1804,24 @@ class TestAutouseManagement(object):
def test_autouse_conftest_mid_directory(self, testdir):
pkgdir = testdir.mkpydir("xyz123")
pkgdir.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
import pytest
@pytest.fixture(autouse=True)
def app():
import sys
sys._myapp = "hello"
"""
import pytest
@pytest.fixture(autouse=True)
def app():
import sys
sys._myapp = "hello"
"""
)
)
t = pkgdir.ensure("tests", "test_app.py")
t.write(
_pytest._code.Source(
textwrap.dedent(
"""\
import sys
def test_app():
assert sys._myapp == "hello"
"""
import sys
def test_app():
assert sys._myapp == "hello"
"""
)
)
reprec = testdir.inline_run("-s")
@@ -2711,17 +2715,17 @@ class TestFixtureMarker(object):
)
b = testdir.mkdir("subdir")
b.join("test_overridden_fixture_finalizer.py").write(
dedent(
"""
import pytest
@pytest.fixture
def browser(browser):
browser['visited'] = True
return browser
textwrap.dedent(
"""\
import pytest
@pytest.fixture
def browser(browser):
browser['visited'] = True
return browser
def test_browser(browser):
assert browser['visited'] is True
"""
def test_browser(browser):
assert browser['visited'] is True
"""
)
)
reprec = testdir.runpytest("-s")
@@ -3213,120 +3217,119 @@ class TestShowFixtures(object):
def test_show_fixtures_trimmed_doc(self, testdir):
p = testdir.makepyfile(
dedent(
textwrap.dedent(
'''\
import pytest
@pytest.fixture
def arg1():
"""
line1
line2
"""
@pytest.fixture
def arg2():
"""
line1
line2
"""
'''
import pytest
@pytest.fixture
def arg1():
"""
line1
line2
"""
@pytest.fixture
def arg2():
"""
line1
line2
"""
'''
)
)
result = testdir.runpytest("--fixtures", p)
result.stdout.fnmatch_lines(
dedent(
textwrap.dedent(
"""\
* fixtures defined from test_show_fixtures_trimmed_doc *
arg2
line1
line2
arg1
line1
line2
"""
* fixtures defined from test_show_fixtures_trimmed_doc *
arg2
line1
line2
arg1
line1
line2
"""
)
)
def test_show_fixtures_indented_doc(self, testdir):
p = testdir.makepyfile(
dedent(
textwrap.dedent(
'''\
import pytest
@pytest.fixture
def fixture1():
"""
line1
indented line
"""
'''
import pytest
@pytest.fixture
def fixture1():
"""
line1
indented line
"""
'''
)
)
result = testdir.runpytest("--fixtures", p)
result.stdout.fnmatch_lines(
dedent(
textwrap.dedent(
"""\
* fixtures defined from test_show_fixtures_indented_doc *
fixture1
line1
indented line
"""
* fixtures defined from test_show_fixtures_indented_doc *
fixture1
line1
indented line
"""
)
)
def test_show_fixtures_indented_doc_first_line_unindented(self, testdir):
p = testdir.makepyfile(
dedent(
textwrap.dedent(
'''\
import pytest
@pytest.fixture
def fixture1():
"""line1
line2
indented line
"""
'''
import pytest
@pytest.fixture
def fixture1():
"""line1
line2
indented line
"""
'''
)
)
result = testdir.runpytest("--fixtures", p)
result.stdout.fnmatch_lines(
dedent(
textwrap.dedent(
"""\
* fixtures defined from test_show_fixtures_indented_doc_first_line_unindented *
fixture1
line1
line2
indented line
"""
* fixtures defined from test_show_fixtures_indented_doc_first_line_unindented *
fixture1
line1
line2
indented line
"""
)
)
def test_show_fixtures_indented_in_class(self, testdir):
p = testdir.makepyfile(
dedent(
textwrap.dedent(
'''\
import pytest
class TestClass(object):
@pytest.fixture
def fixture1(self):
"""line1
line2
indented line
"""
'''
import pytest
class TestClass(object):
@pytest.fixture
def fixture1(self):
"""line1
line2
indented line
"""
'''
)
)
result = testdir.runpytest("--fixtures", p)
result.stdout.fnmatch_lines(
dedent(
textwrap.dedent(
"""\
* fixtures defined from test_show_fixtures_indented_in_class *
fixture1
line1
line2
indented line
"""
* fixtures defined from test_show_fixtures_indented_in_class *
fixture1
line1
line2
indented line
"""
)
)
@@ -3663,26 +3666,26 @@ class TestParameterizedSubRequest(object):
fixdir = testdir.mkdir("fixtures")
fixfile = fixdir.join("fix.py")
fixfile.write(
_pytest._code.Source(
"""
import pytest
textwrap.dedent(
"""\
import pytest
@pytest.fixture(params=[0, 1, 2])
def fix_with_param(request):
return request.param
"""
@pytest.fixture(params=[0, 1, 2])
def fix_with_param(request):
return request.param
"""
)
)
testfile = tests_dir.join("test_foos.py")
testfile.write(
_pytest._code.Source(
"""
from fix import fix_with_param
textwrap.dedent(
"""\
from fix import fix_with_param
def test_foo(request):
request.getfixturevalue('fix_with_param')
"""
def test_foo(request):
request.getfixturevalue('fix_with_param')
"""
)
)
@@ -3694,9 +3697,9 @@ class TestParameterizedSubRequest(object):
E*Failed: The requested fixture has no parameter defined for the current test.
E*
E*Requested fixture 'fix_with_param' defined in:
E*fix.py:5
E*fix.py:4
E*Requested here:
E*test_foos.py:5
E*test_foos.py:4
*1 failed*
"""
)
@@ -3975,3 +3978,71 @@ class TestScopeOrdering(object):
items, _ = testdir.inline_genitems()
request = FixtureRequest(items[0])
assert request.fixturenames == "s1 p1 m1 m2 c1 f2 f1".split()
def test_multiple_packages(self, testdir):
"""Complex test involving multiple package fixtures. Make sure teardowns
are executed in order.
.
└── root
├── __init__.py
├── sub1
│ ├── __init__.py
│ ├── conftest.py
│ └── test_1.py
└── sub2
├── __init__.py
├── conftest.py
└── test_2.py
"""
root = testdir.mkdir("root")
root.join("__init__.py").write("values = []")
sub1 = root.mkdir("sub1")
sub1.ensure("__init__.py")
sub1.join("conftest.py").write(
textwrap.dedent(
"""\
import pytest
from .. import values
@pytest.fixture(scope="package")
def fix():
values.append("pre-sub1")
yield values
assert values.pop() == "pre-sub1"
"""
)
)
sub1.join("test_1.py").write(
textwrap.dedent(
"""\
from .. import values
def test_1(fix):
assert values == ["pre-sub1"]
"""
)
)
sub2 = root.mkdir("sub2")
sub2.ensure("__init__.py")
sub2.join("conftest.py").write(
textwrap.dedent(
"""\
import pytest
from .. import values
@pytest.fixture(scope="package")
def fix():
values.append("pre-sub2")
yield values
assert values.pop() == "pre-sub2"
"""
)
)
sub2.join("test_2.py").write(
textwrap.dedent(
"""\
from .. import values
def test_2(fix):
assert values == ["pre-sub2"]
"""
)
)
reprec = testdir.inline_run()
reprec.assertoutcome(passed=2)

View File

@@ -2,8 +2,7 @@
import re
import sys
import attr
import _pytest._code
import py
import textwrap
import pytest
from _pytest import python, fixtures
@@ -295,9 +294,7 @@ class TestMetafunc(object):
)
assert result == ["a0-1.0", "a1-b1"]
# unicode mixing, issue250
result = idmaker(
(py.builtin._totext("a"), "b"), [pytest.param({}, b"\xc3\xb4")]
)
result = idmaker((u"a", "b"), [pytest.param({}, b"\xc3\xb4")])
assert result == ["a0-\\xc3\\xb4"]
def test_idmaker_with_bytes_regex(self):
@@ -309,7 +306,6 @@ class TestMetafunc(object):
def test_idmaker_native_strings(self):
from _pytest.python import idmaker
totext = py.builtin._totext
result = idmaker(
("a", "b"),
[
@@ -324,7 +320,7 @@ class TestMetafunc(object):
pytest.param({7}, set("seven")),
pytest.param(tuple("eight"), (8, -8, 8)),
pytest.param(b"\xc3\xb4", b"name"),
pytest.param(b"\xc3\xb4", totext("other")),
pytest.param(b"\xc3\xb4", u"other"),
],
)
assert result == [
@@ -1275,19 +1271,19 @@ class TestMetafuncFunctional(object):
sub1 = testdir.mkpydir("sub1")
sub2 = testdir.mkpydir("sub2")
sub1.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
def pytest_generate_tests(metafunc):
assert metafunc.function.__name__ == "test_1"
"""
def pytest_generate_tests(metafunc):
assert metafunc.function.__name__ == "test_1"
"""
)
)
sub2.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
def pytest_generate_tests(metafunc):
assert metafunc.function.__name__ == "test_2"
"""
def pytest_generate_tests(metafunc):
assert metafunc.function.__name__ == "test_2"
"""
)
)
sub1.join("test_in_sub1.py").write("def test_1(): pass")

View File

@@ -11,7 +11,7 @@ def equal_with_bash(prefix, ffc, fc, out=None):
res_bash = set(fc(prefix))
retval = set(res) == res_bash
if out:
out.write("equal_with_bash %s %s\n" % (retval, res))
out.write("equal_with_bash {} {}\n".format(retval, res))
if not retval:
out.write(" python - bash: %s\n" % (set(res) - res_bash))
out.write(" bash - python: %s\n" % (res_bash - set(res)))

View File

@@ -6,6 +6,7 @@ import textwrap
import _pytest.assertion as plugin
import py
import pytest
import six
from _pytest.assertion import util
from _pytest.assertion import truncate
@@ -509,12 +510,12 @@ class TestAssert_reprcompare(object):
assert "raised in repr()" not in expl
def test_unicode(self):
left = py.builtin._totext("£€", "utf-8")
right = py.builtin._totext("£", "utf-8")
left = u"£€"
right = u"£"
expl = callequal(left, right)
assert expl[0] == py.builtin._totext("'£€' == '£'", "utf-8")
assert expl[1] == py.builtin._totext("- £€", "utf-8")
assert expl[2] == py.builtin._totext("+ £", "utf-8")
assert expl[0] == u"'£€' == '£'"
assert expl[1] == u"- £€"
assert expl[2] == u"+ £"
def test_nonascii_text(self):
"""
@@ -541,8 +542,8 @@ class TestAssert_reprcompare(object):
right = bytes(right, "utf-8")
expl = callequal(left, right)
for line in expl:
assert isinstance(line, py.builtin.text)
msg = py.builtin._totext("\n").join(expl)
assert isinstance(line, six.text_type)
msg = u"\n".join(expl)
assert msg

View File

@@ -9,6 +9,7 @@ import textwrap
import zipfile
import py
import pytest
import six
import _pytest._code
from _pytest.assertion import util
@@ -49,7 +50,7 @@ def getmsg(f, extra_ns=None, must_pass=False):
ns = {}
if extra_ns is not None:
ns.update(extra_ns)
py.builtin.exec_(code, ns)
six.exec_(code, ns)
func = ns[f.__name__]
try:
func()
@@ -246,6 +247,15 @@ class TestAssertionRewrite(object):
["*AssertionError: To be escaped: %", "*assert 1 == 2"]
)
@pytest.mark.skipif(
sys.version_info < (3,), reason="bytes is a string type in python 2"
)
def test_assertion_messages_bytes(self, testdir):
testdir.makepyfile("def test_bytes_assertion():\n assert False, b'ohai!'\n")
result = testdir.runpytest()
assert result.ret == 1
result.stdout.fnmatch_lines(["*AssertionError: b'ohai!'", "*assert False"])
def test_boolop(self):
def f():
f = g = False
@@ -551,7 +561,7 @@ class TestAssertionRewrite(object):
assert getmsg(f) == "assert 42"
def my_reprcompare(op, left, right):
return "%s %s %s" % (left, op, right)
return "{} {} {}".format(left, op, right)
monkeypatch.setattr(util, "_reprcompare", my_reprcompare)
@@ -645,12 +655,10 @@ class TestRewriteOnImport(object):
def test_readonly(self, testdir):
sub = testdir.mkdir("testing")
sub.join("test_readonly.py").write(
py.builtin._totext(
"""
b"""
def test_rewritten():
assert "@py_builtins" in globals()
"""
).encode("utf-8"),
""",
"wb",
)
old_mode = sub.stat().mode
@@ -1031,14 +1039,14 @@ class TestAssertionRewriteHookDetails(object):
"""
path = testdir.mkpydir("foo")
path.join("test_foo.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
class Test(object):
def test_foo(self):
import pkgutil
data = pkgutil.get_data('foo.test_foo', 'data.txt')
assert data == b'Hey'
"""
class Test(object):
def test_foo(self):
import pkgutil
data = pkgutil.get_data('foo.test_foo', 'data.txt')
assert data == b'Hey'
"""
)
)
path.join("data.txt").write("Hey")

View File

@@ -1,9 +1,9 @@
from __future__ import absolute_import, division, print_function
import sys
import textwrap
import py
import _pytest
import pytest
import os
import shutil
@@ -150,6 +150,32 @@ def test_cache_reportheader(testdir):
result.stdout.fnmatch_lines(["cachedir: .pytest_cache"])
def test_cache_reportheader_external_abspath(testdir, tmpdir_factory):
external_cache = tmpdir_factory.mktemp(
"test_cache_reportheader_external_abspath_abs"
)
testdir.makepyfile(
"""
def test_hello():
pass
"""
)
testdir.makeini(
"""
[pytest]
cache_dir = {abscache}
""".format(
abscache=external_cache
)
)
result = testdir.runpytest("-v")
result.stdout.fnmatch_lines(
["cachedir: {abscache}".format(abscache=external_cache)]
)
def test_cache_show(testdir):
result = testdir.runpytest("--cache-show")
assert result.ret == 0
@@ -198,17 +224,17 @@ class TestLastFailed(object):
result = testdir.runpytest()
result.stdout.fnmatch_lines(["*2 failed*"])
p.write(
_pytest._code.Source(
textwrap.dedent(
"""\
def test_1():
assert 1
def test_2():
assert 1
def test_3():
assert 0
"""
def test_1():
assert 1
def test_2():
assert 1
def test_3():
assert 0
"""
)
)
result = testdir.runpytest("--lf")
@@ -226,19 +252,19 @@ class TestLastFailed(object):
def test_failedfirst_order(self, testdir):
testdir.tmpdir.join("test_a.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
def test_always_passes():
assert 1
"""
def test_always_passes():
assert 1
"""
)
)
testdir.tmpdir.join("test_b.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
def test_always_fails():
assert 0
"""
def test_always_fails():
assert 0
"""
)
)
result = testdir.runpytest()
@@ -251,14 +277,14 @@ class TestLastFailed(object):
def test_lastfailed_failedfirst_order(self, testdir):
testdir.makepyfile(
**{
"test_a.py": """
"test_a.py": """\
def test_always_passes():
assert 1
""",
"test_b.py": """
""",
"test_b.py": """\
def test_always_fails():
assert 0
""",
""",
}
)
result = testdir.runpytest()
@@ -272,16 +298,16 @@ class TestLastFailed(object):
def test_lastfailed_difference_invocations(self, testdir, monkeypatch):
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
testdir.makepyfile(
test_a="""
test_a="""\
def test_a1():
assert 0
def test_a2():
assert 1
""",
test_b="""
""",
test_b="""\
def test_b1():
assert 0
""",
""",
)
p = testdir.tmpdir.join("test_a.py")
p2 = testdir.tmpdir.join("test_b.py")
@@ -291,11 +317,11 @@ class TestLastFailed(object):
result = testdir.runpytest("--lf", p2)
result.stdout.fnmatch_lines(["*1 failed*"])
p2.write(
_pytest._code.Source(
textwrap.dedent(
"""\
def test_b1():
assert 1
"""
def test_b1():
assert 1
"""
)
)
result = testdir.runpytest("--lf", p2)
@@ -306,18 +332,18 @@ class TestLastFailed(object):
def test_lastfailed_usecase_splice(self, testdir, monkeypatch):
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
testdir.makepyfile(
"""
"""\
def test_1():
assert 0
"""
"""
)
p2 = testdir.tmpdir.join("test_something.py")
p2.write(
_pytest._code.Source(
textwrap.dedent(
"""\
def test_2():
assert 0
"""
def test_2():
assert 0
"""
)
)
result = testdir.runpytest()

View File

@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
# note: py.io capture tests where copied from
@@ -5,13 +6,13 @@ from __future__ import absolute_import, division, print_function
import pickle
import os
import sys
import textwrap
from io import UnsupportedOperation
import _pytest._code
import py
import pytest
import contextlib
from six import binary_type, text_type
from six import text_type
from _pytest import capture
from _pytest.capture import CaptureManager
from _pytest.main import EXIT_NOTESTSCOLLECTED
@@ -23,12 +24,12 @@ needsosdup = pytest.mark.xfail("not hasattr(os, 'dup')")
def tobytes(obj):
if isinstance(obj, text_type):
obj = obj.encode("UTF-8")
assert isinstance(obj, binary_type)
assert isinstance(obj, bytes)
return obj
def totext(obj):
if isinstance(obj, binary_type):
if isinstance(obj, bytes):
obj = text_type(obj, "UTF-8")
assert isinstance(obj, text_type)
return obj
@@ -70,19 +71,23 @@ class TestCaptureManager(object):
try:
capman = CaptureManager(method)
capman.start_global_capturing()
outerr = capman.suspend_global_capture()
capman.suspend_global_capture()
outerr = capman.read_global_capture()
assert outerr == ("", "")
outerr = capman.suspend_global_capture()
capman.suspend_global_capture()
outerr = capman.read_global_capture()
assert outerr == ("", "")
print("hello")
out, err = capman.suspend_global_capture()
capman.suspend_global_capture()
out, err = capman.read_global_capture()
if method == "no":
assert old == (sys.stdout, sys.stderr, sys.stdin)
else:
assert not out
capman.resume_global_capture()
print("hello")
out, err = capman.suspend_global_capture()
capman.suspend_global_capture()
out, err = capman.read_global_capture()
if method != "no":
assert out == "hello\n"
capman.stop_global_capturing()
@@ -264,7 +269,7 @@ class TestPerTestCapturing(object):
def test_capturing_outerr(self, testdir):
p1 = testdir.makepyfile(
"""
"""\
import sys
def test_capturing():
print (42)
@@ -273,7 +278,7 @@ class TestPerTestCapturing(object):
print (1)
sys.stderr.write(str(2))
raise ValueError
"""
"""
)
result = testdir.runpytest(p1)
result.stdout.fnmatch_lines(
@@ -293,21 +298,21 @@ class TestPerTestCapturing(object):
class TestLoggingInteraction(object):
def test_logging_stream_ownership(self, testdir):
p = testdir.makepyfile(
"""
"""\
def test_logging():
import logging
import pytest
stream = capture.CaptureIO()
logging.basicConfig(stream=stream)
stream.close() # to free memory/release resources
"""
"""
)
result = testdir.runpytest_subprocess(p)
assert result.stderr.str().find("atexit") == -1
def test_logging_and_immediate_setupteardown(self, testdir):
p = testdir.makepyfile(
"""
"""\
import logging
def setup_function(function):
logging.warn("hello1")
@@ -319,7 +324,7 @@ class TestLoggingInteraction(object):
def teardown_function(function):
logging.warn("hello3")
assert 0
"""
"""
)
for optargs in (("--capture=sys",), ("--capture=fd",)):
print(optargs)
@@ -333,7 +338,7 @@ class TestLoggingInteraction(object):
def test_logging_and_crossscope_fixtures(self, testdir):
p = testdir.makepyfile(
"""
"""\
import logging
def setup_module(function):
logging.warn("hello1")
@@ -345,7 +350,7 @@ class TestLoggingInteraction(object):
def teardown_module(function):
logging.warn("hello3")
assert 0
"""
"""
)
for optargs in (("--capture=sys",), ("--capture=fd",)):
print(optargs)
@@ -359,11 +364,11 @@ class TestLoggingInteraction(object):
def test_conftestlogging_is_shown(self, testdir):
testdir.makeconftest(
"""
"""\
import logging
logging.basicConfig()
logging.warn("hello435")
"""
"""
)
# make sure that logging is still captured in tests
result = testdir.runpytest_subprocess("-s", "-p", "no:capturelog")
@@ -373,19 +378,19 @@ class TestLoggingInteraction(object):
def test_conftestlogging_and_test_logging(self, testdir):
testdir.makeconftest(
"""
"""\
import logging
logging.basicConfig()
"""
"""
)
# make sure that logging is still captured in tests
p = testdir.makepyfile(
"""
"""\
def test_hello():
import logging
logging.warn("hello433")
assert 0
"""
"""
)
result = testdir.runpytest_subprocess(p, "-p", "no:capturelog")
assert result.ret != 0
@@ -398,24 +403,24 @@ class TestCaptureFixture(object):
@pytest.mark.parametrize("opt", [[], ["-s"]])
def test_std_functional(self, testdir, opt):
reprec = testdir.inline_runsource(
"""
"""\
def test_hello(capsys):
print (42)
out, err = capsys.readouterr()
assert out.startswith("42")
""",
""",
*opt
)
reprec.assertoutcome(passed=1)
def test_capsyscapfd(self, testdir):
p = testdir.makepyfile(
"""
"""\
def test_one(capsys, capfd):
pass
def test_two(capfd, capsys):
pass
"""
"""
)
result = testdir.runpytest(p)
result.stdout.fnmatch_lines(
@@ -433,12 +438,12 @@ class TestCaptureFixture(object):
in the same test is an error.
"""
testdir.makepyfile(
"""
"""\
def test_one(capsys, request):
request.getfixturevalue("capfd")
def test_two(capfd, request):
request.getfixturevalue("capsys")
"""
"""
)
result = testdir.runpytest()
result.stdout.fnmatch_lines(
@@ -453,10 +458,10 @@ class TestCaptureFixture(object):
def test_capsyscapfdbinary(self, testdir):
p = testdir.makepyfile(
"""
"""\
def test_one(capsys, capfdbinary):
pass
"""
"""
)
result = testdir.runpytest(p)
result.stdout.fnmatch_lines(
@@ -466,12 +471,13 @@ class TestCaptureFixture(object):
@pytest.mark.parametrize("method", ["sys", "fd"])
def test_capture_is_represented_on_failure_issue128(self, testdir, method):
p = testdir.makepyfile(
"""
def test_hello(cap%s):
"""\
def test_hello(cap{}):
print ("xxx42xxx")
assert 0
"""
% method
""".format(
method
)
)
result = testdir.runpytest(p)
result.stdout.fnmatch_lines(["xxx42xxx"])
@@ -479,21 +485,21 @@ class TestCaptureFixture(object):
@needsosdup
def test_stdfd_functional(self, testdir):
reprec = testdir.inline_runsource(
"""
"""\
def test_hello(capfd):
import os
os.write(1, "42".encode('ascii'))
out, err = capfd.readouterr()
assert out.startswith("42")
capfd.close()
"""
"""
)
reprec.assertoutcome(passed=1)
@needsosdup
def test_capfdbinary(self, testdir):
reprec = testdir.inline_runsource(
"""
"""\
def test_hello(capfdbinary):
import os
# some likely un-decodable bytes
@@ -501,7 +507,7 @@ class TestCaptureFixture(object):
out, err = capfdbinary.readouterr()
assert out == b'\\xfe\\x98\\x20'
assert err == b''
"""
"""
)
reprec.assertoutcome(passed=1)
@@ -510,7 +516,7 @@ class TestCaptureFixture(object):
)
def test_capsysbinary(self, testdir):
reprec = testdir.inline_runsource(
"""
"""\
def test_hello(capsysbinary):
import sys
# some likely un-decodable bytes
@@ -518,7 +524,7 @@ class TestCaptureFixture(object):
out, err = capsysbinary.readouterr()
assert out == b'\\xfe\\x98\\x20'
assert err == b''
"""
"""
)
reprec.assertoutcome(passed=1)
@@ -527,10 +533,10 @@ class TestCaptureFixture(object):
)
def test_capsysbinary_forbidden_in_python2(self, testdir):
testdir.makepyfile(
"""
"""\
def test_hello(capsysbinary):
pass
"""
"""
)
result = testdir.runpytest()
result.stdout.fnmatch_lines(
@@ -543,10 +549,10 @@ class TestCaptureFixture(object):
def test_partial_setup_failure(self, testdir):
p = testdir.makepyfile(
"""
"""\
def test_hello(capsys, missingarg):
pass
"""
"""
)
result = testdir.runpytest(p)
result.stdout.fnmatch_lines(["*test_partial_setup_failure*", "*1 error*"])
@@ -554,12 +560,12 @@ class TestCaptureFixture(object):
@needsosdup
def test_keyboardinterrupt_disables_capturing(self, testdir):
p = testdir.makepyfile(
"""
"""\
def test_hello(capfd):
import os
os.write(1, str(42).encode('ascii'))
raise KeyboardInterrupt()
"""
"""
)
result = testdir.runpytest_subprocess(p)
result.stdout.fnmatch_lines(["*KeyboardInterrupt*"])
@@ -568,7 +574,7 @@ class TestCaptureFixture(object):
@pytest.mark.issue14
def test_capture_and_logging(self, testdir):
p = testdir.makepyfile(
"""
"""\
import logging
def test_log(capsys):
logging.error('x')
@@ -581,7 +587,7 @@ class TestCaptureFixture(object):
@pytest.mark.parametrize("no_capture", [True, False])
def test_disabled_capture_fixture(self, testdir, fixture, no_capture):
testdir.makepyfile(
"""
"""\
def test_disabled({fixture}):
print('captured before')
with {fixture}.disabled():
@@ -615,7 +621,7 @@ class TestCaptureFixture(object):
Ensure that capsys and capfd can be used by other fixtures during setup and teardown.
"""
testdir.makepyfile(
"""
"""\
from __future__ import print_function
import sys
import pytest
@@ -647,15 +653,43 @@ class TestCaptureFixture(object):
assert "stdout contents begin" not in result.stdout.str()
assert "stderr contents begin" not in result.stdout.str()
@pytest.mark.parametrize("cap", ["capsys", "capfd"])
def test_fixture_use_by_other_fixtures_teardown(self, testdir, cap):
"""Ensure we can access setup and teardown buffers from teardown when using capsys/capfd (##3033)"""
testdir.makepyfile(
"""\
import sys
import pytest
import os
@pytest.fixture()
def fix({cap}):
print("setup out")
sys.stderr.write("setup err\\n")
yield
out, err = {cap}.readouterr()
assert out == 'setup out\\ncall out\\n'
assert err == 'setup err\\ncall err\\n'
def test_a(fix):
print("call out")
sys.stderr.write("call err\\n")
""".format(
cap=cap
)
)
reprec = testdir.inline_run()
reprec.assertoutcome(passed=1)
def test_setup_failure_does_not_kill_capturing(testdir):
sub1 = testdir.mkpydir("sub1")
sub1.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
def pytest_runtest_setup(item):
raise ValueError(42)
"""
def pytest_runtest_setup(item):
raise ValueError(42)
"""
)
)
sub1.join("test_mod.py").write("def test_func1(): pass")
@@ -1051,9 +1085,9 @@ class TestStdCapture(object):
def test_capturing_readouterr_unicode(self):
with self.getcapture() as cap:
print("hx\xc4\x85\xc4\x87")
print("hxąć")
out, err = cap.readouterr()
assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8")
assert out == u"hxąć\n"
@pytest.mark.skipif(
"sys.version_info >= (3,)", reason="text output different for bytes on python3"
@@ -1063,7 +1097,7 @@ class TestStdCapture(object):
# triggered an internal error in pytest
print("\xa6")
out, err = cap.readouterr()
assert out == py.builtin._totext("\ufffd\n", "unicode-escape")
assert out == u"\ufffd\n"
def test_reset_twice_error(self):
with self.getcapture() as cap:
@@ -1385,3 +1419,95 @@ def test_pickling_and_unpickling_encoded_file():
ef = capture.EncodedFile(None, None)
ef_as_str = pickle.dumps(ef)
pickle.loads(ef_as_str)
def test_global_capture_with_live_logging(testdir):
# Issue 3819
# capture should work with live cli logging
# Teardown report seems to have the capture for the whole process (setup, capture, teardown)
testdir.makeconftest(
"""
def pytest_runtest_logreport(report):
if "test_global" in report.nodeid:
if report.when == "teardown":
with open("caplog", "w") as f:
f.write(report.caplog)
with open("capstdout", "w") as f:
f.write(report.capstdout)
"""
)
testdir.makepyfile(
"""
import logging
import sys
import pytest
logger = logging.getLogger(__name__)
@pytest.fixture
def fix1():
print("fix setup")
logging.info("fix setup")
yield
logging.info("fix teardown")
print("fix teardown")
def test_global(fix1):
print("begin test")
logging.info("something in test")
print("end test")
"""
)
result = testdir.runpytest_subprocess("--log-cli-level=INFO")
assert result.ret == 0
with open("caplog", "r") as f:
caplog = f.read()
assert "fix setup" in caplog
assert "something in test" in caplog
assert "fix teardown" in caplog
with open("capstdout", "r") as f:
capstdout = f.read()
assert "fix setup" in capstdout
assert "begin test" in capstdout
assert "end test" in capstdout
assert "fix teardown" in capstdout
@pytest.mark.parametrize("capture_fixture", ["capsys", "capfd"])
def test_capture_with_live_logging(testdir, capture_fixture):
# Issue 3819
# capture should work with live cli logging
testdir.makepyfile(
"""
import logging
import sys
logger = logging.getLogger(__name__)
def test_capture({0}):
print("hello")
sys.stderr.write("world\\n")
captured = {0}.readouterr()
assert captured.out == "hello\\n"
assert captured.err == "world\\n"
logging.info("something")
print("next")
logging.info("something")
captured = {0}.readouterr()
assert captured.out == "next\\n"
""".format(
capture_fixture
)
)
result = testdir.runpytest_subprocess("--log-cli-level=INFO")
assert result.ret == 0

View File

@@ -1,9 +1,9 @@
from __future__ import absolute_import, division, print_function
import pprint
import sys
import textwrap
import pytest
import _pytest._code
from _pytest.main import Session, EXIT_NOTESTSCOLLECTED, _in_venv
@@ -638,6 +638,10 @@ class Test_getinitialnodes(object):
assert col.config is config
def test_pkgfile(self, testdir):
"""Verify nesting when a module is within a package.
The parent chain should match: Module<x.py> -> Package<subdir> -> Session.
Session's parent should always be None.
"""
tmpdir = testdir.tmpdir
subdir = tmpdir.join("subdir")
x = subdir.ensure("x.py")
@@ -645,8 +649,11 @@ class Test_getinitialnodes(object):
with subdir.as_cwd():
config = testdir.parseconfigure(x)
col = testdir.getnode(config, x)
assert isinstance(col, pytest.Module)
assert col.name == "x.py"
assert isinstance(col, pytest.Module)
assert isinstance(col.parent, pytest.Package)
assert isinstance(col.parent.parent, pytest.Session)
# session is batman (has no parents)
assert col.parent.parent.parent is None
for col in col.listchain():
assert col.config is config
@@ -906,13 +913,13 @@ def test_fixture_scope_sibling_conftests(testdir):
"""Regression test case for https://github.com/pytest-dev/pytest/issues/2836"""
foo_path = testdir.mkdir("foo")
foo_path.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
import pytest
@pytest.fixture
def fix():
return 1
"""
import pytest
@pytest.fixture
def fix():
return 1
"""
)
)
foo_path.join("test_foo.py").write("def test_foo(fix): assert fix == 1")
@@ -931,3 +938,17 @@ def test_fixture_scope_sibling_conftests(testdir):
"*1 passed, 1 error*",
]
)
def test_collect_init_tests(testdir):
"""Check that we collect files from __init__.py files when they patch the 'python_files' (#3773)"""
p = testdir.copy_example("collect/collect_init_tests")
result = testdir.runpytest(p, "--collect-only")
result.stdout.fnmatch_lines(
[
"*<Module '__init__.py'>",
"*<Function 'test_init'>",
"*<Module 'test_foo.py'>",
"*<Function 'test_foo'>",
]
)

View File

@@ -1,8 +1,11 @@
from __future__ import absolute_import, division, print_function
import sys
from functools import wraps
import six
import pytest
from _pytest.compat import is_generator, get_real_func, safe_getattr
from _pytest.compat import is_generator, get_real_func, safe_getattr, _PytestWrapper
from _pytest.outcomes import OutcomeException
@@ -38,6 +41,33 @@ def test_real_func_loop_limit():
print(res)
def test_get_real_func():
"""Check that get_real_func correctly unwraps decorators until reaching the real function"""
def decorator(f):
@wraps(f)
def inner():
pass
if six.PY2:
inner.__wrapped__ = f
return inner
def func():
pass
wrapped_func = decorator(decorator(func))
assert get_real_func(wrapped_func) is func
wrapped_func2 = decorator(decorator(wrapped_func))
assert get_real_func(wrapped_func2) is func
# special case for __pytest_wrapped__ attribute: used to obtain the function up until the point
# a function was wrapped by pytest itself
wrapped_func2.__pytest_wrapped__ = _PytestWrapper(wrapped_func)
assert get_real_func(wrapped_func2) is wrapped_func
@pytest.mark.skipif(
sys.version_info < (3, 4), reason="asyncio available in Python 3.4+"
)

View File

@@ -17,11 +17,11 @@ class TestParseIni(object):
sub = tmpdir.mkdir("sub")
sub.chdir()
tmpdir.join(filename).write(
_pytest._code.Source(
"""
[{section}]
name = value
""".format(
textwrap.dedent(
"""\
[{section}]
name = value
""".format(
section=section
)
)
@@ -38,11 +38,11 @@ class TestParseIni(object):
def test_append_parse_args(self, testdir, tmpdir, monkeypatch):
monkeypatch.setenv("PYTEST_ADDOPTS", '--color no -rs --tb="short"')
tmpdir.join("pytest.ini").write(
_pytest._code.Source(
textwrap.dedent(
"""\
[pytest]
addopts = --verbose
"""
[pytest]
addopts = --verbose
"""
)
)
config = testdir.parseconfig(tmpdir)
@@ -438,11 +438,11 @@ class TestConfigFromdictargs(object):
def test_inifilename(self, tmpdir):
tmpdir.join("foo/bar.ini").ensure().write(
_pytest._code.Source(
textwrap.dedent(
"""\
[pytest]
name = value
"""
[pytest]
name = value
"""
)
)
@@ -453,12 +453,12 @@ class TestConfigFromdictargs(object):
cwd = tmpdir.join("a/b")
cwd.join("pytest.ini").ensure().write(
_pytest._code.Source(
textwrap.dedent(
"""\
[pytest]
name = wrong-value
should_not_be_set = true
"""
[pytest]
name = wrong-value
should_not_be_set = true
"""
)
)
with cwd.ensure(dir=True).as_cwd():
@@ -745,6 +745,24 @@ def test_get_plugin_specs_as_list():
assert _get_plugin_specs_as_list(("foo", "bar")) == ["foo", "bar"]
def test_collect_pytest_prefix_bug_integration(testdir):
"""Integration test for issue #3775"""
p = testdir.copy_example("config/collect_pytest_prefix")
result = testdir.runpytest(p)
result.stdout.fnmatch_lines("* 1 passed *")
def test_collect_pytest_prefix_bug(pytestconfig):
"""Ensure we collect only actual functions from conftest files (#3775)"""
class Dummy(object):
class pytest_something(object):
pass
pm = pytestconfig.pluginmanager
assert pm.parse_hookimpl_opts(Dummy(), "pytest_something") is None
class TestWarning(object):
def test_warn_config(self, testdir):
testdir.makeconftest(

View File

@@ -1,7 +1,6 @@
from __future__ import absolute_import, division, print_function
from textwrap import dedent
import textwrap
import _pytest._code
import py
import pytest
from _pytest.config import PytestPluginManager
@@ -174,11 +173,11 @@ def test_conftest_confcutdir(testdir):
testdir.makeconftest("assert 0")
x = testdir.mkdir("x")
x.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
def pytest_addoption(parser):
parser.addoption("--xyz", action="store_true")
"""
def pytest_addoption(parser):
parser.addoption("--xyz", action="store_true")
"""
)
)
result = testdir.runpytest("-h", "--confcutdir=%s" % x, x)
@@ -198,11 +197,11 @@ def test_no_conftest(testdir):
def test_conftest_existing_resultlog(testdir):
x = testdir.mkdir("tests")
x.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
def pytest_addoption(parser):
parser.addoption("--xyz", action="store_true")
"""
def pytest_addoption(parser):
parser.addoption("--xyz", action="store_true")
"""
)
)
testdir.makefile(ext=".log", result="") # Writes result.log
@@ -213,11 +212,11 @@ def test_conftest_existing_resultlog(testdir):
def test_conftest_existing_junitxml(testdir):
x = testdir.mkdir("tests")
x.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
def pytest_addoption(parser):
parser.addoption("--xyz", action="store_true")
"""
def pytest_addoption(parser):
parser.addoption("--xyz", action="store_true")
"""
)
)
testdir.makefile(ext=".xml", junit="") # Writes junit.xml
@@ -247,38 +246,38 @@ def test_fixture_dependency(testdir, monkeypatch):
sub = testdir.mkdir("sub")
sub.join("__init__.py").write("")
sub.join("conftest.py").write(
dedent(
textwrap.dedent(
"""\
import pytest
@pytest.fixture
def not_needed():
assert False, "Should not be called!"
@pytest.fixture
def foo():
assert False, "Should not be called!"
@pytest.fixture
def bar(foo):
return 'bar'
"""
import pytest
@pytest.fixture
def not_needed():
assert False, "Should not be called!"
@pytest.fixture
def foo():
assert False, "Should not be called!"
@pytest.fixture
def bar(foo):
return 'bar'
"""
)
)
subsub = sub.mkdir("subsub")
subsub.join("__init__.py").write("")
subsub.join("test_bar.py").write(
dedent(
textwrap.dedent(
"""\
import pytest
@pytest.fixture
def bar():
return 'sub bar'
def test_event_fixture(bar):
assert bar == 'sub bar'
"""
import pytest
@pytest.fixture
def bar():
return 'sub bar'
def test_event_fixture(bar):
assert bar == 'sub bar'
"""
)
)
result = testdir.runpytest("sub")
@@ -288,11 +287,11 @@ def test_fixture_dependency(testdir, monkeypatch):
def test_conftest_found_with_double_dash(testdir):
sub = testdir.mkdir("sub")
sub.join("conftest.py").write(
dedent(
textwrap.dedent(
"""\
def pytest_addoption(parser):
parser.addoption("--hello-world", action="store_true")
"""
def pytest_addoption(parser):
parser.addoption("--hello-world", action="store_true")
"""
)
)
p = sub.join("test_hello.py")
@@ -313,56 +312,54 @@ class TestConftestVisibility(object):
package = testdir.mkdir("package")
package.join("conftest.py").write(
dedent(
textwrap.dedent(
"""\
import pytest
@pytest.fixture
def fxtr():
return "from-package"
"""
import pytest
@pytest.fixture
def fxtr():
return "from-package"
"""
)
)
package.join("test_pkgroot.py").write(
dedent(
textwrap.dedent(
"""\
def test_pkgroot(fxtr):
assert fxtr == "from-package"
"""
def test_pkgroot(fxtr):
assert fxtr == "from-package"
"""
)
)
swc = package.mkdir("swc")
swc.join("__init__.py").ensure()
swc.join("conftest.py").write(
dedent(
textwrap.dedent(
"""\
import pytest
@pytest.fixture
def fxtr():
return "from-swc"
"""
import pytest
@pytest.fixture
def fxtr():
return "from-swc"
"""
)
)
swc.join("test_with_conftest.py").write(
dedent(
textwrap.dedent(
"""\
def test_with_conftest(fxtr):
assert fxtr == "from-swc"
"""
def test_with_conftest(fxtr):
assert fxtr == "from-swc"
"""
)
)
snc = package.mkdir("snc")
snc.join("__init__.py").ensure()
snc.join("test_no_conftest.py").write(
dedent(
textwrap.dedent(
"""\
def test_no_conftest(fxtr):
assert fxtr == "from-package" # No local conftest.py, so should
# use value from parent dir's
"""
def test_no_conftest(fxtr):
assert fxtr == "from-package" # No local conftest.py, so should
# use value from parent dir's
"""
)
)
print("created directory structure:")
@@ -422,31 +419,31 @@ def test_search_conftest_up_to_inifile(testdir, confcutdir, passed, error):
src = root.join("src").ensure(dir=1)
src.join("pytest.ini").write("[pytest]")
src.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
import pytest
@pytest.fixture
def fix1(): pass
"""
import pytest
@pytest.fixture
def fix1(): pass
"""
)
)
src.join("test_foo.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
def test_1(fix1):
pass
def test_2(out_of_reach):
pass
"""
def test_1(fix1):
pass
def test_2(out_of_reach):
pass
"""
)
)
root.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
import pytest
@pytest.fixture
def out_of_reach(): pass
"""
import pytest
@pytest.fixture
def out_of_reach(): pass
"""
)
)
@@ -464,19 +461,19 @@ def test_search_conftest_up_to_inifile(testdir, confcutdir, passed, error):
def test_issue1073_conftest_special_objects(testdir):
testdir.makeconftest(
"""
"""\
class DontTouchMe(object):
def __getattr__(self, x):
raise Exception('cant touch me')
x = DontTouchMe()
"""
"""
)
testdir.makepyfile(
"""
"""\
def test_some():
pass
"""
"""
)
res = testdir.runpytest()
assert res.ret == 0
@@ -484,15 +481,15 @@ def test_issue1073_conftest_special_objects(testdir):
def test_conftest_exception_handling(testdir):
testdir.makeconftest(
"""
"""\
raise ValueError()
"""
"""
)
testdir.makepyfile(
"""
"""\
def test_some():
pass
"""
"""
)
res = testdir.runpytest()
assert res.ret == 4
@@ -507,7 +504,7 @@ def test_hook_proxy(testdir):
**{
"root/demo-0/test_foo1.py": "def test1(): pass",
"root/demo-a/test_foo2.py": "def test1(): pass",
"root/demo-a/conftest.py": """
"root/demo-a/conftest.py": """\
def pytest_ignore_collect(path, config):
return True
""",
@@ -525,11 +522,11 @@ def test_required_option_help(testdir):
testdir.makeconftest("assert 0")
x = testdir.mkdir("x")
x.join("conftest.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
def pytest_addoption(parser):
parser.addoption("--xyz", action="store_true", required=True)
"""
def pytest_addoption(parser):
parser.addoption("--xyz", action="store_true", required=True)
"""
)
)
result = testdir.runpytest("-h", x)

View File

@@ -1,7 +1,7 @@
# encoding: utf-8
from __future__ import absolute_import, division, print_function
import sys
import _pytest._code
import textwrap
from _pytest.compat import MODULE_NOT_FOUND_ERROR
from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile
import pytest
@@ -258,16 +258,16 @@ class TestDoctests(object):
def test_doctest_linedata_missing(self, testdir):
testdir.tmpdir.join("hello.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
class Fun(object):
@property
def test(self):
'''
>>> a = 1
>>> 1/0
'''
"""
class Fun(object):
@property
def test(self):
'''
>>> a = 1
>>> 1/0
'''
"""
)
)
result = testdir.runpytest("--doctest-modules")
@@ -300,10 +300,10 @@ class TestDoctests(object):
def test_doctest_unex_importerror_with_module(self, testdir):
testdir.tmpdir.join("hello.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
import asdalsdkjaslkdjasd
"""
import asdalsdkjaslkdjasd
"""
)
)
testdir.maketxtfile(
@@ -339,27 +339,27 @@ class TestDoctests(object):
def test_doctestmodule_external_and_issue116(self, testdir):
p = testdir.mkpydir("hello")
p.join("__init__.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
def somefunc():
'''
>>> i = 0
>>> i + 1
2
'''
"""
def somefunc():
'''
>>> i = 0
>>> i + 1
2
'''
"""
)
)
result = testdir.runpytest(p, "--doctest-modules")
result.stdout.fnmatch_lines(
[
"004 *>>> i = 0",
"005 *>>> i + 1",
"003 *>>> i = 0",
"004 *>>> i + 1",
"*Expected:",
"* 2",
"*Got:",
"* 1",
"*:5: DocTestFailure",
"*:4: DocTestFailure",
]
)

View File

@@ -7,7 +7,9 @@ def test_version(testdir, pytestconfig):
result = testdir.runpytest("--version")
assert result.ret == 0
# p = py.path.local(py.__file__).dirpath()
result.stderr.fnmatch_lines(["*pytest*%s*imported from*" % (pytest.__version__,)])
result.stderr.fnmatch_lines(
["*pytest*{}*imported from*".format(pytest.__version__)]
)
if pytestconfig.pluginmanager.list_plugin_distinfo():
result.stderr.fnmatch_lines(["*setuptools registered plugins:", "*at*"])

View File

@@ -941,7 +941,7 @@ def test_double_colon_split_method_issue469(testdir):
def test_unicode_issue368(testdir):
path = testdir.tmpdir.join("test.xml")
log = LogXML(str(path), None)
ustr = py.builtin._totext("ВНИ!", "utf-8")
ustr = u"ВНИ!"
class Report(BaseReport):
longrepr = ustr

View File

@@ -228,11 +228,15 @@ def test_syspath_prepend(mp):
def test_syspath_prepend_double_undo(mp):
mp.syspath_prepend("hello world")
mp.undo()
sys.path.append("more hello world")
mp.undo()
assert sys.path[-1] == "more hello world"
old_syspath = sys.path[:]
try:
mp.syspath_prepend("hello world")
mp.undo()
sys.path.append("more hello world")
mp.undo()
assert sys.path[-1] == "more hello world"
finally:
sys.path[:] = old_syspath
def test_chdir_with_path_local(mp, tmpdir):

View File

@@ -294,7 +294,7 @@ def test_argcomplete(testdir, monkeypatch):
script = str(testdir.tmpdir.join("test_argcomplete"))
pytest_bin = sys.argv[0]
if "pytest" not in os.path.basename(pytest_bin):
pytest.skip("need to be run with pytest executable, not %s" % (pytest_bin,))
pytest.skip("need to be run with pytest executable, not {}".format(pytest_bin))
with open(str(script), "w") as fp:
# redirect output from argcomplete to stdin and stderr is not trivial

View File

@@ -260,7 +260,9 @@ class TestPDB(object):
assert False
"""
)
child = testdir.spawn_pytest("--show-capture=%s --pdb %s" % (showcapture, p1))
child = testdir.spawn_pytest(
"--show-capture={} --pdb {}".format(showcapture, p1)
)
if showcapture in ("all", "log"):
child.expect("captured log")
child.expect("get rekt")
@@ -473,7 +475,7 @@ class TestPDB(object):
x = 5
"""
)
child = testdir.spawn("%s %s" % (sys.executable, p1))
child = testdir.spawn("{} {}".format(sys.executable, p1))
child.expect("x = 5")
child.sendeof()
self.flush(child)

View File

@@ -25,7 +25,6 @@ class TestPytestPluginInteractions(object):
)
conf = testdir.makeconftest(
"""
import sys ; sys.path.insert(0, '.')
import newhooks
def pytest_addhooks(pluginmanager):
pluginmanager.addhooks(newhooks)
@@ -263,8 +262,7 @@ class TestPytestPluginManager(object):
mod.pytest_plugins = "pytest_a"
aplugin = testdir.makepyfile(pytest_a="#")
reprec = testdir.make_hook_recorder(pytestpm)
# syspath.prepend(aplugin.dirpath())
sys.path.insert(0, str(aplugin.dirpath()))
testdir.syspathinsert(aplugin.dirpath())
pytestpm.consider_module(mod)
call = reprec.getcall(pytestpm.hook.pytest_plugin_registered.name)
assert call.plugin.__name__ == "pytest_a"

View File

@@ -8,7 +8,7 @@ import _pytest.pytester as pytester
from _pytest.pytester import HookRecorder
from _pytest.pytester import CwdSnapshot, SysModulesSnapshot, SysPathsSnapshot
from _pytest.config import PytestPluginManager
from _pytest.main import EXIT_OK, EXIT_TESTSFAILED
from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_NOTESTSCOLLECTED
def test_make_hook_recorder(testdir):
@@ -215,57 +215,6 @@ class TestInlineRunModulesCleanup(object):
assert imported.data == 42
def test_inline_run_clean_sys_paths(testdir):
def test_sys_path_change_cleanup(self, testdir):
test_path1 = testdir.tmpdir.join("boink1").strpath
test_path2 = testdir.tmpdir.join("boink2").strpath
test_path3 = testdir.tmpdir.join("boink3").strpath
sys.path.append(test_path1)
sys.meta_path.append(test_path1)
original_path = list(sys.path)
original_meta_path = list(sys.meta_path)
test_mod = testdir.makepyfile(
"""
import sys
sys.path.append({:test_path2})
sys.meta_path.append({:test_path2})
def test_foo():
sys.path.append({:test_path3})
sys.meta_path.append({:test_path3})""".format(
locals()
)
)
testdir.inline_run(str(test_mod))
assert sys.path == original_path
assert sys.meta_path == original_meta_path
def spy_factory(self):
class SysPathsSnapshotSpy(object):
instances = []
def __init__(self):
SysPathsSnapshotSpy.instances.append(self)
self._spy_restore_count = 0
self.__snapshot = SysPathsSnapshot()
def restore(self):
self._spy_restore_count += 1
return self.__snapshot.restore()
return SysPathsSnapshotSpy
def test_inline_run_taking_and_restoring_a_sys_paths_snapshot(
self, testdir, monkeypatch
):
spy_factory = self.spy_factory()
monkeypatch.setattr(pytester, "SysPathsSnapshot", spy_factory)
test_mod = testdir.makepyfile("def test_foo(): pass")
testdir.inline_run(str(test_mod))
assert len(spy_factory.instances) == 1
spy = spy_factory.instances[0]
assert spy._spy_restore_count == 1
def test_assert_outcomes_after_pytest_error(testdir):
testdir.makepyfile("def test_foo(): assert True")
@@ -396,3 +345,8 @@ class TestSysPathsSnapshot(object):
def test_testdir_subprocess(testdir):
testfile = testdir.makepyfile("def test_one(): pass")
assert testdir.runpytest_subprocess(testfile).ret == 0
def test_unicode_args(testdir):
result = testdir.runpytest("-k", u"💩")
assert result.ret == EXIT_NOTESTSCOLLECTED

View File

@@ -4,9 +4,9 @@ terminal reporting of the full testing process.
from __future__ import absolute_import, division, print_function
import collections
import sys
import textwrap
import pluggy
import _pytest._code
import py
import pytest
from _pytest.main import EXIT_NOTESTSCOLLECTED
@@ -161,12 +161,12 @@ class TestTerminal(object):
def test_itemreport_directclasses_not_shown_as_subclasses(self, testdir):
a = testdir.mkpydir("a123")
a.join("test_hello123.py").write(
_pytest._code.Source(
textwrap.dedent(
"""\
class TestClass(object):
def test_method(self):
pass
"""
class TestClass(object):
def test_method(self):
pass
"""
)
)
result = testdir.runpytest("-v")
@@ -312,13 +312,13 @@ class TestCollectonly(object):
result = testdir.runpytest("--collect-only", p)
assert result.ret == 2
result.stdout.fnmatch_lines(
_pytest._code.Source(
textwrap.dedent(
"""\
*ERROR*
*ImportError*
*No module named *Errlk*
*1 error*
"""
*ERROR*
*ImportError*
*No module named *Errlk*
*1 error*
"""
).strip()
)
@@ -948,6 +948,46 @@ def pytest_report_header(config, startdir):
assert "!This is stderr!" not in stdout
assert "!This is a warning log msg!" not in stdout
def test_show_capture_with_teardown_logs(self, testdir):
"""Ensure that the capturing of teardown logs honor --show-capture setting"""
testdir.makepyfile(
"""
import logging
import sys
import pytest
@pytest.fixture(scope="function", autouse="True")
def hook_each_test(request):
yield
sys.stdout.write("!stdout!")
sys.stderr.write("!stderr!")
logging.warning("!log!")
def test_func():
assert False
"""
)
result = testdir.runpytest("--show-capture=stdout", "--tb=short").stdout.str()
assert "!stdout!" in result
assert "!stderr!" not in result
assert "!log!" not in result
result = testdir.runpytest("--show-capture=stderr", "--tb=short").stdout.str()
assert "!stdout!" not in result
assert "!stderr!" in result
assert "!log!" not in result
result = testdir.runpytest("--show-capture=log", "--tb=short").stdout.str()
assert "!stdout!" not in result
assert "!stderr!" not in result
assert "!log!" in result
result = testdir.runpytest("--show-capture=no", "--tb=short").stdout.str()
assert "!stdout!" not in result
assert "!stderr!" not in result
assert "!log!" not in result
@pytest.mark.xfail("not hasattr(os, 'dup')")
def test_fdopen_kept_alive_issue124(testdir):
@@ -1078,9 +1118,9 @@ def test_terminal_summary_warnings_are_displayed(testdir):
)
def test_summary_stats(exp_line, exp_color, stats_arg):
print("Based on stats: %s" % stats_arg)
print('Expect summary: "%s"; with color "%s"' % (exp_line, exp_color))
print('Expect summary: "{}"; with color "{}"'.format(exp_line, exp_color))
(line, color) = build_summary_stats_line(stats_arg)
print('Actually got: "%s"; with color "%s"' % (line, color))
print('Actually got: "{}"; with color "{}"'.format(line, color))
assert line == exp_line
assert color == exp_color

View File

@@ -989,3 +989,24 @@ def test_usefixtures_marker_on_unittest(base, testdir):
result = testdir.runpytest("-s")
result.assert_outcomes(passed=2)
def test_testcase_handles_init_exceptions(testdir):
"""
Regression test to make sure exceptions in the __init__ method are bubbled up correctly.
See https://github.com/pytest-dev/pytest/issues/3788
"""
testdir.makepyfile(
"""
from unittest import TestCase
import pytest
class MyTestCase(TestCase):
def __init__(self, *args, **kwargs):
raise Exception("should raise this exception")
def test_hello(self):
pass
"""
)
result = testdir.runpytest()
assert "should raise this exception" in result.stdout.str()
assert "ERROR at teardown of MyTestCase.test_hello" not in result.stdout.str()

View File

@@ -287,3 +287,18 @@ def test_non_string_warning_argument(testdir):
)
result = testdir.runpytest("-W", "always")
result.stdout.fnmatch_lines(["*= 1 passed, 1 warnings in *"])
def test_filterwarnings_mark_registration(testdir):
"""Ensure filterwarnings mark is registered"""
testdir.makepyfile(
"""
import pytest
@pytest.mark.filterwarnings('error')
def test_func():
pass
"""
)
result = testdir.runpytest("--strict")
assert result.ret == 0

View File

@@ -36,8 +36,7 @@ commands =
[testenv:linting]
skipsdist = True
usedevelop = True
skip_install = True
basepython = python3.6
deps = pre-commit
commands = pre-commit run --all-files --show-diff-on-failure
@@ -115,8 +114,6 @@ skipsdist = True
usedevelop = True
changedir = doc/en
deps =
attrs
more-itertools
PyYAML
sphinx
sphinxcontrib-trio
@@ -165,16 +162,18 @@ commands =
[testenv:coveralls]
passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH COVERALLS_REPO_TOKEN
passenv = CI TRAVIS TRAVIS_* COVERALLS_REPO_TOKEN
usedevelop = True
changedir = .
deps =
{[testenv]deps}
coveralls
codecov
commands =
coverage run --source=_pytest -m pytest testing
coverage report -m
coveralls
codecov
[testenv:release]
decription = do a release, required posarg of the version number