Compare commits

...

881 Commits
2.8.5 ... 3.0.0

Author SHA1 Message Date
Bruno Oliveira
c74ce371ab Add release announcement 2016-08-18 12:02:01 -04:00
Bruno Oliveira
7f18367582 Finalize CHANGELOG 2016-08-18 11:43:21 -04:00
Bruno Oliveira
a3e6c14da3 Bump version to 3.0.0 2016-08-18 11:41:10 -04:00
Florian Bruhin
be9356aeba Merge pull request #1817 from nicoddemus/regendoc-run
Run regendoc for 3.0 release
2016-08-18 15:31:03 +02:00
Bruno Oliveira
9ce30e0085 Run regendoc for 3.0 release 2016-08-18 08:27:16 -04:00
Bruno Oliveira
3748112a94 Merge pull request #1816 from nicoddemus/merge-master-into-features
Merge master into features
2016-08-18 08:48:01 -03:00
Bruno Oliveira
0334e75c30 Use "pytest" instead of "py.test" on trial environments 2016-08-18 08:37:55 -03:00
Bruno Oliveira
56df9fcc72 Tweak CHANGELOG: move "change" entry to the proper place 2016-08-17 23:08:13 -03:00
Bruno Oliveira
66673c0dd3 Remove obsolete docstring 2016-08-17 22:59:18 -03:00
Bruno Oliveira
030c42203d Fix conflicts in CHANGELOG 2016-08-17 22:58:12 -03:00
Bruno Oliveira
3ba475c0f2 Move internal _is_unittest_unexpected_success_a_failure to "compat" module
Fix #1815
2016-08-17 22:50:10 -03:00
Bruno Oliveira
463e6572c5 Merge branch 'master' into merge-master-into-features
Preparing for 3.0
2016-08-17 22:39:23 -03:00
Bruno Oliveira
3e685d6a8d Merge pull request #1795 from hackebrot/fix-report-outcome-for-xpass
WIP Change outcome to 'passed' for xfail unless it's strict
2016-08-17 22:18:06 -03:00
Bruno Oliveira
68ebf552a1 Merge remote-tracking branch 'upstream/master' into fix-report-outcome-for-xpass 2016-08-17 21:55:10 -03:00
Bruno Oliveira
a01cbce662 Merge pull request #1814 from nicoddemus/py35-trial-windows
Enable py35-trial testenv on Windows
2016-08-17 21:54:47 -03:00
Bruno Oliveira
0fb34cd2a1 Update CHANGELOG entries 2016-08-17 21:32:07 -03:00
Bruno Oliveira
224ef67374 Quick fix for tests in config depended on the current directory
When running those tests from pytest's root folder, they would
fail because they would end up picking pytest's own pytest.ini
2016-08-17 21:32:05 -03:00
Bruno Oliveira
4ed412eb59 unittest's unexpectedSuccess should work as non-strict xpass
Make sure tests for that behavior obtain the same return code
using either pytest or unittest to run the same file
2016-08-17 21:32:04 -03:00
Bruno Oliveira
92498109e4 Enable py35-trial testenv on Windows 2016-08-17 21:20:12 -03:00
Raphael Pierzina
dfc659f781 Fix sys.version_info expression in xfail marker 2016-08-17 23:32:56 +01:00
Raphael Pierzina
0173952961 Fix py3 xfail expression evaluation and parametrize strict 2016-08-17 23:15:14 +01:00
Raphael Pierzina
767c28d422 Fix broken test in test_junitxml 2016-08-17 22:32:27 +01:00
Raphael Pierzina
d1f2f779ee Use a better xfail reason 2016-08-17 22:31:56 +01:00
Raphael Pierzina
bb3d6d87b6 Merge branch 'master' into fix-report-outcome-for-xpass 2016-08-17 22:15:29 +01:00
Raphael Pierzina
018197d72a Fix broken test in test_skipping and add one for strict xfail 2016-08-17 22:14:51 +01:00
Raphael Pierzina
ea379e0e4f Fix test in test_junitxml and add one for strict 2016-08-17 22:02:54 +01:00
Florian Bruhin
789e4670e7 Merge pull request #1813 from nicoddemus/pytest-setup.cfg
Support [tool:pytest] in setup.cfg files
2016-08-17 20:33:57 +02:00
Florian Bruhin
c8ab79402c Merge pull request #1811 from nicoddemus/revert-invocation-fixtures
Revert invocation-fixtures code
2016-08-17 20:32:54 +02:00
Bruno Oliveira
ab86dea529 Support [tool:pytest] in setup.cfg files
Also deprecate [pytest] usage in setup.cfg files

Fix #567
2016-08-17 08:19:38 -03:00
Bruno Oliveira
707b6b5e3f Revert all invocation-fixtures code
Due to a serious regression found in #1794, it was decided to pull off
invocation features from 3.0 so it can be (hopefully) re-introduced
in 3.1
2016-08-17 08:12:55 -03:00
Florian Bruhin
09e647c7d9 Merge pull request #1812 from nicoddemus/deprecate-resultlog
Deprecate --resultlog cmdline option
2016-08-17 09:17:13 +02:00
Bruno Oliveira
f25771a101 Deprecate --resultlog cmdline option
Fix #830
2016-08-16 21:40:34 -03:00
Raphael Pierzina
55ec1d7f56 Update test_junitxml.py to interpret XPASS as passed 2016-08-15 23:58:16 +01:00
Bruno Oliveira
497152505e Add CHANGELOG entry for #1809 2016-08-15 19:31:00 -03:00
Bruno Oliveira
ca5957932b Merge pull request #1806 from blueyed/fix-off-by-one-error-with-warnings
Fix off-by-one error with lines from request.node.warn
2016-08-15 19:04:00 -03:00
Bruno Oliveira
d58a8e36d7 Merge pull request #1807 from blueyed/improve-multiline-error-format
Improve display of continuation lines with multiline errors
2016-08-15 19:03:35 -03:00
Bruno Oliveira
d3b855104c Merge pull request #1809 from blueyed/exitstatus-with-pytest_terminal_summary
Pass exitstatus to pytest_terminal_summary hook
2016-08-15 19:02:30 -03:00
Daniel Hahler
c163cc7937 Improve display of continuation lines with multiline errors
Fixes https://github.com/pytest-dev/pytest/issues/717.
Follow-up to https://github.com/pytest-dev/pytest/pull/1762.
2016-08-14 22:34:31 +02:00
Daniel Hahler
16cb5d01b1 Fix off-by-one error with lines from request.node.warn
The line numbers in `node.location` seem to be zero-based?!
2016-08-14 22:10:01 +02:00
Daniel Hahler
5b95ee3c19 Pass exitstatus to pytest_terminal_summary hook
This is useful to know if a testrun has been interrupted
(EXIT_INTERRUPTED).
2016-08-14 22:06:35 +02:00
Raphael Pierzina
225341cf2c Set wasxfail only for xpass w/o strict and else set longrepr 2016-08-13 00:00:51 +01:00
Raphael Pierzina
14a4dd0697 Extend test to verify longrepr in stdout 2016-08-12 23:31:38 +01:00
Raphael Pierzina
296f42a2c9 Treat unittest.expectedFailure pass as a failure 2016-08-12 23:18:36 +01:00
Raphael Pierzina
10a6ed1707 Update unittest test to expect failure for an unexpected success 2016-08-12 23:18:02 +01:00
Bruno Oliveira
34925a31a9 Merge pull request #1799 from cryporchild/junitxml-tests-tally-fix
Fix #1798 to include errors in total tests in junit xml output.
2016-08-08 10:43:38 -03:00
Christian Boelsen
c4d9c7ea55 Add thanks as requested. 2016-08-08 13:45:10 +01:00
Christian Boelsen
e4028b4505 Fix #1798 to include errors in total tests in junit xml output. 2016-08-08 13:35:49 +01:00
Floris Bruynooghe
99a4a1a784 Merge pull request #1791 from nicoddemus/ide-integration-1790
Internal adjustments for easier integration with IDEs
2016-08-07 23:50:50 +01:00
Bruno Oliveira
4ab2e57ebd Merge pull request #1797 from nicoddemus/merge-master-into-features
Merge master into features
2016-08-06 18:48:40 -03:00
Bruno Oliveira
802755ceed Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2016-08-06 17:58:17 -03:00
Bruno Oliveira
ac5c39e534 Merge pull request #1796 from nicoddemus/fix-rootdir-common-ancestor
Fix rootdir common ancestor
2016-08-06 17:38:42 -03:00
Bruno Oliveira
21230aa017 Add CHANGELOG entry 2016-08-06 17:14:13 -03:00
Dave Hunt
eb08135280 Update documentation to describe expected rootdir behaviour 2016-08-06 19:35:40 +02:00
Daniel Hahler
a2891420de Fix determining rootdir from common_ancestor 2016-08-06 19:35:40 +02:00
Raphael Pierzina
4fc20d09fe Change outcome to 'passed' for xfail unless it's strict 2016-08-05 19:25:55 +01:00
Bruno Oliveira
1a79137d04 Add originalname attribute to Function
Related to #1790
2016-08-03 22:56:12 -03:00
Bruno Oliveira
530b0050b4 Improve TestReport.sections docs a bit
Related to #1790
2016-08-03 21:57:58 -03:00
Bruno Oliveira
ff296fd541 Add capstdout and capstderr attrs to TestReport
Related to #1790
2016-08-03 21:49:43 -03:00
Bruno Oliveira
08002ab75a Add longreprtext property to TestReport objects
Related to #1790
2016-08-03 21:49:28 -03:00
Bruno Oliveira
6759b042b5 Merge pull request #1789 from nicoddemus/regendoc-take-2
Regendoc take 2
2016-08-03 18:21:36 -03:00
Bruno Oliveira
8b8c698f1a Add more interpreter versions to multipython example 2016-08-03 16:48:49 -04:00
Bruno Oliveira
d28801d794 Make parametrize example deterministic 2016-08-03 16:48:11 -04:00
Bruno Oliveira
72df32f1fd Fix missing print() 2016-08-03 17:34:55 -03:00
Bruno Oliveira
701d5fc727 Regendoc after more fixes on features branch 2016-08-03 16:31:44 -04:00
Bruno Oliveira
6e3105dc8f Merge pull request #1787 from nicoddemus/fix-rewrite-conftest
Rewrite asserts in test-modules loaded very early in the startup
2016-08-03 16:10:55 -03:00
Bruno Oliveira
6711b1d6ab Rewrite asserts in test-modules loaded very early in the startup
Also now match modules which start with any of the names registered
using register_assert_rewrite as discussed in #1787

Fix #1784
2016-08-03 12:49:48 -03:00
Florian Bruhin
d5be6cba13 Merge pull request #1788 from nicoddemus/available-fixtures-sorted
Sort fixture names when a fixture lookup error occurs
2016-08-03 07:31:28 +02:00
Bruno Oliveira
277b6d3974 Sort fixture names when a fixture lookup error occurs 2016-08-02 19:45:31 -03:00
Bruno Oliveira
ea6191a0cd Merge pull request #1779 from RonnyPfannschmidt/deselect-no-reason
terminal: dont pretend to know the deselection reason
2016-08-02 08:38:17 -03:00
Floris Bruynooghe
9540106fe7 Merge pull request #1780 from nicoddemus/regen-pytest30
Run regen-docs for pytest 3.0
2016-08-02 12:07:16 +01:00
Ronny Pfannschmidt
1c8fe962f3 add changelog entry 2016-08-02 10:51:10 +02:00
Ronny Pfannschmidt
48f4e18280 fix deselect tests to match reason removal 2016-08-02 10:43:25 +02:00
Bruno Oliveira
21a90c8c50 Run regendoc again 2016-08-01 20:12:00 -04:00
Bruno Oliveira
eed21e06db Sort yml items to get same results for regendoc runs 2016-08-01 20:09:35 -04:00
Bruno Oliveira
a6b2732507 Pass list of params to pytest.main() in docs 2016-08-01 20:09:35 -04:00
Bruno Oliveira
946466abf4 Run regen-docs for pytest 3.0 2016-08-01 20:09:35 -04:00
Raphael Pierzina
3cd2e37c55 Merge pull request #1783 from nicoddemus/inv-scoped-fixture-msg
Strip invocation-scope suffix when displaying fixture lookup error
2016-08-02 01:08:25 +01:00
Bruno Oliveira
553dc2600f Strip invocation-scope suffix when displaying fixture lookup error 2016-08-01 20:34:56 -03:00
Raphael Pierzina
226f2795ba Merge pull request #1782 from nicoddemus/invocation-fixtures-help
invocation-scoped fixtures show up once with --fixtures
2016-08-01 23:21:28 +01:00
Bruno Oliveira
b380eb5b34 Merge pull request #1781 from nicoddemus/highlight-file-loc
Highlight the path of file location in error report
2016-08-01 19:18:52 -03:00
Bruno Oliveira
44ecf2ab2f invocation-scoped fixtures show up once with --fixtures 2016-08-01 18:48:29 -03:00
Bruno Oliveira
31c5194562 Fix broken test in py3k 2016-08-01 18:35:42 -03:00
satoru
3c8222f1db Highlight the path of file location in error report
So that it's more obvious when we need to copy the file path.
2016-08-01 17:42:20 -03:00
Ronny Pfannschmidt
ac215e9cff terminal: dont pretend to know the deselection reason
this addresses #1372 - we pretend we know the deselection reason from internal plugins and ignore 3rd party/local reasons
2016-08-01 13:50:59 +02:00
Bruno Oliveira
aa145fa83e Add 'invocation' scope option to fixture docstring 2016-07-29 12:47:30 -03:00
Bruno Oliveira
76fbc6379f Fix deprecated directive in docstring 2016-07-29 12:39:48 -03:00
Bruno Oliveira
80508203b0 Merge pull request #1776 from nicoddemus/pytest-30-version
Set version to 3.0.0.dev1
2016-07-28 21:57:13 -03:00
Bruno Oliveira
eaf8d9ce19 Set version to 3.0.0.dev1
For a long time now we've considering the next version to be 3.0.0
2016-07-28 21:30:16 -03:00
Floris Bruynooghe
510a6083ba Merge pull request #1758 from nicoddemus/deprecated-module
Add deprecation module to centralize deprecation messages and bits of…
2016-07-28 22:36:55 +01:00
Florian Bruhin
ffb583ae91 Merge pull request #1773 from nicoddemus/fix-freeze
Use PyInstaller for freeze test env
2016-07-27 15:06:54 +02:00
Bruno Oliveira
ae9d3bf886 Freeze docs: PyInstaller hook and wording
As discussed during the review, suggest in general
to use PyInstaller and just mention pytest.freeze_includes()
in less detail on how to actually use it, because it varies
from tool to tool.
2016-07-27 09:15:40 -03:00
Florian Bruhin
7b1520687e Merge pull request #1771 from nicoddemus/sort-links-changelog
Sort link refs in CHANGELOG
2016-07-27 06:26:45 +02:00
Florian Bruhin
fd765f854c Merge pull request #1772 from nicoddemus/pdf-link
Fix pdf links in docs and point to docs.pytest.org in README
2016-07-27 06:25:41 +02:00
Bruno Oliveira
ed36d627e4 Use PyInstaller for freeze test env
cx_freeze doesn't seem to be very well supported in Python 3.5.

Using pyinstaller instead and rename environment to "freeze" which
is a more generic term for freezing python code into standalone
executables.

Fix #1769
2016-07-26 22:02:37 -03:00
Bruno Oliveira
9a68681719 Point doc links in README to docs.pytest.org 2016-07-26 20:25:55 -03:00
Bruno Oliveira
0b8a91b858 Fix pdf links in the documentation
Fix #1436
2016-07-26 20:20:22 -03:00
Bruno Oliveira
5565c1f4ae Sort link refs in CHANGELOG 2016-07-26 20:11:49 -03:00
Florian Bruhin
c40dcb3c18 Merge pull request #1768 from ioggstream/1609-fix-description
fix keep-duplicates help line.
2016-07-26 11:34:06 +02:00
Roberto Polli
05728d1317 fix keep-duplicates help line. 2016-07-26 09:53:12 +02:00
Florian Bruhin
b4ad4cc46e Merge pull request #1767 from nicoddemus/merge-master
Merge master into features
2016-07-26 08:44:11 +02:00
Bruno Oliveira
c2864aba3d Merge branch 'master' into merge-master
# Conflicts:
#	AUTHORS
#	CHANGELOG.rst
#	_pytest/monkeypatch.py
#	_pytest/python.py
2016-07-25 19:06:29 -03:00
Bruno Oliveira
9cf09cda7b Remove some comments and improved changelog 2016-07-25 18:41:03 -03:00
Bruno Oliveira
eb5b163698 Merge branch '1609-features' of https://github.com/ioggstream/pytest into features 2016-07-25 18:37:55 -03:00
Bruno Oliveira
d911bfcb8a Merge branch 'issue634-scopes' of https://github.com/Stranger6667/pytest
# Conflicts:
#	CHANGELOG.rst
2016-07-25 18:26:50 -03:00
Bruno Oliveira
8f29ce26e9 Merge branch 'mark_missing_fixture_error' of https://github.com/eolo999/pytest 2016-07-25 18:20:17 -03:00
Bruno Oliveira
6c8b0a28e1 Add deprecation module to centralize deprecation messages and bits of code 2016-07-25 18:14:39 -03:00
Dmitry Dygalo
d72afe7e08 Fixed scope override inside metafunc.parametrize. Fixes #634 2016-07-25 12:42:50 +02:00
Roberto Polli
ab6aef1d1f feature: default behavior now is to ignore duplicate paths specified from the command line. Use --keep-duplicates to retain duplicate paths. 2016-07-25 12:41:58 +02:00
Florian Bruhin
a2b04d02c2 Merge pull request #1759 from Stranger6667/issue1579-invalid-class
Fixed collection of classes with custom ``__new__`` method. Fixes #1579.
2016-07-25 12:27:51 +02:00
Dmitry Dygalo
f7ad173fee Fixed collection of classes with custom `__new__` method 2016-07-25 11:33:37 +02:00
Ronny Pfannschmidt
d37af20527 Merge pull request #1765 from The-Compiler/1763-cleanup
Cleanups for #1763
2016-07-25 10:31:06 +02:00
Florian Bruhin
a309a571d9 Cleanups for #1763 2016-07-25 10:17:46 +02:00
Edoardo Batini
e9d729bd46 drop parenthesis around GH issue number 2016-07-25 10:11:37 +02:00
Bruno Oliveira
c9a2e611ce Merge pull request #1763 from tomviner/issue1210-exit-msg
Fix #1210 display msg for early calls to exit
2016-07-24 13:05:44 -03:00
Bruno Oliveira
a24146dd3c Merge pull request #1757 from tramwaj29/improved-message-when-not-using-parametrized-variable
Improved message when not using parametrized variable
2016-07-24 13:02:03 -03:00
Bruno Oliveira
7862517c28 Merge pull request #1760 from blueyed/followup-pr1718-remove-newline
Followup to #1718: style/formatting
2016-07-24 13:00:14 -03:00
Tom Viner
42adaf5a61 Fix #1210 display msg for early calls to exit 2016-07-24 14:16:34 +02:00
JJ
4aede6faa6 fixed conflicts 2016-07-24 12:10:32 +02:00
JJ
d000f2536a added a test for when the indirect is just a string 2016-07-24 10:47:06 +02:00
Edoardo Batini
0ae77be9f0 Add new target links in CHANGELOG 2016-07-24 00:46:06 +02:00
Daniel Hahler
5c5d7e05f7 Followup to #1718: style/formatting 2016-07-24 00:21:42 +02:00
Edoardo Batini
f450e0a1db Thanks to Tom Viner for his guidance during EuroPython2016 sprint 2016-07-24 00:06:16 +02:00
Edoardo Batini
fabe8cda2f Thanking myself in CHANGELOG 2016-07-23 23:59:34 +02:00
Edoardo Batini
3c4158ac35 Add changelog entry for this bugfix branch 2016-07-23 23:51:11 +02:00
Edoardo Batini
e00199212c Add myself to the AUTHORS 2016-07-23 23:49:44 +02:00
Edoardo Batini
e9a67e6702 Adjust test involving FixtureLookupErrorRepr
I added a starting 'E' to the expected error messages.

The tests were still passing after the previous patch but I think it's
better to have stricter tests.
2016-07-23 23:45:07 +02:00
Edoardo Batini
6799a47c78 Start FixtureLookupErrorRepr with an 'E' 2016-07-23 23:43:34 +02:00
Bruno Oliveira
655df7f839 Merge pull request #1755 from diegorusso/master
Testcase for overriding autouse fixture with a parametrized fixture.
2016-07-23 12:53:24 -03:00
Bruno Oliveira
9891593413 Merge pull request #1754 from hartym/1749_doctest_report_format
Doctest report format option (#1749)
2016-07-23 12:52:54 -03:00
JJ
bbc7f3a631 updated changelog, resolve #1539 2016-07-23 16:57:49 +02:00
Diego Russo
1704b7d265 Test case for overriding autouse fixtures
Test case for overriding autouse fixture with a parametrized fixture.
The test covers the problem explained in the issue 1601
Adding Diego Russo to AUTHORS
2016-07-23 16:53:39 +02:00
JJ
3631719050 added myself to authors 2016-07-23 16:51:55 +02:00
JJ
f26fa5a441 changed error message for unused parametrize name 2016-07-23 16:49:20 +02:00
Romain Dorgueil
94731fc2a1 Changes variable name so it better describes what it does now. 2016-07-23 16:26:33 +02:00
Romain Dorgueil
51fa244650 Cleaner implementation of #1749. 2016-07-23 16:18:12 +02:00
Romain Dorgueil
d5a70acd48 Simplify test in test_doctest_report_none_or_only_first_failure. 2016-07-23 15:58:13 +02:00
Bruno Oliveira
018acc6bae Explain why thanks yourself in the CHANGELOG 2016-07-23 10:44:38 -03:00
Romain Dorgueil
f8f690de64 adds the versionadded flag in docs (#1749) 2016-07-23 15:30:06 +02:00
Romain Dorgueil
e229a27e8b using @pytest.mark.parametrize instead of calling one test many times from another as suggested by @nicoddemus in pr #1754 2016-07-23 15:21:59 +02:00
Romain Dorgueil
ec7695e15d adds a bit of doctest hint on why the key and value getters are separate functions. 2016-07-23 15:19:18 +02:00
Romain Dorgueil
1a33025a76 Refactors test to be in its own class, as discussed with @nicoddemus. 2016-07-23 15:16:23 +02:00
Romain Dorgueil
922a295729 fixes changelog, I think I got it 2016-07-23 15:15:17 +02:00
Romain Dorgueil
014ebc9202 Removed bug reference in changelog because it makes the linting fail. 2016-07-23 15:04:19 +02:00
Romain Dorgueil
87ca4b95fb Separate the option keys and value to avoid importing "doctest" (and tested things like "logging") for argument parsing (fixes #1749) 2016-07-23 14:50:24 +02:00
Romain Dorgueil
fd8e019cc1 Choose the doctest output format in case of failure, still work in progress as a few checks fail (related to #1749) 2016-07-23 14:40:46 +02:00
Romain Dorgueil
625b603f1f Implements an option to choose the doctest output format in case of failure. (fixes #1749) 2016-07-23 13:06:05 +02:00
Bruno Oliveira
38e0e08074 Merge pull request #1751 from javiromero/docstrings
Uppercase first word in docstrings. Change to an imperative form.
2016-07-22 10:33:58 -03:00
Javi Romero
1aab6e3bc2 Add changes to changelog. 2016-07-22 12:54:42 +02:00
Javi Romero
7e37497d5a Uppercase first word in docstrings. Change to an imperative form. Add name to authors. 2016-07-22 12:39:06 +02:00
Bruno Oliveira
ae0798522f Merge pull request #1711 from nicoddemus/invocation-scoped-fixtures
Invocation scoped fixtures
2016-07-21 19:48:52 -03:00
Florian Bruhin
832ada1b44 Merge pull request #1747 from nicoddemus/line-match-stringio
Log LineMatcher output in a stream instead of stderr
2016-07-21 09:38:11 +02:00
Bruno Oliveira
4c112401c5 Log LineMatcher output in a stream instead of stderr
This makes the match/nomatch output appear as part
of the fnmatch_lines() error on pytest's output instead
of globbered together with general stdout/stderr capture
2016-07-20 22:35:43 -03:00
Bruno Oliveira
05f3422d7c Make monkeypatch invocation-scoped 2016-07-20 22:05:49 -03:00
Bruno Oliveira
4f2bf965cb Merge remote-tracking branch 'upstream/features' into invocation-scoped-fixtures 2016-07-20 21:16:27 -03:00
Bruno Oliveira
eaa4ee3fdf Merge pull request #1746 from pytest-dev/conftest-exception-printing
Conftest exception printing
2016-07-20 21:03:39 -03:00
Bruno Oliveira
6aea164b6d Add more tests for invocation scoped fixtures 2016-07-20 21:02:36 -03:00
Bruno Oliveira
20f97c3041 Small documentation improvements 2016-07-20 20:22:28 -03:00
Bruno Oliveira
e0f08a73ab Merge branch 'features' into conftest-exception-printing 2016-07-20 19:33:36 -03:00
Bruno Oliveira
93aae987a2 Merge pull request #1744 from RonnyPfannschmidt/existfirst-override
allow --exitfirst/-x to be overridden by a following --maxfail
2016-07-20 18:16:07 -03:00
Bruno Oliveira
1204cbade4 Merge pull request #1745 from RonnyPfannschmidt/skip-compat
skipping plugin: remove python2.5 compat code
2016-07-20 18:14:20 -03:00
Ronny Pfannschmidt
9dadaa8a41 skipping plugin: remove python2.5 compat code 2016-07-20 17:45:20 +02:00
Ronny Pfannschmidt
0403266bf0 record --exitfirst change in changelog 2016-07-20 17:23:07 +02:00
Ronny Pfannschmidt
3fd8257c17 add test for --maxfail=NUM overiding -x 2016-07-20 17:20:10 +02:00
Ronny Pfannschmidt
2a05c311e9 implement exitfirst as store_const option
this makes it possible to override with a later maxfail
2016-07-20 17:15:29 +02:00
Bruno Oliveira
bcc58ec916 Merge pull request #1740 from RonnyPfannschmidt/float-argument
optparse compatibility - add float and complex
2016-07-19 19:17:35 -03:00
Ronny Pfannschmidt
9af872a230 update changelog 2016-07-19 20:11:47 +02:00
Ronny Pfannschmidt
61cc5c4d4e argument parsing: always warn for string types
fix #1741
2016-07-19 10:33:25 +02:00
Ronny Pfannschmidt
317b3f257d optparse compatibility - add float and complex
also documents the implementation quality
fixes #457
2016-07-19 10:20:41 +02:00
Floris Bruynooghe
2b9973846e Merge pull request #1736 from Avira/features
Add backwards compatibility docs
2016-07-18 11:17:48 +02:00
Oliver Bestwalter
58a8150bc5 add backwards compatibility policy 2016-07-17 21:14:16 +02:00
Bruno Oliveira
0ac3eaa1db Merge pull request #1735 from flub/reinterpret-docs
Document the re-writing of plugins
2016-07-17 12:48:33 -03:00
Floris Bruynooghe
0a53797fa3 Document the re-writing of plugins 2016-07-17 12:30:21 +01:00
Ronny Pfannschmidt
8a73a2ad60 Merge pull request #1734 from nicoddemus/issue-1728-inconsistent-setup-teardown
setup_* and teardown_* functions argument now optional
2016-07-15 14:59:22 +02:00
Floris Bruynooghe
fb21493856 Merge pull request #1733 from flub/remove-reinterpret
Remove assertion reinterpretation
2016-07-15 12:46:25 +01:00
Bruno Oliveira
ff8fb4950e setup_* and teardown_* functions argument now optional
setup_module, setup_function and setup_method
extra argument are now optional and may be omitted.

Fix #1728
2016-07-14 23:41:40 -03:00
Floris Bruynooghe
d1852a48b7 Remove assertion reinterpretation
The assertion reinterpretation is an old backwards compatibility
mode which was no longer being maintained on feature-parity with
the assertion rewriting mode.  It was also responsible for some
dubious patching of builtins and test with side-effects would
suddenly start passing.  Since re-writing has been the default for
a long time and plugins are now also re-written it is time to
retire reinterpretation.
2016-07-15 00:33:39 +01:00
Bruno Oliveira
ee374e3b80 Merge pull request #1731 from nicoddemus/improve-test-args-deprecated
Make assert in test_str_args_deprecated more resilient
2016-07-14 19:18:13 -03:00
Bruno Oliveira
3328cd2620 Make assert in test_str_args_deprecated more resilient
This attempts to fix CI which broke because of this test.

Other warnings introduced in the future could break
this test.
2016-07-14 18:37:59 -03:00
Bruno Oliveira
350ebc9167 Merge pull request #1730 from RedBeardCode/pytest-1536
Added confcutdir in testing/test_conftest.py::test_conftest_import_or…
2016-07-14 17:02:58 -03:00
Floris Bruynooghe
24fbbbef1f Merge pull request #1641 from flub/rewrite-plugins
Rewrite plugins
2016-07-14 19:39:15 +01:00
RedBeardCode
22bb43413f Added confcutdir in testing/test_conftest.py::test_conftest_import_order and
testing/python/fixture.py::TestAutouseManagement::()::
  test_class_function_parametrization_finalization to avoid problems with
  abandoned conftest.py files in /tmp dir.
  Fixes #1536
2016-07-14 18:15:38 +02:00
Florian Bruhin
691dc8bc68 Merge pull request #1727 from nicoddemus/deprecate-str-pytest-main
Deprecate support for passing command-line as string to pytest.main()
2016-07-14 14:42:14 +02:00
Florian Bruhin
14af12cb7b Merge pull request #1717 from nicoddemus/nose-yield-tests-docs
Document limitations for yield-tests in nose
2016-07-14 14:40:27 +02:00
Floris Bruynooghe
51ee7f8734 Fixup things after rebase
Some changes to make things work on top of current features branch.
2016-07-14 12:42:29 +01:00
Bruno Oliveira
9007e16cdf Document limitations for yield-tests in nose
Also add nose doc to the root toctree

Closes #1716
2016-07-14 08:20:01 -03:00
Bruno Oliveira
02dd7d612a Remove duplicated changelog entry and formatting fix 2016-07-14 08:16:27 -03:00
Bruno Oliveira
ab0b6faa5f Deprecate support for passing command-line as string to pytest.main()
Fixes #1723
2016-07-14 08:11:43 -03:00
Bruno Oliveira
1fb09d9dd5 Merge pull request #1726 from nicoddemus/warnings-displayed-by-default
Warnings displayed by default
2016-07-13 19:33:49 -03:00
Bruno Oliveira
1266ebec83 Merge remote-tracking branch 'upstream/features' into warnings-displayed-by-default
# Conflicts:
#	CHANGELOG.rst
#	testing/test_terminal.py
2016-07-13 18:45:15 -03:00
Bruno Oliveira
6e9ee2b766 Merge pull request #1724 from blueyed/followup-pr1718-remove-newline
funcarg_prefix_warning: remove newline
2016-07-13 15:46:16 -03:00
Daniel Hahler
3cfebdd7c5 funcarg_prefix_warning: remove newline
Followup to https://github.com/pytest-dev/pytest/pull/1718.
2016-07-13 18:33:24 +02:00
Floris Bruynooghe
743f59afb2 Introduce pytest.register_assert_rewrite()
Plugins can now explicitly mark modules to be re-written.  By default
only the modules containing the plugin entrypoint are re-written.
2016-07-13 17:31:09 +01:00
Floris Bruynooghe
944da5b98a Avoid rewrite warning for inline runs
When running pytest inline/inprocess we plugins have already been
imported and re-writen, so avoid the warning.
2016-07-13 17:29:19 +01:00
Floris Bruynooghe
a98e3cefc5 Enable re-writing of setuptools-installed plugins
Hook up the PEP 302 import hook very early in pytest startup so
that it gets installed before setuptools-installed plugins are
imported.  Also iterate over all installed plugins and mark them
for rewriting.  If an installed plugin is already imported then
a warning is issued, we can not break since that might break
existing plugins and the fallback will still be gracefull to
plain asserts.

Some existing tests are failing in this commit because of the new
warning triggered by inline pytest runs due to the hypothesis
plugin already being imported.  The tests will be fixed in the next
commit.
2016-07-13 17:29:19 +01:00
Bruno Oliveira
dd5ce96cd7 Merge pull request #1718 from blueyed/fix-funcarg_prefix_warning
Add punctuation to funcarg_prefix_warning
2016-07-13 12:53:54 -03:00
Florian Bruhin
aeccd6b4a2 Merge pull request #1720 from nicoddemus/changelog-formatting
Improve overall CHANGELOG formatting and consistency for 3.0
2016-07-13 15:58:38 +02:00
Bruno Oliveira
44c3055e79 Merge pull request #1721 from bronsen/patch-1
Docs: De-404 links in changelog
2016-07-13 10:05:14 -03:00
Bruno Oliveira
4a763accc5 Improve overall CHANGELOG formatting and consistency for 3.0 2016-07-13 08:52:36 -03:00
Kalle Bronsen
dfe1209d2c De-404 links in changelog 2016-07-13 11:41:27 +02:00
Florian Bruhin
54ea27c283 Merge pull request #1719 from nicoddemus/fix-2.10-versions-in-docs
Fix 2.10 -> 3.0 versions in docs
2016-07-13 08:10:13 +02:00
Bruno Oliveira
f827810fa8 Fix 2.10 -> 3.0 versions in docs 2016-07-12 21:02:40 -03:00
Daniel Hahler
15e97a7c78 Add punctuation to funcarg_prefix_warning 2016-07-12 23:49:09 +02:00
Ronny Pfannschmidt
c4f20a1834 Merge pull request #1712 from anntzer/custom-debugger
Allow passing a custom Pdb subclass via --pdbcls.
2016-07-12 16:45:07 +02:00
Ronny Pfannschmidt
4c56c95eb8 Merge pull request #1714 from nicoddemus/deprecate-yield-tests-funcarg-prefix
Deprecate yield tests funcarg prefix
2016-07-12 11:43:59 +02:00
Antony Lee
7ee3dd1cb5 Add tests for custom pdb class.
(and edit CHANGELOG)
2016-07-11 20:07:29 -07:00
Bruno Oliveira
458ecae1df Replace all usages of "pytest_funcarg__" for @pytest.fixture 2016-07-11 22:21:52 -03:00
Bruno Oliveira
ad4125dc0d Deprecate "pytest_funcarg__" prefix to declare fixtures
Fixes #1684
2016-07-11 22:21:50 -03:00
Bruno Oliveira
5506dc700c Deprecate yield tests
Closes #16
Closes #1324
2016-07-11 22:21:49 -03:00
Bruno Oliveira
0dd1c8bf14 Add test to ensure capsys and capfd error out when using "getfixturevalue" inside a test 2016-07-11 20:48:38 -03:00
Bruno Oliveira
0ca06962e9 Improve docs 2016-07-11 20:33:16 -03:00
Bruno Oliveira
2ffe354f21 Add CHANGELOG for invocation-scoped fixtures 2016-07-11 20:32:59 -03:00
Bruno Oliveira
fb4da00a32 Merge remote-tracking branch 'upstream/features' into invocation-scoped-fixtures 2016-07-11 20:09:13 -03:00
Bruno Oliveira
6f68dfcc47 Merge pull request #1710 from RonnyPfannschmidt/fixture-split
Fixture split 2nd attempt
2016-07-10 12:10:07 -03:00
Antony Lee
6383b53ad9 Allow passing a custom Pdb subclass via --pdbcls.
This obviates the need for plugins such as `pytest-ipdb`; instead one
can simply call `py.test --pdb=IPython.core.debugger:Pdb`
2016-07-09 21:10:52 -07:00
Bruno Oliveira
8ed055efd8 Add acceptance test for invocation-scoped fixtures 2016-07-09 22:52:07 -03:00
Bruno Oliveira
775100881a Implement invocation-scoped fixtures 2016-07-09 22:52:05 -03:00
Bruno Oliveira
29289b472f Add documentation for "invocation" scoped fixture 2016-07-09 16:51:25 -03:00
Ronny Pfannschmidt
8c49561470 split most fixture related code into own plugin 2016-07-09 20:36:00 +02:00
Florian Bruhin
7a2058e3db Merge pull request #1709 from The-Compiler/changelog
Clean up changelog
2016-07-08 18:52:28 +02:00
Florian Bruhin
668ebb102c Clean up changelog
- Merged 3.0 and 2.10 as much stuff from the sprint went to the 2.10
  section.
- Cleaned up mixture of link/text blocks
- Moved some entries to "Bug Fixes" and "Incompatible changes"
- Stop doing "Fixes (#123) ..." because it looks weird
- Add some new empty points for all sections
2016-07-08 18:51:26 +02:00
Bruno Oliveira
293351cfd0 Merge pull request #1705 from RonnyPfannschmidt/merge-master
Merge from master to features
2016-07-08 08:15:54 -03:00
Ronny Pfannschmidt
dad6aa8a16 fix duplicate target in changelog 2016-07-06 13:51:13 +02:00
Ronny Pfannschmidt
b9a91dc112 merge from master to features 2016-07-06 11:51:48 +02:00
Bruno Oliveira
f31c31a73c Merge pull request #1695 from sallner/feature-setup-show
Feature setup show
2016-07-05 21:29:01 -03:00
aostr
94e4a2dd67 * implemented changes recommended by nicoddemus 2016-07-05 15:22:27 +02:00
Javier Domingo Cansino
0171cfa30f Fixing link to issue and creating testcase that shows that it finds the line in the stderr lines 2016-07-05 10:39:12 +01:00
Javier Domingo Cansino
61e605f60b Making conftest import failures easier to debug 2016-07-05 10:04:42 +01:00
Ronny Pfannschmidt
cc0920ceb1 Merge pull request #1699 from nicoddemus/404-links-on-talks-docs
Fix links and removed 404 links from talks.rst
2016-07-05 08:44:23 +02:00
Florian Bruhin
067e044f97 Merge pull request #1700 from nicoddemus/split-appveyor
Split AppVeyor test runs in multiple jobs to avoid timeout issues
2016-07-05 06:43:39 +02:00
Bruno Oliveira
10c5e6fd9c Split AppVeyor test runs in multiple jobs to avoid timeout issues
Some of our builds have been timing out (over 1 hour),
on AppVeyor
2016-07-04 21:54:21 -03:00
Bruno Oliveira
8d39ce17da Fix links and removed 404 links from talks.rst
Fix #1696
2016-07-04 21:32:57 -03:00
Steffen Allner
6438895a23 Fix PEP-8. 2016-07-03 22:33:21 +02:00
Steffen Allner
b650c3c118 Implement --setup-show cli flag
to also be able to see fixture setup with normal test execution.
2016-07-03 22:30:51 +02:00
Steffen Allner
f7b5bb2f97 Merged branch features into features 2016-07-03 21:07:02 +02:00
Bruno Oliveira
f74dd8550f Merge pull request #1692 from pytest-dev/changelog
Add changelog to requirements for pytest-dev plugins
2016-06-30 17:39:14 -03:00
Florian Bruhin
e3c43a1462 Add changelog to requirements for pytest-dev plugins
Closes #1691
2016-06-30 22:25:09 +02:00
Florian Bruhin
7927dff8a1 Merge pull request #1678 from RonnyPfannschmidt/drop-python30-32
drop python 3.0-3.2 support code from setup.py
2016-06-30 11:15:49 +02:00
Ronny Pfannschmidt
1451a1ab00 remove unsupported python versions from code/source xfail 2016-06-30 10:03:40 +02:00
Bruno Oliveira
75ecd94294 Merge pull request #1689 from quodlibetor/autouse-docs
Document the interaction of autouse scopes
2016-06-29 18:08:06 -03:00
Brandon W Maister
771c4539fa Document the interaction of autouse scopes
I wouldn't have even attempted what I did to cause #1688 if this had
been there.
2016-06-29 16:52:13 -04:00
Ronny Pfannschmidt
2a43237527 docs: no longer include python 3.0-3.2 in the index page 2016-06-27 18:20:56 +02:00
Ronny Pfannschmidt
7dc8d1ab60 fix typo and remove python3.2 from readme 2016-06-27 18:19:00 +02:00
Florian Bruhin
1e60294188 Merge pull request #1679 from eli-b/patch-1
catched -> caught
2016-06-27 18:02:16 +02:00
Ronny Pfannschmidt
e877e25587 drop python 3.0-3.2 support code from setup.py
addresses #1627
2016-06-27 17:55:37 +02:00
Eli Boyarski
21d27784eb catched -> caught
Even though catch is a Python keyword, 'catched' just looks terrible in text.
If the text was supposed to reference the keyword, then 'catched' should be changed to "'catch'ed".
2016-06-27 15:41:40 +03:00
Bruno Oliveira
ccd395ffe0 Merge pull request #1659 from RonnyPfannschmidt/failtest-586
xfailing test for #568
2016-06-27 07:38:14 -03:00
Ronny Pfannschmidt
76756c0c0b mark tests: use better name of the test for #568 2016-06-27 11:57:21 +02:00
Bruno Oliveira
44695ae46c Fix CHANGELOG: obestwalter appearing twice due to mergig separate PRs 2016-06-26 23:44:07 +02:00
Bruno Oliveira
7e78965c79 Merge branch 'logic_brackets' 2016-06-26 21:34:37 +02:00
Bruno Oliveira
58e558141d Remove commented out code 2016-06-26 21:19:07 +02:00
Bruno Oliveira
22c2d87633 Fix bad merge in CHANGELOG 2016-06-26 21:18:01 +02:00
Bruno Oliveira
5891061ac1 Merge pull request #1675 from kvas-it/issue-1562
Add warning for assertions on tuples #1562
2016-06-26 16:09:15 -03:00
Bruno Oliveira
48ac1a0986 Merge branch 'remove-old-entry-points' into features 2016-06-26 20:38:59 +02:00
Bruno Oliveira
db9b3e9522 Merge pull request #1677 from nicoddemus/remove_cmd_options
Remove cmd options
2016-06-26 15:37:59 -03:00
Bruno Oliveira
b9536608c5 Add issue and obestwalter to CHANGELOG
Fix #1632
2016-06-26 20:36:04 +02:00
Bruno Oliveira
f7a2048e96 Merge branch 'features' of https://github.com/Avira/pytest into remove-old-entry-points 2016-06-26 20:32:38 +02:00
Bruno Oliveira
7239f36466 Improve formatting in CHANGELOG 2016-06-26 19:41:01 +02:00
RedBeardCode
1b0dbd8c40 Move the freezing function from genscript.py to a new module freeze_support.py 2016-06-26 19:37:24 +02:00
RedBeardCode
0e2ebc96ff Remove deprecated cmd options
Fixes #1657
2016-06-26 19:26:04 +02:00
aostr
b4e0fabf93 * added missing link to the referenced issue 2016-06-26 06:52:50 +02:00
aostr
1ce4670062 * removed tailing whitespaces 2016-06-26 06:51:57 +02:00
Vasily Kuznetsov
6d4cee2159 Add a test for indirect use of tuple in the assert that should not cause a warning 2016-06-26 02:21:51 +02:00
Vasily Kuznetsov
0db4ae15a9 Update changelog 2016-06-25 19:34:55 +02:00
Vasily Kuznetsov
c17027e576 Warn about asserts on tuples (fixes #1562) 2016-06-25 19:21:48 +02:00
aostr
e04d9ff80b * now showing pytest warnings summary by default.
* added ``--disable-pytest-warnings` flag to let users disable the warnings summary.
* extended/changed unit tests for the changes in the pytest core.
2016-06-25 18:16:13 +02:00
RedBeardCode
e2f550156e Improve of the test output for logical expression with brackets.
Fixes #925
2016-06-25 18:10:36 +02:00
holger krekel
68bed00d5b Merge pull request #1667 from fengxx/feature/override_ini_option
Add --overwrite-ini ININAME=INIVALUE cli option
2016-06-25 18:10:10 +02:00
Ted Xiao
856e6cab75 add --override-ini option to overrides ini values
Signed-off-by: Ted Xiao <xiao.xj@gmail.com>
2016-06-25 23:45:32 +08:00
holger krekel
13a188fe37 Merge pull request #1647 from sallner/features
Add new options to report fixture setup and teardown
2016-06-25 16:38:37 +02:00
Oliver Bestwalter
3a9e9fdf82 rephrase changes in CHANGELOG.rst 2016-06-25 16:04:23 +02:00
Oliver Bestwalter
72450408ed add changes in CHANGELOG.rst 2016-06-25 16:00:49 +02:00
Ronny Pfannschmidt
95b83958f4 add xfailing test for issue #568 2016-06-25 15:49:23 +02:00
Ronny Pfannschmidt
2af3a7a9d7 Merge pull request #1519 from omarkohl/pytest_skip_decorator
Raise CollectError if pytest.skip() is called during collection
2016-06-25 15:20:34 +02:00
Florian Bruhin
35cd12e4de Merge pull request #1660 from hackebrot/parametrize-with-fixtures
Proposal: Parametrize with fixtures
2016-06-25 15:05:27 +02:00
Raphael Pierzina
1b6bc4d606 Add feature proposal to changelog 2016-06-25 14:58:18 +02:00
Bruno Oliveira
c519b9517a Merge pull request #1663 from aostr/master
Rename the default plugin "pdb" into "debugging"
2016-06-25 09:56:54 -03:00
Raphael Pierzina
eb98a8eefb Change version in issues section to pytest 3.0 only 2016-06-25 14:52:57 +02:00
Raphael Pierzina
acfdd85dff Move document to proposals subfolder 2016-06-25 14:42:19 +02:00
Bruno Oliveira
e0242146ec Merge pull request #1666 from pytest-dev/1564-changelog
Add changelog entry for #1564
2016-06-25 09:34:21 -03:00
Bruno Oliveira
df17f862fa Merge pull request #1648 from blueyed/simplify-Argument-__repr__
Simplify Argument.__repr__
2016-06-25 09:26:50 -03:00
Bruno Oliveira
7eb1318db2 Merge pull request #1656 from pytest-dev/rm-indiegogo-links
Update website wrt indiegogo campaign
2016-06-25 09:22:07 -03:00
Florian Bruhin
ce603dc0ea Add changelog entry for #1564 2016-06-25 14:18:41 +02:00
Ronny Pfannschmidt
70ea3ce7f7 Merge pull request #1564 from The-Compiler/issue1479
Don't ignore ImportError when importing setuptools plugins
2016-06-25 14:09:48 +02:00
aostr
9a5224e2f8 Renamed the pdb module and changed unit tests accordingly 2016-06-25 12:37:31 +02:00
Danielle Jenkins
32ca5cdb09 Update changelog for new fixture hooks. 2016-06-25 12:33:31 +02:00
Oliver Bestwalter
891e1677b6 Remove all py.test-X* entry points.
The versioned, suffixed entry points are not documented and a leftover from a pre-virtualenv world. They also are broken for wheels.
2016-06-25 12:27:05 +02:00
Danielle Jenkins
9877bf47e3 Improve commenting for setupplan unittest. 2016-06-25 12:21:31 +02:00
Danielle Jenkins
7a3daac85b Add docs for setuponly and setupplan options. 2016-06-25 12:21:12 +02:00
Danielle Jenkins
da5c579d82 Move setupplan and setuponly options to their respective modules.
Also, changed their group from "general" to "debugconfig".
2016-06-25 12:20:56 +02:00
Danielle Jenkins
032ce8baf6 Switch setuponly and setupplan options to a hook-based implementation. 2016-06-25 12:19:46 +02:00
aostr
05b5554cac Renamed pytest pdb to debugging which conflicts with python pdb.
Combining multiple imports the "import pdb" imports the pytest module
as opposed to the python debugger.
2016-06-25 12:09:05 +02:00
Raphael Pierzina
5860c609ae Remove note on scoping 2016-06-25 11:09:46 +02:00
Raphael Pierzina
526c564576 Fix rst bullet point lists 2016-06-25 11:06:17 +02:00
Raphael Pierzina
693859210a Add yielded values to function example 2016-06-25 11:00:54 +02:00
Raphael Pierzina
4f8b8c8d31 Add alternative approach that uses wrappers 2016-06-25 10:55:08 +02:00
Raphael Pierzina
c6a711c2fc Add proposed solution using a module function 2016-06-25 10:50:14 +02:00
Raphael Pierzina
84f0dcecf8 Add issues section to proposal doc 2016-06-25 10:10:57 +02:00
Florian Bruhin
757f37f445 Don't ignore ImportError with setuptools plugins
This was added in b2d66b9e7b but is a bad
idea. When a plugin can't be imported, commandline options (optionally
set in pytest.ini) could be undefined, which means pytest bails out
much earlier before showing the warning, which is hard to debug.

Fixes #1479, also see #1307 and #1497
2016-06-25 09:56:22 +02:00
Daniel Hahler
939407ef63 Simplify Argument.__repr__
I have came across this when noticing that universal-ctags fails to parse
this correctly (https://github.com/universal-ctags/ctags/issues/997).
2016-06-25 09:31:31 +02:00
Omar Kohl
b3615ac29d Update CHANGELOG with #607 and add version 3.0.0.dev1 2016-06-24 20:56:21 +02:00
Omar Kohl
d81f23009b Raise CollectError if pytest.skip() is called during collection
pytest.skip() must not be used at module level because it can easily be
misunderstood and used as a decorator instead of pytest.mark.skip, causing the
whole module to be skipped instead of just the test being decorated.

This is unexpected for users used to the @unittest.skip decorator and therefore
it is best to bail out with a clean error when it happens.

The pytest equivalent of @unittest.skip is @pytest.mark.skip .

Adapt existing tests that were actually relying on this behaviour and add a
test that explicitly test that collection fails.

fix #607
2016-06-24 20:56:21 +02:00
Bruno Oliveira
0c63762d9c Merge pull request #1654 from tomviner/issue1503/remove_collapse_false
Fixes #1503 no longer collapse false explanations
2016-06-24 12:53:49 -03:00
Brianna Laugher
7612b650a0 update sprint page to be past tense 2016-06-24 17:24:06 +02:00
Brianna Laugher
62255d8000 rm global header 2016-06-24 17:12:41 +02:00
Brianna Laugher
ea5bda0898 remove links to funding campaign 2016-06-24 17:11:29 +02:00
Tom Viner
77689eb486 Fixes #1503 no longer collapse false explanations 2016-06-24 15:35:24 +02:00
Bruno Oliveira
3d263c64c3 Merge pull request #1626 from tomviner/issue1625/rename-getfuncargvalue
issue1625, rename getfuncargvalue to getfixturevalue
2016-06-24 08:58:32 -03:00
Raphael Pierzina
dc55551213 Start proposal for parametrize with fixtures 2016-06-24 12:08:57 +02:00
Tom Viner
df9918eda3 issue1625, name getfuncargvalue to getfixturevalue 2016-06-24 10:08:19 +02:00
Vasily Kuznetsov
c6af737d4e Fix fixture parameter display when ids is a function 2016-06-23 10:54:22 +02:00
Vasily Kuznetsov
1a5e530b98 Fix capturing with --setup-only/--setup-plan 2016-06-23 10:23:04 +02:00
Ronny Pfannschmidt
f2bb3df310 Merge pull request #1645 from userzimmermann/sprint/addoption-check
added check for already existing option names to OptionGroup.addoption()
2016-06-23 07:52:46 +02:00
Florian Bruhin
6359e75ff8 Trivial spelling fix in runtest_setup.py 2016-06-22 20:18:00 +02:00
Stefan Zimmermann
69bab4ab04 added check for already existing option names to OptionGroup.addoption() 2016-06-22 18:01:35 +02:00
Steffen Allner
ecc97aa3b9 Use correct links in changelog. 2016-06-22 17:52:13 +02:00
Steffen Allner
dd97a2e7c8 Merge from upstream 2016-06-22 17:51:48 +02:00
Steffen Allner
0dbf77e08e Add to changelog and authors. 2016-06-22 17:37:58 +02:00
Vasily Kuznetsov
f7d50dfa91 Add a test for handling of dynamic requests for fixtures from other fixtures 2016-06-22 17:24:55 +02:00
Ronny Pfannschmidt
7d60fcc098 Merge pull request #1643 from RonnyPfannschmidt/merge-master
Merge master
2016-06-22 17:05:59 +02:00
Vasily Kuznetsov
c8c32fd9c0 Merge with upstream 2016-06-22 17:00:35 +02:00
Vasily Kuznetsov
61992b4e22 Implement --setup-plan option 2016-06-22 16:45:36 +02:00
Vasily Kuznetsov
f59d8f7720 Fix the tests (#3)
* Fix the tests

* Fix .format string failures on python 2.6
2016-06-22 16:23:58 +02:00
Ronny Pfannschmidt
18ef7de96b merge from master again 2016-06-22 16:03:52 +02:00
Ronny Pfannschmidt
e96cd51a2a Merge remote-tracking branch 'upstream/features' into merge-master 2016-06-22 16:03:00 +02:00
Ronny Pfannschmidt
f7585c7549 Merge pull request #1635 from Avira/master
Add test for change in pull request #1631
2016-06-22 15:49:09 +02:00
Ronny Pfannschmidt
9cb851716d Merge pull request #1644 from KangarooCreativeTeam/patch-1
cache.rst: Fix wrong command used
2016-06-22 15:46:09 +02:00
Ronny Pfannschmidt
a2420ce051 Merge pull request #1640 from Avira/features
Add tests for entry points (#1629 )
2016-06-22 15:41:19 +02:00
HEAD KANGAROO
be1dabd6a9 cache.rst: Fix wrong command used
The "Inspecting Cache content" section was showing --cache-clear command,
  but should actually be using --cache-show command.

  Also; update AUTHORS
2016-06-22 14:04:02 +01:00
Vasily Kuznetsov
5e0d78f4f1 Fix .format string failures on python 2.6 2016-06-22 14:53:37 +02:00
Ronny Pfannschmidt
083f64100d merge master into features 2016-06-22 14:39:33 +02:00
Oliver Bestwalter
db79ed5c4d Add tests to make sure expected entry points exist (#1629) 2016-06-22 14:28:53 +02:00
Bruno Oliveira
fb79fa711b Merge branch 'issue1553/diff-final-newline' of https://github.com/tomviner/pytest 2016-06-22 14:21:30 +02:00
Vasily Kuznetsov
1a75139f72 Fix the tests 2016-06-22 13:25:46 +02:00
Steffen Allner
ee311e1eae Merge pull request #2 from kvas-it/features
Add printing of fixture dependencies
2016-06-22 13:04:23 +02:00
Vasily Kuznetsov
2c5c4f3f78 Add printing of fixture dependencies 2016-06-22 12:54:36 +02:00
Steffen Allner
2c6cfa42fa Disable capturing for setuponly output 2016-06-22 12:14:35 +02:00
Steffen Allner
de9ed5e3f4 Merge pull request #1 from kvas-it/features
Factor setuponly code out of runtestprotocol().
2016-06-22 12:04:45 +02:00
Steffen Allner
92bcc36266 Refactor logging of fixtures 2016-06-22 12:01:51 +02:00
Steffen Allner
7d19f83982 Add test for setuponly option 2016-06-22 12:00:45 +02:00
Oliver Bestwalter
7d87a1b127 Add test for failing assertion
Should contain function name that caused the failure (see pull request #1631).
2016-06-22 11:23:50 +02:00
Vasily Kuznetsov
6874c3a3cc Factor setuponly code out of runtestprotocol(). 2016-06-22 11:09:54 +02:00
Ronny Pfannschmidt
24179dc99f Merge pull request #1639 from tomviner/fix-changelog-links
fix changelog links
2016-06-22 10:49:35 +02:00
Tom Viner
ec25d398a5 fix changelog links 2016-06-22 10:06:14 +02:00
Tom Viner
98adf204b2 issue 1553: Include terminal newlines in diffs 2016-06-22 09:50:15 +02:00
Steffen Allner
499c9551c8 Implement working version of --setuponly 2016-06-22 09:42:45 +02:00
Bruno Oliveira
144dc12e55 Merge pull request #1638 from blueyed/fix-CONTRIBUTING
CONTRIBUTING.rst: spelling fixes
2016-06-22 04:28:15 -03:00
Daniel Hahler
09389d2b20 CONTRIBUTING.rst: spelling fixes
[ci skip]
2016-06-22 09:14:07 +02:00
Bruno Oliveira
c3ee1c17bc Merge pull request #1620 from tomviner/issue460/parameterized-subrequest
Issue 460: Fail on getfuncargvalue(<fixture with params>)
2016-06-21 13:43:06 -03:00
Bruno Oliveira
4350f499b2 Merge branch 'issue1629' of https://github.com/davehunt/pytest into features 2016-06-21 18:37:14 +02:00
Bruno Oliveira
61ede096a3 Merge pull request #1631 from Avira/master
help the user in the rare case this assertion actually fails
2016-06-21 13:17:52 -03:00
Bruno Oliveira
2f9d5c2586 Merge pull request #1623 from blueyed/ignore-hidden-files-in-test_pytest_collect_file
Ignore hidden files (.testmondata) in test_pytest_collect_file
2016-06-21 13:17:20 -03:00
Bruno Oliveira
f05d65dc42 Merge pull request #1622 from nicoddemus/issue-1619-conftest-assert-rewrite
Issue 1619 conftest assert rewrite
2016-06-21 13:15:52 -03:00
Bruno Oliveira
573866bfad Merge remote-tracking branch 'upstream/features' into issue-1619-conftest-assert-rewrite 2016-06-21 18:10:19 +02:00
Bruno Oliveira
2305d3271d Merge pull request #1628 from omarkohl/exit_on_collection_error
Exit pytest on collection error (without executing tests)
2016-06-21 13:03:21 -03:00
Bruno Oliveira
406355fd10 Merge pull request #1624 from blueyed/getexecutable-skip-non-zero-returncode
tests: getexecutable: call `--version` on all Pythons
2016-06-21 12:51:48 -03:00
Dave Hunt
393167b94f Update CHANGELOG and add Oliver Bestwalter to AUTHORS 2016-06-21 16:59:14 +02:00
Dave Hunt
ef9dd14963 Introduce pytest command as recommended entry point
Fixes #1629
2016-06-21 16:16:57 +02:00
Oliver Bestwalter
2b5c2f3ed5 help the user in the rare case this assertion actually fails 2016-06-21 14:57:48 +02:00
Omar Kohl
ede7478dcc Exit pytest on collection error (without executing tests)
Add --continue-on-collection-errors option to restore the previous behaviour:
Execute tests (that were successfully collected) even when collection errors
happen.

Some tests had to be modified e.g. because the return code changed to 2
(EXIT_INTERRUPTED) instead of 1 (EXIT_TESTSFAILED) because an Interrupted
exception is raised on collection error.

Implemented via pair programming with:
    Oleg Pidsadnyi <oleg.pidsadnyi@gmail.com>

closes #1421
2016-06-21 13:32:34 +02:00
Bruno Oliveira
819942e964 Return explicit None from rewrite hook's find_module 2016-06-21 12:28:36 +02:00
Bruno Oliveira
8b0fb47c79 Remove print left by accident 2016-06-21 12:23:12 +02:00
Tom Viner
5854a71ece Issue 460: getfuncargvalue fixture w. params err 2016-06-21 11:29:21 +02:00
Bruno Oliveira
5d8d1db4df Update repository transfer instructions 2016-06-21 09:40:22 +02:00
Floris Bruynooghe
9118c0222f Merge .set_config() into constructor 2016-06-21 09:28:10 +02:00
Daniel Hahler
e53e45b55c tests: getexecutable: call --version on all Pythons
This should prevent errors from pyenv:

    pyenv: python2.6: command not found

    The `python2.6' command exists in these Python versions:
      2.6.9

While the pyenv wrapper explicitly returns 127, I think it is better to
just check for non-zero?!
2016-06-21 08:36:02 +02:00
Bruno Oliveira
e0cb046885 Update AUTHORS and CHANGELOG 2016-06-20 23:16:06 +02:00
Bruno Oliveira
3a81d2e012 conftest files now use assertion rewriting
Fix #1619
2016-06-20 23:13:29 +02:00
Daniel Hahler
e9d7989140 Ignore hidden files (.testmondata) in test_pytest_collect_file
`test_pytest_collect_file` fails if you run the tests using `--testmon`,
because pytest-testmon will put its DB there as `.testmondata`.
2016-06-20 22:30:36 +02:00
Florian Bruhin
54872e94b4 Fix test name typo 2016-06-20 18:44:34 +02:00
Florian Bruhin
4f2db6c08d Merge pull request #1616 from palaviv/pytest.raises-message
Pytest.raises custom error message
2016-06-20 18:43:12 +02:00
Ronny Pfannschmidt
f3aead8e49 Merge pull request #1600 from nicoddemus/issue-1599-disable-cap-fixtures
Add disabled() method to capsys and capfd
2016-06-20 15:55:54 +02:00
palaviv
f8d4cadc3d Added versionchanged directives 2016-06-19 23:56:43 +03:00
palaviv
c29130d400 Updated documentation 2016-06-19 23:34:42 +03:00
palaviv
ca093673fb pytest.raises accept cutom message only when used as context manager 2016-06-19 21:24:47 +03:00
Bruno Oliveira
d81ee9acfb Merge pull request #1597 from taschini/pyargs-fix
Ensure that a module within a namespace package can be found by --pyargs
2016-06-19 14:20:52 -03:00
Bruno Oliveira
72bf11cbe9 Add disabled() method to capsys and capfd
Fix #1599
2016-06-19 19:14:36 +02:00
palaviv
e6ff01ada3 CR fixes 2016-06-16 21:09:15 +03:00
palaviv
8ddbca36c9 Add CHANGLOG entry 2016-06-16 20:21:03 +03:00
palaviv
d21886c005 pytest.raises accpets custom message 2016-06-16 20:15:32 +03:00
Bruno Oliveira
7f8e315285 Merge pull request #1615 from gnprice/deadcode
Cut a dead test helper function
2016-06-16 00:42:01 -03:00
Bruno Oliveira
c5424643f0 Merge pull request #1614 from gnprice/loop
Simplify default pytest_runtestloop
2016-06-16 00:29:03 -03:00
Greg Price
ab8b2e75a3 Simplify default pytest_runtestloop
The inner function and the explanatory comment it makes necessary can
all be removed if we switch to an if/else rather than try/except for
this condition.

Perhaps this bit comes from my fondness for C, but I think I would
find this style clearer and easier to understand even if it weren't
for the Python 2 quirk that makes the other style require us to add an
unnecessary-looking function abstraction.  In any case, given that the
alternative does require that abstraction this is definitely simpler.
2016-06-15 18:27:08 -07:00
Greg Price
2a3cbdf4d1 Cut a dead test helper function
This appears to have been unused since commit
320835d "split out pytest-xdist related reporting to the plugin"
in July 2010.  It's the only caller outside of _pytest/runner.py
of the `call_and_report` helper function there, so cutting it out
makes that more of a pure helper function and makes it slightly
easier to understand the code in _pytest/runner.py .
2016-06-15 18:26:10 -07:00
Bruno Oliveira
308396ae3c Merge pull request #1606 from hackebrot/show-fixtures-per-test
Show fixtures per test
2016-06-14 09:54:18 -03:00
Ronny Pfannschmidt
feeee2803e Merge pull request #1586 from nicoddemus/issue-1461-merge-yield-fixture
Make normal fixtures work with "yield"
2016-06-14 10:31:09 +02:00
taschini
1218392413 Added taschini to AUTHORS and #1597 to CHANGELOG.rst. 2016-06-14 07:16:20 +02:00
taschini
4d9e293b4d Incorporated feedback (#1597).
Fixed problem caused in a test on Windows by file left open by PyPy and not immediately garbage collected.
2016-06-14 07:12:40 +02:00
taschini
e2e6e31711 Ensure that a module within a namespace package can be found by --pyargs. 2016-06-14 07:12:40 +02:00
Raphael Pierzina
adc50ac72f Change format for test function locations 2016-06-12 15:58:32 +01:00
Ronny Pfannschmidt
66e66f61e8 Merge pull request #1605 from guyzmo/issue/1604
Fixed issue shadowing error when missing argument on teardown_method
2016-06-12 12:05:37 +02:00
Guyzmo
accd962c9f Fixed issue shadowing error when missing argument on teardown_method
When the method argument is missing on teardown_method, the traceback is
100% internal to pytest, which with default options get pruned. Then
that traceback is empty, leading to a new exception as a traceback shall
not be empty.

This PR fixes that issue by pushing back the last stack on the
traceback, when the stacktrace is empty after pruning. Then the output
is still pruned, but gives meaningful information with the item where it
failed on the stack.

* fixes issue #1604

Signed-off-by: Guyzmo <guyzmo+github@m0g.net>
2016-06-12 03:45:24 +02:00
Raphael Pierzina
b99aace8a9 Fix py26 by using indices in str.format 2016-06-12 00:52:03 +01:00
Raphael Pierzina
bbc6c18448 Add changelog entry for new cli flag 2016-06-12 00:26:32 +01:00
Raphael Pierzina
7eea168106 Implement show_fixtures_per_test and add cli flag 2016-06-12 00:20:06 +01:00
Raphael Pierzina
b47f155d74 Implement tests for --fixtures-per-test 2016-06-12 00:17:50 +01:00
Ronny Pfannschmidt
577cce2554 Merge pull request #1593 from marscher/fix_cwd_explosion
Fix cwd explosion
2016-06-10 07:24:18 +02:00
Bruno Oliveira
bdc29968b8 Remove dead code and simplify code in call_fixture_func 2016-06-08 21:07:56 -03:00
Bruno Oliveira
ed69424917 Remove most content from yieldfixture as it is now deprecated 2016-06-08 21:07:55 -03:00
Bruno Oliveira
371fbe4388 Update CHANGELOG (yield statements in normal fixtures) 2016-06-08 21:07:53 -03:00
Bruno Oliveira
fe4f23c1bf Update docs in prol of using yield statements 2016-06-08 21:07:52 -03:00
Bruno Oliveira
98acda426f Remove yieldctx variable being passed around 2016-06-08 21:07:50 -03:00
Bruno Oliveira
d712428d33 Fix custom name for yield_fixtures 2016-06-08 21:07:49 -03:00
Bruno Oliveira
366879db27 Make normal fixtures work with "yield" 2016-06-08 21:07:47 -03:00
Bruno Oliveira
92323895c9 Use same python which invoked tox for "doctesting" env
This will work for Travis and AppVeyor because both start tox using Python 3
2016-06-08 21:06:19 -03:00
marscher
09d163aa3a [exception handling] Fix case the current working directory (CWD) gets deleted during testing.
Fixes #1235.
2016-06-08 15:18:23 +02:00
Ronny Pfannschmidt
70fdab4cfa Merge pull request #1584 from mgedmin/patch-1
Docs: config.option is deprecated
2016-06-02 09:27:32 +02:00
Marius Gedminas
3ad5b9de86 Docs: config.option is deprecated
https://pytest.org/latest/writing_plugins.html#_pytest.config.Config says config.option is deprecated and one should use config.getoption() instead.
2016-06-02 09:52:56 +03:00
Ronny Pfannschmidt
9b6dc93496 Merge pull request #1583 from nicoddemus/fix-tox-doctesting
Use same python which invoked tox for "doctesting" env
2016-06-02 08:10:33 +02:00
Bruno Oliveira
2c4b76b754 Use same python which invoked tox for "doctesting" env
This will work for Travis and AppVeyor because both start tox using Python 3
2016-06-01 20:30:26 -03:00
Bruno Oliveira
63ced4d486 List contributors alphabetically in AUTHORS 2016-06-01 20:21:51 -03:00
Bruno Oliveira
97c89e6dc3 Merge branch 'fix-all-skipped-but-none-exist' 2016-06-01 20:20:50 -03:00
Bruno Oliveira
dd9c81ca26 Mention #1580 in the CHANGELOG and add Thomas Grainger to AUTHORS 2016-06-01 19:56:45 -03:00
Thomas Grainger
74862b8f2f Don't mark empty doctest files as skipped, fixes #1578 2016-06-01 19:19:14 +01:00
Florian Bruhin
057007fb52 Merge pull request #1582 from graingert/patch-1
Fix rst-lint CHANGELOG error
2016-06-01 19:25:18 +02:00
Thomas Grainger
2898dffb9e Fix rst-lint CHANGELOG error 2016-06-01 17:43:41 +01:00
Ronny Pfannschmidt
7305adfdba fix another copy&paste error in the announcement list 2016-06-01 08:55:44 +02:00
Ronny Pfannschmidt
fad6266a47 finish up the version bump in __init__ i forgot 2016-06-01 08:18:10 +02:00
Ronny Pfannschmidt
b5bd4d959d merge master to features 2016-06-01 08:13:26 +02:00
Ronny Pfannschmidt
f423f08b45 prepare cangelog for the next bugfix release 2016-06-01 08:04:21 +02:00
Ronny Pfannschmidt
5c8b0fb523 fix minor typos 2016-06-01 08:03:22 +02:00
Ronny Pfannschmidt
978bb190a1 add release announcement 2016-05-31 19:06:29 +02:00
Ronny Pfannschmidt
a2a904466c alter changelog header 2016-05-31 19:05:36 +02:00
Ronny Pfannschmidt
77c28825df regendoc and comment out a python2.7 example as per #1573 2016-05-31 19:05:36 +02:00
Ronny Pfannschmidt
d3dcc2b8f1 bump regendoc python version to 3.5 2016-05-31 19:05:36 +02:00
Ronny Pfannschmidt
85541113eb update version 2016-05-31 19:05:36 +02:00
Ronny Pfannschmidt
7ef06822cb ignore hypothesis temp folder 2016-05-31 19:05:36 +02:00
Ronny Pfannschmidt
289e0091de Merge pull request #1577 from pytest-dev/docs-hookwrapper-experimental
Remove "experimental" status from hookwrapper
2016-05-31 17:56:08 +02:00
Bruno Oliveira
28efdebfcd Remove "experimental" status from hookwrapper 2016-05-31 12:17:55 -03:00
Ronny Pfannschmidt
fb2e7cc727 Merge pull request #1575 from hackebrot/fix-showfixtures-for-multiple-fixturedefs
Fix showfixtures for multiple fixturedefs
2016-05-31 13:53:13 +02:00
Raphael Pierzina
fb8ad714b1 Implement a test for showfixtures to show fixtures with same name 2016-05-31 11:47:16 +01:00
Raphael Pierzina
945072b89a Update changelog with --fixtures fix 2016-05-31 11:47:16 +01:00
Raphael Pierzina
0d80a9c729 Change _showfixtures_main to loop over fixturedefs 2016-05-31 11:47:16 +01:00
Ronny Pfannschmidt
357a7c79ef Merge branch 'tomviner-issue-1496-xfail-cond-kw'
fixes  #1496
2016-05-31 11:25:15 +02:00
Ronny Pfannschmidt
158f3cfaea merge master 2016-05-31 11:24:53 +02:00
Bruno Oliveira
82c74fe7e6 Merge pull request #1530 from RonnyPfannschmidt/fix-510
fix #510 by adding a describing skip marker on empty parameterize
2016-05-30 10:54:33 -03:00
Ronny Pfannschmidt
afc5c7e4f6 better message for empty argument skip
include the argument names to help determining the fixture/parametrization
2016-05-30 14:42:55 +02:00
Ronny Pfannschmidt
03eb9203fd remove the old empty argument triggers
unlike the marker based one its not composable
2016-05-30 14:41:00 +02:00
Ronny Pfannschmidt
ae4dff0e0a add missing link to changelog 2016-05-30 14:34:38 +02:00
Ronny Pfannschmidt
d217b52508 fix #510 by adding a describing skip marker on empty parameterize 2016-05-30 14:34:38 +02:00
Bruno Oliveira
8c1be624a6 Merge pull request #1554 from RonnyPfannschmidt/merge-master
Merge master into features
2016-05-24 11:09:33 -03:00
Ronny Pfannschmidt
16794feaf6 Merge branch 'master' into merge-master 2016-05-24 08:15:10 +02:00
Ronny Pfannschmidt
436e13ac25 Merge pull request #1566 from nicoddemus/fix-win32-path
Fix shell argument split in win32
2016-05-24 08:14:01 +02:00
Bruno Oliveira
9fb5ddf778 Fix shell argument split in win32
This fixes the bug inserted by accident in #1523
2016-05-23 20:41:47 -03:00
Bruno Oliveira
5ab5a11544 Merge pull request #1565 from tomviner/issue1544/ignore-build-dist-dirs
Issue 1544: norecursedirs build & dist dirs
2016-05-23 19:30:20 -03:00
Ronny Pfannschmidt
26b526967e merge from master again 2016-05-23 22:33:00 +02:00
TomV
d6dfb1a393 issue 1544: norecursedirs build & dist dirs 2016-05-23 21:02:29 +01:00
Ronny Pfannschmidt
85393d34b6 Merge pull request #1561 from AbdealiJK/ajk/1558_feature
unittest.UnitTestCase: Allow __test__ for methods
2016-05-19 10:17:28 +02:00
Ronny Pfannschmidt
be7a86270c Merge pull request #1541 from RonnyPfannschmidt/document-1540
Document #1540
2016-05-19 10:15:33 +02:00
AbdealiJK
d4c9fa9f1a unittest.UnitTestCase: Allow __test__ for methods
__test__ needs to be checked for methods of a class too. Earlier,
this was not done, and all methods in a class was assumed to be
a test. This commit adds the appropriate condition to ensure that
if the __test__ is set to False, it does not collect that method.

Fixes https://github.com/pytest-dev/pytest/issues/1558
2016-05-19 08:19:57 +05:30
Ronny Pfannschmidt
ec5e05834f fix typo 2016-05-18 21:35:31 +02:00
Ronny Pfannschmidt
6f98cd6faa Merge pull request #1560 from nicoddemus/fix-py26-tox-comment
Move comment in tox.ini due to recent bug in pip
2016-05-18 21:31:56 +02:00
Bruno Oliveira
561a5fb558 Move comment in tox.ini due to recent bug in pip
As discussed in #1554

* https://bitbucket.org/hpk42/tox/issues/332/
* https://github.com/pypa/pip/issues/3667
2016-05-18 16:15:29 -03:00
Bruno Oliveira
47739291cf Merge pull request #1557 from adamchainz/readthedocs.io
Convert readthedocs link for their .org -> .io migration for hosted projects
2016-05-18 13:24:03 -03:00
Adam Chainz
8a39869347 Convert readthedocs link for their .org -> .io migration for hosted projects
As per their email ‘Changes to project subdomains’:

> Starting today, Read the Docs will start hosting projects from subdomains on the domain readthedocs.io, instead of on readthedocs.org. This change addresses some security concerns around site cookies while hosting user generated data on the same domain as our dashboard.

Test Plan: Manually visited all the links I’ve modified. One was not modified - `http://media.readthedocs.org/epub/pytest/latest/pytest.epub` - since it doesn't work on `readthedocs.io`.
2016-05-18 17:12:39 +01:00
Ronny Pfannschmidt
eab762ea99 Merge branch 'master' into merge-master 2016-05-13 19:37:41 +02:00
Ronny Pfannschmidt
c49863aa63 merge next chunk from master and fix changelog linting issue 2016-05-13 19:36:47 +02:00
Ronny Pfannschmidt
01d2ff804b Merge commit '56156bb119194014129ac08c4a2c370f0b893104' into merge-master 2016-05-13 17:55:02 +02:00
Ronny Pfannschmidt
68f658b6cc Merge commit '890c2fa555314a67a8d97a1b8ea4881a14be69c4' into merge-master 2016-05-13 17:52:59 +02:00
Ronny Pfannschmidt
4bde70d060 Merge commit 'ec62a3c9e47c3b5b07aa1656815145ffa2882a09' into merge-master 2016-05-13 17:40:14 +02:00
Ronny Pfannschmidt
8a94c66e68 Merge pull request #1551 from fushi/master
Fix #1549 - count skips in junit-xml
2016-05-05 22:03:30 +02:00
John Towler
0d07b64571 Fixes Issue 1549 2016-05-05 11:29:05 -07:00
Ronny Pfannschmidt
98dd2ce75c document reason for #1540 2016-04-28 16:22:31 +02:00
Ronny Pfannschmidt
308e76e19c add xfailing test for #1540 2016-04-28 16:11:30 +02:00
Florian Bruhin
60212e8831 Merge pull request #1537 from ben4ever/patch-1
Fix typo in doc
2016-04-27 22:13:51 -07:00
Benjamin Dopplinger
75abfbe8d4 Fix typo in doc 2016-04-28 14:40:17 +10:00
Ronny Pfannschmidt
6cc56b4a1b Merge pull request #1535 from palaviv/parametrize-test-ids-hook
introduce pytest_make_parametrize_id hook
2016-04-27 16:16:16 +02:00
palaviv
9733127951 pytest_make_parametrize_id receive config object 2016-04-26 10:23:57 +03:00
Bruno Oliveira
fdee88f086 Merge pull request #1520 from omarkohl/invalid_test_module_name
Raise CollectError if import test module fails
2016-04-25 21:42:41 -03:00
palaviv
53429ed8b8 Added hook to plugin docs and new CHANGELOG record 2016-04-25 18:03:34 +03:00
palaviv
b9faf78d51 Added test_pytest_make_parametrize_id 2016-04-25 17:48:28 +03:00
palaviv
79927428d1 Added pytest_make_parametrize_id hook 2016-04-25 17:11:47 +03:00
Omar Kohl
56855893ca Raise CollectError if import test module fails
One of the reasons for failing to import the test module is invalid Python
identifiers in the full package path of the test module.

fix #1426
2016-04-23 13:50:48 +02:00
Ronny Pfannschmidt
52babba33e Update sprint 2016 2016-04-23 08:56:28 +02:00
Bruno Oliveira
8552860a9b Merge pull request #1532 from RibeiroAna/features
Update sprint2016.rst :)
2016-04-23 01:05:25 -03:00
Ana Ribeiro
f02f72e651 Update sprint2016.rst 2016-04-23 00:11:44 -03:00
Ronny Pfannschmidt
6d661ace0a Merge pull request #1523 from MengJueM/master 2016-04-20 16:55:31 +02:00
MengJueM
f51c34ef31 Add changelog 2016-04-20 22:32:35 +08:00
Ronny Pfannschmidt
b220c96bf8 Merge pull request #1526 from The-Compiler/tracebackhide
Filter selectively with __tracebackhide__
2016-04-20 13:07:02 +02:00
Florian Bruhin
aa87395c39 Use py.builtin.callable
This restores compatibility with Python 3.0/3.1
2016-04-20 11:18:47 +02:00
Florian Bruhin
75160547f2 Use a callable __tracebackhide__ for filtering
While this leads to slightly more complicated user code for the common
case (checking if the exception is of a given type) it's easier to
implement and more flexible.
2016-04-20 11:09:27 +02:00
Florian Bruhin
4c552d4ef7 Fix tests for python 2.6 2016-04-20 10:36:13 +02:00
Florian Bruhin
b607f6728f Filter selectively with __tracebackhide__
When __tracebackhide__ gets set to an exception type or list/tuple of
exception types, only those exceptions get filtered, while the full
traceback is shown if another exception (e.g. a bug in a assertion
helper) happens.
2016-04-20 10:25:33 +02:00
Ronny Pfannschmidt
a986b8f945 Merge pull request #1525 from The-Compiler/traceback-item
Fix TracebackItem documentation in pytest.code
2016-04-20 09:38:03 +02:00
Florian Bruhin
c24e8e01b4 Fix TracebackItem documentation in pytest.code
The TracebackItem class does not exist, but it seems the docstrings refer to
TracebackEntry.
2016-04-20 08:35:17 +02:00
MengJueM
1a37035d71 Add test test_absolute_win32_path 2016-04-20 01:27:37 +08:00
TomV
99c4b6fdc3 issue 1496 - xfail with condition keyword 2016-04-19 18:12:37 +01:00
Meng Jue
dd2425675b Fix a small issue about shlex.split not working well with win32 path 2016-04-19 13:08:08 +08:00
Bruno Oliveira
6a3c943ce2 Merge pull request #1514 from The-Compiler/docfix
doc: Use ascii chars for file tree
2016-04-13 19:13:53 -03:00
Florian Bruhin
98430a17f2 doc: Use ascii chars for file tree
LaTeX doesn't like those particular unicode chars, so let's avoid them so the
PDF builds easily.
2016-04-13 23:38:58 +02:00
Bruno Oliveira
fe6e1b2059 Merge pull request #1506 from prusse-martin/fix-repr-tb-with-unicode
Fix `repr_traceback` to work with unicode errors
2016-04-09 13:03:55 -03:00
Martin Prusse
7ce5873da2 Perform a "unicode aware" check for maximum recursion depth error
Avoid errors `UnicodeErrosr`s due non maximum recursion depth errors
when checking for those errors.
2016-04-08 23:32:18 -03:00
Ronny Pfannschmidt
0eb80bcb5a Merge pull request #1504 from guettli/patch-1
Hudson -> Jenkins
2016-04-05 14:57:03 +02:00
Thomas Güttler
fb45f82840 Hudson -> Jenkins 2016-04-05 14:08:30 +02:00
Ronny Pfannschmidt
0f7aeafe7c Merge pull request #1486 from roolebo/fix-issue-138
Fix issue #138 - support chained exceptions
2016-04-03 19:21:57 +02:00
Bruno Oliveira
e3bc6faa2b Merge pull request #1470 from ceridwen/features
Escape both bytes and unicode strings for "ids" in Metafunc.parametrize
2016-04-03 13:48:30 -03:00
Bruno Oliveira
909d72b474 Merge pull request #1502 from omarkohl/excinfo_match
Implement ExceptionInfo.match() method
2016-04-03 13:48:12 -03:00
Omar Kohl
0c38aacc33 Add self to AUTHORS 2016-04-03 11:23:30 +02:00
Omar Kohl
c578226d43 Implement ExceptionInfo.match() to match regexp on str(exception)
This implements similar functionality to
unittest.TestCase.assertRegexpMatches()

closes #372
2016-04-03 11:22:44 +02:00
Ceridwen
23a8e2b469 Add .hypothesis to .gitignore and try an older version of Hypothesis for 2.6 2016-04-02 11:47:37 -04:00
Ceridwen
08671fcf4a Fix the changelog and dependencies for tox 2016-04-02 10:52:28 -04:00
Ceridwen
491b30c5d9 Add Hypothesis test for _idval and fix bug it found 2016-04-01 22:45:44 -04:00
Ceridwen
b631fc0bc1 Fix test_escaped_parametrized_names_xml 2016-04-01 21:30:45 -04:00
Bruno Oliveira
5af5ba11d3 Merge pull request #1500 from tgoodlet/lstrip-keywordexpr
Always lstrip() keyword expression
2016-04-01 17:48:58 -03:00
Tyler Goodlet
053c052190 Always lstrip() keyword expression 2016-04-01 13:10:05 -04:00
Ceridwen
9b438d56e8 Fix a test_unicode_idval_python2 (now test_unicode_idval) and associated string handling on Python 3 2016-04-01 12:27:17 -04:00
Bruno Oliveira
56156bb119 Merge pull request #1490 from omarkohl/get_issues
Adapt get_issues.py script for GitHub (instead of Bitbucket)
2016-03-28 20:15:29 -03:00
Omar Kohl
e048315d9b Adapt get_issues.py script for GitHub (instead of Bitbucket) 2016-03-28 20:00:57 +02:00
Roman Bolshakov
89df701ae9 Comment a workaround for #1485. 2016-03-27 13:15:56 +03:00
Bruno Oliveira
fed89ef549 Merge pull request #1474 from palaviv/improve-idmaker-duplicate-names
Improve idmaker name selection in case of duplicate ids in parametrize
2016-03-23 20:38:29 -03:00
palaviv
3ffce6ae4a Added thanks and PR links in changlog 2016-03-23 18:53:50 +02:00
palaviv
c66aedfa65 checking first there are duplciates ids before changing to unique names 2016-03-23 18:47:27 +02:00
Florian Bruhin
3155d0ca9c Merge pull request #1475 from nicoddemus/classifier-docs
Mention Pytest::Framework PyPI classifier on docs
2016-03-23 06:12:34 +01:00
Bruno Oliveira
53d319144d Mention Pytest::Framework PyPI classifier on docs 2016-03-22 20:42:52 -03:00
palaviv
412042d987 added entry to CHANGELOG 2016-03-23 00:21:24 +02:00
palaviv
b8c15a0215 improved idmaker name selection in case of duplicate ids 2016-03-23 00:20:58 +02:00
Ceridwen
1f46015de5 Merge remote-tracking branch 'upstream/features' into features 2016-03-22 16:22:00 -04:00
Ronny Pfannschmidt
6ddfd60ce7 Merge pull request #1472 from pquentin/master
Fix pytest.mark.skip mark when used in strict mode
2016-03-22 13:49:57 +01:00
Quentin Pradet
653a53226a Add strict parameter to xfail marker doc 2016-03-22 16:01:51 +04:00
Quentin Pradet
1fbd19b8cb Fix pytest.mark.skip mark when used in strict mode 2016-03-22 15:40:34 +04:00
Ceridwen
4405dd0ffe Escape both bytes and unicode strings for "ids" in Metafunc.parametrize 2016-03-22 01:31:48 -04:00
Ronny Pfannschmidt
890c2fa555 Merge pull request #1469 from omarkohl/doc-fixes
Doc fixes
2016-03-20 20:48:36 +01:00
Omar Kohl
725290a8ab Add 'Minor doc fixes' to CHANGELOG 2016-03-20 20:39:31 +01:00
Ronny Pfannschmidt
da1045151f Merge pull request #1468 from palaviv/allow-none-as-parametrized-test-id
None in paramtrized test id will use automatically generated id
2016-03-20 20:19:50 +01:00
Omar Kohl
98c707561c Document --full-trace option and KeyboardInterrupt
fix #513
2016-03-20 20:07:16 +01:00
Omar Kohl
a341dddc74 Replace --fulltrace with --full-trace in doc
Since 'py.test --help' shows --full-trace as an option (and not --fulltrace)
even though both forms are accepted I assume --full-trace is the preferred form
and should therefore be used in the documentation.
2016-03-20 19:51:23 +01:00
palaviv
dd384f7f78 Added changlog entries 2016-03-20 19:01:47 +02:00
palaviv
7885e43b78 Merge remote-tracking branch 'upstream/features' into allow-none-as-parametrized-test-id 2016-03-20 18:57:17 +02:00
palaviv
32f44ce2fd updated parametrize documentation 2016-03-20 18:54:48 +02:00
Omar Kohl
16e49d96d1 Replace --traceconfig with --trace-config in doc
Since 'py.test --help' shows --trace-config as an option (and not
--traceconfig) even though both forms are accepted I assume --trace-config is
the preferred form and should therefore be used in the documentation.
2016-03-20 17:12:50 +01:00
Omar Kohl
ec62a3c9e4 Fix minor typo in 'writing plugins' doc 2016-03-20 17:01:04 +01:00
Omar Kohl
f70ed83479 Fix 'test grouping by fixture instances' doc example
The fin() function was never added as a finalizer and did therefore not print
anything in the captured output.

In general improve the output by making it more verbose/explicit and extend the
final explanation.
2016-03-20 15:53:32 +01:00
Omar Kohl
dff914cadd Add short explanation and link to yield fixtures in fixture doc 2016-03-20 14:10:20 +01:00
Omar Kohl
3135463573 No longer refer to the 'yield fixture mechanism' as experimental (doc)
The feature has been there for a long time and in the 2.7.1 release notes it
says:

> fixed docs to remove the notion that yield-fixtures are experimental.

Therefore this one place was probably just missed.
2016-03-20 13:54:56 +01:00
Omar Kohl
266b53dfc2 Add Jenkins xUnit Plugin link to doc 2016-03-20 13:39:10 +01:00
Omar Kohl
bdb3581a52 Fix minor mistake in test discovery doc
The example output shown was for Python3 not Python2.

Add Python2 output and rephrase for clarity.
2016-03-20 13:24:43 +01:00
Omar Kohl
27b62740e3 Fix minor mistake in usage doc (pkg instead of pypkg) 2016-03-20 12:47:17 +01:00
Roman Bolshakov
52bc2f8616 Update authors & changelog 2016-03-20 01:06:53 +03:00
Roman Bolshakov
a736e26734 Merge remote-tracking branch 'pytest-dev/master' into fix-issue-138 2016-03-20 01:04:22 +03:00
Roman Bolshakov
fbc5ba08d9 Fix issue #138 2016-03-20 01:02:17 +03:00
palaviv
4b0237c8ee added test for unique names when recievieng identical ids in parametrize 2016-03-19 21:42:47 +02:00
palaviv
877ca5a0bf added test for None in paramtrized ids list 2016-03-19 21:38:24 +02:00
palaviv
a8cfd54871 added test for None in idmaker 2016-03-19 21:23:49 +02:00
palaviv
be1954afbc allow None to be passed in ids list for specific test and recieve idmaker name for test 2016-03-19 21:22:49 +02:00
Bruno Oliveira
2cfcf12d09 Merge pull request #1466 from nicoddemus/merge-master-into-features
Merge master into features after 2.9.1 release
2016-03-18 23:45:14 -03:00
Bruno Oliveira
5fcce8a7d6 Merge branch 'master' into merge-master-into-features 2016-03-18 18:26:56 -03:00
Bruno Oliveira
a70e92777f Bump version to 2.9.2.dev1 2016-03-18 18:24:24 -03:00
Bruno Oliveira
3c011c05db Merge pull request #1460 from nicoddemus/release-2.9.1
Release 2.9.1
2016-03-18 18:22:21 -03:00
nicoddemus
168daaa71f Regendocs 2016-03-17 17:13:41 -04:00
nicoddemus
43fc1b47c0 Add announce doc 2016-03-17 17:10:02 -04:00
nicoddemus
699f094b0c Finalize CHANGELOG for 2.9.1 2016-03-17 17:04:26 -04:00
nicoddemus
9bdf51fcc5 Bump version to 2.9.1 2016-03-17 17:03:46 -04:00
Ronny Pfannschmidt
1d23999033 Merge pull request #1458 from The-Compiler/talks
Update talks page
2016-03-17 09:09:35 +01:00
Florian Bruhin
1d35a03812 Update talks page 2016-03-17 06:37:49 +01:00
Bruno Oliveira
ceacc12b52 Merge pull request #1454 from tareqalayan/add-global-properties-node
junitxml: add properties node in testsuite level
2016-03-16 18:21:51 -03:00
Tareq Alayan
fa6acdcfd4 junitxml: add properties node in testsuite level
The commit allow users to add a properties node in testsuite level see
example below:

<testsuite errors="0" failures="0" name="pytest" skips="1" tests="1"
time="11.824">
  <properties>
    <property name="ARCH" value="PPC"/>
    <property name="OS" value="RHEL 7.2"/>
    <property name="TestPlanURL" value="https://url.."/>
    <property name="Automated" value="True"/>
  </properties>
  <testcase classname="git.....>
  </testcase>
</testsuite>

The current situation is that properties node can be added to every
testcase node. However, sometimes we need some global properties that
applies to all testcases and give better description for the testsuite
itself.
2016-03-16 13:24:33 +02:00
Ronny Pfannschmidt
d70596da91 Merge pull request #1452 from palaviv/specify-paramterized-node-in-command-line
Specify paramterized node in command line - fixes #649
2016-03-16 11:16:54 +01:00
palaviv
a1277aaf0e added TODO comment to remove the parametrized workaround 2016-03-15 23:42:25 +02:00
Ronny Pfannschmidt
5fd82078ad Merge pull request #1441 from kalekundert/features
Add a convenience function for floating-point comparisons
2016-03-15 07:45:07 +01:00
Kale Kundert
5ceee08590 Fix CHANGELOG merge conflicts. 2016-03-14 11:55:50 -07:00
Kale Kundert
0dcc862a56 Fix some typos in the documentation. 2016-03-14 11:38:00 -07:00
Kale Kundert
9e7206a1cf Fix a few stylistic issues. 2016-03-14 11:29:45 -07:00
Ronny Pfannschmidt
19cec79363 Merge pull request #1451 from blueyed/fix-doc-pytest_load_initial_conftests
docs: sort arguments for pytest_load_initial_conftests
the change is purely cosmetic, but its nice to have them ordered by name
2016-03-14 14:14:22 +01:00
Ronny Pfannschmidt
0cacdef6c5 Merge pull request #1444 from novas0x2a/fixture-custom-name
Allow custom fixture names for fixtures
2016-03-14 14:08:02 +01:00
palaviv
981fcb2798 added CHANGELOG and AUTHORS entry 2016-03-13 23:47:41 +02:00
palaviv
1ee3d40dbe allow parametrized nodes to be specified from command line 2016-03-13 23:37:21 +02:00
Kale Kundert
861265411f Add "thanks" line to the CHANGELOG. 2016-03-12 22:15:19 -08:00
Daniel Hahler
bdddc9c38b doc: fix argument order for pytest_load_initial_conftests 2016-03-12 17:35:27 +01:00
Kale Kundert
916c0a8b36 Fix Decimal() and __ne__() errors. 2016-03-11 16:29:18 -08:00
Kale Kundert
078448008c Discuss alternative float comparison algorithms. 2016-03-11 15:59:48 -08:00
Bruno Oliveira
b46c7dddaa Fix 'check' in markdown for github templates 2016-03-11 18:22:15 -03:00
Kale Kundert
42a7e0488d Properly handle inf, nan, and built-in numeric types.
This commit also:

- Dramatically increases the number of unit tests , mostly by borrowing
  from the standard  library's unit tests for math.isclose().

- Refactors approx() into two classes, one of which handles comparing
  individual numbers (ApproxNonIterable) and another which uses the
  first to compare individual numbers or sequences of numbers.
2016-03-11 08:49:26 -08:00
Ronny Pfannschmidt
4636bf6160 Merge pull request #1391 from malinoff/issue1342
LastFailed now creates .cache only when needed. Fixes #1342
2016-03-11 11:18:50 +01:00
Dmitry Malinovsky
52a5acda92 Use testscollected to make xdist happy 2016-03-11 14:48:17 +06:00
Florian Bruhin
2b0ad4630d Merge pull request #1443 from nicoddemus/improve-import-error-msg
Improve error message when a plugin fails to import
2016-03-11 06:10:58 +01:00
Bruno Oliveira
3ea987ef9d Copy over name and path attributes to the re-raised exception 2016-03-10 18:13:59 -03:00
Mike Lundy
9577120592 Allow custom fixture names for fixtures
When defining a fixture in the same module as where it is used, the
function argument shadows the fixture name, which a) annoys pylint and
b) can lead to bugs where you forget to request a fixture into a test
method.

This allows one to define fixtures with a different name than the name
of the function, bypassing that problem.
2016-03-09 14:58:54 -08:00
Bruno Oliveira
7a186df271 Improve error message when a plugin fails to import 2016-03-08 19:18:13 -03:00
Kale Kundert
7d155bd3cf Fix sys.version_info errors. 2016-03-08 10:12:31 -08:00
Kale Kundert
6a902924f8 Fix trailing whitespace errors. 2016-03-07 19:56:23 -08:00
Kale Kundert
c9c73b8d8e Fix zero-length field name error in python2.6 2016-03-07 19:54:43 -08:00
Kale Kundert
4d0f066db7 Add approx() to the CHANGELOG. 2016-03-07 18:29:22 -08:00
Kale Kundert
5dab0954a0 Add approx() to the Sphinx docs. 2016-03-07 18:14:49 -08:00
Kale Kundert
b8a8382c2c Reduce the default absolute error threshold to 1e-12. 2016-03-07 16:43:53 -08:00
Kale Kundert
bf97d5b817 Use the plus/minus unicode symbol in the repr string.
This was a challenge because it had to work in python2 and python3,
which have almost opposite unicode models, and I couldn't use the six
library.  I'm also not sure the solution I found would work in python3
before python3.3, because I use the u'' string prefix which I think was
initially not part of python3.
2016-03-07 16:40:41 -08:00
Kale Kundert
dd28e28b34 Make a few stylistic improvements. 2016-03-07 10:10:54 -08:00
Kale Kundert
6f5e1e386a Add a convenient and correct way to compare floats. 2016-03-07 10:10:54 -08:00
Ronny Pfannschmidt
6d4b14d7ee Merge pull request #1438 from Bachmann1234/issue-1437
Make a good faith effort to display a bytestring when one is provided…
2016-03-06 06:48:32 +01:00
Ronny Pfannschmidt
fd0010e6e9 Merge pull request #1439 from pytest-dev/fix-1178
Support pytest.fail with non-ascii characters

Fixes #1178
2016-03-06 06:42:35 +01:00
Matt Bachmann
8ce32b0795 When a regex pattern contains bytes instead of a string use escape_encode to turn it into a string before further processing. Thanks @nicoddemus for the review and tips! 2016-03-05 21:04:34 -05:00
Bruno Oliveira
5d4703852e Add test when using native strings with non-ascii chars 2016-03-05 19:34:15 -03:00
Ronny Pfannschmidt
9b51536a18 Merge pull request #1440 from nicoddemus/issue-578
Fix decoding issue while formatting SyntaxErrors during collection
2016-03-05 21:47:44 +01:00
Bruno Oliveira
d8403d793f Fix decoding issue while formatting SyntaxErrors during collection
This happens only in Python 2, as in Python 3 we receive
the "badline" in the exception is already properly encoded

Fix #578
2016-03-05 16:58:50 -03:00
Bruno Oliveira
24d3e01548 pytest.fail with non-ascii characters raises an internal pytest error
Fix #1178
2016-03-05 16:09:01 -03:00
Ronny Pfannschmidt
3884398055 Merge pull request #1433 from nicoddemus/issue-1427
Mention that pytest_addoption may be used from conftest files

fixes #1427
2016-03-04 10:48:43 +01:00
Bruno Oliveira
819e0ead44 Mention that pytest_addoption may be used from conftest files
fix #1427
2016-03-03 18:26:01 -03:00
Bruno Oliveira
c8ca1d12d7 Merge pull request #1432 from tomviner/issue469-junit-double-colon
issue469: junit parsing nodeid, add method test
2016-03-03 08:03:27 -03:00
TomV
3d2b7aeea5 issue469: junit parsing nodeid, add method test 2016-03-03 09:12:56 +00:00
Ronny Pfannschmidt
7aa7c6bbfd Merge pull request #1431 from tomviner/issue469-junit-double-colon
fix #469 - correctly mangle junit test names when double colons are in parameter id's
2016-03-03 08:45:30 +01:00
Bruno Oliveira
c2b9196a7c Merge remote-tracking branch 'upstream/master' into features 2016-03-02 23:41:56 -03:00
Bruno Oliveira
4a76b2e9b6 Merge branch 'doctest_namespace_inject' into features 2016-03-02 23:38:51 -03:00
Bruno Oliveira
28937a5cd9 Add versionadded directive to doctest_namespace section 2016-03-02 23:37:51 -03:00
Bruno Oliveira
2e02a1c370 Give proper credit for PR #1428 2016-03-02 23:30:42 -03:00
TomV
28530836c9 issue469: add fix junit double colon split issue 2016-03-02 22:18:57 +00:00
Bruno Oliveira
055f1dc9ea Merge pull request #1430 from vovanbo/patch-1
Reformat versions and header levels in CHANGELOG file
2016-03-02 16:23:55 -03:00
Vladimir Bolshakov
0cbd58e16a Reformat versions and header levels in CHANGELOG file 2016-03-02 22:07:30 +03:00
Bruno Oliveira
b7b863b7bf Merge pull request #1429 from Zearin/patch-1
Minor README tweaks (markup, phrasing)
2016-03-02 15:37:12 -03:00
Zearin
4bcc06362d Minor README tweaks (markup, phrasing) 2016-03-02 11:59:04 -05:00
Matt Williams
6dd2ff5332 Correct indentation in documentation 2016-03-02 13:02:15 +00:00
Matt Williams
891e029518 Add a new doctest_namespace fixture
This fixture can be used to inject names into the namespace in which
your doctests run.
2016-03-02 12:43:57 +00:00
Bruno Oliveira
316e39872a Add 2.9.1.dev1 to CHANGELOG 2016-03-01 18:55:53 -03:00
Bruno Oliveira
5a2500800d Add 2.10.0.dev1 to CHANGELOG 2016-03-01 18:54:08 -03:00
Bruno Oliveira
c8c5a416ef Bump version to 2.10.0.dev1 2016-03-01 18:48:13 -03:00
Bruno Oliveira
81af1c024a Bump version to 2.9.1.dev1 2016-03-01 18:46:17 -03:00
Bruno Oliveira
e656dbb602 Merge branch 'release-290' 2016-03-01 18:45:33 -03:00
Floris Bruynooghe
63b69326b8 Add company name to flub in sprint attendees 2016-03-01 10:17:31 +00:00
nicoddemus
19d05814d2 Fix reference to _CallOutcome in docs 2016-02-29 17:09:31 -05:00
nicoddemus
7d2b65813e Fix typo in CHANGELOG 2016-02-29 16:48:28 -05:00
nicoddemus
f82c03833f Regendocs 2016-02-29 15:52:56 -05:00
nicoddemus
486421fca2 Release announcement for 2.9.0 2016-02-29 15:47:23 -05:00
nicoddemus
2d7cfcd686 Finalize CHANGELOG 2016-02-29 15:42:15 -05:00
nicoddemus
623e786524 Bump version to 2.9.0 2016-02-29 15:39:00 -05:00
Bruno Oliveira
5c09b33150 Merge pull request #1423 from lukas-bednar/junit_properties
junit: allow multiple properties with same name
2016-02-29 15:35:03 -03:00
Lukas Bednar
7e758a9dc6 junit: allow multiple properties with same name
It might happen that test can be affected by two or more bugs.
I need to be able to track them all.
2016-02-29 16:00:26 +01:00
Bruno Oliveira
3445bdaca2 Merge pull request #1418 from hackebrot/update-sprint-info
Add company name sprint doc
2016-02-27 19:48:36 -03:00
Raphael Pierzina
89151b8c63 Add company name to @hackebrot in sprint attendees 2016-02-27 22:15:07 +00:00
Bruno Oliveira
4194ddd5b4 Merge pull request #1417 from hackebrot/fix-docs-on-traceback-styles
Fix docs on traceback styles
2016-02-27 18:28:57 -03:00
Raphael Pierzina
ab90043adc Update changelog with traceback style docs fix 2016-02-27 21:07:13 +00:00
Raphael Pierzina
a95fe3693b Add auto to tb styles (new default since v2.6) 2016-02-27 20:54:44 +00:00
Bruno Oliveira
b64aaac7ec Merge pull request #1416 from blubber/fix-mark-documentation
Fix a typo in the docstring for mark.MarkGenerator
2016-02-27 11:58:43 -03:00
Tiemo Kieft
424b46de1b Fix a typo in the docstring for mark.MarkGenerator 2016-02-27 14:15:42 +01:00
Florian Bruhin
c9927bb66f Merge pull request #1414 from rygwdn/indexerror
catch IndexError exceptions when getting exception source location
2016-02-26 18:53:10 +01:00
Florian Bruhin
cbb5d48fdd Merge pull request #1413 from nicoddemus/skip-if-parametrize
Fix skip/xfail markers in parametrized arguments
2016-02-26 18:41:37 +01:00
Ryan Wooden
d98d655094 Simplify IndexError test. 2016-02-26 08:25:49 -04:00
Anatoly Bubenkov
cf9a09e988 catch IndexError exceptions when getting exception source location 2016-02-26 08:18:12 -04:00
Bruno Oliveira
d9ede1bac2 Add test for unconditional skip with reason 2016-02-25 20:02:34 -03:00
Bruno Oliveira
5f90907509 Fix skip/xfail markers in parametrized arguments
Fix #1412
2016-02-25 19:13:09 -03:00
Bruno Oliveira
310bada6f5 Merge pull request #1409 from nicoddemus/builtin-fixtures-docs
Add reference to some builtin fixtures in "contents"
2016-02-25 15:46:33 -03:00
Bruno Oliveira
08b40396c9 Merge pull request #1400 from nicoddemus/contributing-improvements
Contributions to CONTRIBUTING.rst
2016-02-25 15:46:04 -03:00
holger krekel
a50209b29e add some more people, and preliminary company affiliations 2016-02-25 06:50:19 +01:00
Bruno Oliveira
a7b907d325 Add Raphael Pierzina to AUTHORS 2016-02-23 20:29:08 -03:00
Bruno Oliveira
c78a8b28dc Add reference to some builtin fixtures in "contents"
Fix #1408
2016-02-23 17:26:24 -03:00
Bruno Oliveira
3a6a0f1220 Merge pull request #1410 from milliams/master
Correct JUnit test invocation example
2016-02-23 08:06:21 -03:00
Matt Williams
fc4e240596 Correct JUnit test invocation example 2016-02-23 10:05:51 +00:00
holger krekel
5507a4d239 Merge pull request #1405 from nicoddemus/collection-atty
Display collect progress only when in a terminal
2016-02-21 08:17:35 +01:00
Bruno Oliveira
01479189a5 Merge pull request #1406 from lwm/fix-typo
fix typo
2016-02-20 20:49:58 -02:00
Luke Murphy
ddb7060535 fix typo 2016-02-20 23:44:55 +01:00
Bruno Oliveira
96a331e32f Display collect progress only when in a terminal
Fix #1397
2016-02-20 14:38:30 -02:00
Bruno Oliveira
688622f5cf Code review suggestions 2016-02-19 18:27:33 -02:00
Bruno Oliveira
fa7b0086a2 Move sections around (from easier to hardest) and added howto for repo transfers
#1282
2016-02-18 19:17:06 -02:00
Ronny Pfannschmidt
3874d53ee1 Merge pull request #1399 from nicoddemus/master
Add issue and PR GitHub templates
2016-02-18 21:38:50 +01:00
Bruno Oliveira
b76de91474 Add issue and PR GitHub templates 2016-02-18 18:26:38 -02:00
Bruno Oliveira
6940f82235 Merge pull request #1389 from nicoddemus/fix-strict-xfail
Fix bug in strict xfail: test was not being actually called
2016-02-17 17:58:49 -02:00
Bruno Oliveira
f6a2f779ae Merge pull request #1393 from hackebrot/add-license-section-to-homepage
Add license section to homepage
2016-02-17 17:53:49 -02:00
Raphael Pierzina
19536c9f05 Add full MIT license text to the docs section 2016-02-17 00:54:54 +00:00
Raphael Pierzina
e4c1b9c1c4 Add link to license page to index.rst 2016-02-17 00:47:49 +00:00
Raphael Pierzina
25aed0dca8 Add a license section to the docs toc 2016-02-16 19:42:16 +00:00
Raphael Pierzina
b2f837acd8 Add a reference to LICENSE file to README 2016-02-16 19:40:06 +00:00
Raphael Pierzina
3ef003f0ff Wrap lines at 79 characters 2016-02-16 19:14:55 +00:00
Raphael Pierzina
baabde76ae Insert copyright holders and year as mentioned in README 2016-02-16 19:13:39 +00:00
Raphael Pierzina
c805412d47 Grab official MIT license text from opensource.org 2016-02-16 19:12:22 +00:00
Dmitry Malinovsky
e4d361b093 LastFailed now creates .cache only when needed. Fixes #1342 2016-02-16 11:42:04 +06:00
Bruno Oliveira
bed56e504a Merge pull request #1390 from tomviner/bugfix/docs-typo-plugins
Fix usage of backticks in ReStructured Text files
2016-02-15 22:14:05 -02:00
TomV
3dd50d039d Fix rst syntax 2016-02-15 23:07:31 +00:00
Bruno Oliveira
0eeb466f11 Fix strict xfail: it should behave exactly like xfail when a test fails 2016-02-15 19:18:48 -02:00
Bruno Oliveira
ee88679c54 Fix bug in strict xfail: test was not being actually called 2016-02-15 18:43:45 -02:00
Bruno Oliveira
9af1f63ab6 Small typo in CHANGELOG 2016-02-14 23:36:17 -02:00
Bruno Oliveira
fb7bbf816c Merge branch 'strict-xpass' 2016-02-14 23:35:03 -02:00
Bruno Oliveira
9aae4b782b Add CHANGELOG entry for #1355 2016-02-14 23:31:31 -02:00
Ronny Pfannschmidt
1d190dc618 Merge pull request #1386 from nicoddemus/strict-xpass
Add a "strict" parameter to xfail
2016-02-15 02:29:43 +01:00
Bruno Oliveira
7823838e69 Add strict option to xfail, making tests which XPASS to actually fail the suite
Fix #1355
2016-02-14 20:52:27 -02:00
Bruno Oliveira
a965386b9e Add bool type to addini 2016-02-14 20:52:27 -02:00
Bruno Oliveira
6f0d90cd5a Merge pull request #1385 from RonnyPfannschmidt/release-process-update
update release process to ensure correct usage of base branches and p…
2016-02-14 00:54:25 -02:00
Ronny Pfannschmidt
48424a6bf6 update release process to ensure correct usage of base branches and propper finalization 2016-02-13 20:30:58 +01:00
holger krekel
2a8d58814a Merge pull request #1384 from hpk42/master
trying to settle the new layout
2016-02-13 16:56:44 +01:00
holger krekel
fa601de5c4 update participants 2016-02-13 16:55:16 +01:00
holger krekel
8284d14ec4 re-introduce topics 2016-02-13 16:54:06 +01:00
holger krekel
238dcd8bae some layout refinements 2016-02-13 14:30:10 +01:00
holger krekel
b95ff7104c Merge branch 'master' of https://github.com/pytest-dev/pytest
removed doc/en/announce/sprint-funding-2016.txt
because /doc/en/announnce/sprint2016.txt is the one which
we published.
2016-02-13 14:18:05 +01:00
holger krekel
03f8b50c8a Merge pull request #1383 from RonnyPfannschmidt/founding-header
give the founding header contrast
2016-02-13 14:10:44 +01:00
Ronny Pfannschmidt
dc7f76c276 adapt the header text based on a discussion with bruno 2016-02-13 13:40:59 +01:00
Ronny Pfannschmidt
20bd56f4b2 give the header contrast 2016-02-13 13:24:28 +01:00
Bruno Oliveira
89c75b2c91 Fix tox doc task 2016-02-13 02:09:25 -02:00
Bruno Oliveira
341bc33ff3 Merge pull request #1380 from biern/fix-utf8-explanation
Fix formatting utf-8 error explanation
2016-02-12 19:14:50 -02:00
Bruno Oliveira
fe10057c15 Merge pull request #1382 from RonnyPfannschmidt/founding-header
add links for the funding campaign in the header and sidebar
2016-02-12 18:45:32 -02:00
Ronny Pfannschmidt
48b62e4d89 add links for the funding campaign in the header and sidebar 2016-02-12 21:14:06 +01:00
Marcin Biernat
1b431d6644 fix formatting utf-8 error explanation 2016-02-12 20:28:06 +01:00
holger krekel
b1955c7f84 Merge pull request #1374 from RonnyPfannschmidt/fix-1366
fix issue #1366 by showing a note on the --fulltrace option
2016-02-11 10:45:12 +01:00
Ronny Pfannschmidt
bfa2fadac1 fix issue #1366 by showing a note on the --fulltrace option 2016-02-10 20:27:50 +01:00
holger krekel
48109b0e60 remove superflous file 2016-02-09 15:45:51 +01:00
holger krekel
fdce2306a7 fix names once again 2016-02-09 15:44:32 +01:00
Florian Bruhin
f00577f7c4 Also fix Brianna's name. 2016-02-09 15:37:45 +01:00
Florian Bruhin
569dbeb087 Fix names properly. 2016-02-09 15:36:59 +01:00
Bruno Oliveira
c6938221ab Merge branch 'issue1290-at-operator' 2016-02-06 09:38:45 -02:00
Bruno Oliveira
2b764a0e73 Fix CHANGELOG entry to new format 2016-02-06 09:37:13 -02:00
Bruno Oliveira
51694b8295 Merge branch 'master' into issue1290-at-operator 2016-02-06 09:31:42 -02:00
Bruno Oliveira
2e04771893 Merge pull request #1358 from RonnyPfannschmidt/features
merge features into master to begin the 2.9 line
2016-02-06 00:26:30 -02:00
TomV
7d107018e8 Fix #1290: Py3.5's @ operator/assertion rewriting. 2016-02-05 23:09:57 +00:00
holger krekel
05aad5c381 fix brianna's name once again 2016-02-05 14:12:35 +01:00
holger krekel
1e0088a949 fix EUR->USD 2016-02-05 14:11:30 +01:00
holger krekel
ba3b29e831 add a link to lieve indiegogo campaign in the pytest sprint page 2016-02-05 14:04:32 +01:00
holger krekel
6218e20e88 - preliminary sprint page, not yet linked through the header
- also removed plugins_index call from tox.ini "doc" env because it's not there
2016-02-05 11:34:40 +01:00
Ronny Pfannschmidt
190a52badb correct merge misstake in appveyor.yml 2016-02-05 01:18:11 +01:00
Ronny Pfannschmidt
7b2956e10b merge latest master into features as well 2016-02-05 00:13:48 +01:00
Ronny Pfannschmidt
de1a9f574c merge from master 2016-02-05 00:10:28 +01:00
Ronny Pfannschmidt
05a26d995b Merge pull request #1354 from gdyuldin/no_raise_message
Add expected exceptions to pytest.raises fail message
2016-02-03 13:54:24 +01:00
Georgy Dyuldin
79722ae89b Add expected exceptions to 'DID NOT RAISE' msg 2016-02-03 14:12:41 +03:00
Bruno Oliveira
b5dc7d9be1 Merge pull request #1350 from pytest-dev/appveyor-pypy
Test with pypy and enable coveralls in AppVeyor
2016-02-01 19:31:48 -02:00
Bruno Oliveira
30e61f2777 Test with pypy and enable coveralls in AppVeyor
* Install pypy using chocolatey
* Enable coveralls test environment in AppVeyor
* Suggest maintaining build matrix in .travis.yml by using "tox --listenvs"

 Fix #1254
2016-02-01 17:40:30 -02:00
holger krekel
58af604f82 Merge pull request #1346 from hpk42/master
merge sprint funding draft -- please feel free to commit directly.
2016-01-30 19:13:17 +01:00
holger krekel
c0024a723d fixed name 2016-01-30 19:12:41 +01:00
holger krekel
545bf0d5a1 a few refinements 2016-01-29 15:43:15 +01:00
holger krekel
70b5d5aee9 sprint funding draft 2016-01-29 14:52:07 +01:00
Bruno Oliveira
fde44f4f30 Merge branch 'better_flag_names' of https://github.com/MichaelAquilina/pytest into features 2016-01-27 20:02:50 -02:00
Bruno Oliveira
fa4d832507 Merge branch 'better_flag_names' into features 2016-01-27 19:57:23 -02:00
Bruno Oliveira
74a68b5ec6 Add CHANGELOG and docs for #1345 2016-01-27 19:57:11 -02:00
Michael Aquilina
e35ce98f89 Add explicit flag names for failed first and last failed 2016-01-27 21:28:38 +00:00
Bruno Oliveira
dd0062c177 Dummy change to test readthedocs hook 2016-01-27 08:59:26 -02:00
Bruno Oliveira
6c37a51f95 Fix version at the top of the CHANGELOG 2016-01-27 08:28:18 -02:00
Ronny Pfannschmidt
52ac6cd7a9 Merge pull request #1199 from nicoddemus/move-pycode-to-pytest
Copy py.code code to py.test
2016-01-26 23:29:09 +01:00
Bruno Oliveira
4825678e1a Add CHANGELOG entry for py.code merge 2016-01-26 20:24:08 -02:00
Bruno Oliveira
e43eaffd93 Remove unused import 2016-01-25 23:30:53 -02:00
Bruno Oliveira
9f85d4c952 mark test_comments as xfail on pypy
while migrating this code it was noticed that this test was failing even
on the original py repository, so it was decided to xfail it and investigate
later
2016-01-25 23:18:04 -02:00
Bruno Oliveira
7a6f902f6f Drop assertionnew and assertionold from _pytest._code 2016-01-25 23:18:04 -02:00
Bruno Oliveira
a912d3745b Moved py.code code over to py.test
Fix #103
2016-01-25 23:18:04 -02:00
Ronny Pfannschmidt
f23307b06d Merge release branch into mater 2016-01-25 00:01:43 +01:00
Ronny Pfannschmidt
6c3e6401d4 begin 2.8.8 cycle 2016-01-24 23:56:56 +01:00
Ronny Pfannschmidt
3315b3a12f finalize changelog for 2.8.7 2016-01-24 23:23:15 +01:00
Ronny Pfannschmidt
64d7d00218 Prepare 2.8.7 release 2016-01-24 17:59:48 +01:00
Ronny Pfannschmidt
7c747c97ec Merge pull request #1339 from RonnyPfannschmidt/fix-1338
Fix 1338
2016-01-24 17:51:00 +01:00
Ronny Pfannschmidt
56c5db6e12 add requests dependency to tox.ini to ensure all monkeypatch tests run 2016-01-24 12:30:38 +01:00
Ronny Pfannschmidt
2d05f831fe monkeypatch: unnest handling code
this avoid python3 nested exceptions
2016-01-24 12:28:14 +01:00
Ronny Pfannschmidt
cb6181255e changelog 2016-01-24 00:45:59 +01:00
Ronny Pfannschmidt
cd9e30b221 work around python 2/3 difference by using str(exception) 2016-01-24 00:40:27 +01:00
Ronny Pfannschmidt
d028fe1e66 remove unused import 2016-01-23 20:29:54 +01:00
Ronny Pfannschmidt
b825af2e66 pass trough annotated exceptions 2016-01-23 19:31:17 +01:00
Ronny Pfannschmidt
60e9698530 fix issue 1338 2016-01-23 19:12:51 +01:00
Ronny Pfannschmidt
9e6bb74d71 reformat monkeypatch core plugin/its tests 2016-01-23 19:12:51 +01:00
Bruno Oliveira
ed3c96ee58 Merge pull request #1336 from nicoddemus/merge-master-in-features
Merge master in features
2016-01-22 19:05:34 -02:00
Bruno Oliveira
199fcf93d4 Merge branch 'master' into 'features' 2016-01-22 18:32:45 -02:00
Bruno Oliveira
c8caa87759 Merge branch 'release-2.8.6' 2016-01-22 18:00:01 -02:00
Bruno Oliveira
b7de0401b8 Bump version to 2.8.7.dev1 2016-01-22 17:59:25 -02:00
Bruno Oliveira
01793ed8bc Use a full url to pytest logo on README
So it appers correctly in PyPI
2016-01-22 17:46:28 -02:00
Bruno Oliveira
82d00efa8d 2.8.6 release: version, CHANGELOG
Remove note about expected failing envs in tox, as tox now supports
skipping certain environments based on the platform.
2016-01-21 19:17:53 -02:00
Bruno Oliveira
61c569f960 Merge branch 'junit_stdout_err_on_error' 2016-01-20 19:06:50 -02:00
Bruno Oliveira
dd56d7b7fc Add CHANGELOG and AUTHORS entry for #1334 2016-01-20 19:00:52 -02:00
Bruno Oliveira
4de3d595c9 Merge pull request #1333 from lesteve/fix-practise-typo
Fix practise -> practice typo in documentation
2016-01-20 18:03:15 -02:00
Georgy Dyuldin
b28b3cc271 Add captured stdout to jUnit report on setup error 2016-01-20 20:13:01 +03:00
Loïc Estève
99072ea8c9 Fix practise -> practice typo in documentation 2016-01-20 16:35:27 +01:00
Ronny Pfannschmidt
11a7bcaaa5 Merge pull request #1322 from nicoddemus/missing-hooks-docs
Add a few missing hooks to the docs
2016-01-19 08:12:58 +01:00
Bruno Oliveira
0caee1a673 Add a few missing hooks to the docs 2016-01-18 19:27:35 -02:00
Bruno Oliveira
4c87a6aa09 Merge pull request #1330 from nicoddemus/fix-flakes
Fix flakes
2016-01-18 15:36:20 -02:00
Bruno Oliveira
7b13c4bec0 Fix flakes 2016-01-14 21:01:57 -02:00
Bruno Oliveira
aa8c352c10 Merge pull request #1323 from jeffwidman/patch-1
Change `input` to `test_input` in docs for clarity
2016-01-12 23:28:52 -02:00
Jeff Widman
ee75ecbda0 Change input to test_input in docs for clarity
Using `input` is confusing because it's also the name of a Python built-in function. So we use `test_input` instead. Fix #1321
2016-01-12 17:01:34 -08:00
Ronny Pfannschmidt
bc32e45bb6 Merge pull request #1314 from The-Compiler/ci-verbose
Always show full comparison output if on CI.
2016-01-11 10:05:15 +01:00
Florian Bruhin
3e5c9038ec Always show full comparison output if on CI.
When you don't get enough information with a test running on a CI, it's quite
frustrating, for various reasons:

- It's more likely to be a flaky test, so you might not be able to reproduce
  the failure.
- Passing -vv is quite bothersome (creating a temporary commit and reverting
  it)

For those reasons, if something goes wrong on CI, it's good to have as much
information as possible.
2016-01-11 08:45:04 +01:00
Ronny Pfannschmidt
b2c0864fbf Merge pull request #1318 from nicoddemus/doctest-unicode-error
Fix decode error in Python 2.7 when docstrings contain a non-ascii character
2016-01-11 08:37:24 +01:00
Florian Bruhin
a80efb038a Merge pull request #1320 from bionikspoon/patch-1
update docs plugin.rst typo
2016-01-11 07:37:46 +01:00
Manu Phatak
5b29f579c5 update docs plugin.rst typo 2016-01-11 00:11:46 -06:00
Bruno Oliveira
808cb8e3ad Merge branch 'stdout-pdb-capture' 2016-01-09 11:46:13 -02:00
Bruno Oliveira
8727503dd4 Add CHANGELOG entry for #1223 2016-01-09 11:45:56 -02:00
foxx
f46de68804 Fixes bug with stdout/stderr capture on pdb 2016-01-09 12:04:26 +00:00
Bruno Oliveira
3c19cfcd9a Fix decode error in Python 2.7 when docstrings contain a non-ascii character
Fix #628
2016-01-08 23:10:02 -02:00
Bruno Oliveira
b8784c28c9 Merge branch 'master' into 'features' 2016-01-08 21:51:34 -02:00
Bruno Oliveira
29b05c8391 Merge pull request #1309 from k4rtik/patch-1
Correct platform name osx -> darwin
2016-01-05 23:29:33 -02:00
Kartik Singhal
26c835eea5 Correct platform name osx -> darwin 2016-01-05 18:43:03 -05:00
Florian Bruhin
eebf5c1d2c Merge pull request #1304 from nicoddemus/rst-changelog
Changelog now in rst format (2.9.0 and onward) and add rst-lint check
2016-01-05 14:05:53 -08:00
Bruno Oliveira
3daa0756eb Add CHANGELOG.rst to MANIFEST and small format fix 2016-01-05 20:01:41 -02:00
Bruno Oliveira
3e34db50fb Rename "flakes" testenv to "linting" as requested in review 2016-01-05 18:18:29 -02:00
Ronny Pfannschmidt
e2603d7050 Merge pull request #1306 from nicoddemus/summary-warnings
pytest warnings emitted during ``pytest_terminal_summary`` are now properly displayed.
2016-01-04 11:16:33 +01:00
Bruno Oliveira
369d9ecaa5 pytest warnings emitted during `pytest_terminal_summary` are now properly displayed.
Fix #1305
2016-01-04 00:07:45 -02:00
Bruno Oliveira
02dd6df6e6 Changelog now in rst format (2.9.0 and onward) and add rst-lint check
Fix #1274
2016-01-03 23:09:24 -02:00
Ronny Pfannschmidt
6c170201d6 Merge branch 'master' into features 2016-01-02 23:56:01 +01:00
Ronny Pfannschmidt
bf4de4bd68 Merge pull request #1294 from nicoddemus/doctest-bytes-literals
Doctest bytes literals
2016-01-01 18:18:08 +01:00
Bruno Oliveira
80d6d94635 Merge pull request #1298 from aktech/patch-1
Update copyright in README
2016-01-01 12:06:21 -02:00
AMiT Kumar
63cba1ed0d Update copyright in README 2016-01-01 18:52:56 +05:30
Ronny Pfannschmidt
71ab6b8b05 Merge pull request #1295 from nicoddemus/monkeypatch-perf
Make monkeypatch calls O(1)
2015-12-30 22:21:16 +01:00
Ronny Pfannschmidt
c367180ab2 Merge pull request #1297 from nicoddemus/multi-doctest-glob
Multiple --doctest-glob arguments
2015-12-30 22:18:46 +01:00
Bruno Oliveira
1bdf71730a Complement #1255 by adding tests and docs
Fix #1242
2015-12-30 18:24:59 -02:00
jab
0ea8dc0d40 make --doctest-glob multi-allowed 2015-12-30 17:32:14 -02:00
Bruno Oliveira
638b3f5e39 Make monkeypatch calls O(1)
Fix #1292
2015-12-29 22:04:35 -02:00
Bruno Oliveira
309ecf7ab3 Rename "BugFixes/Adjustments" to just "Bug Fixes" as commented elsewhere 2015-12-29 21:09:15 -02:00
Bruno Oliveira
719d63085d Add CHANGELOG entry for ALLOW_BYTES doctest option 2015-12-29 21:08:25 -02:00
Bruno Oliveira
5a5b732fe1 Add docs for ALLOW_BYTES doctest option
Fix #1287
2015-12-29 21:05:11 -02:00
Bruno Oliveira
a0edbb75a4 Implement ALLOW_BYTES doctest option
Fix #1287
2015-12-29 20:55:19 -02:00
Bruno Oliveira
0ef73ed3e0 Small typo on README 2015-12-28 18:56:14 -02:00
Bruno Oliveira
7cfb750d7f Small wording on README 2015-12-28 18:55:20 -02:00
Bruno Oliveira
70f72229c6 Merge pull request #1291 from ulope/master
Make monkeypatch differentiate ImportError sources
2015-12-28 13:18:25 -02:00
Ulrich Petri
2e02579437 Add CHANGELOG entry for #900 fix 2015-12-28 10:53:38 +01:00
Ulrich Petri
8d49abb0d1 Make monkeypatch differentiate ImportError sources
Previously `monkeypatch` assumed that any `ImportError` was caused by
a mistake in the specified import path. However this assumption is false 
in case the import target itself causes an `ImportError`.

Fixes: #900
2015-12-27 22:44:23 +01:00
Ronny Pfannschmidt
c5631b6567 Merge pull request #1284 from nicoddemus/pyversions-badge
Add badge of supported python versions to README
2015-12-27 16:05:43 +01:00
Ronny Pfannschmidt
522224ee7c Merge pull request #1285 from nicoddemus/readme-changes-logo
Add logo and some readme changes
2015-12-27 15:47:29 +01:00
Bruno Oliveira
015e8e574a Merge pull request #1289 from WoLpH/patch-1
Updated out of date link
2015-12-27 07:22:41 -02:00
Rick van Hattem
87ff7ee232 Updated out of date link 2015-12-27 02:35:02 +01:00
Bruno Oliveira
b5490b289d Add logo and update readme
* Show pytest logo
* Show assertion introspection in the example
* Use separate sections for change-log, docs, license, etc.
2015-12-26 12:47:22 -02:00
Ronny Pfannschmidt
46039f8687 Merge pull request #1286 from nicoddemus/changelog-sections
Separate 2.9.0 CHANGELOG into sections
2015-12-26 11:09:45 +01:00
Bruno Oliveira
6b25fb4d64 Separate 2.9.0 CHANGELOG into sections
Fix #1275
2015-12-25 20:44:03 -02:00
Bruno Oliveira
99a5067edb Merge branch 'pyreadline-workaround' 2015-12-25 18:03:25 -02:00
Bruno Oliveira
5afb61ad26 Fix trailing white-space 2015-12-25 17:51:55 -02:00
Bruno Oliveira
fbfab6778c Give proper thanks for the PR in the CHANGELOG 2015-12-25 17:48:03 -02:00
Bruno Oliveira
57bc14caa0 Apply readline workaround during initial conftest loading 2015-12-25 17:46:19 -02:00
Bruno Oliveira
df3f21afb6 Add badge of supported python versions to README 2015-12-25 16:28:50 -02:00
Ronny Pfannschmidt
1a87bb2416 Merge pull request #1280 from nicoddemus/del-index-from-overview
Remove "index" entry from overview.rst
2015-12-25 07:33:15 +01:00
Erik M. Bray
6e170a4a1c * Moved workaround to its own function, mostly for the sake of adding
a more descriptive docstring (the workaround itself is just to import
  readline earlier).  I removed the conditional on targetfd from the
  workaround since it doesn't really matter.

* Added # noqa marker.

* Added changelog entry, and self to authors.
2015-12-24 16:43:34 -05:00
Erik M. Bray
924a9667e1 Make sure readline has been imported before duping any stdio handles--otherwise pyreadline fails to connect to the correct handle for the console's stdout/in. 2015-12-24 14:56:57 -05:00
Bruno Oliveira
319f6310f8 Remove "index" entry from overview.rst
"index" is already displayed in the main docs, repeating it here breaks the reading flow
2015-12-24 16:08:43 -02:00
Bruno Oliveira
cd3a441304 Merge pull request #1277 from peterdemin/patch-1
doc typo
2015-12-24 15:57:01 -02:00
Peter Demin
8180165229 doc typo 2015-12-24 08:56:54 -05:00
Ronny Pfannschmidt
ec5a429c77 junitxml tests: extend with extra items 2015-12-17 22:30:27 +01:00
Ronny Pfannschmidt
713069ebd4 implement review comments for #1266 2015-12-17 22:27:01 +01:00
Ronny Pfannschmidt
f7af08d309 Merge pull request #1268 from nicoddemus/merge-newinterpret
Merge newinterpret.py into reinterpret.py
2015-12-17 07:10:47 +01:00
Bruno Oliveira
943099ddd1 Merge newinterpret into reinterpret.py 2015-12-16 18:31:43 -02:00
Bruno Oliveira
379562107e Merge pull request #1267 from nicoddemus/remove-pre-2.6
Remove pre-2.6 support code and docs (including obsolete oldinterpret module)
2015-12-16 18:30:53 -02:00
Bruno Oliveira
25c392196f Mention #1226 in the CHANGELOG 2015-12-16 16:48:49 -02:00
Bruno Oliveira
ec597e81a4 Remove references to python pre-2.6 from docs 2015-12-16 16:16:22 -02:00
Bruno Oliveira
81588d7f63 Remove obsolete "oldinterpret" module 2015-12-16 16:16:22 -02:00
Bruno Oliveira
af893aab26 Remove code related to support python <= 2.5
Fix #1226
2015-12-16 16:16:22 -02:00
Ronny Pfannschmidt
8bf7e7cc4b fixes #1259 - take finalized nodes out of the mapping
this allows double node id usage for file based items
2015-12-16 19:09:44 +01:00
Ronny Pfannschmidt
7b20288c2b Merge pull request #1264 from nicoddemus/exception-interact-deprecated
Remove experimental status from pytest_exception_interact
2015-12-16 18:32:58 +01:00
Bruno Oliveira
2b2bec6b97 Remove experimental status from pytest_exception_interact 2015-12-16 14:29:39 -02:00
Ronny Pfannschmidt
fcc20d4181 Merge pull request #1257 from nicoddemus/merge-master-into-features
Merge master into features after 2.8.5
2015-12-12 08:15:55 +01:00
Bruno Oliveira
6ac31088c5 Merge branch 'master' into merge-master-into-features 2015-12-11 23:13:50 -02:00
Bruno Oliveira
7eea6b3b02 Add 2.8.6.dev1 to CHANGELOG 2015-12-11 23:11:36 -02:00
Bruno Oliveira
e87facfb22 Bump master to 2.8.6.dev1 2015-12-11 23:07:30 -02:00
Bruno Oliveira
926c6028bb Merge pull request #1250 from nicoddemus/merge-master-into-features
Merge master into features
2015-12-10 20:56:10 -02:00
Bruno Oliveira
86b6ce5042 Merge remote-tracking branch 'upstream/master' into merge-master-into-features 2015-12-10 19:41:14 -02:00
Bruno Oliveira
8f880e1625 Fix CHANGELOG merge 2015-12-10 19:40:45 -02:00
Bruno Oliveira
8b61a332ba Merge remote-tracking branch 'bukzor/features-merge-master' into features 2015-12-10 19:03:55 -02:00
Ronny Pfannschmidt
6351e2846c Merge pull request #1232 from codewarrior0/report-passing-option
Add -rp and -rP options to report passing tests.
2015-12-09 06:57:42 +01:00
David Vierra
ccfd962170 Add "no -rP" case to test_pass_output_reporting 2015-12-08 17:33:03 -10:00
David Vierra
b417d7cb79 Add tests to test_terminal.py for -rp and -rP 2015-12-08 15:54:23 -10:00
Buck Golemon
5ccb7b1ced update test_recwarn to new style 2015-12-08 11:08:33 -08:00
Buck Golemon
eabf2f9091 Merge branch 'master' into features
Conflicts:
	AUTHORS
	_pytest/__init__.py
	_pytest/hookspec.py
	_pytest/recwarn.py
	testing/test_recwarn.py
2015-12-07 14:28:59 -08:00
David Vierra
1db4cbcc9f Update AUTHORS and CHANGELOG 2015-12-07 12:17:30 -10:00
David Vierra
fbac936596 Add -rp and -rP options to report passing tests.
-rP is an alternative to `-s` for viewing the output of passing tests.
This causes the captured stdout/stderr of passing tests to be output in
the same way as that of failing tests.

-rp adds a simple one-line-per-test summary for passing tests.

Neither option is included by -ra.

Additional changes to `pytest_capturelog` and `pytest_catchlog` are
needed for this option to also output captured logs: They must be
changed to use `rep.sections.add` instead of `rep.longrepr.addsection`,
and to add these additional sections even if the test passes, since
passing tests don't seem to have a `longrepr` at report time.
2015-12-07 11:32:56 -10:00
Bruno Oliveira
84eacf3e3c Small changelog formatting fix 2015-11-26 14:37:55 -02:00
Ronny Pfannschmidt
df767cca8f Merge pull request #1188 from nicoddemus/pytest_enter_pdb
Pass pytest config object to pytest_enter_pdb
2015-11-23 20:55:33 +01:00
Bruno Oliveira
b3166a538c Pass pytest's config object to pytest_enter_pdb 2015-11-23 14:42:21 -02:00
Bruno Oliveira
1f148a93ec Mention pytest_enter_pdb in the docs 2015-11-23 13:02:15 -02:00
Bruno Oliveira
cee828130c Merge pull request #1124 from bukzor/unit-test-config-fromdictargs
tests of (and fixes for) Config.fromdictargs (2)
2015-10-09 15:10:40 -03:00
Buck Golemon
67236d6de3 strengthen the ini assertion 2015-10-09 09:58:12 -07:00
Buck Golemon
470e4f9e91 changelog entry 2015-10-09 09:58:12 -07:00
Buck Golemon
0e55a8793f all tests pass 2015-10-09 09:58:12 -07:00
Buck Golemon
49d46a0059 an ugly patch to fix all but the most important part =/ 2015-10-08 10:44:58 -07:00
Buck Golemon
616d8251f3 unit tests of Config.fromdictargs. currently failing 2015-10-08 10:44:58 -07:00
Bruno Oliveira
a24126effb Add credit for pytest.mark.skip to the CHANGELOG 2015-10-03 14:15:22 -03:00
Michael Aquilina
8984177448 TestXFail also shouldnt explicitly inherit from object 2015-10-03 17:12:44 +01:00
Michael Aquilina
750442909c Add unconditional skip entry to CHANGELOG 2015-10-03 17:04:06 +01:00
Michael Aquilina
df874db817 Update default reason to "unconditional skip" 2015-10-03 17:02:18 +01:00
Michael Aquilina
00d0c74657 Update reason in test to prevent confusing with test_no_reason 2015-10-03 17:01:21 +01:00
Michael Aquilina
122980ecad Add myself to AUTHORS 2015-10-03 17:01:11 +01:00
Michael Aquilina
fc0bd9412c Test that "unconditional skip" is the default reason if none given 2015-10-03 17:00:16 +01:00
Michael Aquilina
5ff9a0ff54 Remove redundant comments 2015-10-03 16:59:27 +01:00
Michael Aquilina
25d74a5919 Dont explicitly inherit from object 2015-10-03 16:55:04 +01:00
Michael Aquilina
213dbe7a5f newlines 2015-10-03 16:42:15 +01:00
Michael Aquilina
9e57954b03 First argument in pytest.mark.skip is a reason 2015-10-03 16:42:15 +01:00
Michael Aquilina
1b5aa2868d Check no reason displayed if none specified 2015-10-03 16:42:15 +01:00
Michael Aquilina
eee24138b0 Fix failing test 2015-10-03 16:42:15 +01:00
Michael Aquilina
04545f8a54 classes inherit from object 2015-10-03 16:42:15 +01:00
Michael Aquilina
d1628944a6 Update skippings tests for better coverage 2015-10-03 16:42:15 +01:00
Michael Aquilina
abc27f56fc Update skipping.rst with correct version marker 2015-10-03 16:42:15 +01:00
Michael Aquilina
771aef9ddb Add a test_skip_class test 2015-10-03 16:42:15 +01:00
Michael Aquilina
dc7153e33c Spelling and grammar fixes 2015-10-03 16:42:15 +01:00
Michael Aquilina
5ec08d3081 Delete trailing whitespace 2015-10-03 16:42:15 +01:00
Michael Aquilina
61b8443723 Update docs with new skip marker 2015-10-03 16:42:14 +01:00
Michael Aquilina
ad0b8e31b8 Fix case where skip is assigned to as an attribute directly 2015-10-03 16:42:14 +01:00
Michael Aquilina
f144666f8b Work towards test coverage of mark.skip 2015-10-03 16:42:14 +01:00
Michael Aquilina
4e94135d36 Remove incorrect use of pytest.mark.skip 2015-10-03 16:42:14 +01:00
Michael Aquilina
b71add27da Add MarkEvaluator for skip 2015-10-03 16:42:14 +01:00
holger krekel
cb58eaa611 Merge remote-tracking branch 'upstream/master' into features
Conflicts:
	_pytest/__init__.py
	testing/test_recwarn.py
2015-09-29 15:56:41 +02:00
holger krekel
0c05ca1fd5 Merge branch 'master' into features 2015-09-26 10:03:42 +02:00
holger krekel
4867554eec Merge branch 'master' into features 2015-09-23 16:42:42 +02:00
Ronny Pfannschmidt
36924b59bd Merge branch '653-deprecated-context-manager' of https://github.com/chiller/pytest into features 2015-09-22 15:14:06 +02:00
holger krekel
7c088d1104 remove nonsense line 2015-09-22 11:23:24 +02:00
holger krekel
48df2f6842 Merge branch 'master' into features 2015-09-22 11:23:05 +02:00
holger krekel
79587d4b70 Merge branch 'master' into features
Conflicts:
	_pytest/__init__.py
2015-09-22 11:06:21 +02:00
holger krekel
8a4517fd17 re-add 2.8.x changelog so that MASTER can be merged into features wrt
to the changelog
2015-09-22 11:05:19 +02:00
holger krekel
97f7815feb also change pytest version to target 2.9.0 2015-09-22 11:01:33 +02:00
holger krekel
d8fbb0b8e3 start features branch 2015-09-22 11:00:55 +02:00
Galaczi Endre
9f77a8507e removed mutation of global state, changed filter addition in WarningsRecorder 2015-09-21 19:01:23 +01:00
Galaczi Endre
beaa8e55bd Fixes #653 use deprecated_call as context_manager 2015-09-21 19:01:23 +01:00
178 changed files with 13618 additions and 5457 deletions

8
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,8 @@
Thanks for submitting an issue!
Here's a quick checklist in what to include:
- [ ] Include a detailed description of the bug or suggestion
- [ ] `pip list` of the virtual environment you are using
- [ ] pytest and operating system versions
- [ ] Minimal example if possible

11
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,11 @@
Thanks for submitting a PR, your contribution is really appreciated!
Here's a quick checklist that should be present in PRs:
- [ ] Target: for bug or doc fixes, target `master`; for new features, target `features`;
- [ ] Make sure to include one or more tests for your change;
- [ ] Add yourself to `AUTHORS`;
- [ ] Add a new entry to `CHANGELOG.rst`
* Choose any open position to avoid merge conflicts with other PRs.
* Add a link to the issue you are fixing (if any) using RST syntax.
* The pytest team likes to have people to acknowledged in the `CHANGELOG`, so please add a thank note to yourself ("Thanks @user for the PR") and a link to your GitHub profile. It may sound weird thanking yourself, but otherwise a maintainer would have to do it manually before or after merging instead of just using GitHub's merge button. This makes it easier on the maintainers to merge PRs.

2
.gitignore vendored
View File

@@ -16,6 +16,7 @@ include/
*.class
*.orig
*~
.hypothesis/
.eggs/
@@ -32,3 +33,4 @@ env/
.coverage
.ropeproject
.idea
.hypothesis

View File

@@ -7,25 +7,25 @@ install: "pip install -U tox"
# # command to run tests
env:
matrix:
# coveralls is not listed in tox's envlist, but should run in travis
- TESTENV=coveralls
- TESTENV=doctesting
- TESTENV=flakes
# note: please use "tox --listenvs" to populate the build matrix below
- TESTENV=linting
- TESTENV=py26
- TESTENV=py27
- TESTENV=py27-cxfreeze
- TESTENV=py27-nobyte
- TESTENV=py27-pexpect
- TESTENV=py27-subprocess
- TESTENV=py27-trial
- TESTENV=py27-xdist
- TESTENV=py33
- TESTENV=py33
- TESTENV=py34
- TESTENV=py35-pexpect
- TESTENV=py35-trial
- TESTENV=py35-xdist
- TESTENV=py35
- TESTENV=pypy
- TESTENV=py27-pexpect
- TESTENV=py27-xdist
- TESTENV=py27-trial
- TESTENV=py35-pexpect
- TESTENV=py35-xdist
- TESTENV=py35-trial
- TESTENV=py27-nobyte
- TESTENV=doctesting
- TESTENV=freeze
script: tox --recreate -e $TESTENV

64
AUTHORS
View File

@@ -3,38 +3,61 @@ merlinux GmbH, Germany, office at merlinux eu
Contributors include::
Abdeali JK
Abhijeet Kasurde
Alexei Kozlenok
Anatoly Bubenkoff
Andreas Zeidler
Andrzej Ostrowski
Andy Freeland
Anthon van der Neut
Antony Lee
Armin Rigo
Aron Curzon
Aviv Palivoda
Ben Webb
Benjamin Peterson
Bernard Pratz
Bob Ippolito
Brian Dorsey
Brian Okken
Brianna Laugher
Bruno Oliveira
Cal Leeming
Carl Friedrich Bolz
Charles Cloud
Charnjit SiNGH (CCSJ)
Chris Lamb
Christian Boelsen
Christian Theunert
Christian Tismer
Christopher Gilling
Daniel Grana
Daniel Hahler
Daniel Nuri
Danielle Jenkins
Dave Hunt
David Díaz-Barquero
David Mohr
David Vierra
Diego Russo
Dmitry Dygalo
Edison Gustavo Muenz
Edoardo Batini
Eduardo Schettino
Elizaveta Shashkova
Endre Galaczi
Eric Hunsberger
Eric Hunsberger
Eric Siegerman
Erik M. Bray
Feng Ma
Florian Bruhin
Floris Bruynooghe
Gabriel Reis
Georgy Dyuldin
Graham Horler
Greg Price
Grig Gheorghiu
Guido Wesdorp
Harald Armin Massa
@@ -43,32 +66,61 @@ Jaap Broekhuizen
Jan Balster
Janne Vanhala
Jason R. Coombs
Javier Domingo Cansino
Javier Romero
John Towler
Jon Sonesen
Joshua Bronson
Jurko Gospodnetić
Justyna Janczyszyn
Kale Kundert
Katarzyna Jachim
Kevin Cox
Lee Kamentsky
Lukas Bednar
Maciek Fijalkowski
Maho
Marc Schlaich
Mark Abramowitz
Markus Unterwaditzer
Martijn Faassen
Martin K. Scherer
Martin Prusse
Matt Bachmann
Matt Williams
Michael Aquilina
Michael Birtwell
Michael Droettboom
Mike Lundy
Nicolas Delaby
Oleg Pidsadnyi
Oliver Bestwalter
Omar Kohl
Pieter Mulder
Piotr Banaszkiewicz
Punyashloka Biswal
Quentin Pradet
Ralf Schmitt
Raphael Pierzina
Roberto Polli
Romain Dorgueil
Roman Bolshakov
Ronny Pfannschmidt
Ross Lawley
Russel Winder
Ryan Wooden
Samuele Pedroni
Simon Gomizelj
Stefan Farmbauer
Stefan Zimmermann
Stefano Taschini
Steffen Allner
Stephan Obermann
Tareq Alayan
Ted Xiao
Thomas Grainger
Tom Viner
Trevor Bekolay
Vasily Kuznetsov
Wouter van Ackooy
David Díaz-Barquero
Eric Hunsberger
Simon Gomizelj
Russel Winder
Ben Webb
Alexei Kozlenok
Xuecong Liao

File diff suppressed because it is too large Load Diff

View File

@@ -9,46 +9,18 @@ so do not hesitate!
:depth: 2
.. _submitplugin:
.. _submitfeedback:
Submit a plugin, co-develop pytest
----------------------------------
Feature requests and feedback
-----------------------------
Pytest development of the core, some plugins and support code happens
in repositories living under:
Do you like pytest? Share some love on Twitter or in your blog posts!
- `the pytest-dev github organisation <https://github.com/pytest-dev>`_
We'd also like to hear about your propositions and suggestions. Feel free to
`submit them as issues <https://github.com/pytest-dev/pytest/issues>`_ and:
- `the pytest-dev bitbucket team <https://bitbucket.org/pytest-dev>`_
All pytest-dev Contributors team members have write access to all contained
repositories. pytest core and plugins are generally developed
using `pull requests`_ to respective repositories.
You can submit your plugin by subscribing to the `pytest-dev mail list
<https://mail.python.org/mailman/listinfo/pytest-dev>`_ and writing a
mail pointing to your existing pytest plugin repository which must have
the following:
- PyPI presence with a ``setup.py`` that contains a license, ``pytest-``
prefixed, version number, authors, short and long description.
- a ``tox.ini`` for running tests using `tox <http://tox.testrun.org>`_.
- a ``README.txt`` describing how to use the plugin and on which
platforms it runs.
- a ``LICENSE.txt`` file or equivalent containing the licensing
information, with matching info in ``setup.py``.
- an issue tracker unless you rather want to use the core ``pytest``
issue tracker.
If no contributor strongly objects and two agree, the repo will be
transferred to the ``pytest-dev`` organisation and you'll become a
member of the ``pytest-dev Contributors`` team, with commit rights
to all projects. We recommend that each plugin has at least three
people who have the right to release to pypi.
* Explain in detail how they should work.
* Keep the scope as narrow as possible. This will make it easier to implement.
.. _reportbugs:
@@ -56,7 +28,7 @@ people who have the right to release to pypi.
Report bugs
-----------
Report bugs for pytest at https://github.com/pytest-dev/pytest/issues
Report bugs for pytest in the `issue tracker <https://github.com/pytest-dev/pytest/issues>`_.
If you are reporting a bug, please include:
@@ -70,29 +42,13 @@ If you can write a demonstration test that currently fails but should pass (xfai
that is a very useful commit to make as well, even if you can't find how
to fix the bug yet.
.. _submitfeedback:
Submit feedback for developers
------------------------------
Do you like pytest? Share some love on Twitter or in your blog posts!
We'd also like to hear about your propositions and suggestions. Feel free to
`submit them as issues <https://github.com/pytest-dev/pytest/issues>`__ and:
* Set the "kind" to "enhancement" or "proposal" so that we can quickly find
about them.
* Explain in detail how they should work.
* Keep the scope as narrow as possible. This will make it easier to implement.
* If you have required skills and/or knowledge, we are very happy for
:ref:`pull requests <pull-requests>`.
.. _fixbugs:
Fix bugs
--------
Look through the GitHub issues for bugs. Here is sample filter you can use:
Look through the GitHub issues for bugs. Here is a filter you can use:
https://github.com/pytest-dev/pytest/labels/bug
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs.
@@ -104,8 +60,7 @@ Don't forget to check the issue trackers of your favourite plugins, too!
Implement features
------------------
Look through the GitHub issues for enhancements. Here is sample filter you
can use:
Look through the GitHub issues for enhancements. Here is a filter you can use:
https://github.com/pytest-dev/pytest/labels/enhancement
:ref:`Talk <contact>` to developers to find out how you can implement specific
@@ -114,16 +69,81 @@ features.
Write documentation
-------------------
pytest could always use more documentation. What exactly is needed?
Pytest could always use more documentation. What exactly is needed?
* More complementary documentation. Have you perhaps found something unclear?
* Documentation translations. We currently have only English.
* Docstrings. There can never be too many of them.
* Blog posts, articles and such -- they're all very appreciated.
You can also edit documentation files directly in the Github web interface
without needing to make a fork and local copy. This can be convenient for
small fixes.
You can also edit documentation files directly in the GitHub web interface,
without using a local copy. This can be convenient for small fixes.
.. _submitplugin:
Submitting Plugins to pytest-dev
--------------------------------
Pytest development of the core, some plugins and support code happens
in repositories living under the ``pytest-dev`` organisations:
- `pytest-dev on GitHub <https://github.com/pytest-dev>`_
- `pytest-dev on Bitbucket <https://bitbucket.org/pytest-dev>`_
All pytest-dev Contributors team members have write access to all contained
repositories. Pytest core and plugins are generally developed
using `pull requests`_ to respective repositories.
The objectives of the ``pytest-dev`` organisation are:
* Having a central location for popular pytest plugins
* Sharing some of the maintenance responsibility (in case a maintainer no
longer wishes to maintain a plugin)
You can submit your plugin by subscribing to the `pytest-dev mail list
<https://mail.python.org/mailman/listinfo/pytest-dev>`_ and writing a
mail pointing to your existing pytest plugin repository which must have
the following:
- PyPI presence with a ``setup.py`` that contains a license, ``pytest-``
prefixed name, version number, authors, short and long description.
- a ``tox.ini`` for running tests using `tox <http://tox.testrun.org>`_.
- a ``README.txt`` describing how to use the plugin and on which
platforms it runs.
- a ``LICENSE.txt`` file or equivalent containing the licensing
information, with matching info in ``setup.py``.
- an issue tracker for bug reports and enhancement requests.
- a `changelog <http://keepachangelog.com/>`_
If no contributor strongly objects and two agree, the repository can then be
transferred to the ``pytest-dev`` organisation.
Here's a rundown of how a repository transfer usually proceeds
(using a repository named ``joedoe/pytest-xyz`` as example):
* ``joedoe`` transfers repository ownership to ``pytest-dev`` administrator ``calvin``.
* ``calvin`` creates ``pytest-xyz-admin`` and ``pytest-xyz-developers`` teams, inviting ``joedoe`` to both as **maintainer**.
* ``calvin`` transfers repository to ``pytest-dev`` and configures team access:
- ``pytest-xyz-admin`` **admin** access;
- ``pytest-xyz-developers`` **write** access;
The ``pytest-dev/Contributors`` team has write access to all projects, and
every project administrator is in it. We recommend that each plugin has at least three
people who have the right to release to PyPI.
Repository owners can rest assured that no ``pytest-dev`` administrator will ever make
releases of your repository or take ownership in any way, except in rare cases
where someone becomes unresponsive after months of contact attempts.
As stated, the objective is to share maintenance and avoid "plugin-abandon".
.. _`pull requests`:
.. _pull-requests:
@@ -135,7 +155,7 @@ Preparing Pull Requests on GitHub
What is a "pull request"? It informs project's core developers about the
changes you want to review and merge. Pull requests are stored on
`GitHub servers <https://github.com/pytest-dev/pytest/pulls>`_.
Once you send pull request, we can discuss it's potential modifications and
Once you send a pull request, we can discuss its potential modifications and
even add more commits to it later on.
There's an excellent tutorial on how Pull Requests work in the
@@ -179,35 +199,35 @@ but here is a simple overview:
You need to have Python 2.7 and 3.5 available in your system. Now
running tests is as simple as issuing this command::
$ python runtox.py -e py27,py35,flakes
$ python3 runtox.py -e linting,py27,py35
This command will run tests via the "tox" tool against Python 2.7 and 3.5
and also perform "flakes" coding-style checks. ``runtox.py`` is
and also perform "lint" coding-style checks. ``runtox.py`` is
a thin wrapper around ``tox`` which installs from a development package
index where newer (not yet released to pypi) versions of dependencies
index where newer (not yet released to PyPI) versions of dependencies
(especially ``py``) might be present.
#. You can now edit your local working copy.
You can now make the changes you want and run the tests again as necessary.
To run tests on py27 and pass options to pytest (e.g. enter pdb on failure)
to pytest you can do::
To run tests on Python 2.7 and pass options to pytest (e.g. enter pdb on
failure) to pytest you can do::
$ python runtox.py -e py27 -- --pdb
$ python3 runtox.py -e py27 -- --pdb
or to only run tests in a particular test module on py35::
Or to only run tests in a particular test module on Python 3.5::
$ python runtox.py -e py35 -- testing/test_config.py
$ python3 runtox.py -e py35 -- testing/test_config.py
#. Commit and push once your tests pass and you are happy with your change(s)::
$ git commit -a -m "<commit message>"
$ git push -u
Make sure you add a CHANGELOG message, and add yourself to AUTHORS. If you
are unsure about either of these steps, submit your pull request and we'll
help you fix it up.
Make sure you add a message to ``CHANGELOG.rst`` and add yourself to
``AUTHORS``. If you are unsure about either of these steps, submit your
pull request and we'll help you fix it up.
#. Finally, submit a pull request through the GitHub website using this data::
@@ -216,6 +236,6 @@ but here is a simple overview:
base-fork: pytest-dev/pytest
base: master # if it's a bugfix
base: feature # if it's a feature
base: features # if it's a feature

View File

@@ -3,6 +3,10 @@ How to release pytest
Note: this assumes you have already registered on pypi.
0. create the branch release-VERSION
use features as base for minor/major releases
and master as base for bugfix releases
1. Bump version numbers in _pytest/__init__.py (setup.py reads it)
2. Check and finalize CHANGELOG
@@ -27,10 +31,6 @@ Note: this assumes you have already registered on pypi.
devpi list pytest
or look at failures with "devpi list -f pytest".
There will be some failed environments like e.g. the py33-trial
or py27-pexpect tox environments on Win32 platforms
which is ok (tox does not support skipping on
per-platform basis yet).
7. Regenerate the docs examples using tox, and check for regressions::
@@ -41,7 +41,7 @@ Note: this assumes you have already registered on pypi.
8. Build the docs, you need a virtualenv with py and sphinx
installed::
cd doc/en
cd doc/en
make html
Commit any changes before tagging the release.
@@ -71,7 +71,7 @@ Note: this assumes you have already registered on pypi.
11. Publish to pypi::
devpi push pytest-VERSION pypi:NAME
devpi push pytest==VERSION pypi:NAME
where NAME is the name of pypi.python.org as configured in your ``~/.pypirc``
file `for devpi <http://doc.devpi.net/latest/quickstart-releaseprocess.html?highlight=pypirc#devpi-push-releasing-to-an-external-index>`_.
@@ -88,4 +88,5 @@ Note: this assumes you have already registered on pypi.
to the next Minor release version (i.e. if you released ``pytest-2.8.0``,
set it to ``pytest-2.9.0.dev1``).
14. merge the actual release into the features branch and do a pull request against it
14. merge the actual release into the master branch and do a pull request against it
15. merge from master to features

View File

@@ -18,6 +18,7 @@ Unlike mock, "args.path" acts on the parsed auto-spec'ed ``os.path.abspath``
so it's independent from if the client side called "os.path.abspath(path=...)"
or "os.path.abspath('positional')".
refine parametrize API
-------------------------------------------------------------
tags: critical feature

36
LICENSE
View File

@@ -1,19 +1,21 @@
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Copyright (c) 2004-2016 Holger Krekel and others
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,4 +1,4 @@
include CHANGELOG
include CHANGELOG.rst
include LICENSE
include AUTHORS

View File

@@ -1,12 +1,14 @@
======
pytest
======
.. image:: http://docs.pytest.org/en/latest/_static/pytest1.png
:target: http://docs.pytest.org
:align: center
:alt: pytest
The ``pytest`` testing tool makes it easy to write small tests, yet
scales to support complex functional testing.
------
.. image:: https://img.shields.io/pypi/v/pytest.svg
:target: https://pypi.python.org/pypi/pytest
.. image:: https://img.shields.io/pypi/pyversions/pytest.svg
:target: https://pypi.python.org/pypi/pytest
.. image:: https://img.shields.io/coveralls/pytest-dev/pytest/master.svg
:target: https://coveralls.io/r/pytest-dev/pytest
.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master
@@ -14,53 +16,88 @@ scales to support complex functional testing.
.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true
:target: https://ci.appveyor.com/project/pytestbot/pytest
Documentation: http://pytest.org/latest/
The ``pytest`` framework makes it easy to write small tests, yet
scales to support complex functional testing for applications and libraries.
Changelog: http://pytest.org/latest/changelog.html
An example of a simple test:
.. code-block:: python
# content of test_sample.py
def func(x):
return x + 1
def test_answer():
assert func(3) == 5
To execute it::
$ pytest
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
collected 1 items
test_sample.py F
======= FAILURES ========
_______ test_answer ________
def test_answer():
> assert func(3) == 5
E assert 4 == 5
E + where 4 = func(3)
test_sample.py:5: AssertionError
======= 1 failed in 0.12 seconds ========
Due to ``py.test``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://docs.pytest.org/en/latest/getting-started.html#our-first-test-run>`_ for more examples.
Issues: https://github.com/pytest-dev/pytest/issues
Features
--------
- `auto-discovery
<http://pytest.org/latest/goodpractises.html#python-test-discovery>`_
of test modules and functions,
- detailed info on failing `assert statements <http://pytest.org/latest/assert.html>`_ (no need to remember ``self.assert*`` names)
- `modular fixtures <http://pytest.org/latest/fixture.html>`_ for
managing small or parametrized long-lived test resources.
- multi-paradigm support: you can use ``pytest`` to run test suites based
on `unittest <http://pytest.org/latest/unittest.html>`_ (or trial),
`nose <http://pytest.org/latest/nose.html>`_
- single-source compatibility from Python2.6 all the way up to
Python3.5, PyPy-2.3, (jython-2.5 untested)
- Detailed info on failing `assert statements <http://docs.pytest.org/en/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
- `Auto-discovery
<http://docs.pytest.org/en/latest/goodpractices.html#python-test-discovery>`_
of test modules and functions;
- `Modular fixtures <http://docs.pytest.org/en/latest/fixture.html>`_ for
managing small or parametrized long-lived test resources;
- Can run `unittest <http://docs.pytest.org/en/latest/unittest.html>`_ (or trial),
`nose <http://docs.pytest.org/en/latest/nose.html>`_ test suites out of the box;
- Python2.6+, Python3.3+, PyPy-2.3, Jython-2.5 (untested);
- Rich plugin architecture, with over 150+ `external plugins <http://docs.pytest.org/en/latest/plugins.html#installing-external-plugins-searching>`_ and thriving community;
- many `external plugins <http://pytest.org/latest/plugins.html#installing-external-plugins-searching>`_.
Documentation
-------------
A simple example for a test:
.. code-block:: python
# content of test_module.py
def test_function():
i = 4
assert i == 3
which can be run with ``py.test test_module.py``. See `getting-started <http://pytest.org/latest/getting-started.html#our-first-test-run>`_ for more examples.
For much more info, including PDF docs, see
http://pytest.org
and report bugs at:
https://github.com/pytest-dev/pytest/issues
and checkout or fork repo at:
https://github.com/pytest-dev/pytest
For full documentation, including installation, tutorials and PDF documents, please see http://docs.pytest.org.
Copyright Holger Krekel and others, 2004-2015
Licensed under the MIT license.
Bugs/Requests
-------------
Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issues>`_ to submit bugs or request features.
Changelog
---------
Consult the `Changelog <http://docs.pytest.org/en/latest/changelog.html>`_ page for fixes and enhancements of each version.
License
-------
Copyright Holger Krekel and others, 2004-2016.
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE

View File

@@ -1,2 +1,2 @@
#
__version__ = '2.8.5'
__version__ = '3.0.0'

View File

@@ -88,9 +88,6 @@ class FastFilesCompleter:
return completion
if os.environ.get('_ARGCOMPLETE'):
# argcomplete 0.5.6 is not compatible with python 2.5.6: print/with/format
if sys.version_info[:2] < (2, 6):
sys.exit(1)
try:
import argcomplete.completers
except ImportError:

View File

@@ -0,0 +1,9 @@
""" python inspection/code generation API """
from .code import Code # noqa
from .code import ExceptionInfo # noqa
from .code import Frame # noqa
from .code import Traceback # noqa
from .code import getrawcode # noqa
from .source import Source # noqa
from .source import compile_ as compile # noqa
from .source import getfslineno # noqa

View File

@@ -0,0 +1,81 @@
# copied from python-2.7.3's traceback.py
# CHANGES:
# - some_str is replaced, trying to create unicode strings
#
import types
def format_exception_only(etype, value):
"""Format the exception part of a traceback.
The arguments are the exception type and value such as given by
sys.last_type and sys.last_value. The return value is a list of
strings, each ending in a newline.
Normally, the list contains a single string; however, for
SyntaxError exceptions, it contains several lines that (when
printed) display detailed information about where the syntax
error occurred.
The message indicating which exception occurred is always the last
string in the list.
"""
# An instance should not have a meaningful value parameter, but
# sometimes does, particularly for string exceptions, such as
# >>> raise string1, string2 # deprecated
#
# Clear these out first because issubtype(string1, SyntaxError)
# would throw another exception and mask the original problem.
if (isinstance(etype, BaseException) or
isinstance(etype, types.InstanceType) or
etype is None or type(etype) is str):
return [_format_final_exc_line(etype, value)]
stype = etype.__name__
if not issubclass(etype, SyntaxError):
return [_format_final_exc_line(stype, value)]
# It was a syntax error; show exactly where the problem was found.
lines = []
try:
msg, (filename, lineno, offset, badline) = value.args
except Exception:
pass
else:
filename = filename or "<string>"
lines.append(' File "%s", line %d\n' % (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())
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))
value = msg
lines.append(_format_final_exc_line(stype, value))
return lines
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
else:
line = "%s: %s\n" % (etype, valuestr)
return line
def _some_str(value):
try:
return unicode(value)
except Exception:
try:
return str(value)
except Exception:
pass
return '<unprintable %s object>' % type(value).__name__

848
_pytest/_code/code.py Normal file
View File

@@ -0,0 +1,848 @@
import sys
from inspect import CO_VARARGS, CO_VARKEYWORDS
import re
import py
builtin_repr = repr
reprlib = py.builtin._tryimport('repr', 'reprlib')
if sys.version_info[0] >= 3:
from traceback import format_exception_only
else:
from ._py2traceback import format_exception_only
class Code(object):
""" wrapper around Python code objects """
def __init__(self, rawcode):
if not hasattr(rawcode, "co_filename"):
rawcode = getrawcode(rawcode)
try:
self.filename = rawcode.co_filename
self.firstlineno = rawcode.co_firstlineno - 1
self.name = rawcode.co_name
except AttributeError:
raise TypeError("not a code object: %r" %(rawcode,))
self.raw = rawcode
def __eq__(self, other):
return self.raw == other.raw
def __ne__(self, other):
return not self == other
@property
def path(self):
""" return a path object pointing to source code (note that it
might not point to an actually existing file). """
try:
p = py.path.local(self.raw.co_filename)
# maybe don't try this checking
if not p.check():
raise OSError("py.path check failed.")
except OSError:
# XXX maybe try harder like the weird logic
# in the standard lib [linecache.updatecache] does?
p = self.raw.co_filename
return p
@property
def fullsource(self):
""" return a _pytest._code.Source object for the full source file of the code
"""
from _pytest._code import source
full, _ = source.findsource(self.raw)
return full
def source(self):
""" return a _pytest._code.Source object for the code object's source only
"""
# return source only for that part of code
import _pytest._code
return _pytest._code.Source(self.raw)
def getargs(self, var=False):
""" return a tuple with the argument names for the code object
if 'var' is set True also return the names of the variable and
keyword arguments when present
"""
# handfull shortcut for getting args
raw = self.raw
argcount = raw.co_argcount
if var:
argcount += raw.co_flags & CO_VARARGS
argcount += raw.co_flags & CO_VARKEYWORDS
return raw.co_varnames[:argcount]
class Frame(object):
"""Wrapper around a Python frame holding f_locals and f_globals
in which expressions can be evaluated."""
def __init__(self, frame):
self.lineno = frame.f_lineno - 1
self.f_globals = frame.f_globals
self.f_locals = frame.f_locals
self.raw = frame
self.code = Code(frame.f_code)
@property
def statement(self):
""" statement this frame is at """
import _pytest._code
if self.code.fullsource is None:
return _pytest._code.Source("")
return self.code.fullsource.getstatement(self.lineno)
def eval(self, code, **vars):
""" evaluate 'code' in the frame
'vars' are optional additional local variables
returns the result of the evaluation
"""
f_locals = self.f_locals.copy()
f_locals.update(vars)
return eval(code, self.f_globals, f_locals)
def exec_(self, code, **vars):
""" exec 'code' in the frame
'vars' are optiona; additional local variables
"""
f_locals = self.f_locals.copy()
f_locals.update(vars)
py.builtin.exec_(code, self.f_globals, f_locals )
def repr(self, object):
""" return a 'safe' (non-recursive, one-line) string repr for 'object'
"""
return py.io.saferepr(object)
def is_true(self, object):
return object
def getargs(self, var=False):
""" return a list of tuples (name, value) for all arguments
if 'var' is set True also include the variable and keyword
arguments when present
"""
retval = []
for arg in self.code.getargs(var):
try:
retval.append((arg, self.f_locals[arg]))
except KeyError:
pass # this can occur when using Psyco
return retval
class TracebackEntry(object):
""" a single entry in a traceback """
_repr_style = None
exprinfo = None
def __init__(self, rawentry, excinfo=None):
self._excinfo = excinfo
self._rawentry = rawentry
self.lineno = rawentry.tb_lineno - 1
def set_repr_style(self, mode):
assert mode in ("short", "long")
self._repr_style = mode
@property
def frame(self):
import _pytest._code
return _pytest._code.Frame(self._rawentry.tb_frame)
@property
def relline(self):
return self.lineno - self.frame.code.firstlineno
def __repr__(self):
return "<TracebackEntry %s:%d>" %(self.frame.code.path, self.lineno+1)
@property
def statement(self):
""" _pytest._code.Source object for the current statement """
source = self.frame.code.fullsource
return source.getstatement(self.lineno)
@property
def path(self):
""" path to the source code """
return self.frame.code.path
def getlocals(self):
return self.frame.f_locals
locals = property(getlocals, None, None, "locals of underlaying frame")
def getfirstlinesource(self):
# on Jython this firstlineno can be -1 apparently
return max(self.frame.code.firstlineno, 0)
def getsource(self, astcache=None):
""" return failing source code. """
# we use the passed in astcache to not reparse asttrees
# within exception info printing
from _pytest._code.source import getstatementrange_ast
source = self.frame.code.fullsource
if source is None:
return None
key = astnode = None
if astcache is not None:
key = self.frame.code.path
if key is not None:
astnode = astcache.get(key, None)
start = self.getfirstlinesource()
try:
astnode, _, end = getstatementrange_ast(self.lineno, source,
astnode=astnode)
except SyntaxError:
end = self.lineno + 1
else:
if key is not None:
astcache[key] = astnode
return source[start:end]
source = property(getsource)
def ishidden(self):
""" return True if the current frame has a var __tracebackhide__
resolving to True
If __tracebackhide__ is a callable, it gets called with the
ExceptionInfo instance and can decide whether to hide the traceback.
mostly for internal use
"""
try:
tbh = self.frame.f_locals['__tracebackhide__']
except KeyError:
try:
tbh = self.frame.f_globals['__tracebackhide__']
except KeyError:
return False
if py.builtin.callable(tbh):
return tbh(self._excinfo)
else:
return tbh
def __str__(self):
try:
fn = str(self.path)
except py.error.Error:
fn = '???'
name = self.frame.code.name
try:
line = str(self.statement).lstrip()
except KeyboardInterrupt:
raise
except:
line = "???"
return " File %r:%d in %s\n %s\n" %(fn, self.lineno+1, name, line)
def name(self):
return self.frame.code.raw.co_name
name = property(name, None, None, "co_name of underlaying code")
class Traceback(list):
""" Traceback objects encapsulate and offer higher level
access to Traceback entries.
"""
Entry = TracebackEntry
def __init__(self, tb, excinfo=None):
""" initialize from given python traceback object and ExceptionInfo """
self._excinfo = excinfo
if hasattr(tb, 'tb_next'):
def f(cur):
while cur is not None:
yield self.Entry(cur, excinfo=excinfo)
cur = cur.tb_next
list.__init__(self, f(tb))
else:
list.__init__(self, tb)
def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None):
""" return a Traceback instance wrapping part of this Traceback
by provding any combination of path, lineno and firstlineno, the
first frame to start the to-be-returned traceback is determined
this allows cutting the first part of a Traceback instance e.g.
for formatting reasons (removing some uninteresting bits that deal
with handling of the exception/traceback)
"""
for x in self:
code = x.frame.code
codepath = code.path
if ((path is None or codepath == path) and
(excludepath is None or not hasattr(codepath, 'relto') or
not codepath.relto(excludepath)) and
(lineno is None or x.lineno == lineno) and
(firstlineno is None or x.frame.code.firstlineno == firstlineno)):
return Traceback(x._rawentry, self._excinfo)
return self
def __getitem__(self, key):
val = super(Traceback, self).__getitem__(key)
if isinstance(key, type(slice(0))):
val = self.__class__(val)
return val
def filter(self, fn=lambda x: not x.ishidden()):
""" return a Traceback instance with certain items removed
fn is a function that gets a single argument, a TracebackEntry
instance, and should return True when the item should be added
to the Traceback, False when not
by default this removes all the TracebackEntries which are hidden
(see ishidden() above)
"""
return Traceback(filter(fn, self), self._excinfo)
def getcrashentry(self):
""" return last non-hidden traceback entry that lead
to the exception of a traceback.
"""
for i in range(-1, -len(self)-1, -1):
entry = self[i]
if not entry.ishidden():
return entry
return self[-1]
def recursionindex(self):
""" return the index of the frame/TracebackEntry where recursion
originates if appropriate, None if no recursion occurred
"""
cache = {}
for i, entry in enumerate(self):
# id for the code.raw is needed to work around
# the strange metaprogramming in the decorator lib from pypi
# which generates code objects that have hash/value equality
#XXX needs a test
key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno
#print "checking for recursion at", key
l = cache.setdefault(key, [])
if l:
f = entry.frame
loc = f.f_locals
for otherloc in l:
if f.is_true(f.eval(co_equal,
__recursioncache_locals_1=loc,
__recursioncache_locals_2=otherloc)):
return i
l.append(entry.frame.f_locals)
return None
co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2',
'?', 'eval')
class ExceptionInfo(object):
""" wraps sys.exc_info() objects and offers
help for navigating the traceback.
"""
_striptext = ''
def __init__(self, tup=None, exprinfo=None):
import _pytest._code
if tup is None:
tup = sys.exc_info()
if exprinfo is None and isinstance(tup[1], AssertionError):
exprinfo = getattr(tup[1], 'msg', None)
if exprinfo is None:
exprinfo = str(tup[1])
if exprinfo and exprinfo.startswith('assert '):
self._striptext = 'AssertionError: '
self._excinfo = tup
#: the exception class
self.type = tup[0]
#: the exception instance
self.value = tup[1]
#: the exception raw traceback
self.tb = tup[2]
#: the exception type name
self.typename = self.type.__name__
#: the exception traceback (_pytest._code.Traceback instance)
self.traceback = _pytest._code.Traceback(self.tb, excinfo=self)
def __repr__(self):
return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback))
def exconly(self, tryshort=False):
""" return the exception as a string
when 'tryshort' resolves to True, and the exception is a
_pytest._code._AssertionError, only the actual exception part of
the exception representation is returned (so 'AssertionError: ' is
removed from the beginning)
"""
lines = format_exception_only(self.type, self.value)
text = ''.join(lines)
text = text.rstrip()
if tryshort:
if text.startswith(self._striptext):
text = text[len(self._striptext):]
return text
def errisinstance(self, exc):
""" return True if the exception is an instance of exc """
return isinstance(self.value, exc)
def _getreprcrash(self):
exconly = self.exconly(tryshort=True)
entry = self.traceback.getcrashentry()
path, lineno = entry.frame.code.raw.co_filename, entry.lineno
return ReprFileLocation(path, lineno+1, exconly)
def getrepr(self, showlocals=False, style="long",
abspath=False, tbfilter=True, funcargs=False):
""" return str()able representation of this exception info.
showlocals: show locals per traceback entry
style: long|short|no|native traceback style
tbfilter: hide entries (where __tracebackhide__ is true)
in case of style==native, tbfilter and showlocals is ignored.
"""
if style == 'native':
return ReprExceptionInfo(ReprTracebackNative(
py.std.traceback.format_exception(
self.type,
self.value,
self.traceback[0]._rawentry,
)), self._getreprcrash())
fmt = FormattedExcinfo(showlocals=showlocals, style=style,
abspath=abspath, tbfilter=tbfilter, funcargs=funcargs)
return fmt.repr_excinfo(self)
def __str__(self):
entry = self.traceback[-1]
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
return str(loc)
def __unicode__(self):
entry = self.traceback[-1]
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
return unicode(loc)
def match(self, regexp):
"""
Match the regular expression 'regexp' on the string representation of
the exception. If it matches then True is returned (so that it is
possible to write 'assert excinfo.match()'). If it doesn't match an
AssertionError is raised.
"""
__tracebackhide__ = True
if not re.search(regexp, str(self.value)):
assert 0, "Pattern '{0!s}' not found in '{1!s}'".format(
regexp, self.value)
return True
class FormattedExcinfo(object):
""" presenting information about failing Functions and Generators. """
# for traceback entries
flow_marker = ">"
fail_marker = "E"
def __init__(self, showlocals=False, style="long", abspath=True, tbfilter=True, funcargs=False):
self.showlocals = showlocals
self.style = style
self.tbfilter = tbfilter
self.funcargs = funcargs
self.abspath = abspath
self.astcache = {}
def _getindent(self, source):
# figure out indent for given source
try:
s = str(source.getstatement(len(source)-1))
except KeyboardInterrupt:
raise
except:
try:
s = str(source[-1])
except KeyboardInterrupt:
raise
except:
return 0
return 4 + (len(s) - len(s.lstrip()))
def _getentrysource(self, entry):
source = entry.getsource(self.astcache)
if source is not None:
source = source.deindent()
return source
def _saferepr(self, obj):
return py.io.saferepr(obj)
def repr_args(self, entry):
if self.funcargs:
args = []
for argname, argvalue in entry.frame.getargs(var=True):
args.append((argname, self._saferepr(argvalue)))
return ReprFuncArgs(args)
def get_source(self, source, line_index=-1, excinfo=None, short=False):
""" return formatted and marked up source lines. """
import _pytest._code
lines = []
if source is None or line_index >= len(source.lines):
source = _pytest._code.Source("???")
line_index = 0
if line_index < 0:
line_index += len(source)
space_prefix = " "
if short:
lines.append(space_prefix + source.lines[line_index].strip())
else:
for line in source.lines[:line_index]:
lines.append(space_prefix + line)
lines.append(self.flow_marker + " " + source.lines[line_index])
for line in source.lines[line_index+1:]:
lines.append(space_prefix + line)
if excinfo is not None:
indent = 4 if short else self._getindent(source)
lines.extend(self.get_exconly(excinfo, indent=indent, markall=True))
return lines
def get_exconly(self, excinfo, indent=4, markall=False):
lines = []
indent = " " * indent
# get the real exception information out
exlines = excinfo.exconly(tryshort=True).split('\n')
failindent = self.fail_marker + indent[1:]
for line in exlines:
lines.append(failindent + line)
if not markall:
failindent = indent
return lines
def repr_locals(self, locals):
if self.showlocals:
lines = []
keys = [loc for loc in locals if loc[0] != "@"]
keys.sort()
for name in keys:
value = locals[name]
if name == '__builtins__':
lines.append("__builtins__ = <builtins>")
else:
# This formatting could all be handled by the
# _repr() function, which is only reprlib.Repr in
# disguise, so is very configurable.
str_repr = self._saferepr(value)
#if len(str_repr) < 70 or not isinstance(value,
# (list, tuple, dict)):
lines.append("%-10s = %s" %(name, str_repr))
#else:
# self._line("%-10s =\\" % (name,))
# # XXX
# py.std.pprint.pprint(value, stream=self.excinfowriter)
return ReprLocals(lines)
def repr_traceback_entry(self, entry, excinfo=None):
import _pytest._code
source = self._getentrysource(entry)
if source is None:
source = _pytest._code.Source("???")
line_index = 0
else:
# entry.getfirstlinesource() can be -1, should be 0 on jython
line_index = entry.lineno - max(entry.getfirstlinesource(), 0)
lines = []
style = entry._repr_style
if style is None:
style = self.style
if style in ("short", "long"):
short = style == "short"
reprargs = self.repr_args(entry) if not short else None
s = self.get_source(source, line_index, excinfo, short=short)
lines.extend(s)
if short:
message = "in %s" %(entry.name)
else:
message = excinfo and excinfo.typename or ""
path = self._makepath(entry.path)
filelocrepr = ReprFileLocation(path, entry.lineno+1, message)
localsrepr = None
if not short:
localsrepr = self.repr_locals(entry.locals)
return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style)
if excinfo:
lines.extend(self.get_exconly(excinfo, indent=4))
return ReprEntry(lines, None, None, None, style)
def _makepath(self, path):
if not self.abspath:
try:
np = py.path.local().bestrelpath(path)
except OSError:
return path
if len(np) < len(str(path)):
path = np
return path
def repr_traceback(self, excinfo):
traceback = excinfo.traceback
if self.tbfilter:
traceback = traceback.filter()
recursionindex = None
if is_recursion_error(excinfo):
recursionindex = traceback.recursionindex()
last = traceback[-1]
entries = []
extraline = None
for index, entry in enumerate(traceback):
einfo = (last == entry) and excinfo or None
reprentry = self.repr_traceback_entry(entry, einfo)
entries.append(reprentry)
if index == recursionindex:
extraline = "!!! Recursion detected (same locals & position)"
break
return ReprTraceback(entries, extraline, style=self.style)
def repr_excinfo(self, excinfo):
if sys.version_info[0] < 3:
reprtraceback = self.repr_traceback(excinfo)
reprcrash = excinfo._getreprcrash()
return ReprExceptionInfo(reprtraceback, reprcrash)
else:
repr_chain = []
e = excinfo.value
descr = None
while e is not None:
reprtraceback = self.repr_traceback(excinfo)
reprcrash = excinfo._getreprcrash()
repr_chain += [(reprtraceback, reprcrash, descr)]
if e.__cause__ is not None:
e = e.__cause__
excinfo = ExceptionInfo((type(e), e, e.__traceback__))
descr = 'The above exception was the direct cause of the following exception:'
elif e.__context__ is not None:
e = e.__context__
excinfo = ExceptionInfo((type(e), e, e.__traceback__))
descr = 'During handling of the above exception, another exception occurred:'
else:
e = None
repr_chain.reverse()
return ExceptionChainRepr(repr_chain)
class TerminalRepr(object):
def __str__(self):
s = self.__unicode__()
if sys.version_info[0] < 3:
s = s.encode('utf-8')
return s
def __unicode__(self):
# FYI this is called from pytest-xdist's serialization of exception
# information.
io = py.io.TextIO()
tw = py.io.TerminalWriter(file=io)
self.toterminal(tw)
return io.getvalue().strip()
def __repr__(self):
return "<%s instance at %0x>" %(self.__class__, id(self))
class ExceptionRepr(TerminalRepr):
def __init__(self):
self.sections = []
def addsection(self, name, content, sep="-"):
self.sections.append((name, content, sep))
def toterminal(self, tw):
for name, content, sep in self.sections:
tw.sep(sep, name)
tw.line(content)
class ExceptionChainRepr(ExceptionRepr):
def __init__(self, chain):
super(ExceptionChainRepr, self).__init__()
self.chain = chain
# reprcrash and reprtraceback of the outermost (the newest) exception
# in the chain
self.reprtraceback = chain[-1][0]
self.reprcrash = chain[-1][1]
def toterminal(self, tw):
for element in self.chain:
element[0].toterminal(tw)
if element[2] is not None:
tw.line("")
tw.line(element[2], yellow=True)
super(ExceptionChainRepr, self).toterminal(tw)
class ReprExceptionInfo(ExceptionRepr):
def __init__(self, reprtraceback, reprcrash):
super(ReprExceptionInfo, self).__init__()
self.reprtraceback = reprtraceback
self.reprcrash = reprcrash
def toterminal(self, tw):
self.reprtraceback.toterminal(tw)
super(ReprExceptionInfo, self).toterminal(tw)
class ReprTraceback(TerminalRepr):
entrysep = "_ "
def __init__(self, reprentries, extraline, style):
self.reprentries = reprentries
self.extraline = extraline
self.style = style
def toterminal(self, tw):
# the entries might have different styles
for i, entry in enumerate(self.reprentries):
if entry.style == "long":
tw.line("")
entry.toterminal(tw)
if i < len(self.reprentries) - 1:
next_entry = self.reprentries[i+1]
if entry.style == "long" or \
entry.style == "short" and next_entry.style == "long":
tw.sep(self.entrysep)
if self.extraline:
tw.line(self.extraline)
class ReprTracebackNative(ReprTraceback):
def __init__(self, tblines):
self.style = "native"
self.reprentries = [ReprEntryNative(tblines)]
self.extraline = None
class ReprEntryNative(TerminalRepr):
style = "native"
def __init__(self, tblines):
self.lines = tblines
def toterminal(self, tw):
tw.write("".join(self.lines))
class ReprEntry(TerminalRepr):
localssep = "_ "
def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, style):
self.lines = lines
self.reprfuncargs = reprfuncargs
self.reprlocals = reprlocals
self.reprfileloc = filelocrepr
self.style = style
def toterminal(self, tw):
if self.style == "short":
self.reprfileloc.toterminal(tw)
for line in self.lines:
red = line.startswith("E ")
tw.line(line, bold=True, red=red)
#tw.line("")
return
if self.reprfuncargs:
self.reprfuncargs.toterminal(tw)
for line in self.lines:
red = line.startswith("E ")
tw.line(line, bold=True, red=red)
if self.reprlocals:
#tw.sep(self.localssep, "Locals")
tw.line("")
self.reprlocals.toterminal(tw)
if self.reprfileloc:
if self.lines:
tw.line("")
self.reprfileloc.toterminal(tw)
def __str__(self):
return "%s\n%s\n%s" % ("\n".join(self.lines),
self.reprlocals,
self.reprfileloc)
class ReprFileLocation(TerminalRepr):
def __init__(self, path, lineno, message):
self.path = str(path)
self.lineno = lineno
self.message = message
def toterminal(self, tw):
# filename and lineno output for each entry,
# using an output format that most editors unterstand
msg = self.message
i = msg.find("\n")
if i != -1:
msg = msg[:i]
tw.write(self.path, bold=True, red=True)
tw.line(":%s: %s" % (self.lineno, msg))
class ReprLocals(TerminalRepr):
def __init__(self, lines):
self.lines = lines
def toterminal(self, tw):
for line in self.lines:
tw.line(line)
class ReprFuncArgs(TerminalRepr):
def __init__(self, args):
self.args = args
def toterminal(self, tw):
if self.args:
linesofar = ""
for name, value in self.args:
ns = "%s = %s" %(name, value)
if len(ns) + len(linesofar) + 2 > tw.fullwidth:
if linesofar:
tw.line(linesofar)
linesofar = ns
else:
if linesofar:
linesofar += ", " + ns
else:
linesofar = ns
if linesofar:
tw.line(linesofar)
tw.line("")
def getrawcode(obj, trycall=True):
""" return code object for given function. """
try:
return obj.__code__
except AttributeError:
obj = getattr(obj, 'im_func', obj)
obj = getattr(obj, 'func_code', obj)
obj = getattr(obj, 'f_code', obj)
obj = getattr(obj, '__code__', obj)
if trycall and not hasattr(obj, 'co_firstlineno'):
if hasattr(obj, '__call__') and not py.std.inspect.isclass(obj):
x = getrawcode(obj.__call__, trycall=False)
if hasattr(x, 'co_firstlineno'):
return x
return obj
if sys.version_info[:2] >= (3, 5): # RecursionError introduced in 3.5
def is_recursion_error(excinfo):
return excinfo.errisinstance(RecursionError) # noqa
else:
def is_recursion_error(excinfo):
if not excinfo.errisinstance(RuntimeError):
return False
try:
return "maximum recursion depth exceeded" in str(excinfo.value)
except UnicodeError:
return False

421
_pytest/_code/source.py Normal file
View File

@@ -0,0 +1,421 @@
from __future__ import generators
from bisect import bisect_right
import sys
import inspect, tokenize
import py
from types import ModuleType
cpy_compile = compile
try:
import _ast
from _ast import PyCF_ONLY_AST as _AST_FLAG
except ImportError:
_AST_FLAG = 0
_ast = None
class Source(object):
""" a immutable object holding a source code fragment,
possibly deindenting it.
"""
_compilecounter = 0
def __init__(self, *parts, **kwargs):
self.lines = lines = []
de = kwargs.get('deindent', True)
rstrip = kwargs.get('rstrip', True)
for part in parts:
if not part:
partlines = []
if isinstance(part, Source):
partlines = part.lines
elif isinstance(part, (tuple, list)):
partlines = [x.rstrip("\n") for x in part]
elif isinstance(part, py.builtin._basestring):
partlines = part.split('\n')
if rstrip:
while partlines:
if partlines[-1].strip():
break
partlines.pop()
else:
partlines = getsource(part, deindent=de).lines
if de:
partlines = deindent(partlines)
lines.extend(partlines)
def __eq__(self, other):
try:
return self.lines == other.lines
except AttributeError:
if isinstance(other, str):
return str(self) == other
return False
def __getitem__(self, key):
if isinstance(key, int):
return self.lines[key]
else:
if key.step not in (None, 1):
raise IndexError("cannot slice a Source with a step")
return self.__getslice__(key.start, key.stop)
def __len__(self):
return len(self.lines)
def __getslice__(self, start, end):
newsource = Source()
newsource.lines = self.lines[start:end]
return newsource
def strip(self):
""" return new source object with trailing
and leading blank lines removed.
"""
start, end = 0, len(self)
while start < end and not self.lines[start].strip():
start += 1
while end > start and not self.lines[end-1].strip():
end -= 1
source = Source()
source.lines[:] = self.lines[start:end]
return source
def putaround(self, before='', after='', indent=' ' * 4):
""" return a copy of the source object with
'before' and 'after' wrapped around it.
"""
before = Source(before)
after = Source(after)
newsource = Source()
lines = [ (indent + line) for line in self.lines]
newsource.lines = before.lines + lines + after.lines
return newsource
def indent(self, indent=' ' * 4):
""" return a copy of the source object with
all lines indented by the given indent-string.
"""
newsource = Source()
newsource.lines = [(indent+line) for line in self.lines]
return newsource
def getstatement(self, lineno, assertion=False):
""" return Source statement which contains the
given linenumber (counted from 0).
"""
start, end = self.getstatementrange(lineno, assertion)
return self[start:end]
def getstatementrange(self, lineno, assertion=False):
""" return (start, end) tuple which spans the minimal
statement region which containing the given lineno.
"""
if not (0 <= lineno < len(self)):
raise IndexError("lineno out of range")
ast, start, end = getstatementrange_ast(lineno, self)
return start, end
def deindent(self, offset=None):
""" return a new source object deindented by offset.
If offset is None then guess an indentation offset from
the first non-blank line. Subsequent lines which have a
lower indentation offset will be copied verbatim as
they are assumed to be part of multilines.
"""
# XXX maybe use the tokenizer to properly handle multiline
# strings etc.pp?
newsource = Source()
newsource.lines[:] = deindent(self.lines, offset)
return newsource
def isparseable(self, deindent=True):
""" return True if source is parseable, heuristically
deindenting it by default.
"""
try:
import parser
except ImportError:
syntax_checker = lambda x: compile(x, 'asd', 'exec')
else:
syntax_checker = parser.suite
if deindent:
source = str(self.deindent())
else:
source = str(self)
try:
#compile(source+'\n', "x", "exec")
syntax_checker(source+'\n')
except KeyboardInterrupt:
raise
except Exception:
return False
else:
return True
def __str__(self):
return "\n".join(self.lines)
def compile(self, filename=None, mode='exec',
flag=generators.compiler_flag,
dont_inherit=0, _genframe=None):
""" return compiled code object. if filename is None
invent an artificial filename which displays
the source/line position of the caller frame.
"""
if not filename or py.path.local(filename).check(file=0):
if _genframe is None:
_genframe = sys._getframe(1) # the caller
fn,lineno = _genframe.f_code.co_filename, _genframe.f_lineno
base = "<%d-codegen " % self._compilecounter
self.__class__._compilecounter += 1
if not filename:
filename = base + '%s:%d>' % (fn, lineno)
else:
filename = base + '%r %s:%d>' % (filename, fn, lineno)
source = "\n".join(self.lines) + '\n'
try:
co = cpy_compile(source, filename, mode, flag)
except SyntaxError:
ex = sys.exc_info()[1]
# re-represent syntax errors from parsing python strings
msglines = self.lines[:ex.lineno]
if ex.offset:
msglines.append(" "*ex.offset + '^')
msglines.append("(code was compiled probably from here: %s)" % filename)
newex = SyntaxError('\n'.join(msglines))
newex.offset = ex.offset
newex.lineno = ex.lineno
newex.text = ex.text
raise newex
else:
if flag & _AST_FLAG:
return co
lines = [(x + "\n") for x in self.lines]
if sys.version_info[0] >= 3:
# XXX py3's inspect.getsourcefile() checks for a module
# and a pep302 __loader__ ... we don't have a module
# at code compile-time so we need to fake it here
m = ModuleType("_pycodecompile_pseudo_module")
py.std.inspect.modulesbyfile[filename] = None
py.std.sys.modules[None] = m
m.__loader__ = 1
py.std.linecache.cache[filename] = (1, None, lines, filename)
return co
#
# public API shortcut functions
#
def compile_(source, filename=None, mode='exec', flags=
generators.compiler_flag, dont_inherit=0):
""" compile the given source to a raw code object,
and maintain an internal cache which allows later
retrieval of the source code for the code object
and any recursively created code objects.
"""
if _ast is not None and isinstance(source, _ast.AST):
# XXX should Source support having AST?
return cpy_compile(source, filename, mode, flags, dont_inherit)
_genframe = sys._getframe(1) # the caller
s = Source(source)
co = s.compile(filename, mode, flags, _genframe=_genframe)
return co
def getfslineno(obj):
""" Return source location (path, lineno) for the given object.
If the source cannot be determined return ("", -1)
"""
import _pytest._code
try:
code = _pytest._code.Code(obj)
except TypeError:
try:
fn = (py.std.inspect.getsourcefile(obj) or
py.std.inspect.getfile(obj))
except TypeError:
return "", -1
fspath = fn and py.path.local(fn) or None
lineno = -1
if fspath:
try:
_, lineno = findsource(obj)
except IOError:
pass
else:
fspath = code.path
lineno = code.firstlineno
assert isinstance(lineno, int)
return fspath, lineno
#
# helper functions
#
def findsource(obj):
try:
sourcelines, lineno = py.std.inspect.findsource(obj)
except py.builtin._sysex:
raise
except:
return None, -1
source = Source()
source.lines = [line.rstrip() for line in sourcelines]
return source, lineno
def getsource(obj, **kwargs):
import _pytest._code
obj = _pytest._code.getrawcode(obj)
try:
strsrc = inspect.getsource(obj)
except IndentationError:
strsrc = "\"Buggy python version consider upgrading, cannot get source\""
assert isinstance(strsrc, str)
return Source(strsrc, **kwargs)
def deindent(lines, offset=None):
if offset is None:
for line in lines:
line = line.expandtabs()
s = line.lstrip()
if s:
offset = len(line)-len(s)
break
else:
offset = 0
if offset == 0:
return list(lines)
newlines = []
def readline_generator(lines):
for line in lines:
yield line + '\n'
while True:
yield ''
it = readline_generator(lines)
try:
for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(lambda: next(it)):
if sline > len(lines):
break # End of input reached
if sline > len(newlines):
line = lines[sline - 1].expandtabs()
if line.lstrip() and line[:offset].isspace():
line = line[offset:] # Deindent
newlines.append(line)
for i in range(sline, eline):
# Don't deindent continuing lines of
# multiline tokens (i.e. multiline strings)
newlines.append(lines[i])
except (IndentationError, tokenize.TokenError):
pass
# Add any lines we didn't see. E.g. if an exception was raised.
newlines.extend(lines[len(newlines):])
return newlines
def get_statement_startend2(lineno, node):
import ast
# flatten all statements and except handlers into one lineno-list
# AST's line numbers start indexing at 1
l = []
for x in ast.walk(node):
if isinstance(x, _ast.stmt) or isinstance(x, _ast.ExceptHandler):
l.append(x.lineno - 1)
for name in "finalbody", "orelse":
val = getattr(x, name, None)
if val:
# treat the finally/orelse part as its own statement
l.append(val[0].lineno - 1 - 1)
l.sort()
insert_index = bisect_right(l, lineno)
start = l[insert_index - 1]
if insert_index >= len(l):
end = None
else:
end = l[insert_index]
return start, end
def getstatementrange_ast(lineno, source, assertion=False, astnode=None):
if astnode is None:
content = str(source)
if sys.version_info < (2,7):
content += "\n"
try:
astnode = compile(content, "source", "exec", 1024) # 1024 for AST
except ValueError:
start, end = getstatementrange_old(lineno, source, assertion)
return None, start, end
start, end = get_statement_startend2(lineno, astnode)
# we need to correct the end:
# - ast-parsing strips comments
# - there might be empty lines
# - we might have lesser indented code blocks at the end
if end is None:
end = len(source.lines)
if end > start + 1:
# make sure we don't span differently indented code blocks
# by using the BlockFinder helper used which inspect.getsource() uses itself
block_finder = inspect.BlockFinder()
# if we start with an indented line, put blockfinder to "started" mode
block_finder.started = source.lines[start][0].isspace()
it = ((x + "\n") for x in source.lines[start:end])
try:
for tok in tokenize.generate_tokens(lambda: next(it)):
block_finder.tokeneater(*tok)
except (inspect.EndOfBlock, IndentationError):
end = block_finder.last + start
except Exception:
pass
# the end might still point to a comment or empty line, correct it
while end:
line = source.lines[end - 1].lstrip()
if line.startswith("#") or not line:
end -= 1
else:
break
return astnode, start, end
def getstatementrange_old(lineno, source, assertion=False):
""" return (start, end) tuple which spans the minimal
statement region which containing the given lineno.
raise an IndexError if no such statementrange can be found.
"""
# XXX this logic is only used on python2.4 and below
# 1. find the start of the statement
from codeop import compile_command
for start in range(lineno, -1, -1):
if assertion:
line = source.lines[start]
# the following lines are not fully tested, change with care
if 'super' in line and 'self' in line and '__init__' in line:
raise IndexError("likely a subclass")
if "assert" not in line and "raise" not in line:
continue
trylines = source.lines[start:lineno+1]
# quick hack to prepare parsing an indented line with
# compile_command() (which errors on "return" outside defs)
trylines.insert(0, 'def xxx():')
trysource = '\n '.join(trylines)
# ^ space here
try:
compile_command(trysource)
except (SyntaxError, OverflowError, ValueError):
continue
# 2. find the end of the statement
for end in range(lineno+1, len(source)+1):
trysource = source[start:end]
if trysource.isparseable():
return start, end
raise SyntaxError("no valid source range around line %d " % (lineno,))

View File

@@ -2,9 +2,11 @@
support for presenting detailed information in failing assertions.
"""
import py
import os
import sys
from _pytest.monkeypatch import monkeypatch
from _pytest.assertion import util
from _pytest.assertion import rewrite
def pytest_addoption(parser):
@@ -12,25 +14,43 @@ def pytest_addoption(parser):
group.addoption('--assert',
action="store",
dest="assertmode",
choices=("rewrite", "reinterp", "plain",),
choices=("rewrite", "plain",),
default="rewrite",
metavar="MODE",
help="""control assertion debugging tools. 'plain'
performs no assertion debugging. 'reinterp'
reinterprets assert statements after they failed
to provide assertion expression information.
'rewrite' (the default) rewrites assert
statements in test modules on import to
provide assert expression information. """)
group.addoption('--no-assert',
action="store_true",
default=False,
dest="noassert",
help="DEPRECATED equivalent to --assert=plain")
group.addoption('--nomagic', '--no-magic',
action="store_true",
default=False,
help="DEPRECATED equivalent to --assert=plain")
help="""Control assertion debugging tools. 'plain'
performs no assertion debugging. 'rewrite'
(the default) rewrites assert statements in
test modules on import to provide assert
expression information.""")
def pytest_namespace():
return {'register_assert_rewrite': register_assert_rewrite}
def register_assert_rewrite(*names):
"""Register a module name to be rewritten on import.
This function will make sure that this module or all modules inside
the package will get their assert statements rewritten.
Thus you should make sure to call this before the module is
actually imported, usually in your __init__.py if you are a plugin
using a package.
"""
for hook in sys.meta_path:
if isinstance(hook, rewrite.AssertionRewritingHook):
importhook = hook
break
else:
importhook = DummyRewriteHook()
importhook.mark_rewrite(*names)
class DummyRewriteHook(object):
"""A no-op import hook for when rewriting is disabled."""
def mark_rewrite(self, *names):
pass
class AssertionState:
@@ -39,51 +59,43 @@ class AssertionState:
def __init__(self, config, mode):
self.mode = mode
self.trace = config.trace.root.get("assertion")
self.hook = None
def pytest_configure(config):
mode = config.getvalue("assertmode")
if config.getvalue("noassert") or config.getvalue("nomagic"):
mode = "plain"
if mode == "rewrite":
try:
import ast # noqa
except ImportError:
mode = "reinterp"
else:
# Both Jython and CPython 2.6.0 have AST bugs that make the
# assertion rewriting hook malfunction.
if (sys.platform.startswith('java') or
sys.version_info[:3] == (2, 6, 0)):
mode = "reinterp"
if mode != "plain":
_load_modules(mode)
m = monkeypatch()
config._cleanup.append(m.undo)
m.setattr(py.builtin.builtins, 'AssertionError',
reinterpret.AssertionError) # noqa
hook = None
if mode == "rewrite":
hook = rewrite.AssertionRewritingHook() # noqa
sys.meta_path.insert(0, hook)
warn_about_missing_assertion(mode)
config._assertstate = AssertionState(config, mode)
config._assertstate.hook = hook
config._assertstate.trace("configured with mode set to %r" % (mode,))
def install_importhook(config):
"""Try to install the rewrite hook, raise SystemError if it fails."""
# Both Jython and CPython 2.6.0 have AST bugs that make the
# assertion rewriting hook malfunction.
if (sys.platform.startswith('java') or
sys.version_info[:3] == (2, 6, 0)):
raise SystemError('rewrite not supported')
config._assertstate = AssertionState(config, 'rewrite')
config._assertstate.hook = hook = rewrite.AssertionRewritingHook(config)
sys.meta_path.insert(0, hook)
config._assertstate.trace('installed rewrite import hook')
def undo():
hook = config._assertstate.hook
if hook is not None and hook in sys.meta_path:
sys.meta_path.remove(hook)
config.add_cleanup(undo)
return hook
def pytest_collection(session):
# this hook is only called when test modules are collected
# so for example not in the master process of pytest-xdist
# (which does not collect test modules)
hook = session.config._assertstate.hook
if hook is not None:
hook.set_session(session)
assertstate = getattr(session.config, '_assertstate', None)
if assertstate:
if assertstate.hook is not None:
assertstate.hook.set_session(session)
def _running_on_ci():
"""Check if we're currently running on a CI system."""
env_vars = ['CI', 'BUILD_NUMBER']
return any(var in os.environ for var in env_vars)
def pytest_runtest_setup(item):
@@ -99,7 +111,8 @@ def pytest_runtest_setup(item):
This uses the first result from the hook and then ensures the
following:
* Overly verbose explanations are dropped unles -vv was used.
* Overly verbose explanations are dropped unless -vv was used or
running on a CI.
* Embedded newlines are escaped to help util.format_explanation()
later.
* If the rewrite mode is used embedded %-characters are replaced
@@ -112,8 +125,9 @@ def pytest_runtest_setup(item):
config=item.config, op=op, left=left, right=right)
for new_expl in hook_result:
if new_expl:
if (sum(len(p) for p in new_expl[1:]) > 80*8
and item.config.option.verbose < 2):
if (sum(len(p) for p in new_expl[1:]) > 80*8 and
item.config.option.verbose < 2 and
not _running_on_ci()):
show_max = 10
truncated_lines = len(new_expl) - show_max
new_expl[show_max:] = [py.builtin._totext(
@@ -132,35 +146,10 @@ def pytest_runtest_teardown(item):
def pytest_sessionfinish(session):
hook = session.config._assertstate.hook
if hook is not None:
hook.session = None
def _load_modules(mode):
"""Lazily import assertion related code."""
global rewrite, reinterpret
from _pytest.assertion import reinterpret # noqa
if mode == "rewrite":
from _pytest.assertion import rewrite # noqa
def warn_about_missing_assertion(mode):
try:
assert False
except AssertionError:
pass
else:
if mode == "rewrite":
specifically = ("assertions which are not in test modules "
"will be ignored")
else:
specifically = "failing tests may report as passing"
sys.stderr.write("WARNING: " + specifically +
" because assert statements are not executed "
"by the underlying Python interpreter "
"(are you using python -O?)\n")
assertstate = getattr(session.config, '_assertstate', None)
if assertstate:
if assertstate.hook is not None:
assertstate.hook.set_session(None)
# Expose this plugin's implementation for the pytest_assertrepr_compare hook

View File

@@ -1,365 +0,0 @@
"""
Find intermediate evalutation results in assert statements through builtin AST.
This should replace oldinterpret.py eventually.
"""
import sys
import ast
import py
from _pytest.assertion import util
from _pytest.assertion.reinterpret import BuiltinAssertionError
if sys.platform.startswith("java"):
# See http://bugs.jython.org/issue1497
_exprs = ("BoolOp", "BinOp", "UnaryOp", "Lambda", "IfExp", "Dict",
"ListComp", "GeneratorExp", "Yield", "Compare", "Call",
"Repr", "Num", "Str", "Attribute", "Subscript", "Name",
"List", "Tuple")
_stmts = ("FunctionDef", "ClassDef", "Return", "Delete", "Assign",
"AugAssign", "Print", "For", "While", "If", "With", "Raise",
"TryExcept", "TryFinally", "Assert", "Import", "ImportFrom",
"Exec", "Global", "Expr", "Pass", "Break", "Continue")
_expr_nodes = set(getattr(ast, name) for name in _exprs)
_stmt_nodes = set(getattr(ast, name) for name in _stmts)
def _is_ast_expr(node):
return node.__class__ in _expr_nodes
def _is_ast_stmt(node):
return node.__class__ in _stmt_nodes
else:
def _is_ast_expr(node):
return isinstance(node, ast.expr)
def _is_ast_stmt(node):
return isinstance(node, ast.stmt)
try:
_Starred = ast.Starred
except AttributeError:
# Python 2. Define a dummy class so isinstance() will always be False.
class _Starred(object): pass
class Failure(Exception):
"""Error found while interpreting AST."""
def __init__(self, explanation=""):
self.cause = sys.exc_info()
self.explanation = explanation
def interpret(source, frame, should_fail=False):
mod = ast.parse(source)
visitor = DebugInterpreter(frame)
try:
visitor.visit(mod)
except Failure:
failure = sys.exc_info()[1]
return getfailure(failure)
if should_fail:
return ("(assertion failed, but when it was re-run for "
"printing intermediate values, it did not fail. Suggestions: "
"compute assert expression before the assert or use --assert=plain)")
def run(offending_line, frame=None):
if frame is None:
frame = py.code.Frame(sys._getframe(1))
return interpret(offending_line, frame)
def getfailure(e):
explanation = util.format_explanation(e.explanation)
value = e.cause[1]
if str(value):
lines = explanation.split('\n')
lines[0] += " << %s" % (value,)
explanation = '\n'.join(lines)
text = "%s: %s" % (e.cause[0].__name__, explanation)
if text.startswith('AssertionError: assert '):
text = text[16:]
return text
operator_map = {
ast.BitOr : "|",
ast.BitXor : "^",
ast.BitAnd : "&",
ast.LShift : "<<",
ast.RShift : ">>",
ast.Add : "+",
ast.Sub : "-",
ast.Mult : "*",
ast.Div : "/",
ast.FloorDiv : "//",
ast.Mod : "%",
ast.Eq : "==",
ast.NotEq : "!=",
ast.Lt : "<",
ast.LtE : "<=",
ast.Gt : ">",
ast.GtE : ">=",
ast.Pow : "**",
ast.Is : "is",
ast.IsNot : "is not",
ast.In : "in",
ast.NotIn : "not in"
}
unary_map = {
ast.Not : "not %s",
ast.Invert : "~%s",
ast.USub : "-%s",
ast.UAdd : "+%s"
}
class DebugInterpreter(ast.NodeVisitor):
"""Interpret AST nodes to gleam useful debugging information. """
def __init__(self, frame):
self.frame = frame
def generic_visit(self, node):
# Fallback when we don't have a special implementation.
if _is_ast_expr(node):
mod = ast.Expression(node)
co = self._compile(mod)
try:
result = self.frame.eval(co)
except Exception:
raise Failure()
explanation = self.frame.repr(result)
return explanation, result
elif _is_ast_stmt(node):
mod = ast.Module([node])
co = self._compile(mod, "exec")
try:
self.frame.exec_(co)
except Exception:
raise Failure()
return None, None
else:
raise AssertionError("can't handle %s" %(node,))
def _compile(self, source, mode="eval"):
return compile(source, "<assertion interpretation>", mode)
def visit_Expr(self, expr):
return self.visit(expr.value)
def visit_Module(self, mod):
for stmt in mod.body:
self.visit(stmt)
def visit_Name(self, name):
explanation, result = self.generic_visit(name)
# See if the name is local.
source = "%r in locals() is not globals()" % (name.id,)
co = self._compile(source)
try:
local = self.frame.eval(co)
except Exception:
# have to assume it isn't
local = None
if local is None or not self.frame.is_true(local):
return name.id, result
return explanation, result
def visit_Compare(self, comp):
left = comp.left
left_explanation, left_result = self.visit(left)
for op, next_op in zip(comp.ops, comp.comparators):
next_explanation, next_result = self.visit(next_op)
op_symbol = operator_map[op.__class__]
explanation = "%s %s %s" % (left_explanation, op_symbol,
next_explanation)
source = "__exprinfo_left %s __exprinfo_right" % (op_symbol,)
co = self._compile(source)
try:
result = self.frame.eval(co, __exprinfo_left=left_result,
__exprinfo_right=next_result)
except Exception:
raise Failure(explanation)
try:
if not self.frame.is_true(result):
break
except KeyboardInterrupt:
raise
except:
break
left_explanation, left_result = next_explanation, next_result
if util._reprcompare is not None:
res = util._reprcompare(op_symbol, left_result, next_result)
if res:
explanation = res
return explanation, result
def visit_BoolOp(self, boolop):
is_or = isinstance(boolop.op, ast.Or)
explanations = []
for operand in boolop.values:
explanation, result = self.visit(operand)
explanations.append(explanation)
if result == is_or:
break
name = is_or and " or " or " and "
explanation = "(" + name.join(explanations) + ")"
return explanation, result
def visit_UnaryOp(self, unary):
pattern = unary_map[unary.op.__class__]
operand_explanation, operand_result = self.visit(unary.operand)
explanation = pattern % (operand_explanation,)
co = self._compile(pattern % ("__exprinfo_expr",))
try:
result = self.frame.eval(co, __exprinfo_expr=operand_result)
except Exception:
raise Failure(explanation)
return explanation, result
def visit_BinOp(self, binop):
left_explanation, left_result = self.visit(binop.left)
right_explanation, right_result = self.visit(binop.right)
symbol = operator_map[binop.op.__class__]
explanation = "(%s %s %s)" % (left_explanation, symbol,
right_explanation)
source = "__exprinfo_left %s __exprinfo_right" % (symbol,)
co = self._compile(source)
try:
result = self.frame.eval(co, __exprinfo_left=left_result,
__exprinfo_right=right_result)
except Exception:
raise Failure(explanation)
return explanation, result
def visit_Call(self, call):
func_explanation, func = self.visit(call.func)
arg_explanations = []
ns = {"__exprinfo_func" : func}
arguments = []
for arg in call.args:
arg_explanation, arg_result = self.visit(arg)
if isinstance(arg, _Starred):
arg_name = "__exprinfo_star"
ns[arg_name] = arg_result
arguments.append("*%s" % (arg_name,))
arg_explanations.append("*%s" % (arg_explanation,))
else:
arg_name = "__exprinfo_%s" % (len(ns),)
ns[arg_name] = arg_result
arguments.append(arg_name)
arg_explanations.append(arg_explanation)
for keyword in call.keywords:
arg_explanation, arg_result = self.visit(keyword.value)
if keyword.arg:
arg_name = "__exprinfo_%s" % (len(ns),)
keyword_source = "%s=%%s" % (keyword.arg)
arguments.append(keyword_source % (arg_name,))
arg_explanations.append(keyword_source % (arg_explanation,))
else:
arg_name = "__exprinfo_kwds"
arguments.append("**%s" % (arg_name,))
arg_explanations.append("**%s" % (arg_explanation,))
ns[arg_name] = arg_result
if getattr(call, 'starargs', None):
arg_explanation, arg_result = self.visit(call.starargs)
arg_name = "__exprinfo_star"
ns[arg_name] = arg_result
arguments.append("*%s" % (arg_name,))
arg_explanations.append("*%s" % (arg_explanation,))
if getattr(call, 'kwargs', None):
arg_explanation, arg_result = self.visit(call.kwargs)
arg_name = "__exprinfo_kwds"
ns[arg_name] = arg_result
arguments.append("**%s" % (arg_name,))
arg_explanations.append("**%s" % (arg_explanation,))
args_explained = ", ".join(arg_explanations)
explanation = "%s(%s)" % (func_explanation, args_explained)
args = ", ".join(arguments)
source = "__exprinfo_func(%s)" % (args,)
co = self._compile(source)
try:
result = self.frame.eval(co, **ns)
except Exception:
raise Failure(explanation)
pattern = "%s\n{%s = %s\n}"
rep = self.frame.repr(result)
explanation = pattern % (rep, rep, explanation)
return explanation, result
def _is_builtin_name(self, name):
pattern = "%r not in globals() and %r not in locals()"
source = pattern % (name.id, name.id)
co = self._compile(source)
try:
return self.frame.eval(co)
except Exception:
return False
def visit_Attribute(self, attr):
if not isinstance(attr.ctx, ast.Load):
return self.generic_visit(attr)
source_explanation, source_result = self.visit(attr.value)
explanation = "%s.%s" % (source_explanation, attr.attr)
source = "__exprinfo_expr.%s" % (attr.attr,)
co = self._compile(source)
try:
try:
result = self.frame.eval(co, __exprinfo_expr=source_result)
except AttributeError:
# Maybe the attribute name needs to be mangled?
if not attr.attr.startswith("__") or attr.attr.endswith("__"):
raise
source = "getattr(__exprinfo_expr.__class__, '__name__', '')"
co = self._compile(source)
class_name = self.frame.eval(co, __exprinfo_expr=source_result)
mangled_attr = "_" + class_name + attr.attr
source = "__exprinfo_expr.%s" % (mangled_attr,)
co = self._compile(source)
result = self.frame.eval(co, __exprinfo_expr=source_result)
except Exception:
raise Failure(explanation)
explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result),
self.frame.repr(result),
source_explanation, attr.attr)
# Check if the attr is from an instance.
source = "%r in getattr(__exprinfo_expr, '__dict__', {})"
source = source % (attr.attr,)
co = self._compile(source)
try:
from_instance = self.frame.eval(co, __exprinfo_expr=source_result)
except Exception:
from_instance = None
if from_instance is None or self.frame.is_true(from_instance):
rep = self.frame.repr(result)
pattern = "%s\n{%s = %s\n}"
explanation = pattern % (rep, rep, explanation)
return explanation, result
def visit_Assert(self, assrt):
test_explanation, test_result = self.visit(assrt.test)
explanation = "assert %s" % (test_explanation,)
if not self.frame.is_true(test_result):
try:
raise BuiltinAssertionError
except Exception:
raise Failure(explanation)
return explanation, test_result
def visit_Assign(self, assign):
value_explanation, value_result = self.visit(assign.value)
explanation = "... = %s" % (value_explanation,)
name = ast.Name("__exprinfo_expr", ast.Load(),
lineno=assign.value.lineno,
col_offset=assign.value.col_offset)
new_assign = ast.Assign(assign.targets, name, lineno=assign.lineno,
col_offset=assign.col_offset)
mod = ast.Module([new_assign])
co = self._compile(mod, "exec")
try:
self.frame.exec_(co, __exprinfo_expr=value_result)
except Exception:
raise Failure(explanation)
return explanation, value_result

View File

@@ -1,566 +0,0 @@
import traceback
import types
import py
import sys, inspect
from compiler import parse, ast, pycodegen
from _pytest.assertion.util import format_explanation, BuiltinAssertionError
passthroughex = py.builtin._sysex
class Failure:
def __init__(self, node):
self.exc, self.value, self.tb = sys.exc_info()
self.node = node
class View(object):
"""View base class.
If C is a subclass of View, then C(x) creates a proxy object around
the object x. The actual class of the proxy is not C in general,
but a *subclass* of C determined by the rules below. To avoid confusion
we call view class the class of the proxy (a subclass of C, so of View)
and object class the class of x.
Attributes and methods not found in the proxy are automatically read on x.
Other operations like setting attributes are performed on the proxy, as
determined by its view class. The object x is available from the proxy
as its __obj__ attribute.
The view class selection is determined by the __view__ tuples and the
optional __viewkey__ method. By default, the selected view class is the
most specific subclass of C whose __view__ mentions the class of x.
If no such subclass is found, the search proceeds with the parent
object classes. For example, C(True) will first look for a subclass
of C with __view__ = (..., bool, ...) and only if it doesn't find any
look for one with __view__ = (..., int, ...), and then ..., object,...
If everything fails the class C itself is considered to be the default.
Alternatively, the view class selection can be driven by another aspect
of the object x, instead of the class of x, by overriding __viewkey__.
See last example at the end of this module.
"""
_viewcache = {}
__view__ = ()
def __new__(rootclass, obj, *args, **kwds):
self = object.__new__(rootclass)
self.__obj__ = obj
self.__rootclass__ = rootclass
key = self.__viewkey__()
try:
self.__class__ = self._viewcache[key]
except KeyError:
self.__class__ = self._selectsubclass(key)
return self
def __getattr__(self, attr):
# attributes not found in the normal hierarchy rooted on View
# are looked up in the object's real class
return getattr(object.__getattribute__(self, '__obj__'), attr)
def __viewkey__(self):
return self.__obj__.__class__
def __matchkey__(self, key, subclasses):
if inspect.isclass(key):
keys = inspect.getmro(key)
else:
keys = [key]
for key in keys:
result = [C for C in subclasses if key in C.__view__]
if result:
return result
return []
def _selectsubclass(self, key):
subclasses = list(enumsubclasses(self.__rootclass__))
for C in subclasses:
if not isinstance(C.__view__, tuple):
C.__view__ = (C.__view__,)
choices = self.__matchkey__(key, subclasses)
if not choices:
return self.__rootclass__
elif len(choices) == 1:
return choices[0]
else:
# combine the multiple choices
return type('?', tuple(choices), {})
def __repr__(self):
return '%s(%r)' % (self.__rootclass__.__name__, self.__obj__)
def enumsubclasses(cls):
for subcls in cls.__subclasses__():
for subsubclass in enumsubclasses(subcls):
yield subsubclass
yield cls
class Interpretable(View):
"""A parse tree node with a few extra methods."""
explanation = None
def is_builtin(self, frame):
return False
def eval(self, frame):
# fall-back for unknown expression nodes
try:
expr = ast.Expression(self.__obj__)
expr.filename = '<eval>'
self.__obj__.filename = '<eval>'
co = pycodegen.ExpressionCodeGenerator(expr).getCode()
result = frame.eval(co)
except passthroughex:
raise
except:
raise Failure(self)
self.result = result
self.explanation = self.explanation or frame.repr(self.result)
def run(self, frame):
# fall-back for unknown statement nodes
try:
expr = ast.Module(None, ast.Stmt([self.__obj__]))
expr.filename = '<run>'
co = pycodegen.ModuleCodeGenerator(expr).getCode()
frame.exec_(co)
except passthroughex:
raise
except:
raise Failure(self)
def nice_explanation(self):
return format_explanation(self.explanation)
class Name(Interpretable):
__view__ = ast.Name
def is_local(self, frame):
source = '%r in locals() is not globals()' % self.name
try:
return frame.is_true(frame.eval(source))
except passthroughex:
raise
except:
return False
def is_global(self, frame):
source = '%r in globals()' % self.name
try:
return frame.is_true(frame.eval(source))
except passthroughex:
raise
except:
return False
def is_builtin(self, frame):
source = '%r not in locals() and %r not in globals()' % (
self.name, self.name)
try:
return frame.is_true(frame.eval(source))
except passthroughex:
raise
except:
return False
def eval(self, frame):
super(Name, self).eval(frame)
if not self.is_local(frame):
self.explanation = self.name
class Compare(Interpretable):
__view__ = ast.Compare
def eval(self, frame):
expr = Interpretable(self.expr)
expr.eval(frame)
for operation, expr2 in self.ops:
if hasattr(self, 'result'):
# shortcutting in chained expressions
if not frame.is_true(self.result):
break
expr2 = Interpretable(expr2)
expr2.eval(frame)
self.explanation = "%s %s %s" % (
expr.explanation, operation, expr2.explanation)
source = "__exprinfo_left %s __exprinfo_right" % operation
try:
self.result = frame.eval(source,
__exprinfo_left=expr.result,
__exprinfo_right=expr2.result)
except passthroughex:
raise
except:
raise Failure(self)
expr = expr2
class And(Interpretable):
__view__ = ast.And
def eval(self, frame):
explanations = []
for expr in self.nodes:
expr = Interpretable(expr)
expr.eval(frame)
explanations.append(expr.explanation)
self.result = expr.result
if not frame.is_true(expr.result):
break
self.explanation = '(' + ' and '.join(explanations) + ')'
class Or(Interpretable):
__view__ = ast.Or
def eval(self, frame):
explanations = []
for expr in self.nodes:
expr = Interpretable(expr)
expr.eval(frame)
explanations.append(expr.explanation)
self.result = expr.result
if frame.is_true(expr.result):
break
self.explanation = '(' + ' or '.join(explanations) + ')'
# == Unary operations ==
keepalive = []
for astclass, astpattern in {
ast.Not : 'not __exprinfo_expr',
ast.Invert : '(~__exprinfo_expr)',
}.items():
class UnaryArith(Interpretable):
__view__ = astclass
def eval(self, frame, astpattern=astpattern):
expr = Interpretable(self.expr)
expr.eval(frame)
self.explanation = astpattern.replace('__exprinfo_expr',
expr.explanation)
try:
self.result = frame.eval(astpattern,
__exprinfo_expr=expr.result)
except passthroughex:
raise
except:
raise Failure(self)
keepalive.append(UnaryArith)
# == Binary operations ==
for astclass, astpattern in {
ast.Add : '(__exprinfo_left + __exprinfo_right)',
ast.Sub : '(__exprinfo_left - __exprinfo_right)',
ast.Mul : '(__exprinfo_left * __exprinfo_right)',
ast.Div : '(__exprinfo_left / __exprinfo_right)',
ast.Mod : '(__exprinfo_left % __exprinfo_right)',
ast.Power : '(__exprinfo_left ** __exprinfo_right)',
}.items():
class BinaryArith(Interpretable):
__view__ = astclass
def eval(self, frame, astpattern=astpattern):
left = Interpretable(self.left)
left.eval(frame)
right = Interpretable(self.right)
right.eval(frame)
self.explanation = (astpattern
.replace('__exprinfo_left', left .explanation)
.replace('__exprinfo_right', right.explanation))
try:
self.result = frame.eval(astpattern,
__exprinfo_left=left.result,
__exprinfo_right=right.result)
except passthroughex:
raise
except:
raise Failure(self)
keepalive.append(BinaryArith)
class CallFunc(Interpretable):
__view__ = ast.CallFunc
def is_bool(self, frame):
source = 'isinstance(__exprinfo_value, bool)'
try:
return frame.is_true(frame.eval(source,
__exprinfo_value=self.result))
except passthroughex:
raise
except:
return False
def eval(self, frame):
node = Interpretable(self.node)
node.eval(frame)
explanations = []
vars = {'__exprinfo_fn': node.result}
source = '__exprinfo_fn('
for a in self.args:
if isinstance(a, ast.Keyword):
keyword = a.name
a = a.expr
else:
keyword = None
a = Interpretable(a)
a.eval(frame)
argname = '__exprinfo_%d' % len(vars)
vars[argname] = a.result
if keyword is None:
source += argname + ','
explanations.append(a.explanation)
else:
source += '%s=%s,' % (keyword, argname)
explanations.append('%s=%s' % (keyword, a.explanation))
if self.star_args:
star_args = Interpretable(self.star_args)
star_args.eval(frame)
argname = '__exprinfo_star'
vars[argname] = star_args.result
source += '*' + argname + ','
explanations.append('*' + star_args.explanation)
if self.dstar_args:
dstar_args = Interpretable(self.dstar_args)
dstar_args.eval(frame)
argname = '__exprinfo_kwds'
vars[argname] = dstar_args.result
source += '**' + argname + ','
explanations.append('**' + dstar_args.explanation)
self.explanation = "%s(%s)" % (
node.explanation, ', '.join(explanations))
if source.endswith(','):
source = source[:-1]
source += ')'
try:
self.result = frame.eval(source, **vars)
except passthroughex:
raise
except:
raise Failure(self)
if not node.is_builtin(frame) or not self.is_bool(frame):
r = frame.repr(self.result)
self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
class Getattr(Interpretable):
__view__ = ast.Getattr
def eval(self, frame):
expr = Interpretable(self.expr)
expr.eval(frame)
source = '__exprinfo_expr.%s' % self.attrname
try:
try:
self.result = frame.eval(source, __exprinfo_expr=expr.result)
except AttributeError:
# Maybe the attribute name needs to be mangled?
if (not self.attrname.startswith("__") or
self.attrname.endswith("__")):
raise
source = "getattr(__exprinfo_expr.__class__, '__name__', '')"
class_name = frame.eval(source, __exprinfo_expr=expr.result)
mangled_attr = "_" + class_name + self.attrname
source = "__exprinfo_expr.%s" % (mangled_attr,)
self.result = frame.eval(source, __exprinfo_expr=expr.result)
except passthroughex:
raise
except:
raise Failure(self)
self.explanation = '%s.%s' % (expr.explanation, self.attrname)
# if the attribute comes from the instance, its value is interesting
source = ('hasattr(__exprinfo_expr, "__dict__") and '
'%r in __exprinfo_expr.__dict__' % self.attrname)
try:
from_instance = frame.is_true(
frame.eval(source, __exprinfo_expr=expr.result))
except passthroughex:
raise
except:
from_instance = True
if from_instance:
r = frame.repr(self.result)
self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
# == Re-interpretation of full statements ==
class Assert(Interpretable):
__view__ = ast.Assert
def run(self, frame):
test = Interpretable(self.test)
test.eval(frame)
# print the result as 'assert <explanation>'
self.result = test.result
self.explanation = 'assert ' + test.explanation
if not frame.is_true(test.result):
try:
raise BuiltinAssertionError
except passthroughex:
raise
except:
raise Failure(self)
class Assign(Interpretable):
__view__ = ast.Assign
def run(self, frame):
expr = Interpretable(self.expr)
expr.eval(frame)
self.result = expr.result
self.explanation = '... = ' + expr.explanation
# fall-back-run the rest of the assignment
ass = ast.Assign(self.nodes, ast.Name('__exprinfo_expr'))
mod = ast.Module(None, ast.Stmt([ass]))
mod.filename = '<run>'
co = pycodegen.ModuleCodeGenerator(mod).getCode()
try:
frame.exec_(co, __exprinfo_expr=expr.result)
except passthroughex:
raise
except:
raise Failure(self)
class Discard(Interpretable):
__view__ = ast.Discard
def run(self, frame):
expr = Interpretable(self.expr)
expr.eval(frame)
self.result = expr.result
self.explanation = expr.explanation
class Stmt(Interpretable):
__view__ = ast.Stmt
def run(self, frame):
for stmt in self.nodes:
stmt = Interpretable(stmt)
stmt.run(frame)
def report_failure(e):
explanation = e.node.nice_explanation()
if explanation:
explanation = ", in: " + explanation
else:
explanation = ""
sys.stdout.write("%s: %s%s\n" % (e.exc.__name__, e.value, explanation))
def check(s, frame=None):
if frame is None:
frame = sys._getframe(1)
frame = py.code.Frame(frame)
expr = parse(s, 'eval')
assert isinstance(expr, ast.Expression)
node = Interpretable(expr.node)
try:
node.eval(frame)
except passthroughex:
raise
except Failure:
e = sys.exc_info()[1]
report_failure(e)
else:
if not frame.is_true(node.result):
sys.stderr.write("assertion failed: %s\n" % node.nice_explanation())
###########################################################
# API / Entry points
# #########################################################
def interpret(source, frame, should_fail=False):
module = Interpretable(parse(source, 'exec').node)
#print "got module", module
if isinstance(frame, types.FrameType):
frame = py.code.Frame(frame)
try:
module.run(frame)
except Failure:
e = sys.exc_info()[1]
return getfailure(e)
except passthroughex:
raise
except:
traceback.print_exc()
if should_fail:
return ("(assertion failed, but when it was re-run for "
"printing intermediate values, it did not fail. Suggestions: "
"compute assert expression before the assert or use --assert=plain)")
else:
return None
def getmsg(excinfo):
if isinstance(excinfo, tuple):
excinfo = py.code.ExceptionInfo(excinfo)
#frame, line = gettbline(tb)
#frame = py.code.Frame(frame)
#return interpret(line, frame)
tb = excinfo.traceback[-1]
source = str(tb.statement).strip()
x = interpret(source, tb.frame, should_fail=True)
if not isinstance(x, str):
raise TypeError("interpret returned non-string %r" % (x,))
return x
def getfailure(e):
explanation = e.node.nice_explanation()
if str(e.value):
lines = explanation.split('\n')
lines[0] += " << %s" % (e.value,)
explanation = '\n'.join(lines)
text = "%s: %s" % (e.exc.__name__, explanation)
if text.startswith('AssertionError: assert '):
text = text[16:]
return text
def run(s, frame=None):
if frame is None:
frame = sys._getframe(1)
frame = py.code.Frame(frame)
module = Interpretable(parse(s, 'exec').node)
try:
module.run(frame)
except Failure:
e = sys.exc_info()[1]
report_failure(e)
if __name__ == '__main__':
# example:
def f():
return 5
def g():
return 3
def h(x):
return 'never'
check("f() * g() == 5")
check("not f()")
check("not (f() and g() or 0)")
check("f() == g()")
i = 4
check("i == f()")
check("len(f()) == 0")
check("isinstance(2+3+4, float)")
run("x = i")
check("x == 5")
run("assert not f(), 'oops'")
run("a, b, c = 1, 2")
run("a, b, c = f()")
check("max([f(),g()]) == 4")
check("'hello'[g()] == 'h'")
run("'guk%d' % h(f())")

View File

@@ -1,52 +0,0 @@
import sys
import py
from _pytest.assertion.util import BuiltinAssertionError
u = py.builtin._totext
class AssertionError(BuiltinAssertionError):
def __init__(self, *args):
BuiltinAssertionError.__init__(self, *args)
if args:
# on Python2.6 we get len(args)==2 for: assert 0, (x,y)
# on Python2.7 and above we always get len(args) == 1
# with args[0] being the (x,y) tuple.
if len(args) > 1:
toprint = args
else:
toprint = args[0]
try:
self.msg = u(toprint)
except Exception:
self.msg = u(
"<[broken __repr__] %s at %0xd>"
% (toprint.__class__, id(toprint)))
else:
f = py.code.Frame(sys._getframe(1))
try:
source = f.code.fullsource
if source is not None:
try:
source = source.getstatement(f.lineno, assertion=True)
except IndexError:
source = None
else:
source = str(source.deindent()).strip()
except py.error.ENOENT:
source = None
# this can also occur during reinterpretation, when the
# co_filename is set to "<run>".
if source:
self.msg = reinterpret(source, f, should_fail=True)
else:
self.msg = "<could not determine information>"
if not self.args:
self.args = (self.msg,)
if sys.version_info > (3, 0):
AssertionError.__module__ = "builtins"
if sys.version_info >= (2, 6) or sys.platform.startswith("java"):
from _pytest.assertion.newinterpret import interpret as reinterpret
else:
from _pytest.assertion.oldinterpret import interpret as reinterpret

View File

@@ -1,6 +1,7 @@
"""Rewrite assertion AST to produce nice error messages"""
import ast
import _ast
import errno
import itertools
import imp
@@ -10,6 +11,7 @@ import re
import struct
import sys
import types
from fnmatch import fnmatch
import py
from _pytest.assertion import util
@@ -44,20 +46,19 @@ else:
class AssertionRewritingHook(object):
"""PEP302 Import hook which rewrites asserts."""
def __init__(self):
def __init__(self, config):
self.config = config
self.fnpats = config.getini("python_files")
self.session = None
self.modules = {}
self._register_with_pkg_resources()
self._must_rewrite = set()
def set_session(self, session):
self.fnpats = session.config.getini("python_files")
self.session = session
def find_module(self, name, path=None):
if self.session is None:
return None
sess = self.session
state = sess.config._assertstate
state = self.config._assertstate
state.trace("find_module called for: %s" % name)
names = name.rsplit(".", 1)
lastname = names[-1]
@@ -86,24 +87,11 @@ class AssertionRewritingHook(object):
return None
else:
fn = os.path.join(pth, name.rpartition(".")[2] + ".py")
fn_pypath = py.path.local(fn)
# Is this a test file?
if not sess.isinitpath(fn):
# We have to be very careful here because imports in this code can
# trigger a cycle.
self.session = None
try:
for pat in self.fnpats:
if fn_pypath.fnmatch(pat):
state.trace("matched test file %r" % (fn,))
break
else:
return None
finally:
self.session = sess
else:
state.trace("matched test file (was specified on cmdline): %r" %
(fn,))
if not self._should_rewrite(name, fn_pypath, state):
return None
# The requested module looks like a test file, so rewrite it. This is
# the most magical part of the process: load the source, rewrite the
# asserts, and load the rewritten source. We also cache the rewritten
@@ -140,7 +128,7 @@ class AssertionRewritingHook(object):
co = _read_pyc(fn_pypath, pyc, state.trace)
if co is None:
state.trace("rewriting %r" % (fn,))
source_stat, co = _rewrite_test(state, fn_pypath)
source_stat, co = _rewrite_test(self.config, fn_pypath)
if co is None:
# Probably a SyntaxError in the test.
return None
@@ -151,6 +139,54 @@ class AssertionRewritingHook(object):
self.modules[name] = co, pyc
return self
def _should_rewrite(self, name, fn_pypath, state):
# always rewrite conftest files
fn = str(fn_pypath)
if fn_pypath.basename == 'conftest.py':
state.trace("rewriting conftest file: %r" % (fn,))
return True
if self.session is not None:
if self.session.isinitpath(fn):
state.trace("matched test file (was specified on cmdline): %r" %
(fn,))
return True
# modules not passed explicitly on the command line are only
# rewritten if they match the naming convention for test files
for pat in self.fnpats:
# use fnmatch instead of fn_pypath.fnmatch because the
# latter might trigger an import to fnmatch.fnmatch
# internally, which would cause this method to be
# called recursively
if fnmatch(fn_pypath.basename, pat):
state.trace("matched test file %r" % (fn,))
return True
for marked in self._must_rewrite:
if name.startswith(marked):
state.trace("matched marked file %r (from %r)" % (name, marked))
return True
return False
def mark_rewrite(self, *names):
"""Mark import names as needing to be re-written.
The named module or package as well as any nested modules will
be re-written on import.
"""
already_imported = set(names).intersection(set(sys.modules))
if already_imported:
self._warn_already_imported(already_imported)
self._must_rewrite.update(names)
def _warn_already_imported(self, names):
self.config.warn(
'P1',
'Modules are already imported so can not be re-written: %s' %
','.join(names))
def load_module(self, name):
# If there is an existing module object named 'fullname' in
# sys.modules, the loader must use that existing module. (Otherwise,
@@ -241,8 +277,9 @@ N = "\n".encode("utf-8")
cookie_re = re.compile(r"^[ \t\f]*#.*coding[:=][ \t]*[-\w.]+")
BOM_UTF8 = '\xef\xbb\xbf'
def _rewrite_test(state, fn):
def _rewrite_test(config, fn):
"""Try to read and rewrite *fn* and return the code object."""
state = config._assertstate
try:
stat = fn.stat()
source = fn.read("rb")
@@ -287,7 +324,7 @@ def _rewrite_test(state, fn):
# Let this pop up again in the real import.
state.trace("failed to parse: %r" % (fn,))
return None, None
rewrite_asserts(tree)
rewrite_asserts(tree, fn, config)
try:
co = compile(tree, fn.strpath, "exec")
except SyntaxError:
@@ -343,9 +380,9 @@ def _read_pyc(source, pyc, trace=lambda x: None):
return co
def rewrite_asserts(mod):
def rewrite_asserts(mod, module_path=None, config=None):
"""Rewrite the assert statements in mod."""
AssertionRewriter().run(mod)
AssertionRewriter(module_path, config).run(mod)
def _saferepr(obj):
@@ -453,6 +490,11 @@ binop_map = {
ast.In: "in",
ast.NotIn: "not in"
}
# Python 3.5+ compatibility
try:
binop_map[ast.MatMult] = "@"
except AttributeError:
pass
# Python 3.4+ compatibility
if hasattr(ast, "NameConstant"):
@@ -527,6 +569,11 @@ class AssertionRewriter(ast.NodeVisitor):
"""
def __init__(self, module_path, config):
super(AssertionRewriter, self).__init__()
self.module_path = module_path
self.config = config
def run(self, mod):
"""Find all assert statements in *mod* and rewrite them."""
if not mod.body:
@@ -667,6 +714,10 @@ class AssertionRewriter(ast.NodeVisitor):
the expression is false.
"""
if isinstance(assert_.test, ast.Tuple) and self.config is not None:
fslocation = (self.module_path, assert_.lineno)
self.config.warn('R1', 'assertion is always true, perhaps '
'remove parentheses?', fslocation=fslocation)
self.statements = []
self.variables = []
self.variable_counter = itertools.count()
@@ -850,6 +901,8 @@ class AssertionRewriter(ast.NodeVisitor):
def visit_Compare(self, comp):
self.push_format_context()
left_res, left_expl = self.visit(comp.left)
if isinstance(comp.left, (_ast.Compare, _ast.BoolOp)):
left_expl = "({0})".format(left_expl)
res_variables = [self.variable() for i in range(len(comp.ops))]
load_names = [ast.Name(v, ast.Load()) for v in res_variables]
store_names = [ast.Name(v, ast.Store()) for v in res_variables]
@@ -859,6 +912,8 @@ class AssertionRewriter(ast.NodeVisitor):
results = [left_res]
for i, op, next_operand in it:
next_res, next_expl = self.visit(next_operand)
if isinstance(next_operand, (_ast.Compare, _ast.BoolOp)):
next_expl = "({0})".format(next_expl)
results.append(next_res)
sym = binop_map[op.__class__]
syms.append(ast.Str(sym))

View File

@@ -1,6 +1,7 @@
"""Utilities for assertion debugging"""
import pprint
import _pytest._code
import py
try:
from collections import Sequence
@@ -17,6 +18,15 @@ u = py.builtin._totext
_reprcompare = None
# the re-encoding is needed for python2 repr
# with non-ascii characters (see issue 877 and 1379)
def ecu(s):
try:
return u(s, 'utf-8', 'replace')
except TypeError:
return s
def format_explanation(explanation):
"""This formats an explanation
@@ -27,44 +37,12 @@ def format_explanation(explanation):
for when one explanation needs to span multiple lines, e.g. when
displaying diffs.
"""
explanation = _collapse_false(explanation)
explanation = ecu(explanation)
lines = _split_explanation(explanation)
result = _format_lines(lines)
return u('\n').join(result)
def _collapse_false(explanation):
"""Collapse expansions of False
So this strips out any "assert False\n{where False = ...\n}"
blocks.
"""
where = 0
while True:
start = where = explanation.find("False\n{False = ", where)
if where == -1:
break
level = 0
prev_c = explanation[start]
for i, c in enumerate(explanation[start:]):
if prev_c + c == "\n{":
level += 1
elif prev_c + c == "\n}":
level -= 1
if not level:
break
prev_c = c
else:
raise AssertionError("unbalanced braces: %r" % (explanation,))
end = start + i
where = end
if explanation[end - 1] == '\n':
explanation = (explanation[:start] + explanation[start+15:end-1] +
explanation[end+1:])
where -= 17
return explanation
def _split_explanation(explanation):
"""Return a list of individual lines in the explanation
@@ -130,18 +108,10 @@ def assertrepr_compare(config, op, left, right):
left_repr = py.io.saferepr(left, maxsize=int(width/2))
right_repr = py.io.saferepr(right, maxsize=width-len(left_repr))
# the re-encoding is needed for python2 repr
# with non-ascii characters (see issue 877)
def ecu(s):
try:
return u(s, 'utf-8', 'replace')
except TypeError:
return s
summary = u('%s %s %s') % (ecu(left_repr), op, ecu(right_repr))
issequence = lambda x: (isinstance(x, (list, tuple, Sequence))
and not isinstance(x, basestring))
issequence = lambda x: (isinstance(x, (list, tuple, Sequence)) and
not isinstance(x, basestring))
istext = lambda x: isinstance(x, basestring)
isdict = lambda x: isinstance(x, dict)
isset = lambda x: isinstance(x, (set, frozenset))
@@ -179,7 +149,7 @@ def assertrepr_compare(config, op, left, right):
explanation = [
u('(pytest_assertion plugin: representation of details failed. '
'Probably an object has a faulty __repr__.)'),
u(py.code.ExceptionInfo())]
u(_pytest._code.ExceptionInfo())]
if not explanation:
return None
@@ -222,9 +192,10 @@ def _diff_text(left, right, verbose=False):
'characters in diff, use -v to show') % i]
left = left[:-i]
right = right[:-i]
keepends = True
explanation += [line.strip('\n')
for line in ndiff(left.splitlines(),
right.splitlines())]
for line in ndiff(left.splitlines(keepends),
right.splitlines(keepends))]
return explanation
@@ -263,8 +234,7 @@ def _compare_eq_sequence(left, right, verbose=False):
explanation += [
u('Right contains more items, first extra item: %s') %
py.io.saferepr(right[len(left)],)]
return explanation # + _diff_text(pprint.pformat(left),
# pprint.pformat(right))
return explanation
def _compare_eq_set(left, right, verbose=False):

View File

@@ -149,17 +149,19 @@ class LFPlugin:
config = self.config
if config.getvalue("cacheshow") or hasattr(config, "slaveinput"):
return
config.cache.set("cache/lastfailed", self.lastfailed)
prev_failed = config.cache.get("cache/lastfailed", None) is not None
if (session.testscollected and prev_failed) or self.lastfailed:
config.cache.set("cache/lastfailed", self.lastfailed)
def pytest_addoption(parser):
group = parser.getgroup("general")
group.addoption(
'--lf', action='store_true', dest="lf",
'--lf', '--last-failed', action='store_true', dest="lf",
help="rerun only the tests that failed "
"at the last run (or all if none failed)")
group.addoption(
'--ff', action='store_true', dest="failedfirst",
'--ff', '--failed-first', action='store_true', dest="failedfirst",
help="run all tests but run the last failures first. "
"This may re-order tests and thus lead to "
"repeated fixture setup/teardown")

View File

@@ -4,6 +4,7 @@ per-test stdout/stderr capturing mechanism.
"""
from __future__ import with_statement
import contextlib
import sys
import os
from tempfile import TemporaryFile
@@ -31,6 +32,7 @@ def pytest_addoption(parser):
@pytest.hookimpl(hookwrapper=True)
def pytest_load_initial_conftests(early_config, parser, args):
_readline_workaround()
ns = early_config.known_args_namespace
pluginmanager = early_config.pluginmanager
capman = CaptureManager(ns.capture)
@@ -145,8 +147,8 @@ class CaptureManager:
def pytest_internalerror(self, excinfo):
self.reset_capturings()
def suspendcapture_item(self, item, when):
out, err = self.suspendcapture()
def suspendcapture_item(self, item, when, in_=False):
out, err = self.suspendcapture(in_=in_)
item.add_report_section(when, "stdout", out)
item.add_report_section(when, "stderr", err)
@@ -155,36 +157,37 @@ error_capsysfderror = "cannot use capsys and capfd at the same time"
@pytest.fixture
def capsys(request):
"""enables capturing of writes to sys.stdout/sys.stderr and makes
"""Enable capturing of writes to sys.stdout/sys.stderr and make
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple.
"""
if "capfd" in request._funcargs:
if "capfd" in request.fixturenames:
raise request.raiseerror(error_capsysfderror)
request.node._capfuncarg = c = CaptureFixture(SysCapture)
request.node._capfuncarg = c = CaptureFixture(SysCapture, request)
return c
@pytest.fixture
def capfd(request):
"""enables capturing of writes to file descriptors 1 and 2 and makes
"""Enable capturing of writes to file descriptors 1 and 2 and make
captured output available via ``capfd.readouterr()`` method calls
which return a ``(out, err)`` tuple.
"""
if "capsys" in request._funcargs:
if "capsys" in request.fixturenames:
request.raiseerror(error_capsysfderror)
if not hasattr(os, 'dup'):
pytest.skip("capfd funcarg needs os.dup")
request.node._capfuncarg = c = CaptureFixture(FDCapture)
request.node._capfuncarg = c = CaptureFixture(FDCapture, request)
return c
class CaptureFixture:
def __init__(self, captureclass):
def __init__(self, captureclass, request):
self.captureclass = captureclass
self.request = request
def _start(self):
self._capture = MultiCapture(out=True, err=True, in_=False,
Capture=self.captureclass)
Capture=self.captureclass)
self._capture.start_capturing()
def close(self):
@@ -199,6 +202,15 @@ class CaptureFixture:
except AttributeError:
return self._outerr
@contextlib.contextmanager
def disabled(self):
capmanager = self.request.config.pluginmanager.getplugin('capturemanager')
capmanager.suspendcapture_item(self.request.node, "call", in_=True)
try:
yield
finally:
capmanager.resumecapture()
def safe_text_dupfile(f, mode, default_encoding="UTF8"):
""" return a open text file object that's a duplicate of f on the
@@ -442,3 +454,30 @@ class DontReadFromInput:
def close(self):
pass
def _readline_workaround():
"""
Ensure readline is imported so that it attaches to the correct stdio
handles on Windows.
Pdb uses readline support where available--when not running from the Python
prompt, the readline module is not imported until running the pdb REPL. If
running pytest with the --pdb option this means the readline module is not
imported until after I/O capture has been started.
This is a problem for pyreadline, which is often used to implement readline
support on Windows, as it does not attach to the correct handles for stdout
and/or stdin if they have been redirected by the FDCapture mechanism. This
workaround ensures that readline is imported before I/O capture is setup so
that it can attach to the actual stdin/out for the console.
See https://github.com/pytest-dev/pytest/pull/1281
"""
if not sys.platform.startswith('win32'):
return
try:
import readline # noqa
except ImportError:
pass

216
_pytest/compat.py Normal file
View File

@@ -0,0 +1,216 @@
"""
python version compatibility code
"""
import sys
import inspect
import types
import re
import functools
import py
import _pytest
try:
import enum
except ImportError: # pragma: no cover
# Only available in Python 3.4+ or as a backport
enum = None
_PY3 = sys.version_info > (3, 0)
_PY2 = not _PY3
NoneType = type(None)
NOTSET = object()
if hasattr(inspect, 'signature'):
def _format_args(func):
return str(inspect.signature(func))
else:
def _format_args(func):
return inspect.formatargspec(*inspect.getargspec(func))
isfunction = inspect.isfunction
isclass = inspect.isclass
# used to work around a python2 exception info leak
exc_clear = getattr(sys, 'exc_clear', lambda: None)
# The type of re.compile objects is not exposed in Python.
REGEX_TYPE = type(re.compile(''))
def is_generator(func):
try:
return _pytest._code.getrawcode(func).co_flags & 32 # generator function
except AttributeError: # builtin functions have no bytecode
# assume them to not be generators
return False
def getlocation(function, curdir):
import inspect
fn = py.path.local(inspect.getfile(function))
lineno = py.builtin._getcode(function).co_firstlineno
if fn.relto(curdir):
fn = fn.relto(curdir)
return "%s:%d" %(fn, lineno+1)
def num_mock_patch_args(function):
""" return number of arguments used up by mock arguments (if any) """
patchings = getattr(function, "patchings", None)
if not patchings:
return 0
mock = sys.modules.get("mock", sys.modules.get("unittest.mock", None))
if mock is not None:
return len([p for p in patchings
if not p.attribute_name and p.new is mock.DEFAULT])
return len(patchings)
def getfuncargnames(function, startindex=None):
# XXX merge with main.py's varnames
#assert not isclass(function)
realfunction = function
while hasattr(realfunction, "__wrapped__"):
realfunction = realfunction.__wrapped__
if startindex is None:
startindex = inspect.ismethod(function) and 1 or 0
if realfunction != function:
startindex += num_mock_patch_args(function)
function = realfunction
if isinstance(function, functools.partial):
argnames = inspect.getargs(_pytest._code.getrawcode(function.func))[0]
partial = function
argnames = argnames[len(partial.args):]
if partial.keywords:
for kw in partial.keywords:
argnames.remove(kw)
else:
argnames = inspect.getargs(_pytest._code.getrawcode(function))[0]
defaults = getattr(function, 'func_defaults',
getattr(function, '__defaults__', None)) or ()
numdefaults = len(defaults)
if numdefaults:
return tuple(argnames[startindex:-numdefaults])
return tuple(argnames[startindex:])
if sys.version_info[:2] == (2, 6):
def isclass(object):
""" Return true if the object is a class. Overrides inspect.isclass for
python 2.6 because it will return True for objects which always return
something on __getattr__ calls (see #1035).
Backport of https://hg.python.org/cpython/rev/35bf8f7a8edc
"""
return isinstance(object, (type, types.ClassType))
if _PY3:
import codecs
STRING_TYPES = bytes, str
def _escape_strings(val):
"""If val is pure ascii, returns it as a str(). Otherwise, escapes
bytes objects into a sequence of escaped bytes:
b'\xc3\xb4\xc5\xd6' -> u'\\xc3\\xb4\\xc5\\xd6'
and escapes unicode objects into a sequence of escaped unicode
ids, e.g.:
'4\\nV\\U00043efa\\x0eMXWB\\x1e\\u3028\\u15fd\\xcd\\U0007d944'
note:
the obvious "v.decode('unicode-escape')" will return
valid utf-8 unicode if it finds them in bytes, but we
want to return escaped bytes for any byte, even if they match
a utf-8 string.
"""
if isinstance(val, bytes):
if val:
# source: http://goo.gl/bGsnwC
encoded_bytes, _ = codecs.escape_encode(val)
return encoded_bytes.decode('ascii')
else:
# empty bytes crashes codecs.escape_encode (#1087)
return ''
else:
return val.encode('unicode_escape').decode('ascii')
else:
STRING_TYPES = bytes, str, unicode
def _escape_strings(val):
"""In py2 bytes and str are the same type, so return if it's a bytes
object, return it unchanged if it is a full ascii string,
otherwise escape it into its binary form.
If it's a unicode string, change the unicode characters into
unicode escapes.
"""
if isinstance(val, bytes):
try:
return val.encode('ascii')
except UnicodeDecodeError:
return val.encode('string-escape')
else:
return val.encode('unicode-escape')
def get_real_func(obj):
""" gets the real function object of the (possibly) wrapped object by
functools.wraps or functools.partial.
"""
while hasattr(obj, "__wrapped__"):
obj = obj.__wrapped__
if isinstance(obj, functools.partial):
obj = obj.func
return obj
def getfslineno(obj):
# xxx let decorators etc specify a sane ordering
obj = get_real_func(obj)
if hasattr(obj, 'place_as'):
obj = obj.place_as
fslineno = _pytest._code.getfslineno(obj)
assert isinstance(fslineno[1], int), obj
return fslineno
def getimfunc(func):
try:
return func.__func__
except AttributeError:
try:
return func.im_func
except AttributeError:
return func
def safe_getattr(object, name, default):
""" Like getattr but return default upon any Exception.
Attribute access can potentially fail for 'evil' Python objects.
See issue214
"""
try:
return getattr(object, name, default)
except Exception:
return default
def _is_unittest_unexpected_success_a_failure():
"""Return if the test suite should fail if a @expectedFailure unittest test PASSES.
From https://docs.python.org/3/library/unittest.html?highlight=unittest#unittest.TestResult.wasSuccessful:
Changed in version 3.4: Returns False if there were any
unexpectedSuccesses from tests marked with the expectedFailure() decorator.
"""
return sys.version_info >= (3, 4)

View File

@@ -5,10 +5,13 @@ import traceback
import types
import warnings
import pkg_resources
import py
# DON't import pytest here because it causes import cycle troubles
import sys, os
import _pytest._code
import _pytest.hookspec # the extension point definitions
import _pytest.assertion
from _pytest._pluggy import PluginManager, HookimplMarker, HookspecMarker
hookimpl = HookimplMarker("pytest")
@@ -24,6 +27,12 @@ class ConftestImportFailure(Exception):
self.path = path
self.excinfo = excinfo
def __str__(self):
etype, evalue, etb = self.excinfo
formatted = traceback.format_tb(etb)
# The level of the tracebacks we want to print is hand crafted :(
return repr(evalue) + '\n' + ''.join(formatted[2:])
def main(args=None, plugins=None):
""" return exit code, after performing an in-process test run.
@@ -62,9 +71,10 @@ class UsageError(Exception):
_preinit = []
default_plugins = (
"mark main terminal runner python pdb unittest capture skipping "
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion genscript "
"junitxml resultlog doctest cacheprovider").split()
"mark main terminal runner python fixtures debugging unittest capture skipping "
"tmpdir monkeypatch recwarn pastebin helpconfig nose assertion "
"junitxml resultlog doctest cacheprovider freeze_support "
"setuponly setupplan").split()
builtin_plugins = set(default_plugins)
builtin_plugins.add("pytester")
@@ -96,6 +106,7 @@ def get_plugin_manager():
return get_config().pluginmanager
def _prepareconfig(args=None, plugins=None):
warning = None
if args is None:
args = sys.argv[1:]
elif isinstance(args, py.path.local):
@@ -103,7 +114,9 @@ def _prepareconfig(args=None, plugins=None):
elif not isinstance(args, (tuple, list)):
if not isinstance(args, str):
raise ValueError("not a string or argument list: %r" % (args,))
args = shlex.split(args)
args = shlex.split(args, posix=sys.platform != "win32")
from _pytest import deprecated
warning = deprecated.MAIN_STR_ARGS
config = get_config()
pluginmanager = config.pluginmanager
try:
@@ -113,6 +126,8 @@ def _prepareconfig(args=None, plugins=None):
pluginmanager.consider_pluginarg(plugin)
else:
pluginmanager.register(plugin)
if warning:
config.warn('C1', warning)
return pluginmanager.hook.pytest_cmdline_parse(
pluginmanager=pluginmanager, args=args)
except BaseException:
@@ -138,6 +153,7 @@ class PytestPluginManager(PluginManager):
self._conftestpath2mod = {}
self._confcutdir = None
self._noconftest = False
self._duplicatepaths = set()
self.add_hookspecs(_pytest.hookspec)
self.register(self)
@@ -151,6 +167,9 @@ class PytestPluginManager(PluginManager):
self.trace.root.setwriter(err.write)
self.enable_tracing()
# Config._consider_importhook will set a real object if required.
self.rewrite_hook = _pytest.assertion.DummyRewriteHook()
def addhooks(self, module_or_class):
"""
.. deprecated:: 2.8
@@ -158,7 +177,7 @@ class PytestPluginManager(PluginManager):
Use :py:meth:`pluggy.PluginManager.add_hookspecs` instead.
"""
warning = dict(code="I2",
fslocation=py.code.getfslineno(sys._getframe(1)),
fslocation=_pytest._code.getfslineno(sys._getframe(1)),
nodeid=None,
message="use pluginmanager.add_hookspecs instead of "
"deprecated addhooks() method.")
@@ -195,7 +214,7 @@ class PytestPluginManager(PluginManager):
def _verify_hook(self, hook, hookmethod):
super(PytestPluginManager, self)._verify_hook(hook, hookmethod)
if "__multicall__" in hookmethod.argnames:
fslineno = py.code.getfslineno(hookmethod.function)
fslineno = _pytest._code.getfslineno(hookmethod.function)
warning = dict(code="I1",
fslocation=fslineno,
nodeid=None,
@@ -359,7 +378,9 @@ class PytestPluginManager(PluginManager):
self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
def consider_module(self, mod):
self._import_plugin_specs(getattr(mod, "pytest_plugins", None))
plugins = getattr(mod, 'pytest_plugins', [])
self.rewrite_hook.mark_rewrite(*plugins)
self._import_plugin_specs(plugins)
def _import_plugin_specs(self, spec):
if spec:
@@ -382,8 +403,13 @@ class PytestPluginManager(PluginManager):
importspec = modname
try:
__import__(importspec)
except ImportError:
raise
except ImportError as e:
new_exc = ImportError('Error importing plugin "%s": %s' % (modname, e))
# copy over name and path attributes
for attr in ('name', 'path'):
if hasattr(e, attr):
setattr(new_exc, attr, getattr(e, attr))
raise new_exc
except Exception as e:
import pytest
if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception):
@@ -455,11 +481,11 @@ class Parser:
"""
self._anonymous.addoption(*opts, **attrs)
def parse(self, args):
def parse(self, args, namespace=None):
from _pytest._argcomplete import try_argcomplete
self.optparser = self._getparser()
try_argcomplete(self.optparser)
return self.optparser.parse_args([str(x) for x in args])
return self.optparser.parse_args([str(x) for x in args], namespace=namespace)
def _getparser(self):
from _pytest._argcomplete import filescompleter
@@ -477,37 +503,38 @@ class Parser:
optparser.add_argument(FILE_OR_DIR, nargs='*').completer=filescompleter
return optparser
def parse_setoption(self, args, option):
parsedoption = self.parse(args)
def parse_setoption(self, args, option, namespace=None):
parsedoption = self.parse(args, namespace=namespace)
for name, value in parsedoption.__dict__.items():
setattr(option, name, value)
return getattr(parsedoption, FILE_OR_DIR)
def parse_known_args(self, args):
def parse_known_args(self, args, namespace=None):
"""parses and returns a namespace object with known arguments at this
point.
"""
return self.parse_known_and_unknown_args(args)[0]
return self.parse_known_and_unknown_args(args, namespace=namespace)[0]
def parse_known_and_unknown_args(self, args):
def parse_known_and_unknown_args(self, args, namespace=None):
"""parses and returns a namespace object with known arguments, and
the remaining arguments unknown at this point.
"""
optparser = self._getparser()
args = [str(x) for x in args]
return optparser.parse_known_args(args)
return optparser.parse_known_args(args, namespace=namespace)
def addini(self, name, help, type=None, default=None):
""" register an ini-file option.
:name: name of the ini-variable
:type: type of the variable, can be ``pathlist``, ``args`` or ``linelist``.
:type: type of the variable, can be ``pathlist``, ``args``, ``linelist``
or ``bool``.
:default: default value if no ini-file option exists but is queried.
The value of ini-variables can be retrieved via a call to
:py:func:`config.getini(name) <_pytest.config.Config.getini>`.
"""
assert type in (None, "pathlist", "args", "linelist")
assert type in (None, "pathlist", "args", "linelist", "bool")
self._inidict[name] = (help, type, default)
self._ininames.append(name)
@@ -530,13 +557,18 @@ class ArgumentError(Exception):
class Argument:
"""class that mimics the necessary behaviour of optparse.Option """
"""class that mimics the necessary behaviour of optparse.Option
its currently a least effort implementation
and ignoring choices and integer prefixes
https://docs.python.org/3/library/optparse.html#optparse-standard-option-types
"""
_typ_map = {
'int': int,
'string': str,
}
# enable after some grace period for plugin writers
TYPE_WARN = False
'float': float,
'complex': complex,
}
def __init__(self, *names, **attrs):
"""store parms in private vars for use in add_argument"""
@@ -544,17 +576,12 @@ class Argument:
self._short_opts = []
self._long_opts = []
self.dest = attrs.get('dest')
if self.TYPE_WARN:
try:
help = attrs['help']
if '%default' in help:
warnings.warn(
'pytest now uses argparse. "%default" should be'
' changed to "%(default)s" ',
FutureWarning,
stacklevel=3)
except KeyError:
pass
if '%default' in (attrs.get('help') or ''):
warnings.warn(
'pytest now uses argparse. "%default" should be'
' changed to "%(default)s" ',
DeprecationWarning,
stacklevel=3)
try:
typ = attrs['type']
except KeyError:
@@ -563,25 +590,23 @@ class Argument:
# this might raise a keyerror as well, don't want to catch that
if isinstance(typ, py.builtin._basestring):
if typ == 'choice':
if self.TYPE_WARN:
warnings.warn(
'type argument to addoption() is a string %r.'
' For parsearg this is optional and when supplied '
' should be a type.'
' (options: %s)' % (typ, names),
FutureWarning,
stacklevel=3)
warnings.warn(
'type argument to addoption() is a string %r.'
' For parsearg this is optional and when supplied '
' should be a type.'
' (options: %s)' % (typ, names),
DeprecationWarning,
stacklevel=3)
# argparse expects a type here take it from
# the type of the first element
attrs['type'] = type(attrs['choices'][0])
else:
if self.TYPE_WARN:
warnings.warn(
'type argument to addoption() is a string %r.'
' For parsearg this should be a type.'
' (options: %s)' % (typ, names),
FutureWarning,
stacklevel=3)
warnings.warn(
'type argument to addoption() is a string %r.'
' For parsearg this should be a type.'
' (options: %s)' % (typ, names),
DeprecationWarning,
stacklevel=3)
attrs['type'] = Argument._typ_map[typ]
# used in test_parseopt -> test_parse_defaultgetter
self.type = attrs['type']
@@ -648,20 +673,17 @@ class Argument:
self._long_opts.append(opt)
def __repr__(self):
retval = 'Argument('
args = []
if self._short_opts:
retval += '_short_opts: ' + repr(self._short_opts) + ', '
args += ['_short_opts: ' + repr(self._short_opts)]
if self._long_opts:
retval += '_long_opts: ' + repr(self._long_opts) + ', '
retval += 'dest: ' + repr(self.dest) + ', '
args += ['_long_opts: ' + repr(self._long_opts)]
args += ['dest: ' + repr(self.dest)]
if hasattr(self, 'type'):
retval += 'type: ' + repr(self.type) + ', '
args += ['type: ' + repr(self.type)]
if hasattr(self, 'default'):
retval += 'default: ' + repr(self.default) + ', '
if retval[-2:] == ', ': # always long enough to test ("Argument(" )
retval = retval[:-2]
retval += ')'
return retval
args += ['default: ' + repr(self.default)]
return 'Argument({0})'.format(', '.join(args))
class OptionGroup:
@@ -679,6 +701,10 @@ class OptionGroup:
results in help showing '--two-words' only, but --twowords gets
accepted **and** the automatic destination is in args.twowords
"""
conflict = set(optnames).intersection(
name for opt in self.options for name in opt.names())
if conflict:
raise ValueError("option names %s already added" % conflict)
option = Argument(*optnames, **attrs)
self._addoption_instance(option, shortupper=False)
@@ -779,10 +805,12 @@ def _ensure_removed_sysmodule(modname):
class CmdOptions(object):
""" holds cmdline options as attributes."""
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __init__(self, values=()):
self.__dict__.update(values)
def __repr__(self):
return "<CmdOptions %r>" %(self.__dict__,)
def copy(self):
return CmdOptions(self.__dict__)
class Notset:
def __repr__(self):
@@ -879,8 +907,8 @@ class Config(object):
def fromdictargs(cls, option_dict, args):
""" constructor useable for subprocesses. """
config = get_config()
config._preparse(args, addopts=False)
config.option.__dict__.update(option_dict)
config.parse(args, addopts=False)
for x in config.option.plugins:
config.pluginmanager.consider_pluginarg(x)
return config
@@ -898,8 +926,8 @@ class Config(object):
self.pluginmanager._set_initial_conftests(early_config.known_args_namespace)
def _initini(self, args):
ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args)
ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=self.option.copy())
r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args, warnfunc=self.warn)
self.rootdir, self.inifile, self.inicfg = r
self._parser.extra_info['rootdir'] = self.rootdir
self._parser.extra_info['inifile'] = self.inifile
@@ -907,19 +935,64 @@ class Config(object):
self._parser.addini('addopts', 'extra command line options', 'args')
self._parser.addini('minversion', 'minimally required pytest version')
def _consider_importhook(self, args, entrypoint_name):
"""Install the PEP 302 import hook if using assertion re-writing.
Needs to parse the --assert=<mode> option from the commandline
and find all the installed plugins to mark them for re-writing
by the importhook.
"""
ns, unknown_args = self._parser.parse_known_and_unknown_args(args)
mode = ns.assertmode
if mode == 'rewrite':
try:
hook = _pytest.assertion.install_importhook(self)
except SystemError:
mode = 'plain'
else:
self.pluginmanager.rewrite_hook = hook
for entrypoint in pkg_resources.iter_entry_points('pytest11'):
for entry in entrypoint.dist._get_metadata('RECORD'):
fn = entry.split(',')[0]
is_simple_module = os.sep not in fn and fn.endswith('.py')
is_package = fn.count(os.sep) == 1 and fn.endswith('__init__.py')
if is_simple_module:
module_name, ext = os.path.splitext(fn)
hook.mark_rewrite(module_name)
elif is_package:
package_name = os.path.dirname(fn)
hook.mark_rewrite(package_name)
self._warn_about_missing_assertion(mode)
def _warn_about_missing_assertion(self, mode):
try:
assert False
except AssertionError:
pass
else:
if mode == 'plain':
sys.stderr.write("WARNING: ASSERTIONS ARE NOT EXECUTED"
" and FAILING TESTS WILL PASS. Are you"
" using python -O?")
else:
sys.stderr.write("WARNING: assertions not in test modules or"
" plugins will be ignored"
" because assert statements are not executed "
"by the underlying Python interpreter "
"(are you using python -O?)\n")
def _preparse(self, args, addopts=True):
self._initini(args)
if addopts:
args[:] = shlex.split(os.environ.get('PYTEST_ADDOPTS', '')) + args
args[:] = self.getini("addopts") + args
self._checkversion()
entrypoint_name = 'pytest11'
self._consider_importhook(args, entrypoint_name)
self.pluginmanager.consider_preparse(args)
try:
self.pluginmanager.load_setuptools_entrypoints("pytest11")
except ImportError as e:
self.warn("I2", "could not load setuptools entry import: %s" % (e,))
self.pluginmanager.load_setuptools_entrypoints(entrypoint_name)
self.pluginmanager.consider_env()
self.known_args_namespace = ns = self._parser.parse_known_args(args)
self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
if self.known_args_namespace.confcutdir is None and self.inifile:
confcutdir = py.path.local(self.inifile).dirname
self.known_args_namespace.confcutdir = confcutdir
@@ -947,17 +1020,17 @@ class Config(object):
self.inicfg.config.path, self.inicfg.lineof('minversion'),
minver, pytest.__version__))
def parse(self, args):
def parse(self, args, addopts=True):
# parse given cmdline arguments into this config object.
assert not hasattr(self, 'args'), (
"can only parse cmdline args at most once per Config object")
self._origargs = args
self.hook.pytest_addhooks.call_historic(
kwargs=dict(pluginmanager=self.pluginmanager))
self._preparse(args)
self._preparse(args, addopts=addopts)
# XXX deprecated hook:
self.hook.pytest_cmdline_preparse(config=self, args=args)
args = self._parser.parse_setoption(args, self.option)
args = self._parser.parse_setoption(args, self.option, namespace=self.option)
if not args:
cwd = os.getcwd()
if cwd == self.rootdir:
@@ -990,14 +1063,16 @@ class Config(object):
description, type, default = self._parser._inidict[name]
except KeyError:
raise ValueError("unknown configuration value: %r" %(name,))
try:
value = self.inicfg[name]
except KeyError:
if default is not None:
return default
if type is None:
return ''
return []
value = self._get_override_ini_value(name)
if value is None:
try:
value = self.inicfg[name]
except KeyError:
if default is not None:
return default
if type is None:
return ''
return []
if type == "pathlist":
dp = py.path.local(self.inicfg.config.path).dirpath()
l = []
@@ -1008,6 +1083,8 @@ class Config(object):
return shlex.split(value)
elif type == "linelist":
return [t for t in map(lambda x: x.strip(), value.split("\n")) if t]
elif type == "bool":
return bool(_strtobool(value.strip()))
else:
assert type is None
return value
@@ -1026,6 +1103,20 @@ class Config(object):
l.append(relroot)
return l
def _get_override_ini_value(self, name):
value = None
# override_ini is a list of list, to support both -o foo1=bar1 foo2=bar2 and
# and -o foo1=bar1 -o foo2=bar2 options
# always use the last item if multiple value set for same ini-name,
# e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2
if self.getoption("override_ini", None):
for ini_config_list in self.option.override_ini:
for ini_config in ini_config_list:
(key, user_ini_value) = ini_config.split("=", 1)
if key == name:
value = user_ini_value
return value
def getoption(self, name, default=notset, skip=False):
""" return command line option value.
@@ -1063,7 +1154,18 @@ def exists(path, ignore=EnvironmentError):
except ignore:
return False
def getcfg(args, inibasenames):
def getcfg(args, warnfunc=None):
"""
Search the list of arguments for a valid ini-file for pytest,
and return a tuple of (rootdir, inifile, cfg-dict).
note: warnfunc is an optional function used to warn
about ini-files that use deprecated features.
This parameter should be removed when pytest
adopts standard deprecation warnings (#1804).
"""
from _pytest.deprecated import SETUP_CFG_PYTEST
inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"]
args = [x for x in args if not str(x).startswith("-")]
if not args:
args = [py.path.local()]
@@ -1075,7 +1177,11 @@ def getcfg(args, inibasenames):
if exists(p):
iniconfig = py.iniconfig.IniConfig(p)
if 'pytest' in iniconfig.sections:
if inibasename == 'setup.cfg' and warnfunc:
warnfunc('C1', SETUP_CFG_PYTEST)
return base, p, iniconfig['pytest']
if inibasename == 'setup.cfg' and 'tool:pytest' in iniconfig.sections:
return base, p, iniconfig['tool:pytest']
elif inibasename == "pytest.ini":
# allowed to be empty
return base, p, {}
@@ -1090,6 +1196,8 @@ def get_common_ancestor(args):
if str(arg)[0] == "-":
continue
p = py.path.local(arg)
if not p.exists():
continue
if common_ancestor is None:
common_ancestor = p
else:
@@ -1103,29 +1211,40 @@ def get_common_ancestor(args):
common_ancestor = shared
if common_ancestor is None:
common_ancestor = py.path.local()
elif not common_ancestor.isdir():
elif common_ancestor.isfile():
common_ancestor = common_ancestor.dirpath()
return common_ancestor
def determine_setup(inifile, args):
def get_dirs_from_args(args):
return [d for d in (py.path.local(x) for x in args
if not str(x).startswith("-"))
if d.exists()]
def determine_setup(inifile, args, warnfunc=None):
dirs = get_dirs_from_args(args)
if inifile:
iniconfig = py.iniconfig.IniConfig(inifile)
try:
inicfg = iniconfig["pytest"]
except KeyError:
inicfg = None
rootdir = get_common_ancestor(args)
rootdir = get_common_ancestor(dirs)
else:
ancestor = get_common_ancestor(args)
rootdir, inifile, inicfg = getcfg(
[ancestor], ["pytest.ini", "tox.ini", "setup.cfg"])
ancestor = get_common_ancestor(dirs)
rootdir, inifile, inicfg = getcfg([ancestor], warnfunc=warnfunc)
if rootdir is None:
for rootdir in ancestor.parts(reverse=True):
if rootdir.join("setup.py").exists():
break
else:
rootdir = ancestor
rootdir, inifile, inicfg = getcfg(dirs, warnfunc=warnfunc)
if rootdir is None:
rootdir = get_common_ancestor([py.path.local(), ancestor])
is_fs_root = os.path.splitdrive(str(rootdir))[1] == os.sep
if is_fs_root:
rootdir = ancestor
return rootdir, inifile, inicfg or {}
@@ -1161,3 +1280,21 @@ def create_terminal_writer(config, *args, **kwargs):
if config.option.color == 'no':
tw.hasmarkup = False
return tw
def _strtobool(val):
"""Convert a string representation of truth to true (1) or false (0).
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if
'val' is anything else.
.. note:: copied from distutils.util
"""
val = val.lower()
if val in ('y', 'yes', 't', 'true', 'on', '1'):
return 1
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
return 0
else:
raise ValueError("invalid truth value %r" % (val,))

View File

@@ -8,21 +8,33 @@ import pytest
def pytest_addoption(parser):
group = parser.getgroup("general")
group._addoption('--pdb',
action="store_true", dest="usepdb", default=False,
help="start the interactive Python debugger on errors.")
group._addoption(
'--pdb', dest="usepdb", action="store_true",
help="start the interactive Python debugger on errors.")
group._addoption(
'--pdbcls', dest="usepdb_cls", metavar="modulename:classname",
help="start a custom interactive Python debugger on errors. "
"For example: --pdbcls=IPython.core.debugger:Pdb")
def pytest_namespace():
return {'set_trace': pytestPDB().set_trace}
def pytest_configure(config):
if config.getvalue("usepdb"):
if config.getvalue("usepdb") or config.getvalue("usepdb_cls"):
config.pluginmanager.register(PdbInvoke(), 'pdbinvoke')
if config.getvalue("usepdb_cls"):
modname, classname = config.getvalue("usepdb_cls").split(":")
__import__(modname)
pdb_cls = getattr(sys.modules[modname], classname)
else:
pdb_cls = pdb.Pdb
pytestPDB._pdb_cls = pdb_cls
old = (pdb.set_trace, pytestPDB._pluginmanager)
def fin():
pdb.set_trace, pytestPDB._pluginmanager = old
pytestPDB._config = None
pytestPDB._pdb_cls = pdb.Pdb
pdb.set_trace = pytest.set_trace
pytestPDB._pluginmanager = config.pluginmanager
pytestPDB._config = config
@@ -32,12 +44,12 @@ class pytestPDB:
""" Pseudo PDB that defers to the real pdb. """
_pluginmanager = None
_config = None
_pdb_cls = pdb.Pdb
def set_trace(self):
""" invoke PDB set_trace debugging, dropping any IO capturing. """
import _pytest.config
frame = sys._getframe().f_back
capman = None
if self._pluginmanager is not None:
capman = self._pluginmanager.getplugin("capturemanager")
if capman:
@@ -45,15 +57,17 @@ class pytestPDB:
tw = _pytest.config.create_terminal_writer(self._config)
tw.line()
tw.sep(">", "PDB set_trace (IO-capturing turned off)")
self._pluginmanager.hook.pytest_enter_pdb()
pdb.Pdb().set_trace(frame)
self._pluginmanager.hook.pytest_enter_pdb(config=self._config)
self._pdb_cls().set_trace(frame)
class PdbInvoke:
def pytest_exception_interact(self, node, call, report):
capman = node.config.pluginmanager.getplugin("capturemanager")
if capman:
capman.suspendcapture(in_=True)
out, err = capman.suspendcapture(in_=True)
sys.stdout.write(out)
sys.stdout.write(err)
_enter_pdb(node, call.excinfo, report)
def pytest_internalerror(self, excrepr, excinfo):
@@ -97,7 +111,7 @@ def _find_last_non_hidden_frame(stack):
def post_mortem(t):
class Pdb(pdb.Pdb):
class Pdb(pytestPDB._pdb_cls):
def get_stack(self, f, t):
stack, i = pdb.Pdb.get_stack(self, f, t)
if f is None:

24
_pytest/deprecated.py Normal file
View File

@@ -0,0 +1,24 @@
"""
This module contains deprecation messages and bits of code used elsewhere in the codebase
that is planned to be removed in the next pytest release.
Keeping it in a central location makes it easy to track what is deprecated and should
be removed when the time comes.
"""
MAIN_STR_ARGS = 'passing a string to pytest.main() is deprecated, ' \
'pass a list of arguments instead.'
YIELD_TESTS = 'yield tests are deprecated, and scheduled to be removed in pytest 4.0'
FUNCARG_PREFIX = (
'{name}: declaring fixtures using "pytest_funcarg__" prefix is deprecated '
'and scheduled to be removed in pytest 4.0. '
'Please remove the prefix and use the @pytest.fixture decorator instead.')
SETUP_CFG_PYTEST = '[pytest] section in setup.cfg files is deprecated, use [tool:pytest] instead.'
GETFUNCARGVALUE = "use of getfuncargvalue is deprecated, use getfixturevalue"
RESULT_LOG = '--result-log is deprecated and scheduled for removal in pytest 4.0'

View File

@@ -1,10 +1,26 @@
""" discover and run doctests in modules and test files."""
from __future__ import absolute_import
import traceback
import pytest, py
from _pytest.python import FixtureRequest
from py._code.code import TerminalRepr, ReprFileLocation
import traceback
import pytest
from _pytest._code.code import ExceptionInfo, ReprFileLocation, TerminalRepr
from _pytest.fixtures import FixtureRequest
DOCTEST_REPORT_CHOICE_NONE = 'none'
DOCTEST_REPORT_CHOICE_CDIFF = 'cdiff'
DOCTEST_REPORT_CHOICE_NDIFF = 'ndiff'
DOCTEST_REPORT_CHOICE_UDIFF = 'udiff'
DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE = 'only_first_failure'
DOCTEST_REPORT_CHOICES = (
DOCTEST_REPORT_CHOICE_NONE,
DOCTEST_REPORT_CHOICE_CDIFF,
DOCTEST_REPORT_CHOICE_NDIFF,
DOCTEST_REPORT_CHOICE_UDIFF,
DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE,
)
def pytest_addoption(parser):
parser.addini('doctest_optionflags', 'option flags for doctests',
@@ -14,8 +30,13 @@ def pytest_addoption(parser):
action="store_true", default=False,
help="run doctests in all .py modules",
dest="doctestmodules")
group.addoption("--doctest-report",
type=str.lower, default="udiff",
help="choose another output format for diffs on doctest failure",
choices=DOCTEST_REPORT_CHOICES,
dest="doctestreport")
group.addoption("--doctest-glob",
action="store", default="test*.txt", metavar="pat",
action="append", default=[], metavar="pat",
help="doctests file matching pattern, default: test*.txt",
dest="doctestglob")
group.addoption("--doctest-ignore-import-errors",
@@ -29,11 +50,20 @@ def pytest_collect_file(path, parent):
if path.ext == ".py":
if config.option.doctestmodules:
return DoctestModule(path, parent)
elif (path.ext in ('.txt', '.rst') and parent.session.isinitpath(path)) or \
path.check(fnmatch=config.getvalue("doctestglob")):
elif _is_doctest(config, path, parent):
return DoctestTextfile(path, parent)
def _is_doctest(config, path, parent):
if path.ext in ('.txt', '.rst') and parent.session.isinitpath(path):
return True
globs = config.getoption("doctestglob") or ['test*.txt']
for glob in globs:
if path.check(fnmatch=glob):
return True
return False
class ReprFailDoctest(TerminalRepr):
def __init__(self, reprlocation, lines):
@@ -47,7 +77,6 @@ class ReprFailDoctest(TerminalRepr):
class DoctestItem(pytest.Item):
def __init__(self, name, parent, runner=None, dtest=None):
super(DoctestItem, self).__init__(name, parent)
self.runner = runner
@@ -58,7 +87,9 @@ class DoctestItem(pytest.Item):
def setup(self):
if self.dtest is not None:
self.fixture_request = _setup_fixtures(self)
globs = dict(getfixture=self.fixture_request.getfuncargvalue)
globs = dict(getfixture=self.fixture_request.getfixturevalue)
for name, value in self.fixture_request.getfuncargvalue('doctest_namespace').items():
globs[name] = value
self.dtest.globs.update(globs)
def runtest(self):
@@ -79,26 +110,26 @@ class DoctestItem(pytest.Item):
lineno = test.lineno + example.lineno + 1
message = excinfo.type.__name__
reprlocation = ReprFileLocation(filename, lineno, message)
checker = _get_unicode_checker()
REPORT_UDIFF = doctest.REPORT_UDIFF
filelines = py.path.local(filename).readlines(cr=0)
lines = []
checker = _get_checker()
report_choice = _get_report_choice(self.config.getoption("doctestreport"))
if lineno is not None:
i = max(test.lineno, max(0, lineno - 10)) # XXX?
for line in filelines[i:lineno]:
lines.append("%03d %s" % (i+1, line))
i += 1
lines = doctestfailure.test.docstring.splitlines(False)
# add line numbers to the left of the error message
lines = ["%03d %s" % (i + test.lineno + 1, x)
for (i, x) in enumerate(lines)]
# trim docstring error lines to 10
lines = lines[example.lineno - 9:example.lineno + 1]
else:
lines.append('EXAMPLE LOCATION UNKNOWN, not showing all tests of that example')
lines = ['EXAMPLE LOCATION UNKNOWN, not showing all tests of that example']
indent = '>>>'
for line in example.source.splitlines():
lines.append('??? %s %s' % (indent, line))
indent = '...'
if excinfo.errisinstance(doctest.DocTestFailure):
lines += checker.output_difference(example,
doctestfailure.got, REPORT_UDIFF).split("\n")
doctestfailure.got, report_choice).split("\n")
else:
inner_excinfo = py.code.ExceptionInfo(excinfo.value.exc_info)
inner_excinfo = ExceptionInfo(excinfo.value.exc_info)
lines += ["UNEXPECTED EXCEPTION: %s" %
repr(inner_excinfo.value)]
lines += traceback.format_exception(*excinfo.value.exc_info)
@@ -118,7 +149,9 @@ def _get_flag_lookup():
ELLIPSIS=doctest.ELLIPSIS,
IGNORE_EXCEPTION_DETAIL=doctest.IGNORE_EXCEPTION_DETAIL,
COMPARISON_FLAGS=doctest.COMPARISON_FLAGS,
ALLOW_UNICODE=_get_allow_unicode_flag())
ALLOW_UNICODE=_get_allow_unicode_flag(),
ALLOW_BYTES=_get_allow_bytes_flag(),
)
def get_optionflags(parent):
@@ -130,29 +163,28 @@ def get_optionflags(parent):
return flag_acc
class DoctestTextfile(DoctestItem, pytest.Module):
class DoctestTextfile(pytest.Module):
obj = None
def runtest(self):
def collect(self):
import doctest
fixture_request = _setup_fixtures(self)
# inspired by doctest.testfile; ideally we would use it directly,
# but it doesn't support passing a custom checker
text = self.fspath.read()
filename = str(self.fspath)
name = self.fspath.basename
globs = dict(getfixture=fixture_request.getfuncargvalue)
if '__name__' not in globs:
globs['__name__'] = '__main__'
globs = {'__name__': '__main__'}
optionflags = get_optionflags(self)
runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
checker=_get_unicode_checker())
checker=_get_checker())
parser = doctest.DocTestParser()
test = parser.get_doctest(text, globs, name, filename, 0)
_check_all_skipped(test)
runner.run(test)
if test.examples:
yield DoctestItem(test.name, self, runner, test)
def _check_all_skipped(test):
@@ -182,7 +214,7 @@ class DoctestModule(pytest.Module):
finder = doctest.DocTestFinder()
optionflags = get_optionflags(self)
runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
checker=_get_unicode_checker())
checker=_get_checker())
for test in finder.find(module, module.__name__):
if test.examples: # skip empty doctests
yield DoctestItem(test.name, self, runner, test)
@@ -204,28 +236,32 @@ def _setup_fixtures(doctest_item):
return fixture_request
def _get_unicode_checker():
def _get_checker():
"""
Returns a doctest.OutputChecker subclass that takes in account the
ALLOW_UNICODE option to ignore u'' prefixes in strings. Useful
when the same doctest should run in Python 2 and Python 3.
ALLOW_UNICODE option to ignore u'' prefixes in strings and ALLOW_BYTES
to strip b'' prefixes.
Useful when the same doctest should run in Python 2 and Python 3.
An inner class is used to avoid importing "doctest" at the module
level.
"""
if hasattr(_get_unicode_checker, 'UnicodeOutputChecker'):
return _get_unicode_checker.UnicodeOutputChecker()
if hasattr(_get_checker, 'LiteralsOutputChecker'):
return _get_checker.LiteralsOutputChecker()
import doctest
import re
class UnicodeOutputChecker(doctest.OutputChecker):
class LiteralsOutputChecker(doctest.OutputChecker):
"""
Copied from doctest_nose_plugin.py from the nltk project:
https://github.com/nltk/nltk
Further extended to also support byte literals.
"""
_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE)
_unicode_literal_re = re.compile(r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE)
_bytes_literal_re = re.compile(r"(\W|^)[bB]([rR]?[\'\"])", re.UNICODE)
def check_output(self, want, got, optionflags):
res = doctest.OutputChecker.check_output(self, want, got,
@@ -233,23 +269,27 @@ def _get_unicode_checker():
if res:
return True
if not (optionflags & _get_allow_unicode_flag()):
allow_unicode = optionflags & _get_allow_unicode_flag()
allow_bytes = optionflags & _get_allow_bytes_flag()
if not allow_unicode and not allow_bytes:
return False
else: # pragma: no cover
# the code below will end up executed only in Python 2 in
# our tests, and our coverage check runs in Python 3 only
def remove_u_prefixes(txt):
return re.sub(self._literal_re, r'\1\2', txt)
def remove_prefixes(regex, txt):
return re.sub(regex, r'\1\2', txt)
want = remove_u_prefixes(want)
got = remove_u_prefixes(got)
if allow_unicode:
want = remove_prefixes(self._unicode_literal_re, want)
got = remove_prefixes(self._unicode_literal_re, got)
if allow_bytes:
want = remove_prefixes(self._bytes_literal_re, want)
got = remove_prefixes(self._bytes_literal_re, got)
res = doctest.OutputChecker.check_output(self, want, got,
optionflags)
return res
_get_unicode_checker.UnicodeOutputChecker = UnicodeOutputChecker
return _get_unicode_checker.UnicodeOutputChecker()
_get_checker.LiteralsOutputChecker = LiteralsOutputChecker
return _get_checker.LiteralsOutputChecker()
def _get_allow_unicode_flag():
@@ -258,3 +298,34 @@ def _get_allow_unicode_flag():
"""
import doctest
return doctest.register_optionflag('ALLOW_UNICODE')
def _get_allow_bytes_flag():
"""
Registers and returns the ALLOW_BYTES flag.
"""
import doctest
return doctest.register_optionflag('ALLOW_BYTES')
def _get_report_choice(key):
"""
This function returns the actual `doctest` module flag value, we want to do it as late as possible to avoid
importing `doctest` and all its dependencies when parsing options, as it adds overhead and breaks tests.
"""
import doctest
return {
DOCTEST_REPORT_CHOICE_UDIFF: doctest.REPORT_UDIFF,
DOCTEST_REPORT_CHOICE_CDIFF: doctest.REPORT_CDIFF,
DOCTEST_REPORT_CHOICE_NDIFF: doctest.REPORT_NDIFF,
DOCTEST_REPORT_CHOICE_ONLY_FIRST_FAILURE: doctest.REPORT_ONLY_FIRST_FAILURE,
DOCTEST_REPORT_CHOICE_NONE: 0,
}[key]
@pytest.fixture(scope='session')
def doctest_namespace():
"""
Inject names into the doctest namespace.
"""
return dict()

1110
_pytest/fixtures.py Normal file

File diff suppressed because it is too large Load Diff

45
_pytest/freeze_support.py Normal file
View File

@@ -0,0 +1,45 @@
"""
Provides a function to report all internal modules for using freezing tools
pytest
"""
def pytest_namespace():
return {'freeze_includes': freeze_includes}
def freeze_includes():
"""
Returns a list of module names used by py.test that should be
included by cx_freeze.
"""
import py
import _pytest
result = list(_iter_all_modules(py))
result += list(_iter_all_modules(_pytest))
return result
def _iter_all_modules(package, prefix=''):
"""
Iterates over the names of all modules that can be found in the given
package, recursively.
Example:
_iter_all_modules(_pytest) ->
['_pytest.assertion.newinterpret',
'_pytest.capture',
'_pytest.core',
...
]
"""
import os
import pkgutil
if type(package) is not str:
path, prefix = package.__path__[0], package.__name__ + '.'
else:
path = package
for _, name, is_package in pkgutil.iter_modules([path]):
if is_package:
for m in _iter_all_modules(os.path.join(path, name), prefix=name + '.'):
yield prefix + m
else:
yield prefix + name

View File

@@ -1,132 +0,0 @@
""" (deprecated) generate a single-file self-contained version of pytest """
import os
import sys
import pkgutil
import py
import _pytest
def find_toplevel(name):
for syspath in sys.path:
base = py.path.local(syspath)
lib = base/name
if lib.check(dir=1):
return lib
mod = base.join("%s.py" % name)
if mod.check(file=1):
return mod
raise LookupError(name)
def pkgname(toplevel, rootpath, path):
parts = path.parts()[len(rootpath.parts()):]
return '.'.join([toplevel] + [x.purebasename for x in parts])
def pkg_to_mapping(name):
toplevel = find_toplevel(name)
name2src = {}
if toplevel.check(file=1): # module
name2src[toplevel.purebasename] = toplevel.read()
else: # package
for pyfile in toplevel.visit('*.py'):
pkg = pkgname(name, toplevel, pyfile)
name2src[pkg] = pyfile.read()
# with wheels py source code might be not be installed
# and the resulting genscript is useless, just bail out.
assert name2src, "no source code found for %r at %r" %(name, toplevel)
return name2src
def compress_mapping(mapping):
import base64, pickle, zlib
data = pickle.dumps(mapping, 2)
data = zlib.compress(data, 9)
data = base64.encodestring(data)
data = data.decode('ascii')
return data
def compress_packages(names):
mapping = {}
for name in names:
mapping.update(pkg_to_mapping(name))
return compress_mapping(mapping)
def generate_script(entry, packages):
data = compress_packages(packages)
tmpl = py.path.local(__file__).dirpath().join('standalonetemplate.py')
exe = tmpl.read()
exe = exe.replace('@SOURCES@', data)
exe = exe.replace('@ENTRY@', entry)
return exe
def pytest_addoption(parser):
group = parser.getgroup("debugconfig")
group.addoption("--genscript", action="store", default=None,
dest="genscript", metavar="path",
help="create standalone pytest script at given target path.")
def pytest_cmdline_main(config):
import _pytest.config
genscript = config.getvalue("genscript")
if genscript:
tw = _pytest.config.create_terminal_writer(config)
tw.line("WARNING: usage of genscript is deprecated.",
red=True)
deps = ['py', '_pytest', 'pytest'] # pluggy is vendored
if sys.version_info < (2,7):
deps.append("argparse")
tw.line("generated script will run on python2.6-python3.3++")
else:
tw.line("WARNING: generated script will not run on python2.6 "
"due to 'argparse' dependency. Use python2.6 "
"to generate a python2.6 compatible script", red=True)
script = generate_script(
'import pytest; raise SystemExit(pytest.cmdline.main())',
deps,
)
genscript = py.path.local(genscript)
genscript.write(script)
tw.line("generated pytest standalone script: %s" % genscript,
bold=True)
return 0
def pytest_namespace():
return {'freeze_includes': freeze_includes}
def freeze_includes():
"""
Returns a list of module names used by py.test that should be
included by cx_freeze.
"""
result = list(_iter_all_modules(py))
result += list(_iter_all_modules(_pytest))
return result
def _iter_all_modules(package, prefix=''):
"""
Iterates over the names of all modules that can be found in the given
package, recursively.
Example:
_iter_all_modules(_pytest) ->
['_pytest.assertion.newinterpret',
'_pytest.capture',
'_pytest.core',
...
]
"""
if type(package) is not str:
path, prefix = package.__path__[0], package.__name__ + '.'
else:
path = package
for _, name, is_package in pkgutil.iter_modules([path]):
if is_package:
for m in _iter_all_modules(os.path.join(path, name), prefix=name + '.'):
yield prefix + m
else:
yield prefix + name

View File

@@ -20,6 +20,10 @@ def pytest_addoption(parser):
group.addoption('--debug',
action="store_true", dest="debug", default=False,
help="store internal tracing debug information in 'pytestdebug.log'.")
group._addoption(
'-o', '--override-ini', nargs='*', dest="override_ini",
action="append",
help="override config option, e.g. `-o xfail_strict=True`.")
@pytest.hookimpl(hookwrapper=True)
@@ -67,7 +71,6 @@ def showhelp(config):
tw.write(config._parser.optparser.format_help())
tw.line()
tw.line()
#tw.sep( "=", "config file settings")
tw.line("[pytest] ini-options in the next "
"pytest.ini|tox.ini|setup.cfg file:")
tw.line()
@@ -92,8 +95,8 @@ def showhelp(config):
tw.line()
tw.line()
tw.line("to see available markers type: py.test --markers")
tw.line("to see available fixtures type: py.test --fixtures")
tw.line("to see available markers type: pytest --markers")
tw.line("to see available fixtures type: pytest --fixtures")
tw.line("(shown according to specified file_or_dir or current dir "
"if not specified)")

View File

@@ -28,17 +28,14 @@ def pytest_plugin_registered(plugin, manager):
@hookspec(historic=True)
def pytest_addoption(parser):
"""register argparse-style options and ini-style config values.
"""register argparse-style options and ini-style config values,
called once at the beginning of a test run.
.. warning::
.. note::
This function must be implemented in a :ref:`plugin <pluginorder>`
and is called once at the beginning of a test run.
Implementing this hook from ``conftest.py`` files is **strongly**
discouraged because ``conftest.py`` files are lazily loaded and
may give strange *unknown option* errors depending on the directory
``py.test`` is invoked from.
This function should be implemented only in plugins or ``conftest.py``
files situated at the tests root directory due to how pytest
:ref:`discovers plugins during startup <pluginorder>`.
:arg parser: To add command line options, call
:py:func:`parser.addoption(...) <_pytest.config.Parser.addoption>`.
@@ -84,7 +81,7 @@ def pytest_cmdline_main(config):
""" called for performing the main command line action. The default
implementation will invoke the configure hooks and runtest_mainloop. """
def pytest_load_initial_conftests(args, early_config, parser):
def pytest_load_initial_conftests(early_config, parser, args):
""" implements the loading of initial conftest files ahead
of command line option parsing. """
@@ -159,6 +156,12 @@ def pytest_pyfunc_call(pyfuncitem):
def pytest_generate_tests(metafunc):
""" generate (multiple) parametrized calls to a test function."""
@hookspec(firstresult=True)
def pytest_make_parametrize_id(config, val):
"""Return a user-friendly string representation of the given ``val`` that will be used
by @pytest.mark.parametrize calls. Return None if the hook doesn't know about ``val``.
"""
# -------------------------------------------------------------------------
# generic runtest related hooks
# -------------------------------------------------------------------------
@@ -215,6 +218,19 @@ def pytest_runtest_logreport(report):
""" process a test setup/call/teardown report relating to
the respective phase of executing a test. """
# -------------------------------------------------------------------------
# Fixture related hooks
# -------------------------------------------------------------------------
@hookspec(firstresult=True)
def pytest_fixture_setup(fixturedef, request):
""" performs fixture setup execution. """
def pytest_fixture_post_finalizer(fixturedef):
""" called after fixture teardown, but before the cache is cleared so
the fixture result cache ``fixturedef.cached_result`` can
still be accessed."""
# -------------------------------------------------------------------------
# test session related hooks
# -------------------------------------------------------------------------
@@ -253,7 +269,7 @@ def pytest_report_header(config, startdir):
def pytest_report_teststatus(report):
""" return result-category, shortletter and verbose word for reporting."""
def pytest_terminal_summary(terminalreporter):
def pytest_terminal_summary(terminalreporter, exitstatus):
""" add additional section in terminal summary reporting. """
@@ -282,15 +298,17 @@ def pytest_keyboard_interrupt(excinfo):
""" called for keyboard interrupt. """
def pytest_exception_interact(node, call, report):
""" (experimental, new in 2.4) called when
an exception was raised which can potentially be
"""called when an exception was raised which can potentially be
interactively handled.
This hook is only called if an exception was raised
that is not an internal exception like "skip.Exception".
that is not an internal exception like ``skip.Exception``.
"""
def pytest_enter_pdb():
def pytest_enter_pdb(config):
""" called upon pdb.set_trace(), can be used by plugins to take special
action just before the python debugger enters in interactive mode.
:arg config: pytest config object
:type config: _pytest.config.Config
"""

View File

@@ -46,6 +46,8 @@ del _legal_chars
del _legal_ranges
del _legal_xml_re
_py_ext_re = re.compile(r"\.py$")
def bin_xml_escape(arg):
def repl(matchobj):
@@ -65,38 +67,31 @@ class _NodeReporter(object):
self.xml = xml
self.add_stats = self.xml.add_stats
self.duration = 0
self.properties = {}
self.property_insert_order = []
self.properties = []
self.nodes = []
self.testcase = None
self.attrs = {}
def append(self, node):
self.xml.add_stats(type(node).__name__)
self.nodes.append(node)
def add_property(self, name, value):
name = str(name)
if name not in self.property_insert_order:
self.property_insert_order.append(name)
self.properties[name] = bin_xml_escape(value)
self.properties.append((str(name), bin_xml_escape(value)))
def make_properties_node(self):
"""Return a Junit node containing custom properties, if any.
"""
if self.properties:
return Junit.properties([
Junit.property(name=name, value=self.properties[name])
for name in self.property_insert_order
Junit.property(name=name, value=value)
for name, value in self.properties
])
return ''
def record_testreport(self, testreport):
assert not self.testcase
names = mangle_testnames(testreport.nodeid.split("::"))
names = mangle_test_address(testreport.nodeid)
classnames = names[:-1]
if self.xml.prefix:
classnames.insert(0, self.xml.prefix)
@@ -123,13 +118,10 @@ class _NodeReporter(object):
def _write_captured_output(self, report):
for capname in ('out', 'err'):
allcontent = ""
for name, content in report.get_sections("Captured std%s" %
capname):
allcontent += content
if allcontent:
content = getattr(report, 'capstd' + capname)
if content:
tag = getattr(Junit, 'system-' + capname)
self.append(tag(bin_xml_escape(allcontent)))
self.append(tag(bin_xml_escape(content)))
def append_pass(self, report):
self.add_stats('passed')
@@ -166,6 +158,7 @@ class _NodeReporter(object):
def append_error(self, report):
self._add_simple(
Junit.error, "test setup failure", report.longrepr)
self._write_captured_output(report)
def append_skipped(self, report):
if hasattr(report, "wasxfail"):
@@ -182,7 +175,6 @@ class _NodeReporter(object):
message=skipreason))
self._write_captured_output(report)
def finalize(self):
data = self.to_xml().unicode(indent=0)
self.__dict__.clear()
@@ -191,8 +183,8 @@ class _NodeReporter(object):
@pytest.fixture
def record_xml_property(request):
"""Fixture that adds extra xml properties to the tag for the calling test.
The fixture is callable with (name, value), with value being automatically
"""Add extra xml properties to the tag for the calling test.
The fixture is callable with ``(name, value)``, with value being automatically
xml-encoded.
"""
request.node.warn(
@@ -242,9 +234,18 @@ def pytest_unconfigure(config):
config.pluginmanager.unregister(xml)
def mangle_testnames(names):
names = [x.replace(".py", "") for x in names if x != '()']
def mangle_test_address(address):
path, possible_open_bracket, params = address.partition('[')
names = path.split("::")
try:
names.remove('()')
except ValueError:
pass
# convert file path to dotted path
names[0] = names[0].replace("/", '.')
names[0] = _py_ext_re.sub("", names[0])
# put any params back
names[-1] += possible_open_bracket + params
return names
@@ -261,6 +262,15 @@ class LogXML(object):
], 0)
self.node_reporters = {} # nodeid -> _NodeReporter
self.node_reporters_ordered = []
self.global_properties = []
def finalize(self, report):
nodeid = getattr(report, 'nodeid', report)
# local hack to handle xdist report order
slavenode = getattr(report, 'node', None)
reporter = self.node_reporters.pop((nodeid, slavenode))
if reporter is not None:
reporter.finalize()
def node_reporter(self, report):
nodeid = getattr(report, 'nodeid', report)
@@ -270,11 +280,14 @@ class LogXML(object):
key = nodeid, slavenode
if key in self.node_reporters:
#TODO: breasks for --dist=each
# TODO: breasks for --dist=each
return self.node_reporters[key]
reporter = _NodeReporter(nodeid, self)
self.node_reporters[key] = reporter
self.node_reporters_ordered.append(reporter)
return reporter
def add_stats(self, key):
@@ -324,7 +337,7 @@ class LogXML(object):
reporter.append_skipped(report)
self.update_testcase_duration(report)
if report.when == "teardown":
self.node_reporter(report).finalize()
self.finalize(report)
def update_testcase_duration(self, report):
"""accumulates total duration for nodeid from given report and updates
@@ -357,10 +370,12 @@ class LogXML(object):
suite_stop_time = time.time()
suite_time_delta = suite_stop_time - self.suite_start_time
numtests = self.stats['passed'] + self.stats['failure']
numtests = self.stats['passed'] + self.stats['failure'] + self.stats['skipped'] + self.stats['error']
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
logfile.write(Junit.testsuite(
self._get_global_properties_node(),
[x.to_xml() for x in self.node_reporters_ordered],
name="pytest",
errors=self.stats['error'],
@@ -373,3 +388,18 @@ class LogXML(object):
def pytest_terminal_summary(self, terminalreporter):
terminalreporter.write_sep("-",
"generated xml file: %s" % (self.logfile))
def add_global_property(self, name, value):
self.global_properties.append((str(name), bin_xml_escape(value)))
def _get_global_properties_node(self):
"""Return a Junit node containing custom properties, if any.
"""
if self.global_properties:
return Junit.properties(
[
Junit.property(name=name, value=value)
for name, value in self.global_properties
]
)
return ''

View File

@@ -1,9 +1,11 @@
""" core implementation of testing process: init, session, runtest loop. """
import re
import os
import sys
import _pytest
import _pytest._code
import py
import pytest, _pytest
import os, sys, imp
import pytest
try:
from collections import MutableMapping as MappingMixin
except ImportError:
@@ -21,11 +23,9 @@ EXIT_INTERNALERROR = 3
EXIT_USAGEERROR = 4
EXIT_NOTESTSCOLLECTED = 5
name_re = re.compile("^[a-zA-Z_]\w*$")
def pytest_addoption(parser):
parser.addini("norecursedirs", "directory patterns to avoid for recursion",
type="args", default=['.*', 'CVS', '_darcs', '{arch}', '*.egg'])
type="args", default=['.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg'])
parser.addini("testpaths", "directories to search for tests when no files or directories are given in the command line.",
type="args", default=[])
#parser.addini("dirpatterns",
@@ -34,8 +34,8 @@ def pytest_addoption(parser):
# "**/test_*.py", "**/*_test.py"]
#)
group = parser.getgroup("general", "running and selection options")
group._addoption('-x', '--exitfirst', action="store_true", default=False,
dest="exitfirst",
group._addoption('-x', '--exitfirst', action="store_const",
dest="maxfail", const=1,
help="exit instantly on first error or failed test."),
group._addoption('--maxfail', metavar="num",
action="store", type=int, dest="maxfail", default=0,
@@ -44,6 +44,9 @@ def pytest_addoption(parser):
help="run pytest in strict mode, warnings become errors.")
group._addoption("-c", metavar="file", type=str, dest="inifilename",
help="load configuration from `file` instead of trying to locate one of the implicit configuration files.")
group._addoption("--continue-on-collection-errors", action="store_true",
default=False, dest="continue_on_collection_errors",
help="Force test execution even if collection errors occur.")
group = parser.getgroup("collect", "collection")
group.addoption('--collectonly', '--collect-only', action="store_true",
@@ -60,6 +63,9 @@ def pytest_addoption(parser):
group.addoption('--noconftest', action="store_true",
dest="noconftest", default=False,
help="Don't load any conftest.py files.")
group.addoption('--keepduplicates', '--keep-duplicates', action="store_true",
dest="keepduplicates", default=False,
help="Keep duplicate tests.")
group = parser.getgroup("debugconfig",
"test session debugging and configuration")
@@ -71,10 +77,10 @@ def pytest_namespace():
collect = dict(Item=Item, Collector=Collector, File=File, Session=Session)
return dict(collect=collect)
def pytest_configure(config):
pytest.config = config # compatibiltiy
if config.option.exitfirst:
config.option.maxfail = 1
def wrap_session(config, doit):
"""Skeleton command line program"""
@@ -91,11 +97,15 @@ def wrap_session(config, doit):
except pytest.UsageError:
raise
except KeyboardInterrupt:
excinfo = py.code.ExceptionInfo()
excinfo = _pytest._code.ExceptionInfo()
if initstate < 2 and isinstance(
excinfo.value, pytest.exit.Exception):
sys.stderr.write('{0}: {1}\n'.format(
excinfo.typename, excinfo.value.msg))
config.hook.pytest_keyboard_interrupt(excinfo=excinfo)
session.exitstatus = EXIT_INTERRUPTED
except:
excinfo = py.code.ExceptionInfo()
excinfo = _pytest._code.ExceptionInfo()
config.notify_exception(excinfo, config.option)
session.exitstatus = EXIT_INTERNALERROR
if excinfo.errisinstance(SystemExit):
@@ -129,20 +139,16 @@ def pytest_collection(session):
return session.perform_collect()
def pytest_runtestloop(session):
if (session.testsfailed and
not session.config.option.continue_on_collection_errors):
raise session.Interrupted(
"%d errors during collection" % session.testsfailed)
if session.config.option.collectonly:
return True
def getnextitem(i):
# this is a function to avoid python2
# keeping sys.exc_info set when calling into a test
# python2 keeps sys.exc_info till the frame is left
try:
return session.items[i+1]
except IndexError:
return None
for i, item in enumerate(session.items):
nextitem = getnextitem(i)
nextitem = session.items[i+1] if i+1 < len(session.items) else None
item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
if session.shouldstop:
raise session.Interrupted(session.shouldstop)
@@ -155,7 +161,21 @@ def pytest_ignore_collect(path, config):
excludeopt = config.getoption("ignore")
if excludeopt:
ignore_paths.extend([py.path.local(x) for x in excludeopt])
return path in ignore_paths
if path in ignore_paths:
return True
# Skip duplicate paths.
keepduplicates = config.getoption("keepduplicates")
duplicate_paths = config.pluginmanager._duplicatepaths
if not keepduplicates:
if path in duplicate_paths:
return True
else:
duplicate_paths.add(path)
return False
class FSHookProxy:
def __init__(self, fspath, pm, remove_mods):
@@ -272,7 +292,7 @@ class Node(object):
if fslocation is None:
fslocation = getattr(self, "fspath", None)
else:
fslocation = "%s:%s" % fslocation[:2]
fslocation = "%s:%s" % (fslocation[0], fslocation[1] + 1)
self.ihook.pytest_logwarning.call_historic(kwargs=dict(
code=code, message=message,
@@ -388,7 +408,10 @@ class Node(object):
if self.config.option.fulltrace:
style="long"
else:
tb = _pytest._code.Traceback([excinfo.traceback[-1]])
self._prunetraceback(excinfo)
if len(excinfo.traceback) == 0:
excinfo.traceback = tb
tbfilter = False # prunetraceback already does it
if style == "auto":
style = "long"
@@ -399,7 +422,13 @@ class Node(object):
else:
style = "long"
return excinfo.getrepr(funcargs=True,
try:
os.getcwd()
abspath = False
except OSError:
abspath = True
return excinfo.getrepr(funcargs=True, abspath=abspath,
showlocals=self.config.option.showlocals,
style=style, tbfilter=tbfilter)
@@ -645,36 +674,32 @@ class Session(FSCollector):
return True
def _tryconvertpyarg(self, x):
mod = None
path = [os.path.abspath('.')] + sys.path
for name in x.split('.'):
# ignore anything that's not a proper name here
# else something like --pyargs will mess up '.'
# since imp.find_module will actually sometimes work for it
# but it's supposed to be considered a filesystem path
# not a package
if name_re.match(name) is None:
return x
try:
fd, mod, type_ = imp.find_module(name, path)
except ImportError:
return x
else:
if fd is not None:
fd.close()
"""Convert a dotted module name to path.
if type_[2] != imp.PKG_DIRECTORY:
path = [os.path.dirname(mod)]
else:
path = [mod]
return mod
"""
import pkgutil
try:
loader = pkgutil.find_loader(x)
except ImportError:
return x
if loader is None:
return x
# This method is sometimes invoked when AssertionRewritingHook, which
# does not define a get_filename method, is already in place:
try:
path = loader.get_filename()
except AttributeError:
# Retrieve path from AssertionRewritingHook:
path = loader.modules[x][0].co_filename
if loader.is_package(x):
path = os.path.dirname(path)
return path
def _parsearg(self, arg):
""" return (fspath, names) tuple after checking the file exists. """
arg = str(arg)
if self.config.option.pyargs:
arg = self._tryconvertpyarg(arg)
parts = str(arg).split("::")
if self.config.option.pyargs:
parts[0] = self._tryconvertpyarg(parts[0])
relpath = parts[0].replace("/", os.sep)
path = self.config.invocation_dir.join(relpath, abs=True)
if not path.check():
@@ -714,7 +739,8 @@ class Session(FSCollector):
if rep.passed:
has_matched = False
for x in rep.result:
if x.name == name:
# TODO: remove parametrized workaround once collection structure contains parametrization
if x.name == name or x.name.split("[")[0] == name:
resultnodes.extend(self.matchnodes([x], nextnames))
has_matched = True
# XXX accept IDs that don't have "()" for class instances

View File

@@ -58,7 +58,7 @@ pytest_cmdline_main.tryfirst = True
def pytest_collection_modifyitems(items, config):
keywordexpr = config.option.keyword
keywordexpr = config.option.keyword.lstrip()
matchexpr = config.option.markexpr
if not keywordexpr and not matchexpr:
return
@@ -169,7 +169,7 @@ class MarkGenerator:
""" Factory for :class:`MarkDecorator` objects - exposed as
a ``pytest.mark`` singleton instance. Example::
import py
import pytest
@pytest.mark.slowtest
def test_function():
pass

View File

@@ -1,10 +1,18 @@
""" monkeypatching and mocking functionality. """
import os, sys
import re
from py.builtin import _basestring
def pytest_funcarg__monkeypatch(request):
"""The returned ``monkeypatch`` funcarg provides these
import pytest
RE_IMPORT_ERROR_NAME = re.compile("^No module named (.*)$")
@pytest.fixture
def monkeypatch(request):
"""The returned ``monkeypatch`` fixture provides these
helper methods to modify objects, dictionaries or os.environ::
monkeypatch.setattr(obj, name, value, raising=True)
@@ -17,57 +25,81 @@ def pytest_funcarg__monkeypatch(request):
monkeypatch.chdir(path)
All modifications will be undone after the requesting
test function has finished. The ``raising``
test function or fixture has finished. The ``raising``
parameter determines if a KeyError or AttributeError
will be raised if the set/deletion operation has no target.
"""
mpatch = monkeypatch()
mpatch = MonkeyPatch()
request.addfinalizer(mpatch.undo)
return mpatch
def resolve(name):
# simplified from zope.dottedname
parts = name.split('.')
used = parts.pop(0)
found = __import__(used)
for part in parts:
used += '.' + part
try:
found = getattr(found, part)
except AttributeError:
pass
else:
continue
# we use explicit un-nesting of the handling block in order
# to avoid nested exceptions on python 3
try:
__import__(used)
except ImportError as ex:
# str is used for py2 vs py3
expected = str(ex).split()[-1]
if expected == used:
raise
else:
raise ImportError(
'import error in %s: %s' % (used, ex)
)
found = annotated_getattr(found, part, used)
return found
def annotated_getattr(obj, name, ann):
try:
obj = getattr(obj, name)
except AttributeError:
raise AttributeError(
'%r object at %s has no attribute %r' % (
type(obj).__name__, ann, name
)
)
return obj
def derive_importpath(import_path, raising):
import pytest
if not isinstance(import_path, _basestring) or "." not in import_path:
raise TypeError("must be absolute import path string, not %r" %
(import_path,))
rest = []
target = import_path
while target:
try:
obj = __import__(target, None, None, "__doc__")
except ImportError:
if "." not in target:
__tracebackhide__ = True
pytest.fail("could not import any sub part: %s" %
import_path)
target, name = target.rsplit(".", 1)
rest.append(name)
else:
assert rest
try:
while len(rest) > 1:
attr = rest.pop()
obj = getattr(obj, attr)
attr = rest[0]
if raising:
getattr(obj, attr)
except AttributeError:
__tracebackhide__ = True
pytest.fail("object %r has no attribute %r" % (obj, attr))
return attr, obj
module, attr = import_path.rsplit('.', 1)
target = resolve(module)
if raising:
annotated_getattr(target, attr, ann=module)
return attr, target
class Notset:
def __repr__(self):
return "<notset>"
notset = Notset()
class monkeypatch:
""" Object keeping a record of setattr/item/env/syspath changes. """
class MonkeyPatch:
""" Object returned by the ``monkeypatch`` fixture keeping a record of setattr/item/env/syspath changes.
"""
def __init__(self):
self._setattr = []
self._setitem = []
@@ -94,19 +126,19 @@ class monkeypatch:
if value is notset:
if not isinstance(target, _basestring):
raise TypeError("use setattr(target, name, value) or "
"setattr(target, value) with target being a dotted "
"import string")
"setattr(target, value) with target being a dotted "
"import string")
value = name
name, target = derive_importpath(target, raising)
oldval = getattr(target, name, notset)
if raising and oldval is notset:
raise AttributeError("%r has no attribute %r" %(target, name))
raise AttributeError("%r has no attribute %r" % (target, name))
# avoid class descriptors like staticmethod/classmethod
if inspect.isclass(target):
oldval = target.__dict__.get(name, notset)
self._setattr.insert(0, (target, name, oldval))
self._setattr.append((target, name, oldval))
setattr(target, name, value)
def delattr(self, target, name=notset, raising=True):
@@ -132,13 +164,12 @@ class monkeypatch:
if raising:
raise AttributeError(name)
else:
self._setattr.insert(0, (target, name,
getattr(target, name, notset)))
self._setattr.append((target, name, getattr(target, name, notset)))
delattr(target, name)
def setitem(self, dic, name, value):
""" Set dictionary entry ``name`` to value. """
self._setitem.insert(0, (dic, name, dic.get(name, notset)))
self._setitem.append((dic, name, dic.get(name, notset)))
dic[name] = value
def delitem(self, dic, name, raising=True):
@@ -151,7 +182,7 @@ class monkeypatch:
if raising:
raise KeyError(name)
else:
self._setitem.insert(0, (dic, name, dic.get(name, notset)))
self._setitem.append((dic, name, dic.get(name, notset)))
del dic[name]
def setenv(self, name, value, prepend=None):
@@ -193,28 +224,28 @@ class monkeypatch:
""" Undo previous changes. This call consumes the
undo stack. Calling it a second time has no effect unless
you do more monkeypatching after the undo call.
There is generally no need to call `undo()`, since it is
called automatically during tear-down.
Note that the same `monkeypatch` fixture is used across a
single test function invocation. If `monkeypatch` is used both by
the test function itself and one of the test fixtures,
calling `undo()` will undo all of the changes made in
both functions.
"""
for obj, name, value in self._setattr:
for obj, name, value in reversed(self._setattr):
if value is not notset:
setattr(obj, name, value)
else:
delattr(obj, name)
self._setattr[:] = []
for dictionary, name, value in self._setitem:
for dictionary, name, value in reversed(self._setitem):
if value is notset:
try:
del dictionary[name]
except KeyError:
pass # was already deleted, so we have the desired state
pass # was already deleted, so we have the desired state
else:
dictionary[name] = value
self._setitem[:] = []

View File

@@ -1,20 +1,22 @@
""" (disabled by default) support for testing pytest and pytest plugins. """
import gc
import sys
import traceback
import os
import codecs
import re
import time
import gc
import os
import platform
from fnmatch import fnmatch
import re
import subprocess
import sys
import time
import traceback
from fnmatch import fnmatch
import py
import pytest
from py.builtin import print_
from _pytest._code import Source
import py
import pytest
from _pytest.main import Session, EXIT_OK
from _pytest.assertion.rewrite import AssertionRewritingHook
def pytest_addoption(parser):
@@ -122,15 +124,18 @@ def getexecutable(name, cache={}):
except KeyError:
executable = py.path.local.sysfind(name)
if executable:
import subprocess
popen = subprocess.Popen([str(executable), "--version"],
universal_newlines=True, stderr=subprocess.PIPE)
out, err = popen.communicate()
if name == "jython":
import subprocess
popen = subprocess.Popen([str(executable), "--version"],
universal_newlines=True, stderr=subprocess.PIPE)
out, err = popen.communicate()
if not err or "2.5" not in err:
executable = None
if "2.5.2" in err:
executable = None # http://bugs.jython.org/issue1790
elif popen.returncode != 0:
# Handle pyenv's 127.
executable = None
cache[name] = executable
return executable
@@ -317,7 +322,8 @@ def linecomp(request):
return LineComp()
def pytest_funcarg__LineMatcher(request):
@pytest.fixture(name='LineMatcher')
def LineMatcher_fixture(request):
return LineMatcher
@@ -373,10 +379,10 @@ class RunResult:
class Testdir:
"""Temporary test directory with tools to test/run py.test itself.
"""Temporary test directory with tools to test/run pytest itself.
This is based on the ``tmpdir`` fixture but provides a number of
methods which aid with testing py.test itself. Unless
methods which aid with testing pytest itself. Unless
:py:meth:`chdir` is used all methods will use :py:attr:`tmpdir` as
current working directory.
@@ -472,7 +478,7 @@ class Testdir:
ret = None
for name, value in items:
p = self.tmpdir.join(name).new(ext=ext)
source = py.code.Source(value)
source = Source(value)
def my_totext(s, encoding="utf-8"):
if py.builtin._isbytes(s):
s = py.builtin._totext(s, encoding=encoding)
@@ -587,7 +593,7 @@ class Testdir:
"""Return the collection node of a file.
This is like :py:meth:`getnode` but uses
:py:meth:`parseconfigure` to create the (configured) py.test
:py:meth:`parseconfigure` to create the (configured) pytest
Config instance.
:param path: A :py:class:`py.path.local` instance of the file.
@@ -655,7 +661,7 @@ class Testdir:
:py:class:`HookRecorder` instance.
This runs the :py:func:`pytest.main` function to run all of
py.test inside the test process itself like
pytest inside the test process itself like
:py:meth:`inline_run`. However the return value is a tuple of
the collection items and a :py:class:`HookRecorder` instance.
@@ -668,7 +674,7 @@ class Testdir:
"""Run ``pytest.main()`` in-process, returning a HookRecorder.
This runs the :py:func:`pytest.main` function to run all of
py.test inside the test process itself. This means it can
pytest inside the test process itself. This means it can
return a :py:class:`HookRecorder` instance which gives more
detailed results from then run then can be done by matching
stdout/stderr from :py:meth:`runpytest`.
@@ -680,8 +686,17 @@ class Testdir:
``pytest.main()`` instance should use.
:return: A :py:class:`HookRecorder` instance.
"""
# When running py.test inline any plugins active in the main
# test process are already imported. So this disables the
# warning which will trigger to say they can no longer be
# re-written, which is fine as they are already re-written.
orig_warn = AssertionRewritingHook._warn_already_imported
def revert():
AssertionRewritingHook._warn_already_imported = orig_warn
self.request.addfinalizer(revert)
AssertionRewritingHook._warn_already_imported = lambda *a: None
rec = []
class Collect:
def pytest_configure(x, config):
@@ -754,9 +769,9 @@ class Testdir:
return args
def parseconfig(self, *args):
"""Return a new py.test Config instance from given commandline args.
"""Return a new pytest Config instance from given commandline args.
This invokes the py.test bootstrapping code in _pytest.config
This invokes the pytest bootstrapping code in _pytest.config
to create a new :py:class:`_pytest.core.PluginManager` and
call the pytest_cmdline_parse hook to create new
:py:class:`_pytest.config.Config` instance.
@@ -776,7 +791,7 @@ class Testdir:
return config
def parseconfigure(self, *args):
"""Return a new py.test configured Config instance.
"""Return a new pytest configured Config instance.
This returns a new :py:class:`_pytest.config.Config` instance
like :py:meth:`parseconfig`, but also calls the
@@ -791,7 +806,7 @@ class Testdir:
def getitem(self, source, funcname="test_func"):
"""Return the test item for a test function.
This writes the source to a python file and runs py.test's
This writes the source to a python file and runs pytest's
collection on the resulting module, returning the test item
for the requested function name.
@@ -811,7 +826,7 @@ class Testdir:
def getitems(self, source):
"""Return all test items collected from the module.
This writes the source to a python file and runs py.test's
This writes the source to a python file and runs pytest's
collection on the resulting module, returning all test items
contained within.
@@ -823,7 +838,7 @@ class Testdir:
"""Return the module collection node for ``source``.
This writes ``source`` to a file using :py:meth:`makepyfile`
and then runs the py.test collection on it, returning the
and then runs the pytest collection on it, returning the
collection node for the test module.
:param source: The source code of the module to collect.
@@ -835,7 +850,7 @@ class Testdir:
to the temporarly directory to ensure it is a package.
"""
kw = {self.request.function.__name__: py.code.Source(source).strip()}
kw = {self.request.function.__name__: Source(source).strip()}
path = self.makepyfile(**kw)
if withinit:
self.makepyfile(__init__ = "#")
@@ -923,7 +938,7 @@ class Testdir:
def _getpytestargs(self):
# we cannot use "(sys.executable,script)"
# because on windows the script is e.g. a py.test.exe
# because on windows the script is e.g. a pytest.exe
return (sys.executable, _pytest_fullpath,) # noqa
def runpython(self, script):
@@ -938,7 +953,7 @@ class Testdir:
return self.run(sys.executable, "-c", command)
def runpytest_subprocess(self, *args, **kwargs):
"""Run py.test as a subprocess with given arguments.
"""Run pytest as a subprocess with given arguments.
Any plugins added to the :py:attr:`plugins` list will added
using the ``-p`` command line option. Addtionally
@@ -966,9 +981,9 @@ class Testdir:
return self.run(*args)
def spawn_pytest(self, string, expect_timeout=10.0):
"""Run py.test using pexpect.
"""Run pytest using pexpect.
This makes sure to use the right py.test and sets up the
This makes sure to use the right pytest and sets up the
temporary directory locations.
The pexpect child is returned.
@@ -1034,6 +1049,7 @@ class LineMatcher:
def __init__(self, lines):
self.lines = lines
self._log_output = []
def str(self):
"""Return the entire original text."""
@@ -1041,8 +1057,8 @@ class LineMatcher:
def _getlines(self, lines2):
if isinstance(lines2, str):
lines2 = py.code.Source(lines2)
if isinstance(lines2, py.code.Source):
lines2 = Source(lines2)
if isinstance(lines2, Source):
lines2 = lines2.strip().lines
return lines2
@@ -1057,10 +1073,11 @@ class LineMatcher:
for line in lines2:
for x in self.lines:
if line == x or fnmatch(x, line):
print_("matched: ", repr(line))
self._log("matched: ", repr(line))
break
else:
raise ValueError("line %r not found in output" % line)
self._log("line %r not found in output" % line)
raise ValueError(self._log_text)
def get_lines_after(self, fnline):
"""Return all lines following the given line in the text.
@@ -1072,6 +1089,13 @@ class LineMatcher:
return self.lines[i+1:]
raise ValueError("line %r not found in output" % fnline)
def _log(self, *args):
self._log_output.append(' '.join((str(x) for x in args)))
@property
def _log_text(self):
return '\n'.join(self._log_output)
def fnmatch_lines(self, lines2):
"""Search the text for matching lines.
@@ -1081,8 +1105,6 @@ class LineMatcher:
stdout.
"""
def show(arg1, arg2):
py.builtin.print_(arg1, arg2, file=sys.stderr)
lines2 = self._getlines(lines2)
lines1 = self.lines[:]
nextline = None
@@ -1093,17 +1115,18 @@ class LineMatcher:
while lines1:
nextline = lines1.pop(0)
if line == nextline:
show("exact match:", repr(line))
self._log("exact match:", repr(line))
break
elif fnmatch(nextline, line):
show("fnmatch:", repr(line))
show(" with:", repr(nextline))
self._log("fnmatch:", repr(line))
self._log(" with:", repr(nextline))
break
else:
if not nomatchprinted:
show("nomatch:", repr(line))
self._log("nomatch:", repr(line))
nomatchprinted = True
show(" and:", repr(nextline))
self._log(" and:", repr(nextline))
extralines.append(nextline)
else:
pytest.fail("remains unmatched: %r, see stderr" % (line,))
self._log("remains unmatched: %r" % (line,))
pytest.fail(self._log_text)

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,8 @@
""" recording warnings during test function execution. """
import inspect
import _pytest._code
import py
import sys
import warnings
@@ -28,14 +30,22 @@ def pytest_namespace():
'warns': warns}
def deprecated_call(func, *args, **kwargs):
def deprecated_call(func=None, *args, **kwargs):
""" assert that calling ``func(*args, **kwargs)`` triggers a
``DeprecationWarning`` or ``PendingDeprecationWarning``.
This function can be used as a context manager::
>>> with deprecated_call():
... myobject.deprecated_method()
Note: we cannot use WarningsRecorder here because it is still subject
to the mechanism that prevents warnings of the same type from being
triggered twice for the same module. See #1190.
"""
if not func:
return WarningsChecker(expected_warning=DeprecationWarning)
categories = []
def warn_explicit(message, category, *args, **kwargs):
@@ -92,7 +102,7 @@ def warns(expected_warning, *args, **kwargs):
loc.update(kwargs)
with wcheck:
code = py.code.Source(code).compile()
code = _pytest._code.Source(code).compile()
py.builtin.exec_(code, frame.f_globals, loc)
else:
func = args[0]
@@ -171,8 +181,8 @@ class WarningsRecorder(object):
self._module.showwarning = showwarning
# allow the same warning to be raised more than once
self._module.simplefilter('always', append=True)
self._module.simplefilter('always')
return self
def __exit__(self, *exc_info):

View File

@@ -9,7 +9,7 @@ def pytest_addoption(parser):
group = parser.getgroup("terminal reporting", "resultlog plugin options")
group.addoption('--resultlog', '--result-log', action="store",
metavar="path", default=None,
help="path for machine-readable result log.")
help="DEPRECATED path for machine-readable result log.")
def pytest_configure(config):
resultlog = config.option.resultlog
@@ -22,6 +22,9 @@ def pytest_configure(config):
config._resultlog = ResultLog(config, logfile)
config.pluginmanager.register(config._resultlog)
from _pytest.deprecated import RESULT_LOG
config.warn('C1', RESULT_LOG)
def pytest_unconfigure(config):
resultlog = getattr(config, '_resultlog', None)
if resultlog:

View File

@@ -5,7 +5,8 @@ from time import time
import py
import pytest
from py._code.code import TerminalRepr
from _pytest._code.code import TerminalRepr, ExceptionInfo
def pytest_namespace():
return {
@@ -72,7 +73,10 @@ def runtestprotocol(item, log=True, nextitem=None):
rep = call_and_report(item, "setup", log)
reports = [rep]
if rep.passed:
reports.append(call_and_report(item, "call", log))
if item.config.option.setupshow:
show_test_item(item)
if not item.config.option.setuponly:
reports.append(call_and_report(item, "call", log))
reports.append(call_and_report(item, "teardown", log,
nextitem=nextitem))
# after all teardown hooks have been called
@@ -82,6 +86,16 @@ def runtestprotocol(item, log=True, nextitem=None):
item.funcargs = None
return reports
def show_test_item(item):
"""Show test function, parameters and the fixtures of the test item."""
tw = item.config.get_terminal_writer()
tw.line()
tw.write(' ' * 8)
tw.write(item._nodeid)
used_fixtures = sorted(item._fixtureinfo.name2fixturedefs.keys())
if used_fixtures:
tw.write(' (fixtures used: {0})'.format(', '.join(used_fixtures)))
def pytest_runtest_setup(item):
item.session._setupstate.prepare(item)
@@ -151,7 +165,7 @@ class CallInfo:
self.stop = time()
raise
except:
self.excinfo = py.code.ExceptionInfo()
self.excinfo = ExceptionInfo()
self.stop = time()
def __repr__(self):
@@ -177,9 +191,13 @@ class BaseReport(object):
self.__dict__.update(kw)
def toterminal(self, out):
longrepr = self.longrepr
if hasattr(self, 'node'):
out.line(getslaveinfoline(self.node))
longrepr = self.longrepr
if longrepr is None:
return
if hasattr(longrepr, 'toterminal'):
longrepr.toterminal(out)
else:
@@ -193,6 +211,36 @@ class BaseReport(object):
if name.startswith(prefix):
yield prefix, content
@property
def longreprtext(self):
"""
Read-only property that returns the full string representation
of ``longrepr``.
.. versionadded:: 3.0
"""
tw = py.io.TerminalWriter(stringio=True)
tw.hasmarkup = False
self.toterminal(tw)
exc = tw.stringio.getvalue()
return exc.strip()
@property
def capstdout(self):
"""Return captured text from stdout, if capturing is enabled
.. versionadded:: 3.0
"""
return ''.join(content for (prefix, content) in self.get_sections('Captured stdout'))
@property
def capstderr(self):
"""Return captured text from stderr, if capturing is enabled
.. versionadded:: 3.0
"""
return ''.join(content for (prefix, content) in self.get_sections('Captured stderr'))
passed = property(lambda x: x.outcome == "passed")
failed = property(lambda x: x.outcome == "failed")
skipped = property(lambda x: x.outcome == "skipped")
@@ -211,7 +259,7 @@ def pytest_runtest_makereport(item, call):
outcome = "passed"
longrepr = None
else:
if not isinstance(excinfo, py.code.ExceptionInfo):
if not isinstance(excinfo, ExceptionInfo):
outcome = "failed"
longrepr = excinfo
elif excinfo.errisinstance(pytest.skip.Exception):
@@ -258,8 +306,10 @@ class TestReport(BaseReport):
#: one of 'setup', 'call', 'teardown' to indicate runtest phase.
self.when = when
#: list of (secname, data) extra information which needs to
#: marshallable
#: list of pairs ``(str, str)`` of extra information which needs to
#: marshallable. Used by pytest to add captured text
#: from ``stdout`` and ``stderr``, but may be used by other plugins
#: to add arbitrary information to reports.
self.sections = list(sections)
#: time it took to run just the test
@@ -430,7 +480,10 @@ class OutcomeException(Exception):
def __repr__(self):
if self.msg:
return str(self.msg)
val = self.msg
if isinstance(val, bytes):
val = py._builtin._totext(val, errors='replace')
return val
return "<%s instance>" %(self.__class__.__name__,)
__str__ = __repr__
@@ -486,9 +539,13 @@ def importorskip(modname, minversion=None):
"""
__tracebackhide__ = True
compile(modname, '', 'eval') # to catch syntaxerrors
should_skip = False
try:
__import__(modname)
except ImportError:
# Do not raise chained exception here(#1485)
should_skip = True
if should_skip:
skip("could not import %r" %(modname,))
mod = sys.modules[modname]
if minversion is None:

72
_pytest/setuponly.py Normal file
View File

@@ -0,0 +1,72 @@
import pytest
import sys
def pytest_addoption(parser):
group = parser.getgroup("debugconfig")
group.addoption('--setuponly', '--setup-only', action="store_true",
help="only setup fixtures, don't execute the tests.")
group.addoption('--setupshow', '--setup-show', action="store_true",
help="show setup fixtures while executing the tests.")
@pytest.hookimpl(hookwrapper=True)
def pytest_fixture_setup(fixturedef, request):
yield
config = request.config
if config.option.setupshow:
if hasattr(request, 'param'):
# Save the fixture parameter so ._show_fixture_action() can
# display it now and during the teardown (in .finish()).
if fixturedef.ids:
if callable(fixturedef.ids):
fixturedef.cached_param = fixturedef.ids(request.param)
else:
fixturedef.cached_param = fixturedef.ids[
request.param_index]
else:
fixturedef.cached_param = request.param
_show_fixture_action(fixturedef, 'SETUP')
def pytest_fixture_post_finalizer(fixturedef):
if hasattr(fixturedef, "cached_result"):
config = fixturedef._fixturemanager.config
if config.option.setupshow:
_show_fixture_action(fixturedef, 'TEARDOWN')
if hasattr(fixturedef, "cached_param"):
del fixturedef.cached_param
def _show_fixture_action(fixturedef, msg):
config = fixturedef._fixturemanager.config
capman = config.pluginmanager.getplugin('capturemanager')
if capman:
out, err = capman.suspendcapture()
tw = config.get_terminal_writer()
tw.line()
tw.write(' ' * 2 * fixturedef.scopenum)
tw.write('{step} {scope} {fixture}'.format(
step=msg.ljust(8), # align the output to TEARDOWN
scope=fixturedef.scope[0].upper(),
fixture=fixturedef.argname))
if msg == 'SETUP':
deps = sorted(arg for arg in fixturedef.argnames if arg != 'request')
if deps:
tw.write(' (fixtures used: {0})'.format(', '.join(deps)))
if hasattr(fixturedef, 'cached_param'):
tw.write('[{0}]'.format(fixturedef.cached_param))
if capman:
capman.resumecapture()
sys.stdout.write(out)
sys.stderr.write(err)
@pytest.hookimpl(tryfirst=True)
def pytest_cmdline_main(config):
if config.option.setuponly:
config.option.setupshow = True

23
_pytest/setupplan.py Normal file
View File

@@ -0,0 +1,23 @@
import pytest
def pytest_addoption(parser):
group = parser.getgroup("debugconfig")
group.addoption('--setupplan', '--setup-plan', action="store_true",
help="show what fixtures and tests would be executed but "
"don't execute anything.")
@pytest.hookimpl(tryfirst=True)
def pytest_fixture_setup(fixturedef, request):
# Will return a dummy fixture if the setuponly option is provided.
if request.config.option.setupplan:
fixturedef.cached_result = (None, None, None)
return fixturedef.cached_result
@pytest.hookimpl(tryfirst=True)
def pytest_cmdline_main(config):
if config.option.setupplan:
config.option.setuponly = True
config.option.setupshow = True

View File

@@ -5,6 +5,8 @@ import traceback
import py
import pytest
from _pytest.mark import MarkInfo, MarkDecorator
def pytest_addoption(parser):
group = parser.getgroup("general")
@@ -12,6 +14,13 @@ def pytest_addoption(parser):
action="store_true", dest="runxfail", default=False,
help="run tests even if they are marked xfail")
parser.addini("xfail_strict", "default for the strict parameter of xfail "
"markers when not given explicitly (default: "
"False)",
default=False,
type="bool")
def pytest_configure(config):
if config.option.runxfail:
old = pytest.xfail
@@ -21,6 +30,11 @@ def pytest_configure(config):
nop.Exception = XFailed
setattr(pytest, "xfail", nop)
config.addinivalue_line("markers",
"skip(reason=None): skip the given test function with an optional reason. "
"Example: skip(reason=\"no way of currently testing this\") skips the "
"test."
)
config.addinivalue_line("markers",
"skipif(condition): skip the given test function if eval(condition) "
"results in a True value. Evaluation happens within the "
@@ -29,27 +43,31 @@ def pytest_configure(config):
"http://pytest.org/latest/skipping.html"
)
config.addinivalue_line("markers",
"xfail(condition, reason=None, run=True, raises=None): mark the the test function "
"as an expected failure if eval(condition) has a True value. "
"Optionally specify a reason for better reporting and run=False if "
"you don't even want to execute the test function. If only specific "
"exception(s) are expected, you can list them in raises, and if the test fails "
"in other ways, it will be reported as a true failure. "
"See http://pytest.org/latest/skipping.html"
"xfail(condition, reason=None, run=True, raises=None, strict=False): "
"mark the the test function as an expected failure if eval(condition) "
"has a True value. Optionally specify a reason for better reporting "
"and run=False if you don't even want to execute the test function. "
"If only specific exception(s) are expected, you can list them in "
"raises, and if the test fails in other ways, it will be reported as "
"a true failure. See http://pytest.org/latest/skipping.html"
)
def pytest_namespace():
return dict(xfail=xfail)
class XFailed(pytest.fail.Exception):
""" raised from an explicit call to pytest.xfail() """
def xfail(reason=""):
""" xfail an executing test or setup functions with the given reason."""
__tracebackhide__ = True
raise XFailed(reason)
xfail.Exception = XFailed
class MarkEvaluator:
def __init__(self, item, name):
self.item = item
@@ -90,11 +108,7 @@ class MarkEvaluator:
def _getglobals(self):
d = {'os': os, 'sys': sys, 'config': self.item.config}
func = self.item.obj
try:
d.update(func.__globals__)
except AttributeError:
d.update(func.func_globals)
d.update(self.item.obj.__globals__)
return d
def _istrue(self):
@@ -102,7 +116,7 @@ class MarkEvaluator:
return self.result
if self.holder:
d = self._getglobals()
if self.holder.args:
if self.holder.args or 'condition' in self.holder.kwargs:
self.result = False
# "holder" might be a MarkInfo or a MarkDecorator; only
# MarkInfo keeps track of all parameters it received in an
@@ -112,6 +126,8 @@ class MarkEvaluator:
else:
arglist = [(self.holder.args, self.holder.kwargs)]
for args, kwargs in arglist:
if 'condition' in kwargs:
args = (kwargs['condition'],)
for expr in args:
self.expr = expr
if isinstance(expr, py.builtin._basestring):
@@ -147,23 +163,59 @@ class MarkEvaluator:
@pytest.hookimpl(tryfirst=True)
def pytest_runtest_setup(item):
evalskip = MarkEvaluator(item, 'skipif')
if evalskip.istrue():
item._evalskip = evalskip
pytest.skip(evalskip.getexplanation())
# Check if skip or skipif are specified as pytest marks
skipif_info = item.keywords.get('skipif')
if isinstance(skipif_info, (MarkInfo, MarkDecorator)):
eval_skipif = MarkEvaluator(item, 'skipif')
if eval_skipif.istrue():
item._evalskip = eval_skipif
pytest.skip(eval_skipif.getexplanation())
skip_info = item.keywords.get('skip')
if isinstance(skip_info, (MarkInfo, MarkDecorator)):
item._evalskip = True
if 'reason' in skip_info.kwargs:
pytest.skip(skip_info.kwargs['reason'])
elif skip_info.args:
pytest.skip(skip_info.args[0])
else:
pytest.skip("unconditional skip")
item._evalxfail = MarkEvaluator(item, 'xfail')
check_xfail_no_run(item)
@pytest.mark.hookwrapper
def pytest_pyfunc_call(pyfuncitem):
check_xfail_no_run(pyfuncitem)
outcome = yield
passed = outcome.excinfo is None
if passed:
check_strict_xfail(pyfuncitem)
def check_xfail_no_run(item):
"""check xfail(run=False)"""
if not item.config.option.runxfail:
evalxfail = item._evalxfail
if evalxfail.istrue():
if not evalxfail.get('run', True):
pytest.xfail("[NOTRUN] " + evalxfail.getexplanation())
def check_strict_xfail(pyfuncitem):
"""check xfail(strict=True) for the given PASSING test"""
evalxfail = pyfuncitem._evalxfail
if evalxfail.istrue():
strict_default = pyfuncitem.config.getini('xfail_strict')
is_strict_xfail = evalxfail.get('strict', strict_default)
if is_strict_xfail:
del pyfuncitem._evalxfail
explanation = evalxfail.getexplanation()
pytest.fail('[XPASS(strict)] ' + explanation, pytrace=False)
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
@@ -172,9 +224,16 @@ def pytest_runtest_makereport(item, call):
evalskip = getattr(item, '_evalskip', None)
# unitttest special case, see setting of _unexpectedsuccess
if hasattr(item, '_unexpectedsuccess') and rep.when == "call":
# we need to translate into how pytest encodes xpass
rep.wasxfail = "reason: " + repr(item._unexpectedsuccess)
rep.outcome = "failed"
from _pytest.compat import _is_unittest_unexpected_success_a_failure
if item._unexpectedsuccess:
rep.longrepr = "Unexpected success: {0}".format(item._unexpectedsuccess)
else:
rep.longrepr = "Unexpected success"
if _is_unittest_unexpected_success_a_failure():
rep.outcome = "failed"
else:
rep.outcome = "passed"
rep.wasxfail = rep.longrepr
elif item.config.option.runxfail:
pass # don't interefere
elif call.excinfo and call.excinfo.errisinstance(pytest.xfail.Exception):
@@ -189,8 +248,15 @@ def pytest_runtest_makereport(item, call):
rep.outcome = "skipped"
rep.wasxfail = evalxfail.getexplanation()
elif call.when == "call":
rep.outcome = "failed" # xpass outcome
rep.wasxfail = evalxfail.getexplanation()
strict_default = item.config.getini('xfail_strict')
is_strict_xfail = evalxfail.get('strict', strict_default)
explanation = evalxfail.getexplanation()
if is_strict_xfail:
rep.outcome = "failed"
rep.longrepr = "[XPASS(strict)] {0}".format(explanation)
else:
rep.outcome = "passed"
rep.wasxfail = explanation
elif evalskip is not None and rep.skipped and type(rep.longrepr) is tuple:
# skipped by mark.skipif; change the location of the failure
# to point to the item definition, otherwise it will display
@@ -204,7 +270,7 @@ def pytest_report_teststatus(report):
if hasattr(report, "wasxfail"):
if report.skipped:
return "xfailed", "x", "xfail"
elif report.failed:
elif report.passed:
return "xpassed", "X", ("XPASS", {'yellow': True})
# called by the terminalreporter instance/plugin
@@ -230,6 +296,9 @@ def pytest_terminal_summary(terminalreporter):
show_skipped(terminalreporter, lines)
elif char == "E":
show_simple(terminalreporter, lines, 'error', "ERROR %s")
elif char == 'p':
show_simple(terminalreporter, lines, 'passed', "PASSED %s")
if lines:
tr._tw.sep("=", "short test summary info")
for line in lines:
@@ -266,9 +335,8 @@ def cached_eval(config, expr, d):
try:
return config._evalcache[expr]
except KeyError:
#import sys
#print >>sys.stderr, ("cache-miss: %r" % expr)
exprcode = py.code.compile(expr, mode="eval")
import _pytest._code
exprcode = _pytest._code.compile(expr, mode="eval")
config._evalcache[expr] = x = eval(exprcode, d)
return x

View File

@@ -1,89 +0,0 @@
#! /usr/bin/env python
# Hi There!
# You may be wondering what this giant blob of binary data here is, you might
# even be worried that we're up to something nefarious (good for you for being
# paranoid!). This is a base64 encoding of a zip file, this zip file contains
# a fully functional basic pytest script.
#
# Pytest is a thing that tests packages, pytest itself is a package that some-
# one might want to install, especially if they're looking to run tests inside
# some package they want to install. Pytest has a lot of code to collect and
# execute tests, and other such sort of "tribal knowledge" that has been en-
# coded in its code base. Because of this we basically include a basic copy
# of pytest inside this blob. We do this because it let's you as a maintainer
# or application developer who wants people who don't deal with python much to
# easily run tests without installing the complete pytest package.
#
# If you're wondering how this is created: you can create it yourself if you
# have a complete pytest installation by using this command on the command-
# line: ``py.test --genscript=runtests.py``.
sources = """
@SOURCES@"""
import sys
import base64
import zlib
class DictImporter(object):
def __init__(self, sources):
self.sources = sources
def find_module(self, fullname, path=None):
if fullname == "argparse" and sys.version_info >= (2,7):
# we were generated with <python2.7 (which pulls in argparse)
# but we are running now on a stdlib which has it, so use that.
return None
if fullname in self.sources:
return self
if fullname + '.__init__' in self.sources:
return self
return None
def load_module(self, fullname):
# print "load_module:", fullname
from types import ModuleType
try:
s = self.sources[fullname]
is_pkg = False
except KeyError:
s = self.sources[fullname + '.__init__']
is_pkg = True
co = compile(s, fullname, 'exec')
module = sys.modules.setdefault(fullname, ModuleType(fullname))
module.__file__ = "%s/%s" % (__file__, fullname)
module.__loader__ = self
if is_pkg:
module.__path__ = [fullname]
do_exec(co, module.__dict__) # noqa
return sys.modules[fullname]
def get_source(self, name):
res = self.sources.get(name)
if res is None:
res = self.sources.get(name + '.__init__')
return res
if __name__ == "__main__":
try:
import pkg_resources # noqa
except ImportError:
sys.stderr.write("ERROR: setuptools not installed\n")
sys.exit(2)
if sys.version_info >= (3, 0):
exec("def do_exec(co, loc): exec(co, loc)\n")
import pickle
sources = sources.encode("ascii") # ensure bytes
sources = pickle.loads(zlib.decompress(base64.decodebytes(sources)))
else:
import cPickle as pickle
exec("def do_exec(co, loc): exec co in loc\n")
sources = pickle.loads(zlib.decompress(base64.decodestring(sources)))
importer = DictImporter(sources)
sys.meta_path.insert(0, importer)
entry = "@ENTRY@"
do_exec(entry, locals()) # noqa

View File

@@ -20,15 +20,18 @@ def pytest_addoption(parser):
group._addoption('-q', '--quiet', action="count",
dest="quiet", default=0, help="decrease verbosity."),
group._addoption('-r',
action="store", dest="reportchars", default=None, metavar="chars",
action="store", dest="reportchars", default='', metavar="chars",
help="show extra test summary info as specified by chars (f)ailed, "
"(E)error, (s)skipped, (x)failed, (X)passed (w)pytest-warnings (a)all.")
"(E)error, (s)skipped, (x)failed, (X)passed, "
"(p)passed, (P)passed with output, (a)all except pP. "
"The pytest warnings are displayed at all times except when "
"--disable-pytest-warnings is set")
group._addoption('--disable-pytest-warnings', default=False,
dest='disablepytestwarnings', action='store_true',
help='disable warnings summary, overrides -r w flag')
group._addoption('-l', '--showlocals',
action="store_true", dest="showlocals", default=False,
help="show locals in tracebacks (disabled by default).")
group._addoption('--report',
action="store", dest="report", default=None, metavar="opts",
help="(deprecated, use -r)")
group._addoption('--tb', metavar="style",
action="store", dest="tbstyle", default='auto',
choices=['auto', 'long', 'short', 'no', 'line', 'native'],
@@ -53,18 +56,11 @@ def pytest_configure(config):
def getreportopt(config):
reportopts = ""
optvalue = config.option.report
if optvalue:
py.builtin.print_("DEPRECATED: use -r instead of --report option.",
file=sys.stderr)
if optvalue:
for setting in optvalue.split(","):
setting = setting.strip()
if setting == "skipped":
reportopts += "s"
elif setting == "xfailed":
reportopts += "x"
reportchars = config.option.reportchars
if not config.option.disablepytestwarnings and 'w' not in reportchars:
reportchars += 'w'
elif config.option.disablepytestwarnings and 'w' in reportchars:
reportchars = reportchars.replace('w', '')
if reportchars:
for char in reportchars:
if char not in reportopts and char != 'a':
@@ -111,6 +107,7 @@ class TerminalReporter:
self.currentfspath = None
self.reportchars = getreportopt(config)
self.hasmarkup = self._tw.hasmarkup
self.isatty = file.isatty()
def hasopt(self, char):
char = {'xfailed': 'x', 'skipped': 's'}.get(char, char)
@@ -233,7 +230,7 @@ class TerminalReporter:
self.currentfspath = -2
def pytest_collection(self):
if not self.hasmarkup and self.config.option.verbose >= 1:
if not self.isatty and self.config.option.verbose >= 1:
self.write("collecting ... ", bold=True)
def pytest_collectreport(self, report):
@@ -243,7 +240,7 @@ class TerminalReporter:
self.stats.setdefault("skipped", []).append(report)
items = [x for x in report.result if isinstance(x, pytest.Item)]
self._numcollected += len(items)
if self.hasmarkup:
if self.isatty:
#self.write_fspath_result(report.nodeid, 'E')
self.report_collect()
@@ -262,7 +259,7 @@ class TerminalReporter:
line += " / %d errors" % errors
if skipped:
line += " / %d skipped" % skipped
if self.hasmarkup:
if self.isatty:
if final:
line += " \n"
self.rewrite(line, bold=True)
@@ -364,10 +361,12 @@ class TerminalReporter:
EXIT_OK, EXIT_TESTSFAILED, EXIT_INTERRUPTED, EXIT_USAGEERROR,
EXIT_NOTESTSCOLLECTED)
if exitstatus in summary_exit_codes:
self.config.hook.pytest_terminal_summary(terminalreporter=self,
exitstatus=exitstatus)
self.summary_errors()
self.summary_failures()
self.summary_warnings()
self.config.hook.pytest_terminal_summary(terminalreporter=self)
self.summary_passes()
if exitstatus == EXIT_INTERRUPTED:
self._report_keyboardinterrupt()
del self._keyboardinterrupt_memo
@@ -389,6 +388,7 @@ class TerminalReporter:
if self.config.option.fulltrace:
excrepr.toterminal(self._tw)
else:
self._tw.line("to show a full traceback on KeyboardInterrupt use --fulltrace", yellow=True)
excrepr.reprcrash.toterminal(self._tw)
def _locationline(self, nodeid, fspath, lineno, domain):
@@ -446,6 +446,18 @@ class TerminalReporter:
self._tw.line("W%s %s %s" % (w.code,
w.fslocation, w.message))
def summary_passes(self):
if self.config.option.tbstyle != "no":
if self.hasopt("P"):
reports = self.getreports('passed')
if not reports:
return
self.write_sep("=", "PASSES")
for rep in reports:
msg = self._getfailureheadline(rep)
self.write_sep("_", msg)
self._outrep_summary(rep)
def summary_failures(self):
if self.config.option.tbstyle != "no":
reports = self.getreports('failed')
@@ -501,16 +513,8 @@ class TerminalReporter:
def summary_deselected(self):
if 'deselected' in self.stats:
l = []
k = self.config.option.keyword
if k:
l.append("-k%s" % k)
m = self.config.option.markexpr
if m:
l.append("-m %r" % m)
if l:
self.write_sep("=", "%d tests deselected by %r" % (
len(self.stats['deselected']), " ".join(l)), bold=True)
self.write_sep("=", "%d tests deselected" % (
len(self.stats['deselected'])), bold=True)
def repr_pythonversion(v=None):
if v is None:

View File

@@ -3,7 +3,7 @@ import re
import pytest
import py
from _pytest.monkeypatch import monkeypatch
from _pytest.monkeypatch import MonkeyPatch
class TempdirFactory:
@@ -92,7 +92,7 @@ def pytest_configure(config):
available at pytest_configure time, but ideally should be moved entirely
to the tmpdir_factory session fixture.
"""
mp = monkeypatch()
mp = MonkeyPatch()
t = TempdirFactory(config)
config._cleanup.extend([mp.undo, t.finish])
mp.setattr(config, '_tmpdirhandler', t, raising=False)
@@ -108,7 +108,7 @@ def tmpdir_factory(request):
@pytest.fixture
def tmpdir(request, tmpdir_factory):
"""return a temporary directory path object
"""Return a temporary directory path object
which is unique to each test function invocation,
created as a sub directory of the base temporary
directory. The returned object is a `py.path.local`_

View File

@@ -1,13 +1,12 @@
""" discovery and running of std-library "unittest" style tests. """
from __future__ import absolute_import
import traceback
import sys
import traceback
import pytest
import py
# for transfering markers
import _pytest._code
from _pytest.python import transfer_markers
from _pytest.skipping import MarkEvaluator
@@ -24,9 +23,10 @@ def pytest_pycollect_makeitem(collector, name, obj):
class UnitTestCase(pytest.Class):
nofuncargs = True # marker for fixturemanger.getfixtureinfo()
# to declare that our children do not support funcargs
#
# marker for fixturemanger.getfixtureinfo()
# to declare that our children do not support funcargs
nofuncargs = True
def setup(self):
cls = self.obj
if getattr(cls, '__unittest_skip__', False):
@@ -50,6 +50,8 @@ class UnitTestCase(pytest.Class):
foundsomething = False
for name in loader.getTestCaseNames(self.obj):
x = getattr(self.obj, name)
if not getattr(x, '__test__', True):
continue
funcobj = getattr(x, 'im_func', x)
transfer_markers(funcobj, cls, module)
yield TestCaseFunction(name, parent=self)
@@ -100,7 +102,7 @@ class TestCaseFunction(pytest.Function):
# unwrap potential exception info (see twisted trial support below)
rawexcinfo = getattr(rawexcinfo, '_rawexcinfo', rawexcinfo)
try:
excinfo = py.code.ExceptionInfo(rawexcinfo)
excinfo = _pytest._code.ExceptionInfo(rawexcinfo)
except TypeError:
try:
try:
@@ -116,7 +118,7 @@ class TestCaseFunction(pytest.Function):
except KeyboardInterrupt:
raise
except pytest.fail.Exception:
excinfo = py.code.ExceptionInfo()
excinfo = _pytest._code.ExceptionInfo()
self.__dict__.setdefault('_excinfo', []).append(excinfo)
def addError(self, testcase, rawexcinfo):

View File

@@ -1,19 +1,35 @@
environment:
COVERALLS_REPO_TOKEN:
secure: 2NJ5Ct55cHJ9WEg3xbSqCuv0rdgzzb6pnzOIG5OkMbTndw3wOBrXntWFoQrXiMFi
# this is pytest's token in coveralls.io, encrypted
# using pytestbot account as detailed here:
# https://www.appveyor.com/docs/build-configuration#secure-variables
matrix:
# create multiple jobs to execute a set of tox runs on each; this is to workaround having
# builds timing out in AppVeyor
- TOXENV: "linting,py26,py27,py33,py34,py35,pypy"
- TOXENV: "py27-pexpect,py27-xdist,py27-trial,py35-pexpect,py35-xdist,py35-trial"
- TOXENV: "py27-nobyte,doctesting,freeze"
install:
- echo Installed Pythons
- dir c:\Python*
# install pypy using choco (redirect to a file and write to console in case
# choco install returns non-zero, because choco install python.pypy is too
# noisy)
- choco install python.pypy > pypy-inst.log 2>&1 || (type pypy-inst.log & exit /b 1)
- set PATH=C:\tools\pypy\pypy;%PATH% # so tox can find pypy
- echo PyPy installed
- pypy --version
- C:\Python35\python -m pip install tox
build: false # Not a C# project, build stuff at the test step instead.
test_script:
- 'set TESTENVS=
flakes,
py26,
py27,
py33,
py34,
py27-xdist,
py35-xdist
'
- C:\Python35\python -m tox -e "%TESTENVS%"
- C:\Python35\python -m tox
# coveralls is not in tox's envlist, plus for PRs the secure variable
# is not defined so we have to check for it
- if defined COVERALLS_REPO_TOKEN C:\Python35\python -m tox -e coveralls

View File

@@ -9,6 +9,7 @@
<li><a href="{{ pathto('contact') }}">Contact</a></li>
<li><a href="{{ pathto('talks') }}">Talks/Posts</a></li>
<li><a href="{{ pathto('changelog') }}">Changelog</a></li>
<li><a href="{{ pathto('license') }}">License</a></li>
</ul>
{%- if display_toc %}

View File

@@ -1,5 +1,7 @@
{% extends "!layout.html" %}
{% block header %}
{{super()}}
{% endblock %}
{% block footer %}
{{ super() }}
<script type="text/javascript">

View File

@@ -6,6 +6,12 @@ Release announcements
:maxdepth: 2
sprint2016
release-2.9.2
release-2.9.1
release-2.9.0
release-2.8.7
release-2.8.6
release-2.8.5
release-2.8.4
release-2.8.3
@@ -41,4 +47,3 @@ Release announcements
release-2.0.2
release-2.0.1
release-2.0.0

View File

@@ -0,0 +1,67 @@
pytest-2.8.6
============
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
This release is supposed to be drop-in compatible to 2.8.5.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
AMiT Kumar
Bruno Oliveira
Erik M. Bray
Florian Bruhin
Georgy Dyuldin
Jeff Widman
Kartik Singhal
Loïc Estève
Manu Phatak
Peter Demin
Rick van Hattem
Ronny Pfannschmidt
Ulrich Petri
foxx
Happy testing,
The py.test Development Team
2.8.6 (compared to 2.8.5)
-------------------------
- fix #1259: allow for double nodeids in junitxml,
this was a regression failing plugins combinations
like pytest-pep8 + pytest-flakes
- Workaround for exception that occurs in pyreadline when using
``--pdb`` with standard I/O capture enabled.
Thanks Erik M. Bray for the PR.
- fix #900: Better error message in case the target of a ``monkeypatch`` call
raises an ``ImportError``.
- fix #1292: monkeypatch calls (setattr, setenv, etc.) are now O(1).
Thanks David R. MacIver for the report and Bruno Oliveira for the PR.
- fix #1223: captured stdout and stderr are now properly displayed before
entering pdb when ``--pdb`` is used instead of being thrown away.
Thanks Cal Leeming for the PR.
- fix #1305: pytest warnings emitted during ``pytest_terminal_summary`` are now
properly displayed.
Thanks Ionel Maries Cristian for the report and Bruno Oliveira for the PR.
- fix #628: fixed internal UnicodeDecodeError when doctests contain unicode.
Thanks Jason R. Coombs for the report and Bruno Oliveira for the PR.
- fix #1334: Add captured stdout to jUnit XML report on setup error.
Thanks Georgy Dyuldin for the PR.

View File

@@ -0,0 +1,31 @@
pytest-2.8.7
============
This is a hotfix release to solve a regression
in the builtin monkeypatch plugin that got introduced in 2.8.6.
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
This release is supposed to be drop-in compatible to 2.8.5.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
Ronny Pfannschmidt
Happy testing,
The py.test Development Team
2.8.7 (compared to 2.8.6)
-------------------------
- fix #1338: use predictable object resolution for monkeypatch

View File

@@ -0,0 +1,159 @@
pytest-2.9.0
============
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
Anatoly Bubenkov
Bruno Oliveira
Buck Golemon
David Vierra
Florian Bruhin
Galaczi Endre
Georgy Dyuldin
Lukas Bednar
Luke Murphy
Marcin Biernat
Matt Williams
Michael Aquilina
Raphael Pierzina
Ronny Pfannschmidt
Ryan Wooden
Tiemo Kieft
TomV
holger krekel
jab
Happy testing,
The py.test Development Team
2.9.0 (compared to 2.8.7)
-------------------------
**New Features**
* New ``pytest.mark.skip`` mark, which unconditionally skips marked tests.
Thanks `@MichaelAquilina`_ for the complete PR (`#1040`_).
* ``--doctest-glob`` may now be passed multiple times in the command-line.
Thanks `@jab`_ and `@nicoddemus`_ for the PR.
* New ``-rp`` and ``-rP`` reporting options give the summary and full output
of passing tests, respectively. Thanks to `@codewarrior0`_ for the PR.
* ``pytest.mark.xfail`` now has a ``strict`` option which makes ``XPASS``
tests to fail the test suite, defaulting to ``False``. There's also a
``xfail_strict`` ini option that can be used to configure it project-wise.
Thanks `@rabbbit`_ for the request and `@nicoddemus`_ for the PR (`#1355`_).
* ``Parser.addini`` now supports options of type ``bool``. Thanks
`@nicoddemus`_ for the PR.
* New ``ALLOW_BYTES`` doctest option strips ``b`` prefixes from byte strings
in doctest output (similar to ``ALLOW_UNICODE``).
Thanks `@jaraco`_ for the request and `@nicoddemus`_ for the PR (`#1287`_).
* give a hint on KeyboardInterrupt to use the --fulltrace option to show the errors,
this fixes `#1366`_.
Thanks to `@hpk42`_ for the report and `@RonnyPfannschmidt`_ for the PR.
* catch IndexError exceptions when getting exception source location. This fixes
pytest internal error for dynamically generated code (fixtures and tests)
where source lines are fake by intention
**Changes**
* **Important**: `py.code <https://pylib.readthedocs.io/en/latest/code.html>`_ has been
merged into the ``pytest`` repository as ``pytest._code``. This decision
was made because ``py.code`` had very few uses outside ``pytest`` and the
fact that it was in a different repository made it difficult to fix bugs on
its code in a timely manner. The team hopes with this to be able to better
refactor out and improve that code.
This change shouldn't affect users, but it is useful to let users aware
if they encounter any strange behavior.
Keep in mind that the code for ``pytest._code`` is **private** and
**experimental**, so you definitely should not import it explicitly!
Please note that the original ``py.code`` is still available in
`pylib <https://pylib.readthedocs.io>`_.
* ``pytest_enter_pdb`` now optionally receives the pytest config object.
Thanks `@nicoddemus`_ for the PR.
* Removed code and documentation for Python 2.5 or lower versions,
including removal of the obsolete ``_pytest.assertion.oldinterpret`` module.
Thanks `@nicoddemus`_ for the PR (`#1226`_).
* Comparisons now always show up in full when ``CI`` or ``BUILD_NUMBER`` is
found in the environment, even when -vv isn't used.
Thanks `@The-Compiler`_ for the PR.
* ``--lf`` and ``--ff`` now support long names: ``--last-failed`` and
``--failed-first`` respectively.
Thanks `@MichaelAquilina`_ for the PR.
* Added expected exceptions to pytest.raises fail message
* Collection only displays progress ("collecting X items") when in a terminal.
This avoids cluttering the output when using ``--color=yes`` to obtain
colors in CI integrations systems (`#1397`_).
**Bug Fixes**
* The ``-s`` and ``-c`` options should now work under ``xdist``;
``Config.fromdictargs`` now represents its input much more faithfully.
Thanks to `@bukzor`_ for the complete PR (`#680`_).
* Fix (`#1290`_): support Python 3.5's ``@`` operator in assertion rewriting.
Thanks `@Shinkenjoe`_ for report with test case and `@tomviner`_ for the PR.
* Fix formatting utf-8 explanation messages (`#1379`_).
Thanks `@biern`_ for the PR.
* Fix `traceback style docs`_ to describe all of the available options
(auto/long/short/line/native/no), with `auto` being the default since v2.6.
Thanks `@hackebrot`_ for the PR.
* Fix (`#1422`_): junit record_xml_property doesn't allow multiple records
with same name.
.. _`traceback style docs`: https://pytest.org/latest/usage.html#modifying-python-traceback-printing
.. _#1422: https://github.com/pytest-dev/pytest/issues/1422
.. _#1379: https://github.com/pytest-dev/pytest/issues/1379
.. _#1366: https://github.com/pytest-dev/pytest/issues/1366
.. _#1040: https://github.com/pytest-dev/pytest/pull/1040
.. _#680: https://github.com/pytest-dev/pytest/issues/680
.. _#1287: https://github.com/pytest-dev/pytest/pull/1287
.. _#1226: https://github.com/pytest-dev/pytest/pull/1226
.. _#1290: https://github.com/pytest-dev/pytest/pull/1290
.. _#1355: https://github.com/pytest-dev/pytest/pull/1355
.. _#1397: https://github.com/pytest-dev/pytest/issues/1397
.. _@biern: https://github.com/biern
.. _@MichaelAquilina: https://github.com/MichaelAquilina
.. _@bukzor: https://github.com/bukzor
.. _@hpk42: https://github.com/hpk42
.. _@nicoddemus: https://github.com/nicoddemus
.. _@jab: https://github.com/jab
.. _@codewarrior0: https://github.com/codewarrior0
.. _@jaraco: https://github.com/jaraco
.. _@The-Compiler: https://github.com/The-Compiler
.. _@Shinkenjoe: https://github.com/Shinkenjoe
.. _@tomviner: https://github.com/tomviner
.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
.. _@rabbbit: https://github.com/rabbbit
.. _@hackebrot: https://github.com/hackebrot

View File

@@ -0,0 +1,65 @@
pytest-2.9.1
============
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
Bruno Oliveira
Daniel Hahler
Dmitry Malinovsky
Florian Bruhin
Floris Bruynooghe
Matt Bachmann
Ronny Pfannschmidt
TomV
Vladimir Bolshakov
Zearin
palaviv
Happy testing,
The py.test Development Team
2.9.1 (compared to 2.9.0)
-------------------------
**Bug Fixes**
* Improve error message when a plugin fails to load.
Thanks `@nicoddemus`_ for the PR.
* Fix (`#1178 <https://github.com/pytest-dev/pytest/issues/1178>`_):
``pytest.fail`` with non-ascii characters raises an internal pytest error.
Thanks `@nicoddemus`_ for the PR.
* Fix (`#469`_): junit parses report.nodeid incorrectly, when params IDs
contain ``::``. Thanks `@tomviner`_ for the PR (`#1431`_).
* Fix (`#578 <https://github.com/pytest-dev/pytest/issues/578>`_): SyntaxErrors
containing non-ascii lines at the point of failure generated an internal
py.test error.
Thanks `@asottile`_ for the report and `@nicoddemus`_ for the PR.
* Fix (`#1437`_): When passing in a bytestring regex pattern to parameterize
attempt to decode it as utf-8 ignoring errors.
* Fix (`#649`_): parametrized test nodes cannot be specified to run on the command line.
.. _#1437: https://github.com/pytest-dev/pytest/issues/1437
.. _#469: https://github.com/pytest-dev/pytest/issues/469
.. _#1431: https://github.com/pytest-dev/pytest/pull/1431
.. _#649: https://github.com/pytest-dev/pytest/issues/649
.. _@asottile: https://github.com/asottile

View File

@@ -0,0 +1,73 @@
pytest-2.9.2
============
pytest is a mature Python testing tool with more than a 1100 tests
against itself, passing on many different interpreters and platforms.
See below for the changes and see docs at:
http://pytest.org
As usual, you can upgrade from pypi via::
pip install -U pytest
Thanks to all who contributed to this release, among them:
Adam Chainz
Benjamin Dopplinger
Bruno Oliveira
Florian Bruhin
John Towler
Martin Prusse
Meng Jue
MengJueM
Omar Kohl
Quentin Pradet
Ronny Pfannschmidt
Thomas Güttler
TomV
Tyler Goodlet
Happy testing,
The py.test Development Team
2.9.2 (compared to 2.9.1)
---------------------------
**Bug Fixes**
* fix `#510`_: skip tests where one parameterize dimension was empty
thanks Alex Stapleton for the Report and `@RonnyPfannschmidt`_ for the PR
* Fix Xfail does not work with condition keyword argument.
Thanks `@astraw38`_ for reporting the issue (`#1496`_) and `@tomviner`_
for PR the (`#1524`_).
* Fix win32 path issue when puttinging custom config file with absolute path
in ``pytest.main("-c your_absolute_path")``.
* Fix maximum recursion depth detection when raised error class is not aware
of unicode/encoded bytes.
Thanks `@prusse-martin`_ for the PR (`#1506`_).
* Fix ``pytest.mark.skip`` mark when used in strict mode.
Thanks `@pquentin`_ for the PR and `@RonnyPfannschmidt`_ for
showing how to fix the bug.
* Minor improvements and fixes to the documentation.
Thanks `@omarkohl`_ for the PR.
* Fix ``--fixtures`` to show all fixture definitions as opposed to just
one per fixture name.
Thanks to `@hackebrot`_ for the PR.
.. _#510: https://github.com/pytest-dev/pytest/issues/510
.. _#1506: https://github.com/pytest-dev/pytest/pull/1506
.. _#1496: https://github.com/pytest-dev/pytest/issue/1496
.. _#1524: https://github.com/pytest-dev/pytest/issue/1524
.. _@prusse-martin: https://github.com/prusse-martin
.. _@astraw38: https://github.com/astraw38

View File

@@ -0,0 +1,82 @@
pytest-3.0.0
============
The pytest team is proud to announce the 3.0.0 release!
pytest is a mature Python testing tool with more than a 1600 tests
against itself, passing on many different interpreters and platforms.
This release contains a lot of bugs and improvements, and much of
the work done on it was possible because of the 2016 Sprint[1], which
was funded by an indiegogo campaign which raised over US$12,000 with
nearly 100 backers.
There's a "What's new in pytest 3.0" [2] blog post highlighting the
major features in this release.
To see the complete changelog and documentation, please visit:
http://docs.pytest.org
As usual, you can upgrade from pypi via:
pip install -U pytest
Thanks to all who contributed to this release, among them:
AbdealiJK
Ana Ribeiro
Antony Lee
Brandon W Maister
Brianna Laugher
Bruno Oliveira
Ceridwen
Christian Boelsen
Daniel Hahler
Danielle Jenkins
Dave Hunt
Diego Russo
Dmitry Dygalo
Edoardo Batini
Eli Boyarski
Florian Bruhin
Floris Bruynooghe
Greg Price
Guyzmo
HEAD KANGAROO
JJ
Javi Romero
Javier Domingo Cansino
Kale Kundert
Kalle Bronsen
Marius Gedminas
Matt Williams
Mike Lundy
Oliver Bestwalter
Omar Kohl
Raphael Pierzina
RedBeardCode
Roberto Polli
Romain Dorgueil
Roman Bolshakov
Ronny Pfannschmidt
Stefan Zimmermann
Steffen Allner
Tareq Alayan
Ted Xiao
Thomas Grainger
Tom Viner
TomV
Vasily Kuznetsov
aostr
marscher
palaviv
satoru
taschini
Happy testing,
The py.test Development Team
[1] http://blog.pytest.org/2016/pytest-development-sprint/
[2] http://blog.pytest.org/2016/whats-new-in-pytest-30/

View File

@@ -0,0 +1,66 @@
python testing sprint June 20th-26th 2016
======================================================
.. image:: ../img/freiburg2.jpg
:width: 400
The pytest core group held the biggest sprint
in its history in June 2016, taking place in the black forest town Freiburg
in Germany. In February 2016 we started a `funding
campaign on Indiegogo to cover expenses
<http://igg.me/at/pytest-sprint/x/4034848>`_ The page also mentions
some preliminary topics:
- improving pytest-xdist test scheduling to take into account
fixture setups and explicit user hints.
- provide info on fixture dependencies during --collect-only
- tying pytest-xdist to tox so that you can do "py.test -e py34"
to run tests in a particular tox-managed virtualenv. Also
look into making pytest-xdist use tox environments on
remote ssh-sides so that remote dependency management becomes
easier.
- refactoring the fixture system so more people understand it :)
- integrating PyUnit setup methods as autouse fixtures.
possibly adding ways to influence ordering of same-scoped
fixtures (so you can make a choice of which fixtures come
before others)
- fixing bugs and issues from the tracker, really an endless source :)
Participants
--------------
Over 20 participants took part from 4 continents, including employees
from Splunk, Personalkollen, Cobe.io, FanDuel and Dolby. Some newcomers
mixed with developers who have worked on pytest since its beginning, and
of course everyone in between.
Ana Ribeiro, Brazil
Ronny Pfannschmidt, Germany
Sprint organisation, schedule
-------------------------------
People arrived in Freiburg on the 19th, with sprint development taking
place on 20th, 21st, 22nd, 24th and 25th. On the 23rd we took a break
day for some hot hiking in the Black Forest.
Sprint activity was organised heavily around pairing, with plenty of group
discusssions to take advantage of the high bandwidth, and lightning talks
as well.
Money / funding
---------------
The Indiegogo campaign aimed for 11000 USD and in the end raised over
12000, to reimburse travel costs, pay for a sprint venue and catering.
Excess money is reserved for further sprint/travel funding for pytest/tox
contributors.

View File

@@ -24,9 +24,9 @@ following::
to assert that your function returns a certain value. If this assertion fails
you will see the return value of the function call::
$ py.test test_assert1.py
$ pytest test_assert1.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
@@ -81,12 +81,18 @@ and if you need to have access to the actual exception info you may use::
f()
assert 'maximum recursion' in str(excinfo.value)
``excinfo`` is a `py.code.ExceptionInfo`_ instance, which is a wrapper around
``excinfo`` is a ``ExceptionInfo`` instance, which is a wrapper around
the actual exception raised. The main attributes of interest are
``.type``, ``.value`` and ``.traceback``.
.. _py.code.ExceptionInfo:
http://pylib.readthedocs.org/en/latest/code.html#py-code-exceptioninfo
.. versionchanged:: 3.0
In the context manager form you may use the keyword argument
``message`` to specify a custom failure message::
>>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"):
... pass
... Failed: Expecting ZeroDivisionError
If you want to write test code that works on Python 2.4 as well,
you may also use two other ways to test for an expected exception::
@@ -113,6 +119,24 @@ exceptions your own code is deliberately raising, whereas using
like documenting unfixed bugs (where the test describes what "should" happen)
or bugs in dependencies.
If you want to test that a regular expression matches on the string
representation of an exception (like the ``TestCase.assertRaisesRegexp`` method
from ``unittest``) you can use the ``ExceptionInfo.match`` method::
import pytest
def myfunc():
raise ValueError("Exception 123 raised")
def test_match():
with pytest.raises(ValueError) as excinfo:
myfunc()
excinfo.match(r'.* 123 .*')
The regexp parameter of the ``match`` method is matched with the ``re.search``
function. So in the above example ``excinfo.match('123')`` would have worked as
well.
.. _`assertwarns`:
@@ -144,9 +168,9 @@ when it encounters comparisons. For example::
if you run this module::
$ py.test test_assert2.py
$ pytest test_assert2.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
@@ -159,7 +183,7 @@ if you run this module::
set1 = set("1308")
set2 = set("8035")
> assert set1 == set2
E assert set(['0', '1', '3', '8']) == set(['0', '3', '5', '8'])
E assert {'0', '1', '3', '8'} == {'0', '3', '5', '8'}
E Extra items in the left set:
E '1'
E Extra items in the right set:
@@ -213,7 +237,7 @@ now, given this test module::
you can run the test module and get the custom output defined in
the conftest file::
$ py.test -q test_foocompare.py
$ pytest -q test_foocompare.py
F
======= FAILURES ========
_______ test_compare ________
@@ -243,10 +267,9 @@ recording the intermediate values. Which technique is used depends on the
location of the assert, ``pytest`` configuration, and Python version being used
to run ``pytest``.
By default, if the Python version is greater than or equal to 2.6, ``pytest``
rewrites assert statements in test modules. Rewritten assert statements put
introspection information into the assertion failure message. ``pytest`` only
rewrites test modules directly discovered by its test collection process, so
By default, ``pytest`` rewrites assert statements in test modules.
Rewritten assert statements put introspection information into the assertion failure message.
``pytest`` only rewrites test modules directly discovered by its test collection process, so
asserts in supporting modules which are not themselves test modules will not be
rewritten.
@@ -291,3 +314,6 @@ For further information, Benjamin Peterson wrote up `Behind the scenes of pytest
.. versionchanged:: 2.1
Introduce the ``--assert`` option. Deprecate ``--no-assert`` and
``--nomagic``.
.. versionchanged:: 3.0
Removes the ``--no-assert`` and``--nomagic`` options.

View File

@@ -0,0 +1,12 @@
.. _backwards-compatibility:
Backwards Compatibility Policy
==============================
Keeping backwards compatibility has a very high priority in the pytest project. Although we have deprecated functionality over the years, most of it is still supported. All deprecations in pytest were done because simpler or more efficient ways of accomplishing the same tasks have emerged, making the old way of doing things unnecessary.
With the pytest 3.0 release we introduced a clear communication scheme for when we will actually remove the old busted joint and politely ask you to use the new hotness instead, while giving you enough time to adjust your tests or raise concerns if there are valid reasons to keep deprecated functionality around.
To communicate changes we are already issuing deprecation warnings, but they are not displayed by default. In pytest 3.0 we changed the default setting so that pytest deprecation warnings are displayed if not explicitly silenced (with ``--disable-pytest-warnings``).
We will only remove deprecated functionality in major releases (e.g. if we deprecate something in 3.0 we will remove it in 4.0), and keep it around for at least two minor releases (e.g. if we deprecate something in 3.9 and 4.0 is the next release, we will not remove it in 4.0 but in 5.0).

View File

@@ -5,7 +5,7 @@ Setting up bash completion
==========================
When using bash as your shell, ``pytest`` can use argcomplete
(https://argcomplete.readthedocs.org/) for auto-completion.
(https://argcomplete.readthedocs.io/) for auto-completion.
For this ``argcomplete`` needs to be installed **and** enabled.
Install argcomplete using::
@@ -18,11 +18,11 @@ For global activation of all argcomplete enabled python applications run::
For permanent (but not global) ``pytest`` activation, use::
register-python-argcomplete py.test >> ~/.bashrc
register-python-argcomplete pytest >> ~/.bashrc
For one-time activation of argcomplete for ``pytest`` only, use::
eval "$(register-python-argcomplete py.test)"
eval "$(register-python-argcomplete pytest)"

View File

@@ -35,6 +35,11 @@ Examples at :ref:`assertraises`.
.. autofunction:: deprecated_call
Comparing floating point numbers
--------------------------------
.. autoclass:: approx
Raising a specific test outcome
--------------------------------------
@@ -48,7 +53,7 @@ you can rather use declarative marks, see :ref:`skipping`.
.. autofunction:: _pytest.skipping.xfail
.. autofunction:: _pytest.runner.exit
fixtures and requests
Fixtures and requests
-----------------------------------------------------
To mark a fixture function:
@@ -72,7 +77,7 @@ Builtin fixtures/function arguments
You can ask for available builtin or project-custom
:ref:`fixtures <fixtures>` by typing::
$ py.test -q --fixtures
$ pytest -q --fixtures
cache
Return a cache object that can persist state between testing sessions.
@@ -84,19 +89,23 @@ You can ask for available builtin or project-custom
Values can be any object handled by the json stdlib module.
capsys
enables capturing of writes to sys.stdout/sys.stderr and makes
Enable capturing of writes to sys.stdout/sys.stderr and make
captured output available via ``capsys.readouterr()`` method calls
which return a ``(out, err)`` tuple.
capfd
enables capturing of writes to file descriptors 1 and 2 and makes
Enable capturing of writes to file descriptors 1 and 2 and make
captured output available via ``capfd.readouterr()`` method calls
which return a ``(out, err)`` tuple.
doctest_namespace
Inject names into the doctest namespace.
pytestconfig
the pytest config object with access to command line opts.
record_xml_property
Fixture that adds extra xml properties to the tag for the calling test.
The fixture is callable with (name, value), with value being automatically
Add extra xml properties to the tag for the calling test.
The fixture is callable with ``(name, value)``, with value being automatically
xml-encoded.
monkeypatch
The returned ``monkeypatch`` funcarg provides these
The returned ``monkeypatch`` fixture provides these
helper methods to modify objects, dictionaries or os.environ::
monkeypatch.setattr(obj, name, value, raising=True)
@@ -109,11 +118,9 @@ You can ask for available builtin or project-custom
monkeypatch.chdir(path)
All modifications will be undone after the requesting
test function has finished. The ``raising``
test function or fixture has finished. The ``raising``
parameter determines if a KeyError or AttributeError
will be raised if the set/deletion operation has no target.
pytestconfig
the pytest config object with access to command line opts.
recwarn
Return a WarningsRecorder instance that provides these methods:
@@ -125,7 +132,7 @@ You can ask for available builtin or project-custom
tmpdir_factory
Return a TempdirFactory instance for the test session.
tmpdir
return a temporary directory path object
Return a temporary directory path object
which is unique to each test function invocation,
created as a sub directory of the base temporary
directory. The returned object is a `py.path.local`_

View File

@@ -15,17 +15,17 @@ Usage
---------
The plugin provides two command line options to rerun failures from the
last ``py.test`` invocation:
last ``pytest`` invocation:
* ``--lf`` (last failures) - to only re-run the failures.
* ``--ff`` (failures first) - to run the failures first and then the rest of
* ``--lf``, ``--last-failed`` - to only re-run the failures.
* ``--ff``, ``--failed-first`` - to run the failures first and then the rest of
the tests.
For cleanup (usually not needed), a ``--cache-clear`` option allows to remove
all cross-session cache contents ahead of a test run.
Other plugins may access the `config.cache`_ object to set/get
**json encodable** values between ``py.test`` invocations.
**json encodable** values between ``pytest`` invocations.
.. note::
@@ -49,7 +49,7 @@ First, let's create 50 test invocation of which only 2 fail::
If you run this for the first time you will see two failures::
$ py.test -q
$ pytest -q
.................F.......F........................
======= FAILURES ========
_______ test_num[17] ________
@@ -78,9 +78,9 @@ If you run this for the first time you will see two failures::
If you then run it with ``--lf``::
$ py.test --lf
$ pytest --lf
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
run-last-failure: rerun last 2 failures
rootdir: $REGENDOC_TMPDIR, inifile:
collected 50 items
@@ -110,6 +110,7 @@ If you then run it with ``--lf``::
E Failed: bad luck
test_50.py:6: Failed
======= 48 tests deselected ========
======= 2 failed, 48 deselected in 0.12 seconds ========
You have run only the two failing test from the last run, while 48 tests have
@@ -119,9 +120,9 @@ Now, if you run with the ``--ff`` option, all tests will be run but the first
previous failures will be executed first (as can be seen from the series
of ``FF`` and dots)::
$ py.test --ff
$ pytest --ff
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
run-last-failure: rerun last 2 failures first
rootdir: $REGENDOC_TMPDIR, inifile:
collected 50 items
@@ -163,7 +164,7 @@ The new config.cache object
Plugins or conftest.py support code can get a cached value using the
pytest ``config`` object. Here is a basic example plugin which
implements a :ref:`fixture` which re-uses previously created state
across py.test invocations::
across pytest invocations::
# content of test_caching.py
import pytest
@@ -184,7 +185,7 @@ across py.test invocations::
If you run this command once, it will take a while because
of the sleep::
$ py.test -q
$ pytest -q
F
======= FAILURES ========
_______ test_function ________
@@ -201,7 +202,7 @@ of the sleep::
If you run it a second time the value will be retrieved from
the cache and this will be quick::
$ py.test -q
$ pytest -q
F
======= FAILURES ========
_______ test_function ________
@@ -222,27 +223,20 @@ Inspecting Cache content
-------------------------------
You can always peek at the content of the cache using the
``--cache-clear`` command line option::
``--cache-show`` command line option::
$ py.test --cache-clear
$ py.test --cache-show
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
cachedir: $REGENDOC_TMPDIR/.cache
------------------------------- cache values -------------------------------
cache/lastfailed contains:
{'test_caching.py::test_function': True}
example/value contains:
42
test_caching.py F
======= FAILURES ========
_______ test_function ________
mydata = 42
def test_function(mydata):
> assert mydata == 23
E assert 42 == 23
test_caching.py:14: AssertionError
======= 1 failed in 0.12 seconds ========
======= no tests ran in 0.12 seconds ========
Clearing Cache content
-------------------------------
@@ -250,7 +244,7 @@ Clearing Cache content
You can instruct pytest to clear all cache files and values
by adding the ``--cache-clear`` option like this::
py.test --cache-clear
pytest --cache-clear
This is recommended for invocations from Continous Integration
servers where isolation and correctness is more important
@@ -262,7 +256,7 @@ than speed.
config.cache API
------------------
The `config.cache`` object allows other plugins,
The ``config.cache`` object allows other plugins,
including ``conftest.py`` files,
to safely and flexibly store and retrieve values across
test runs because the ``config`` object is available

View File

@@ -36,9 +36,9 @@ There are two ways in which ``pytest`` can perform capturing:
You can influence output capturing mechanisms from the command line::
py.test -s # disable all capturing
py.test --capture=sys # replace sys.stdout/stderr with in-mem files
py.test --capture=fd # also point filedescriptors 1 and 2 to temp file
pytest -s # disable all capturing
pytest --capture=sys # replace sys.stdout/stderr with in-mem files
pytest --capture=fd # also point filedescriptors 1 and 2 to temp file
.. _printdebugging:
@@ -62,9 +62,9 @@ is that you can use print statements for debugging::
and running this module will show you precisely the output
of the failing function and hide the other one::
$ py.test
$ pytest
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items
@@ -97,7 +97,7 @@ that performs some output related checks:
out, err = capsys.readouterr()
assert out == "hello\n"
assert err == "world\n"
print "next"
print ("next")
out, err = capsys.readouterr()
assert out == "next\n"
@@ -115,4 +115,19 @@ same interface but allows to also capture output from
libraries or subprocesses that directly write to operating
system level output streams (FD1 and FD2).
.. versionadded:: 3.0
To temporarily disable capture within a test, both ``capsys``
and ``capfd`` have a ``disabled()`` method that can be used
as a context manager, disabling capture inside the ``with`` block:
.. code-block:: python
def test_disabling_capturing(capsys):
print('this output is captured')
with capsys.disabled():
print('output not captured, going directly to sys.stdout')
print('this output is also captured')
.. include:: links.inc

View File

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

View File

@@ -3,7 +3,7 @@
Full pytest documentation
===========================
`Download latest version as PDF <pytest.pdf>`_
`Download latest version as PDF <https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
.. `Download latest version as EPUB <http://media.readthedocs.org/epub/pytest/latest/pytest.epub>`_
@@ -13,8 +13,15 @@ Full pytest documentation
overview
apiref
example/index
plugins
monkeypatch
tmpdir
capture
recwarn
cache
plugins
nose
backwards-compatibility
contributing
talks

View File

@@ -7,7 +7,7 @@ Command line options and configuration file settings
You can get help on command line options and values in INI-style
configurations files by using the general help option::
py.test -h # prints options _and_ config file settings
pytest -h # prints options _and_ config file settings
This will display command line and configuration file settings
which were registered by installed plugins.
@@ -29,25 +29,29 @@ project/testrun-specific information.
Here is the algorithm which finds the rootdir from ``args``:
- determine the common ancestor directory for the specified ``args``.
- determine the common ancestor directory for the specified ``args`` that are
recognised as paths that exist in the file system. If no such paths are
found, the common ancestor directory is set to the current working directory.
- look for ``pytest.ini``, ``tox.ini`` and ``setup.cfg`` files in the
ancestor directory and upwards. If one is matched, it becomes the
ini-file and its directory becomes the rootdir. An existing
``pytest.ini`` file will always be considered a match whereas
``tox.ini`` and ``setup.cfg`` will only match if they contain
a ``[pytest]`` section.
- look for ``pytest.ini``, ``tox.ini`` and ``setup.cfg`` files in the ancestor
directory and upwards. If one is matched, it becomes the ini-file and its
directory becomes the rootdir.
- if no ini-file was found, look for ``setup.py`` upwards from
the common ancestor directory to determine the ``rootdir``.
- if no ini-file was found, look for ``setup.py`` upwards from the common
ancestor directory to determine the ``rootdir``.
- if no ini-file and no ``setup.py`` was found, use the already
determined common ancestor as root directory. This allows to
work with pytest in structures that are not part of a package
and don't have any particular ini-file configuration.
- if no ``setup.py`` was found, look for ``pytest.ini``, ``tox.ini`` and
``setup.cfg`` in each of the specified ``args`` and upwards. If one is
matched, it becomes the ini-file and its directory becomes the rootdir.
Note that options from multiple ini-files candidates are never merged,
the first one wins (``pytest.ini`` always wins even if it does not
- if no ini-file was found, use the already determined common ancestor as root
directory. This allows to work with pytest in structures that are not part of
a package and don't have any particular ini-file configuration.
Note that an existing ``pytest.ini`` file will always be considered a match,
whereas ``tox.ini`` and ``setup.cfg`` will only match if they contain a
``[pytest]`` or ``[tool:pytest]`` section, respectively. Options from multiple ini-files candidates are never
merged - the first one wins (``pytest.ini`` always wins, even if it does not
contain a ``[pytest]`` section).
The ``config`` object will subsequently carry these attributes:
@@ -62,14 +66,14 @@ per-testrun information.
Example::
py.test path/to/testdir path/other/
pytest path/to/testdir path/other/
will determine the common ancestor as ``path`` and then
check for ini-files as follows::
# first look for pytest.ini files
path/pytest.ini
path/setup.cfg # must also contain [pytest] section to match
path/setup.cfg # must also contain [tool:pytest] section to match
path/tox.ini # must also contain [pytest] section to match
pytest.ini
... # all the way down to the root
@@ -126,9 +130,9 @@ Builtin configuration file options
[pytest]
addopts = --maxfail=2 -rf # exit after 2 failures, report fail info
issuing ``py.test test_hello.py`` actually means::
issuing ``pytest test_hello.py`` actually means::
py.test --maxfail=2 -rf test_hello.py
pytest --maxfail=2 -rf test_hello.py
Default is to add no options.
@@ -144,13 +148,13 @@ Builtin configuration file options
[seq] matches any character in seq
[!seq] matches any char not in seq
Default patterns are ``'.*', 'CVS', '_darcs', '{arch}', '*.egg'``.
Default patterns are ``'.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg'``.
Setting a ``norecursedirs`` replaces the default. Here is an example of
how to avoid certain directories:
.. code-block:: ini
# content of setup.cfg
# content of pytest.ini
[pytest]
norecursedirs = .svn _build tmp*
@@ -218,7 +222,7 @@ Builtin configuration file options
.. confval:: doctest_optionflags
One or more doctest flag names from the standard ``doctest`` module.
:doc:`See how py.test handles doctests <doctest>`.
:doc:`See how pytest handles doctests <doctest>`.
.. confval:: confcutdir

View File

@@ -6,14 +6,16 @@ By default all files matching the ``test*.txt`` pattern will
be run through the python standard ``doctest`` module. You
can change the pattern by issuing::
py.test --doctest-glob='*.rst'
pytest --doctest-glob='*.rst'
on the command line. You can also trigger running of doctests
on the command line. Since version ``2.9``, ``--doctest-glob``
can be given multiple times in the command-line.
You can also trigger running of doctests
from docstrings in all python modules (including regular
python test modules)::
py.test --doctest-modules
pytest --doctest-modules
You can make these changes permanent in your project by
putting them into a pytest.ini file like this:
@@ -42,11 +44,11 @@ and another like this::
"""
return 42
then you can just invoke ``py.test`` without command line options::
then you can just invoke ``pytest`` without command line options::
$ py.test
$ pytest
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 1 items
@@ -65,21 +67,34 @@ Also, :ref:`usefixtures` and :ref:`autouse` fixtures are supported
when executing text doctest files.
The standard ``doctest`` module provides some setting flags to configure the
strictness of doctest tests. In py.test You can enable those flags those flags
strictness of doctest tests. In pytest You can enable those flags those flags
using the configuration file. To make pytest ignore trailing whitespaces and
ignore lengthy exception stack traces you can just write::
ignore lengthy exception stack traces you can just write:
.. code-block:: ini
# content of pytest.ini
[pytest]
doctest_optionflags= NORMALIZE_WHITESPACE IGNORE_EXCEPTION_DETAIL
pytest also introduces new options to allow doctests to run in Python 2 and
Python 3 unchanged:
py.test also introduces a new ``ALLOW_UNICODE`` option flag: when enabled, the
``u`` prefix is stripped from unicode strings in expected doctest output. This
allows doctests which use unicode to run in Python 2 and 3 unchanged.
* ``ALLOW_UNICODE``: when enabled, the ``u`` prefix is stripped from unicode
strings in expected doctest output.
As with any other option flag, this flag can be enabled in ``pytest.ini`` using
the ``doctest_optionflags`` ini option or by an inline comment in the doc test
* ``ALLOW_BYTES``: when enabled, the ``b`` prefix is stripped from byte strings
in expected doctest output.
As with any other option flag, these flags can be enabled in ``pytest.ini`` using
the ``doctest_optionflags`` ini option:
.. code-block:: ini
[pytest]
doctest_optionflags = ALLOW_UNICODE ALLOW_BYTES
Alternatively, it can be enabled by an inline comment in the doc test
itself::
# content of example.rst
@@ -87,3 +102,50 @@ itself::
'Hello'
The 'doctest_namespace' fixture
-------------------------------
.. versionadded:: 3.0
The ``doctest_namespace`` fixture can be used to inject items into the
namespace in which your doctests run. It is intended to be used within
your own fixtures to provide the tests that use them with context.
``doctest_namespace`` is a standard ``dict`` object into which you
place the objects you want to appear in the doctest namespace::
# content of conftest.py
import numpy
@pytest.fixture(autouse=True)
def add_np(doctest_namespace):
doctest_namespace['np'] = numpy
which can then be used in your doctests directly::
# content of numpy.py
def arange():
"""
>>> a = np.arange(10)
>>> len(a)
10
"""
pass
Output format
-------------
.. versionadded:: 3.0
You can change the diff output format on failure for your doctests
by using one of standard doctest modules format in options
(see :data:`python:doctest.REPORT_UDIFF`, :data:`python:doctest.REPORT_CDIFF`,
:data:`python:doctest.REPORT_NDIFF`, :data:`python:doctest.REPORT_ONLY_FIRST_FAILURE`)::
pytest --doctest-modules --doctest-report none
pytest --doctest-modules --doctest-report udiff
pytest --doctest-modules --doctest-report cdiff
pytest --doctest-modules --doctest-report ndiff
pytest --doctest-modules --doctest-report only_first_failure

View File

@@ -1,4 +1,5 @@
from pytest import raises
import _pytest._code
import py
def otherfunc(a,b):
@@ -159,7 +160,7 @@ def test_dynamic_compile_shows_nicely():
src = 'def foo():\n assert 1 == 0\n'
name = 'abc-123'
module = py.std.imp.new_module(name)
code = py.code.compile(src, name, 'exec')
code = _pytest._code.compile(src, name, 'exec')
py.builtin.exec_(code, module.__dict__)
py.std.sys.modules[name] = module
module.foo()

View File

@@ -4,8 +4,8 @@ import pytest
@pytest.fixture("session")
def setup(request):
setup = CostlySetup()
request.addfinalizer(setup.finalize)
return setup
yield setup
setup.finalize()
class CostlySetup:
def __init__(self):

View File

@@ -29,23 +29,23 @@ You can "mark" a test function with custom metadata like this::
You can then restrict a test run to only run tests marked with ``webtest``::
$ py.test -v -m webtest
$ pytest -v -m webtest
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
test_server.py::test_send_http PASSED
======= 3 tests deselected by "-m 'webtest'" ========
======= 3 tests deselected ========
======= 1 passed, 3 deselected in 0.12 seconds ========
Or the inverse, running all tests except the webtest ones::
$ py.test -v -m "not webtest"
$ pytest -v -m "not webtest"
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
@@ -54,7 +54,7 @@ Or the inverse, running all tests except the webtest ones::
test_server.py::test_another PASSED
test_server.py::TestClass::test_method PASSED
======= 1 tests deselected by "-m 'not webtest'" ========
======= 1 tests deselected ========
======= 3 passed, 1 deselected in 0.12 seconds ========
Selecting tests based on their node ID
@@ -64,9 +64,9 @@ You can provide one or more :ref:`node IDs <node-id>` as positional
arguments to select only specified tests. This makes it easy to select
tests based on their module, class, method, or function name::
$ py.test -v test_server.py::TestClass::test_method
$ pytest -v test_server.py::TestClass::test_method
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 5 items
@@ -77,9 +77,9 @@ tests based on their module, class, method, or function name::
You can also select on the class::
$ py.test -v test_server.py::TestClass
$ pytest -v test_server.py::TestClass
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
@@ -90,9 +90,9 @@ You can also select on the class::
Or select multiple nodes::
$ py.test -v test_server.py::TestClass test_server.py::test_send_http
$ pytest -v test_server.py::TestClass test_server.py::test_send_http
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 8 items
@@ -115,8 +115,8 @@ Or select multiple nodes::
``module.py::function[param]``.
Node IDs for failing tests are displayed in the test summary info
when running py.test with the ``-rf`` option. You can also
construct Node IDs from the output of ``py.test --collectonly``.
when running pytest with the ``-rf`` option. You can also
construct Node IDs from the output of ``pytest --collectonly``.
Using ``-k expr`` to select tests based on their name
-------------------------------------------------------
@@ -128,23 +128,23 @@ which implements a substring match on the test names instead of the
exact match on markers that ``-m`` provides. This makes it easy to
select tests based on their names::
$ py.test -v -k http # running with the above defined example module
$ pytest -v -k http # running with the above defined example module
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
test_server.py::test_send_http PASSED
======= 3 tests deselected by '-khttp' ========
======= 3 tests deselected ========
======= 1 passed, 3 deselected in 0.12 seconds ========
And you can also run all tests except the ones that match the keyword::
$ py.test -k "not send_http" -v
$ pytest -k "not send_http" -v
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
@@ -153,14 +153,14 @@ And you can also run all tests except the ones that match the keyword::
test_server.py::test_another PASSED
test_server.py::TestClass::test_method PASSED
======= 1 tests deselected by '-knot send_http' ========
======= 1 tests deselected ========
======= 3 passed, 1 deselected in 0.12 seconds ========
Or to select "http" and "quick" tests::
$ py.test -k "http or quick" -v
$ pytest -k "http or quick" -v
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 4 items
@@ -168,7 +168,7 @@ Or to select "http" and "quick" tests::
test_server.py::test_send_http PASSED
test_server.py::test_something_quick PASSED
======= 2 tests deselected by '-khttp or quick' ========
======= 2 tests deselected ========
======= 2 passed, 2 deselected in 0.12 seconds ========
.. note::
@@ -198,12 +198,14 @@ Registering markers for your test suite is simple::
You can ask which markers exist for your test suite - the list includes our just defined ``webtest`` markers::
$ py.test --markers
$ pytest --markers
@pytest.mark.webtest: mark a test as a webtest.
@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
@pytest.mark.xfail(condition, reason=None, run=True, raises=None): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
@@ -223,7 +225,7 @@ For an example on how to add and work with markers from a plugin, see
* there is one place in your test suite defining your markers
* asking for existing markers via ``py.test --markers`` gives good output
* asking for existing markers via ``pytest --markers`` gives good output
* typos in function markers are treated as an error if you use
the ``--strict`` option. Future versions of ``pytest`` are probably
@@ -234,8 +236,8 @@ For an example on how to add and work with markers from a plugin, see
Marking whole classes or modules
----------------------------------------------------
If you are programming with Python 2.6 or later you may use ``pytest.mark``
decorators with classes to apply markers to all of its test methods::
You may use ``pytest.mark`` decorators with classes to apply markers to all of
its test methods::
# content of test_mark_classlevel.py
import pytest
@@ -348,9 +350,9 @@ A test file using this local plugin::
and an example invocations specifying a different environment than what
the test needs::
$ py.test -E stage2
$ pytest -E stage2
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
@@ -360,9 +362,9 @@ the test needs::
and here is one that specifies exactly the environment needed::
$ py.test -E stage1
$ pytest -E stage1
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
@@ -372,12 +374,14 @@ and here is one that specifies exactly the environment needed::
The ``--markers`` option always gives you a list of available markers::
$ py.test --markers
$ pytest --markers
@pytest.mark.env(name): mark test to run only on named environment
@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
@pytest.mark.xfail(condition, reason=None, run=True, raises=None): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
@@ -423,7 +427,7 @@ test function. From a conftest file we can read it like this::
Let's run this without capturing output and see what we get::
$ py.test -q -s
$ pytest -q -s
glob args=('function',) kwargs={'x': 3}
glob args=('class',) kwargs={'x': 2}
glob args=('module',) kwargs={'x': 1}
@@ -436,7 +440,7 @@ marking platform specific tests with pytest
.. regendoc:wipe
Consider you have a test suite which marks tests for particular platforms,
namely ``pytest.mark.osx``, ``pytest.mark.win32`` etc. and you
namely ``pytest.mark.darwin``, ``pytest.mark.win32`` etc. and you
also have tests that run on all platforms and have no specific
marker. If you now want to have a way to only run the tests
for your particular platform, you could use the following plugin::
@@ -446,7 +450,7 @@ for your particular platform, you could use the following plugin::
import sys
import pytest
ALL = set("osx linux2 win32".split())
ALL = set("darwin linux2 win32".split())
def pytest_runtest_setup(item):
if isinstance(item, item.Function):
@@ -462,7 +466,7 @@ Let's do a little test file to show how this looks like::
import pytest
@pytest.mark.osx
@pytest.mark.darwin
def test_if_apple_is_evil():
pass
@@ -479,9 +483,9 @@ Let's do a little test file to show how this looks like::
then you will see two test skipped and two executed tests as expected::
$ py.test -rs # this option reports skip reasons
$ pytest -rs # this option reports skip reasons
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
@@ -493,15 +497,15 @@ then you will see two test skipped and two executed tests as expected::
Note that if you specify a platform via the marker-command line option like this::
$ py.test -m linux2
$ pytest -m linux2
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
test_plat.py s
======= 3 tests deselected by "-m 'linux2'" ========
======= 3 tests deselected ========
======= 1 skipped, 3 deselected in 0.12 seconds ========
then the unmarked-tests will not be run. It is thus a way to restrict the run to the specific tests.
@@ -545,9 +549,9 @@ We want to dynamically define two markers and can do it in a
We can now use the ``-m option`` to select one set::
$ py.test -m interface --tb=short
$ pytest -m interface --tb=short
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
@@ -562,14 +566,14 @@ We can now use the ``-m option`` to select one set::
test_module.py:6: in test_interface_complex
assert 0
E assert 0
======= 2 tests deselected by "-m 'interface'" ========
======= 2 tests deselected ========
======= 2 failed, 2 deselected in 0.12 seconds ========
or to select both "event" and "interface" tests::
$ py.test -m "interface or event" --tb=short
$ pytest -m "interface or event" --tb=short
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
@@ -588,5 +592,5 @@ or to select both "event" and "interface" tests::
test_module.py:9: in test_event_simple
assert 0
E assert 0
======= 1 tests deselected by "-m 'interface or event'" ========
======= 1 tests deselected ========
======= 3 failed, 1 deselected in 0.12 seconds ========

View File

@@ -4,8 +4,9 @@ serialization via the pickle module.
"""
import py
import pytest
import _pytest._code
pythonlist = ['python2.6', 'python2.7', 'python3.3']
pythonlist = ['python2.6', 'python2.7', 'python3.4', 'python3.5']
@pytest.fixture(params=pythonlist)
def python1(request, tmpdir):
picklefile = tmpdir.join("data.pickle")
@@ -23,7 +24,7 @@ class Python:
self.picklefile = picklefile
def dumps(self, obj):
dumpfile = self.picklefile.dirpath("dump.py")
dumpfile.write(py.code.Source("""
dumpfile.write(_pytest._code.Source("""
import pickle
f = open(%r, 'wb')
s = pickle.dump(%r, f, protocol=2)
@@ -33,7 +34,7 @@ class Python:
def load_and_is_true(self, expression):
loadfile = self.picklefile.dirpath("load.py")
loadfile.write(py.code.Source("""
loadfile.write(_pytest._code.Source("""
import pickle
f = open(%r, 'rb')
obj = pickle.load(f)

View File

@@ -25,9 +25,9 @@ You can create a simple example file:
and if you installed `PyYAML`_ or a compatible YAML-parser you can
now execute the test specification::
nonpython $ py.test test_simple.yml
nonpython $ pytest test_simple.yml
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
collected 2 items
@@ -57,9 +57,9 @@ your own domain specific testing language this way.
``reportinfo()`` is used for representing the test location and is also
consulted when reporting in ``verbose`` mode::
nonpython $ py.test -v
nonpython $ pytest -v
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
collecting ... collected 2 items
@@ -79,9 +79,9 @@ consulted when reporting in ``verbose`` mode::
While developing your custom test collection and execution it's also
interesting to just look at the collection tree::
nonpython $ py.test --collect-only
nonpython $ pytest --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
collected 2 items
<YamlFile 'test_simple.yml'>

View File

@@ -10,7 +10,7 @@ class YamlFile(pytest.File):
def collect(self):
import yaml # we need a yaml parser, e.g. PyYAML
raw = yaml.safe_load(self.fspath.open())
for name, spec in raw.items():
for name, spec in sorted(raw.items()):
yield YamlItem(name, self, spec)
class YamlItem(pytest.Item):
@@ -19,7 +19,7 @@ class YamlItem(pytest.Item):
self.spec = spec
def runtest(self):
for name, value in self.spec.items():
for name, value in sorted(self.spec.items()):
# some custom test execution (dumb example follows)
if name != value:
raise YamlException(self, name, value)

View File

@@ -44,14 +44,14 @@ Now we add a test configuration like this::
This means that we only run 2 tests if we do not pass ``--all``::
$ py.test -q test_compute.py
$ pytest -q test_compute.py
..
2 passed in 0.12 seconds
We run only two computations, so we see two dots.
let's run the full monty::
$ py.test -q --all
$ pytest -q --all
....F
======= FAILURES ========
_______ test_compute[4] ________
@@ -128,9 +128,9 @@ label generated by ``idfn``, but because we didn't generate a label for ``timede
objects, they are still using the default pytest representation::
$ py.test test_time.py --collect-only
$ pytest test_time.py --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 6 items
<Module 'test_time.py'>
@@ -179,9 +179,9 @@ only have to work a bit to construct the correct arguments for pytest's
this is a fully self-contained example which you can run with::
$ py.test test_scenarios.py
$ pytest test_scenarios.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
@@ -192,9 +192,9 @@ this is a fully self-contained example which you can run with::
If you just collect tests you'll also nicely see 'advanced' and 'basic' as variants for the test function::
$ py.test --collect-only test_scenarios.py
$ pytest --collect-only test_scenarios.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
<Module 'test_scenarios.py'>
@@ -257,9 +257,9 @@ creates a database object for the actual test invocations::
Let's first see how it looks like at collection time::
$ py.test test_backends.py --collect-only
$ pytest test_backends.py --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items
<Module 'test_backends.py'>
@@ -270,7 +270,7 @@ Let's first see how it looks like at collection time::
And then when we run the test::
$ py.test -q test_backends.py
$ pytest -q test_backends.py
.F
======= FAILURES ========
_______ test_db_initialized[d2] ________
@@ -318,9 +318,9 @@ will be passed to respective fixture function::
The result of this test will be successful::
$ py.test test_indirect_list.py --collect-only
$ pytest test_indirect_list.py --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
<Module 'test_indirect_list.py'>
@@ -333,7 +333,7 @@ The result of this test will be successful::
Parametrizing test methods through per-class configuration
--------------------------------------------------------------
.. _`unittest parametrizer`: http://code.google.com/p/unittest-ext/source/browse/trunk/params.py
.. _`unittest parametrizer`: https://github.com/testing-cabal/unittest-ext/blob/master/params.py
Here is an example ``pytest_generate_function`` function implementing a
@@ -346,7 +346,7 @@ parametrizer`_ but in a lot less code::
def pytest_generate_tests(metafunc):
# called once per each test function
funcarglist = metafunc.cls.params[metafunc.function.__name__]
argnames = list(funcarglist[0])
argnames = sorted(funcarglist[0])
metafunc.parametrize(argnames, [[funcargs[name] for name in argnames]
for funcargs in funcarglist])
@@ -366,10 +366,10 @@ parametrizer`_ but in a lot less code::
Our test generator looks up a class-level definition which specifies which
argument sets to use for each test function. Let's run it::
$ py.test -q
$ pytest -q
F..
======= FAILURES ========
_______ TestClass.test_equals[2-1] ________
_______ TestClass.test_equals[1-2] ________
self = <test_parametrize.TestClass object at 0xdeadbeef>, a = 1, b = 2
@@ -396,12 +396,11 @@ is to be run with different sets of arguments for its three arguments:
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize)::
. $ py.test -rs -q multipython.py
ssssssssssss...ssssssssssss
. $ pytest -rs -q multipython.py
sssssssssssssss.........sss.........sss.........
======= short test summary info ========
SKIP [12] $REGENDOC_TMPDIR/CWD/multipython.py:22: 'python2.6' not found
SKIP [12] $REGENDOC_TMPDIR/CWD/multipython.py:22: 'python3.3' not found
3 passed, 24 skipped in 0.12 seconds
SKIP [21] $REGENDOC_TMPDIR/CWD/multipython.py:23: 'python2.6' not found
27 passed, 21 skipped in 0.12 seconds
Indirect parametrization of optional implementations/imports
--------------------------------------------------------------------
@@ -446,9 +445,9 @@ And finally a little test module::
If you run this with reporting for skips enabled::
$ py.test -rs test_module.py
$ pytest -rs test_module.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items

View File

@@ -1,5 +1,5 @@
# run this with $ py.test --collect-only test_collectonly.py
# run this with $ pytest --collect-only test_collectonly.py
#
def test_function():
pass

View File

@@ -9,19 +9,19 @@ by passing the ``--ignore=path`` option on the cli. ``pytest`` allows multiple
``--ignore`` options. Example::
tests/
├── example
│   ├── test_example_01.py
│   ├── test_example_02.py
│   └── test_example_03.py
├── foobar
│   ├── test_foobar_01.py
│   ├── test_foobar_02.py
│   └── test_foobar_03.py
└── hello
└── world
├── test_world_01.py
├── test_world_02.py
└── test_world_03.py
|-- example
| |-- test_example_01.py
| |-- test_example_02.py
| '-- test_example_03.py
|-- foobar
| |-- test_foobar_01.py
| |-- test_foobar_02.py
| '-- test_foobar_03.py
'-- hello
'-- world
|-- test_world_01.py
|-- test_world_02.py
'-- test_world_03.py
Now if you invoke ``pytest`` with ``--ignore=tests/foobar/test_foobar_03.py --ignore=tests/hello/``,
you will see that ``pytest`` only collects test-modules, which do not match the patterns specified::
@@ -40,12 +40,46 @@ you will see that ``pytest`` only collects test-modules, which do not match the
======= 5 passed in 0.02 seconds =======
Keeping duplicate paths specified from command line
----------------------------------------------------
Default behavior of ``pytest`` is to ignore duplicate paths specified from the command line.
Example::
py.test path_a path_a
...
collected 1 item
...
Just collect tests once.
To collect duplicate tests, use the ``--keep-duplicates`` option on the cli.
Example::
py.test --keep-duplicates path_a path_a
...
collected 2 items
...
As the collector just works on directories, if you specify twice a single test file, ``pytest`` will
still collect it twice, no matter if the ``--keep-duplicates`` is not specified.
Example::
py.test test_a.py test_a.py
...
collected 2 items
...
Changing directory recursion
-----------------------------------------------------
You can set the :confval:`norecursedirs` option in an ini-file, for example your ``setup.cfg`` in the project root directory::
You can set the :confval:`norecursedirs` option in an ini-file, for example your ``pytest.ini`` in the project root directory::
# content of setup.cfg
# content of pytest.ini
[pytest]
norecursedirs = .svn _build tmp*
@@ -60,8 +94,9 @@ You can configure different naming conventions by setting
the :confval:`python_files`, :confval:`python_classes` and
:confval:`python_functions` configuration options. Example::
# content of setup.cfg
# can also be defined in in tox.ini or pytest.ini file
# content of pytest.ini
# can also be defined in 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
@@ -80,10 +115,10 @@ that match ``*_check``. For example, if we have::
then the test collection looks like this::
$ py.test --collect-only
$ pytest --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile: setup.cfg
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 2 items
<Module 'check_myapp.py'>
<Class 'CheckMyApp'>
@@ -107,7 +142,7 @@ interpreting arguments as python package names, deriving
their file system path and then running the test. For
example if you have unittest2 installed you can type::
py.test --pyargs unittest2.test.test_skipping -q
pytest --pyargs unittest2.test.test_skipping -q
which would run the respective test module. Like with
other options, through an ini-file and the :confval:`addopts` option you
@@ -117,7 +152,7 @@ can make this change more permanently::
[pytest]
addopts = --pyargs
Now a simple invocation of ``py.test NAME`` will check
Now a simple invocation of ``pytest NAME`` will check
if NAME exists as an importable package/module and otherwise
treat it as a filesystem path.
@@ -126,9 +161,9 @@ Finding out what is collected
You can always peek at the collection tree without running tests like this::
. $ py.test --collect-only pythoncollection.py
. $ pytest --collect-only pythoncollection.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 3 items
<Module 'CWD/pythoncollection.py'>
@@ -177,16 +212,26 @@ and a setup.py dummy file like this::
# content of setup.py
0/0 # will raise exception if imported
then a pytest run on python2 will find the one test when run with a python2
interpreters and will leave out the setup.py file::
then a pytest run on Python2 will find the one test and will leave out the
setup.py file::
$ py.test --collect-only
#$ pytest --collect-only
====== test session starts ======
platform linux2 -- Python 2.7.10, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 1 items
<Module 'pkg/module_py2.py'>
<Function 'test_only_on_python2'>
====== no tests ran in 0.04 seconds ======
If you run with a Python3 interpreter both the one test and the setup.py file
will be left out::
$ pytest --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
collected 0 items
======= no tests ran in 0.12 seconds ========
If you run with a Python3 interpreter the moduled added through the conftest.py file will not be considered for test collection.

View File

@@ -11,9 +11,9 @@ get on the terminal - we are working on that):
.. code-block:: python
assertion $ py.test failure_demo.py
assertion $ pytest failure_demo.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR/assertion, inifile:
collected 42 items
@@ -28,7 +28,7 @@ get on the terminal - we are working on that):
> assert param1 * 2 < param2
E assert (3 * 2) < 6
failure_demo.py:15: AssertionError
failure_demo.py:16: AssertionError
_______ TestFailing.test_simple ________
self = <failure_demo.TestFailing object at 0xdeadbeef>
@@ -44,7 +44,7 @@ get on the terminal - we are working on that):
E + where 42 = <function TestFailing.test_simple.<locals>.f at 0xdeadbeef>()
E + and 43 = <function TestFailing.test_simple.<locals>.g at 0xdeadbeef>()
failure_demo.py:28: AssertionError
failure_demo.py:29: AssertionError
_______ TestFailing.test_simple_multiline ________
self = <failure_demo.TestFailing object at 0xdeadbeef>
@@ -54,7 +54,7 @@ get on the terminal - we are working on that):
42,
> 6*9)
failure_demo.py:33:
failure_demo.py:34:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
a = 42, b = 54
@@ -64,7 +64,7 @@ get on the terminal - we are working on that):
b)
E assert 42 == 54
failure_demo.py:11: AssertionError
failure_demo.py:12: AssertionError
_______ TestFailing.test_not ________
self = <failure_demo.TestFailing object at 0xdeadbeef>
@@ -76,7 +76,7 @@ get on the terminal - we are working on that):
E assert not 42
E + where 42 = <function TestFailing.test_not.<locals>.f at 0xdeadbeef>()
failure_demo.py:38: AssertionError
failure_demo.py:39: AssertionError
_______ TestSpecialisedExplanations.test_eq_text ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -87,7 +87,7 @@ get on the terminal - we are working on that):
E - spam
E + eggs
failure_demo.py:42: AssertionError
failure_demo.py:43: AssertionError
_______ TestSpecialisedExplanations.test_eq_similar_text ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -100,7 +100,7 @@ get on the terminal - we are working on that):
E + foo 2 bar
E ? ^
failure_demo.py:45: AssertionError
failure_demo.py:46: AssertionError
_______ TestSpecialisedExplanations.test_eq_multiline_text ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -113,7 +113,7 @@ get on the terminal - we are working on that):
E + eggs
E bar
failure_demo.py:48: AssertionError
failure_demo.py:49: AssertionError
_______ TestSpecialisedExplanations.test_eq_long_text ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -130,7 +130,7 @@ get on the terminal - we are working on that):
E + 1111111111b222222222
E ? ^
failure_demo.py:53: AssertionError
failure_demo.py:54: AssertionError
_______ TestSpecialisedExplanations.test_eq_long_text_multiline ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -154,7 +154,7 @@ get on the terminal - we are working on that):
E 2
E 2
failure_demo.py:58: AssertionError
failure_demo.py:59: AssertionError
_______ TestSpecialisedExplanations.test_eq_list ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -165,7 +165,7 @@ get on the terminal - we are working on that):
E At index 2 diff: 2 != 3
E Use -v to get the full diff
failure_demo.py:61: AssertionError
failure_demo.py:62: AssertionError
_______ TestSpecialisedExplanations.test_eq_list_long ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -178,7 +178,7 @@ get on the terminal - we are working on that):
E At index 100 diff: 1 != 2
E Use -v to get the full diff
failure_demo.py:66: AssertionError
failure_demo.py:67: AssertionError
_______ TestSpecialisedExplanations.test_eq_dict ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -195,14 +195,14 @@ get on the terminal - we are working on that):
E {'d': 0}
E Use -v to get the full diff
failure_demo.py:69: AssertionError
failure_demo.py:70: AssertionError
_______ TestSpecialisedExplanations.test_eq_set ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
def test_eq_set(self):
> assert set([0, 10, 11, 12]) == set([0, 20, 21])
E assert set([0, 10, 11, 12]) == set([0, 20, 21])
E assert {0, 10, 11, 12} == {0, 20, 21}
E Extra items in the left set:
E 10
E 11
@@ -212,7 +212,7 @@ get on the terminal - we are working on that):
E 21
E Use -v to get the full diff
failure_demo.py:72: AssertionError
failure_demo.py:73: AssertionError
_______ TestSpecialisedExplanations.test_eq_longer_list ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -223,7 +223,7 @@ get on the terminal - we are working on that):
E Right contains more items, first extra item: 3
E Use -v to get the full diff
failure_demo.py:75: AssertionError
failure_demo.py:76: AssertionError
_______ TestSpecialisedExplanations.test_in_list ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -232,7 +232,7 @@ get on the terminal - we are working on that):
> assert 1 in [0, 2, 3, 4, 5]
E assert 1 in [0, 2, 3, 4, 5]
failure_demo.py:78: AssertionError
failure_demo.py:79: AssertionError
_______ TestSpecialisedExplanations.test_not_in_text_multiline ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -250,7 +250,7 @@ get on the terminal - we are working on that):
E and a
E tail
failure_demo.py:82: AssertionError
failure_demo.py:83: AssertionError
_______ TestSpecialisedExplanations.test_not_in_text_single ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -263,7 +263,7 @@ get on the terminal - we are working on that):
E single foo line
E ? +++
failure_demo.py:86: AssertionError
failure_demo.py:87: AssertionError
_______ TestSpecialisedExplanations.test_not_in_text_single_long ________
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -276,7 +276,7 @@ get on the terminal - we are working on that):
E head head foo tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
E ? +++
failure_demo.py:90: AssertionError
failure_demo.py:91: AssertionError
______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______
self = <failure_demo.TestSpecialisedExplanations object at 0xdeadbeef>
@@ -289,7 +289,7 @@ get on the terminal - we are working on that):
E head head fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffftail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail tail
E ? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
failure_demo.py:94: AssertionError
failure_demo.py:95: AssertionError
_______ test_attribute ________
def test_attribute():
@@ -300,7 +300,7 @@ get on the terminal - we are working on that):
E assert 1 == 2
E + where 1 = <failure_demo.test_attribute.<locals>.Foo object at 0xdeadbeef>.b
failure_demo.py:101: AssertionError
failure_demo.py:102: AssertionError
_______ test_attribute_instance ________
def test_attribute_instance():
@@ -311,7 +311,7 @@ get on the terminal - we are working on that):
E + where 1 = <failure_demo.test_attribute_instance.<locals>.Foo object at 0xdeadbeef>.b
E + where <failure_demo.test_attribute_instance.<locals>.Foo object at 0xdeadbeef> = <class 'failure_demo.test_attribute_instance.<locals>.Foo'>()
failure_demo.py:107: AssertionError
failure_demo.py:108: AssertionError
_______ test_attribute_failure ________
def test_attribute_failure():
@@ -322,7 +322,7 @@ get on the terminal - we are working on that):
i = Foo()
> assert i.b == 2
failure_demo.py:116:
failure_demo.py:117:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <failure_demo.test_attribute_failure.<locals>.Foo object at 0xdeadbeef>
@@ -331,7 +331,7 @@ get on the terminal - we are working on that):
> raise Exception('Failed to get attrib')
E Exception: Failed to get attrib
failure_demo.py:113: Exception
failure_demo.py:114: Exception
_______ test_attribute_multiple ________
def test_attribute_multiple():
@@ -346,7 +346,7 @@ get on the terminal - we are working on that):
E + and 2 = <failure_demo.test_attribute_multiple.<locals>.Bar object at 0xdeadbeef>.b
E + where <failure_demo.test_attribute_multiple.<locals>.Bar object at 0xdeadbeef> = <class 'failure_demo.test_attribute_multiple.<locals>.Bar'>()
failure_demo.py:124: AssertionError
failure_demo.py:125: AssertionError
_______ TestRaises.test_raises ________
self = <failure_demo.TestRaises object at 0xdeadbeef>
@@ -355,22 +355,22 @@ get on the terminal - we are working on that):
s = 'qwe'
> raises(TypeError, "int(s)")
failure_demo.py:133:
failure_demo.py:134:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
> int(s)
E ValueError: invalid literal for int() with base 10: 'qwe'
<0-codegen $PYTHON_PREFIX/lib/python3.4/site-packages/_pytest/python.py:1300>:1: ValueError
<0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python.py:1174>:1: ValueError
_______ TestRaises.test_raises_doesnt ________
self = <failure_demo.TestRaises object at 0xdeadbeef>
def test_raises_doesnt(self):
> raises(IOError, "int('3')")
E Failed: DID NOT RAISE
E Failed: DID NOT RAISE <class 'OSError'>
failure_demo.py:136: Failed
failure_demo.py:137: Failed
_______ TestRaises.test_raise ________
self = <failure_demo.TestRaises object at 0xdeadbeef>
@@ -379,16 +379,16 @@ get on the terminal - we are working on that):
> raise ValueError("demo error")
E ValueError: demo error
failure_demo.py:139: ValueError
failure_demo.py:140: ValueError
_______ TestRaises.test_tupleerror ________
self = <failure_demo.TestRaises object at 0xdeadbeef>
def test_tupleerror(self):
> a,b = [1]
E ValueError: need more than 1 value to unpack
E ValueError: not enough values to unpack (expected 2, got 1)
failure_demo.py:142: ValueError
failure_demo.py:143: ValueError
______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______
self = <failure_demo.TestRaises object at 0xdeadbeef>
@@ -399,7 +399,7 @@ get on the terminal - we are working on that):
> a,b = l.pop()
E TypeError: 'int' object is not iterable
failure_demo.py:147: TypeError
failure_demo.py:148: TypeError
--------------------------- Captured stdout call ---------------------------
l is [1, 2, 3]
_______ TestRaises.test_some_error ________
@@ -410,26 +410,26 @@ get on the terminal - we are working on that):
> if namenotexi:
E NameError: name 'namenotexi' is not defined
failure_demo.py:150: NameError
failure_demo.py:151: NameError
_______ test_dynamic_compile_shows_nicely ________
def test_dynamic_compile_shows_nicely():
src = 'def foo():\n assert 1 == 0\n'
name = 'abc-123'
module = py.std.imp.new_module(name)
code = py.code.compile(src, name, 'exec')
code = _pytest._code.compile(src, name, 'exec')
py.builtin.exec_(code, module.__dict__)
py.std.sys.modules[name] = module
> module.foo()
failure_demo.py:165:
failure_demo.py:166:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
def foo():
> assert 1 == 0
E assert 1 == 0
E AssertionError
<2-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:162>:2: AssertionError
<2-codegen 'abc-123' $REGENDOC_TMPDIR/assertion/failure_demo.py:163>:2: AssertionError
_______ TestMoreErrors.test_complex_error ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -441,9 +441,9 @@ get on the terminal - we are working on that):
return 43
> somefunc(f(), g())
failure_demo.py:175:
failure_demo.py:176:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
failure_demo.py:8: in somefunc
failure_demo.py:9: in somefunc
otherfunc(x,y)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
@@ -453,7 +453,7 @@ get on the terminal - we are working on that):
> assert a==b
E assert 44 == 43
failure_demo.py:5: AssertionError
failure_demo.py:6: AssertionError
_______ TestMoreErrors.test_z1_unpack_error ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -461,9 +461,9 @@ get on the terminal - we are working on that):
def test_z1_unpack_error(self):
l = []
> a,b = l
E ValueError: need more than 0 values to unpack
E ValueError: not enough values to unpack (expected 2, got 0)
failure_demo.py:179: ValueError
failure_demo.py:180: ValueError
_______ TestMoreErrors.test_z2_type_error ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -473,7 +473,7 @@ get on the terminal - we are working on that):
> a,b = l
E TypeError: 'int' object is not iterable
failure_demo.py:183: TypeError
failure_demo.py:184: TypeError
_______ TestMoreErrors.test_startswith ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -482,10 +482,11 @@ get on the terminal - we are working on that):
s = "123"
g = "456"
> assert s.startswith(g)
E assert <built-in method startswith of str object at 0xdeadbeef>('456')
E + where <built-in method startswith of str object at 0xdeadbeef> = '123'.startswith
E assert False
E + where False = <built-in method startswith of str object at 0xdeadbeef>('456')
E + where <built-in method startswith of str object at 0xdeadbeef> = '123'.startswith
failure_demo.py:188: AssertionError
failure_demo.py:189: AssertionError
_______ TestMoreErrors.test_startswith_nested ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -496,22 +497,24 @@ get on the terminal - we are working on that):
def g():
return "456"
> assert f().startswith(g())
E assert <built-in method startswith of str object at 0xdeadbeef>('456')
E + where <built-in method startswith of str object at 0xdeadbeef> = '123'.startswith
E + where '123' = <function TestMoreErrors.test_startswith_nested.<locals>.f at 0xdeadbeef>()
E + and '456' = <function TestMoreErrors.test_startswith_nested.<locals>.g at 0xdeadbeef>()
E assert False
E + where False = <built-in method startswith of str object at 0xdeadbeef>('456')
E + where <built-in method startswith of str object at 0xdeadbeef> = '123'.startswith
E + where '123' = <function TestMoreErrors.test_startswith_nested.<locals>.f at 0xdeadbeef>()
E + and '456' = <function TestMoreErrors.test_startswith_nested.<locals>.g at 0xdeadbeef>()
failure_demo.py:195: AssertionError
failure_demo.py:196: AssertionError
_______ TestMoreErrors.test_global_func ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
def test_global_func(self):
> assert isinstance(globf(42), float)
E assert isinstance(43, float)
E + where 43 = globf(42)
E assert False
E + where False = isinstance(43, float)
E + where 43 = globf(42)
failure_demo.py:198: AssertionError
failure_demo.py:199: AssertionError
_______ TestMoreErrors.test_instance ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -522,7 +525,7 @@ get on the terminal - we are working on that):
E assert 42 != 42
E + where 42 = <failure_demo.TestMoreErrors object at 0xdeadbeef>.x
failure_demo.py:202: AssertionError
failure_demo.py:203: AssertionError
_______ TestMoreErrors.test_compare ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -532,7 +535,7 @@ get on the terminal - we are working on that):
E assert 11 < 5
E + where 11 = globf(10)
failure_demo.py:205: AssertionError
failure_demo.py:206: AssertionError
_______ TestMoreErrors.test_try_finally ________
self = <failure_demo.TestMoreErrors object at 0xdeadbeef>
@@ -543,7 +546,7 @@ get on the terminal - we are working on that):
> assert x == 0
E assert 1 == 0
failure_demo.py:210: AssertionError
failure_demo.py:211: AssertionError
_______ TestCustomAssertMsg.test_single_line ________
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
@@ -557,7 +560,7 @@ get on the terminal - we are working on that):
E assert 1 == 2
E + where 1 = <class 'failure_demo.TestCustomAssertMsg.test_single_line.<locals>.A'>.a
failure_demo.py:221: AssertionError
failure_demo.py:222: AssertionError
_______ TestCustomAssertMsg.test_multiline ________
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
@@ -574,7 +577,7 @@ get on the terminal - we are working on that):
E assert 1 == 2
E + where 1 = <class 'failure_demo.TestCustomAssertMsg.test_multiline.<locals>.A'>.a
failure_demo.py:227: AssertionError
failure_demo.py:228: AssertionError
_______ TestCustomAssertMsg.test_custom_repr ________
self = <failure_demo.TestCustomAssertMsg object at 0xdeadbeef>
@@ -594,5 +597,5 @@ get on the terminal - we are working on that):
E assert 1 == 2
E + where 1 = This is JSON\n{\n 'foo': 'bar'\n}.a
failure_demo.py:237: AssertionError
failure_demo.py:238: AssertionError
======= 42 failed in 0.12 seconds ========

View File

@@ -37,7 +37,7 @@ provide the ``cmdopt`` through a :ref:`fixture function <fixture function>`::
Let's run this without supplying our new option::
$ py.test -q test_sample.py
$ pytest -q test_sample.py
F
======= FAILURES ========
_______ test_answer ________
@@ -59,7 +59,7 @@ Let's run this without supplying our new option::
And now with supplying a command line option::
$ py.test -q --cmdopt=type2
$ pytest -q --cmdopt=type2
F
======= FAILURES ========
_______ test_answer ________
@@ -106,9 +106,9 @@ you will now always perform test runs using a number
of subprocesses close to your CPU. Running in an empty
directory with the above conftest.py::
$ py.test
$ pytest
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 0 items
@@ -154,9 +154,9 @@ We can now write a test module like this::
and when running it will see a skipped "slow" test::
$ py.test -rs # "-rs" means report details on the little 's'
$ pytest -rs # "-rs" means report details on the little 's'
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items
@@ -168,9 +168,9 @@ and when running it will see a skipped "slow" test::
Or run it including the ``slow`` marked test::
$ py.test --runslow
$ pytest --runslow
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items
@@ -201,10 +201,10 @@ Example::
The ``__tracebackhide__`` setting influences ``pytest`` showing
of tracebacks: the ``checkconfig`` function will not be shown
unless the ``--fulltrace`` command line option is specified.
unless the ``--full-trace`` command line option is specified.
Let's run our little function::
$ py.test -q test_checkconfig.py
$ pytest -q test_checkconfig.py
F
======= FAILURES ========
_______ test_something ________
@@ -216,6 +216,28 @@ Let's run our little function::
test_checkconfig.py:8: Failed
1 failed in 0.12 seconds
If you only want to hide certain exceptions, you can set ``__tracebackhide__``
to a callable which gets the ``ExceptionInfo`` object. You can for example use
this to make sure unexpected exception types aren't hidden::
import operator
import pytest
class ConfigException(Exception):
pass
def checkconfig(x):
__tracebackhide__ = operator.methodcaller('errisinstance', ConfigException)
if not hasattr(x, "config"):
raise ConfigException("not configured: %s" %(x,))
def test_something():
checkconfig(42)
This will avoid hiding the exception traceback on unrelated exceptions (i.e.
bugs in assertion helpers).
Detect if running from within a pytest run
--------------------------------------------------------------
@@ -260,9 +282,9 @@ It's easy to present extra information in a ``pytest`` run::
which will add the string to the test header accordingly::
$ py.test
$ pytest
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
project deps: mylib-1.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 0 items
@@ -273,20 +295,20 @@ which will add the string to the test header accordingly::
You can also return a list of strings which will be considered as several
lines of information. You can of course also make the amount of reporting
information on e.g. the value of ``config.option.verbose`` so that
information on e.g. the value of ``config.getoption('verbose')`` so that
you present more information appropriately::
# content of conftest.py
def pytest_report_header(config):
if config.option.verbose > 0:
if config.getoption('verbose') > 0:
return ["info1: did you know that ...", "did you?"]
which will add info only when run with "--v"::
$ py.test -v
$ pytest -v
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
info1: did you know that ...
did you?
@@ -297,9 +319,9 @@ which will add info only when run with "--v"::
and nothing when run plainly::
$ py.test
$ pytest
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 0 items
@@ -330,9 +352,9 @@ out which tests are the slowest. Let's make an artificial test suite::
Now we can profile which test functions execute the slowest::
$ py.test --durations=3
$ pytest --durations=3
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 3 items
@@ -341,7 +363,7 @@ Now we can profile which test functions execute the slowest::
======= slowest 3 test durations ========
0.20s call test_some_are_slow.py::test_funcslow2
0.10s call test_some_are_slow.py::test_funcslow1
0.00s setup test_some_are_slow.py::test_funcslow2
0.00s setup test_some_are_slow.py::test_funcfast
======= 3 passed in 0.12 seconds ========
incremental testing - test steps
@@ -392,13 +414,16 @@ tests in a class. Here is a test module example::
If we run this::
$ py.test -rx
$ pytest -rx
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 4 items
test_step.py .Fx.
======= short test summary info ========
XFAIL test_step.py::TestUserHandling::()::test_deletion
reason: previous test failed (test_modification)
======= FAILURES ========
_______ TestUserHandling.test_modification ________
@@ -410,9 +435,6 @@ If we run this::
E assert 0
test_step.py:9: AssertionError
======= short test summary info ========
XFAIL test_step.py::TestUserHandling::()::test_deletion
reason: previous test failed (test_modification)
======= 1 failed, 2 passed, 1 xfailed in 0.12 seconds ========
We'll see that ``test_deletion`` was not executed because ``test_modification``
@@ -463,9 +485,9 @@ the ``db`` fixture::
We can run this::
$ py.test
$ pytest
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 7 items
@@ -478,9 +500,9 @@ We can run this::
_______ ERROR at setup of test_root ________
file $REGENDOC_TMPDIR/b/test_error.py, line 1
def test_root(db): # no db here, will error out
fixture 'db' not found
available fixtures: capsys, capfd, pytestconfig, tmpdir_factory, monkeypatch, record_xml_property, cache, tmpdir, recwarn
use 'py.test --fixtures [testpath]' for help on them.
E fixture 'db' not found
> available fixtures: cache, capfd, capsys, doctest_namespace, monkeypatch, pytestconfig, record_xml_property, recwarn, tmpdir, tmpdir_factory
> use 'pytest --fixtures [testpath]' for help on them.
$REGENDOC_TMPDIR/b/test_error.py:1
======= FAILURES ========
@@ -567,9 +589,9 @@ if you then have failing tests::
and run them::
$ py.test test_module.py
$ pytest test_module.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items
@@ -626,15 +648,14 @@ here is a little example implemented via a local plugin::
@pytest.fixture
def something(request):
def fin():
# request.node is an "item" because we use the default
# "function" scope
if request.node.rep_setup.failed:
print ("setting up a test failed!", request.node.nodeid)
elif request.node.rep_setup.passed:
if request.node.rep_call.failed:
print ("executing test failed", request.node.nodeid)
request.addfinalizer(fin)
yield
# request.node is an "item" because we use the default
# "function" scope
if request.node.rep_setup.failed:
print ("setting up a test failed!", request.node.nodeid)
elif request.node.rep_setup.passed:
if request.node.rep_call.failed:
print ("executing test failed", request.node.nodeid)
if you then have failing tests::
@@ -658,9 +679,9 @@ if you then have failing tests::
and run it::
$ py.test -s test_module.py
$ pytest -s test_module.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 3 items
@@ -699,40 +720,29 @@ and run it::
You'll see that the fixture finalizers could use the precise reporting
information.
Integrating pytest runner and cx_freeze
-----------------------------------------------------------
Freezing pytest
---------------
If you freeze your application using a tool like
`cx_freeze <http://cx-freeze.readthedocs.org>`_ in order to distribute it
to your end-users, it is a good idea to also package your test runner and run
your tests using the frozen application.
`PyInstaller <https://pyinstaller.readthedocs.io>`_
in order to distribute it to your end-users, it is a good idea to also package
your test runner and run your tests using the frozen application. This way packaging
errors such as dependencies not being included into the executable can be detected early
while also allowing you to send test files to users so they can run them in their
machines, which can be useful to obtain more information about a hard to reproduce bug.
This way packaging errors such as dependencies not being
included into the executable can be detected early while also allowing you to
send test files to users so they can run them in their machines, which can be
invaluable to obtain more information about a hard to reproduce bug.
Fortunately recent ``PyInstaller`` releases already have a custom hook
for pytest, but if you are using another tool to freeze executables
such as ``cx_freeze`` or ``py2exe``, you can use ``pytest.freeze_includes()``
to obtain the full list of internal pytest modules. How to configure the tools
to find the internal modules varies from tool to tool, however.
Unfortunately ``cx_freeze`` can't discover them
automatically because of ``pytest``'s use of dynamic module loading, so you
must declare them explicitly by using ``pytest.freeze_includes()``::
Instead of freezing the pytest runner as a separate executable, you can make
your frozen program work as the pytest runner by some clever
argument handling during program startup. This allows you to
have a single executable, which is usually more convenient.
# contents of setup.py
from cx_Freeze import setup, Executable
import pytest
setup(
name="app_main",
executables=[Executable("app_main.py")],
options={"build_exe":
{
'includes': pytest.freeze_includes()}
},
# ... other options
)
If you don't want to ship a different executable just in order to run your tests,
you can make your program check for a certain flag and pass control
over to ``pytest`` instead. For example::
.. code-block:: python
# contents of app_main.py
import sys
@@ -745,7 +755,8 @@ over to ``pytest`` instead. For example::
# by your argument-parsing library of choice as usual
...
This makes it convenient to execute your tests from within your frozen
application, using standard ``py.test`` command-line options::
./app_main --pytest --verbose --tb=long --junit-xml=results.xml test-suite/
This allows you to execute tests using the frozen
application with standard ``pytest`` command-line options::
./app_main --pytest --verbose --tb=long --junitxml=results.xml test-suite/

View File

@@ -59,7 +59,7 @@ will be called ahead of running any tests::
If you run this without output capturing::
$ py.test -q -s test_module.py
$ pytest -q -s test_module.py
callattr_ahead_of_alltests called
callme called!
callme other called

View File

@@ -66,9 +66,8 @@ This completely avoids previous issues of confusing assertion-reporting.
It also means, that you can use Python's ``-O`` optimization without losing
assertions in test modules.
``pytest`` contains a second, mostly obsolete, assert debugging technique,
invoked via ``--assert=reinterpret``, activated by default on
Python-2.5: When an ``assert`` statement fails, ``pytest`` re-interprets
``pytest`` contains a second, mostly obsolete, assert debugging technique
invoked via ``--assert=reinterpret``: When an ``assert`` statement fails, ``pytest`` re-interprets
the expression part to show intermediate values. This technique suffers
from a caveat that the rewriting does not: If your expression has side
effects (better to avoid them anyway!) the intermediate values may not
@@ -82,18 +81,17 @@ You can also turn off all assertion interaction using the
.. _`py/__init__.py`: http://bitbucket.org/hpk42/py-trunk/src/trunk/py/__init__.py
Why a ``py.test`` instead of a ``pytest`` command?
++++++++++++++++++++++++++++++++++++++++++++++++++
Why can I use both ``pytest`` and ``py.test`` commands?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Some of the reasons are historic, others are practical. ``pytest``
used to be part of the ``py`` package which provided several developer
utilities, all starting with ``py.<TAB>``, thus providing nice
TAB-completion. If
you install ``pip install pycmd`` you get these tools from a separate
package. These days the command line tool could be called ``pytest``
but since many people have gotten used to the old name and there
is another tool named "pytest" we just decided to stick with
``py.test`` for now.
pytest used to be part of the py package, which provided several developer
utilities, all starting with ``py.<TAB>``, thus providing nice TAB-completion.
If you install ``pip install pycmd`` you get these tools from a separate
package. Once ``pytest`` became a separate package, the ``py.test`` name was
retained due to avoid a naming conflict with another tool. This conflict was
eventually resolved, and the ``pytest`` command was therefore introduced. In
future versions of pytest, we may deprecate and later remove the ``py.test``
command to avoid perpetuating the confusion.
pytest fixtures, parametrized tests
-------------------------------------------------------
@@ -121,7 +119,7 @@ in a managed class/module/function scope.
.. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration
Can I yield multiple values from a fixture function function?
Can I yield multiple values from a fixture function?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
There are two conceptual reasons why yielding from a factory function

View File

@@ -34,11 +34,6 @@ both styles, moving incrementally from classic to new style, as you
prefer. You can also start out from existing :ref:`unittest.TestCase
style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
.. note::
pytest-2.4 introduced an additional experimental
:ref:`yield fixture mechanism <yieldfixture>` for easier context manager
integration and more linear writing of teardown code.
.. _`funcargs`:
.. _`funcarg mechanism`:
@@ -73,9 +68,9 @@ Here, the ``test_ehlo`` needs the ``smtp`` fixture value. pytest
will discover and call the :py:func:`@pytest.fixture <_pytest.python.fixture>`
marked ``smtp`` fixture function. Running the test looks like this::
$ py.test test_smtpsimple.py
$ pytest test_smtpsimple.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
@@ -118,7 +113,7 @@ with a list of available function arguments.
You can always issue::
py.test --fixtures test_simplefactory.py
pytest --fixtures test_simplefactory.py
to see available fixtures.
@@ -191,9 +186,9 @@ function (in or below the directory where ``conftest.py`` is located)::
We deliberately insert failing ``assert 0`` statements in order to
inspect what is going on and can now run the tests::
$ py.test test_module.py
$ pytest test_module.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 2 items
@@ -247,9 +242,8 @@ Fixture finalization / executing teardown code
-------------------------------------------------------------
pytest supports execution of fixture specific finalization code
when the fixture goes out of scope. By accepting a ``request`` object
into your fixture function you can call its ``request.addfinalizer`` one
or multiple times::
when the fixture goes out of scope. By using a ``yield`` statement instead of ``return``, all
the code after the *yield* statement serves as the teardown code.::
# content of conftest.py
@@ -259,18 +253,16 @@ or multiple times::
@pytest.fixture(scope="module")
def smtp(request):
smtp = smtplib.SMTP("smtp.gmail.com")
def fin():
print ("teardown smtp")
smtp.close()
request.addfinalizer(fin)
return smtp # provide the fixture value
yield smtp # provide the fixture value
print("teardown smtp")
smtp.close()
The ``fin`` function will execute when the last test using
the fixture in the module has finished execution.
The ``print`` and ``smtp.close()`` statements will execute when the last test using
the fixture in the module has finished execution, regardless of the exception status of the tests.
Let's execute it::
$ py.test -s -q --tb=no
$ pytest -s -q --tb=no
FFteardown smtp
2 failed in 0.12 seconds
@@ -282,6 +274,55 @@ occur around each single test. In either case the test
module itself does not need to change or know about these details
of fixture setup.
Note that we can also seamlessly use the ``yield`` syntax with ``with`` statements::
# content of test_yield2.py
import pytest
@pytest.fixture
def passwd():
with open("/etc/passwd") as f:
yield f.readlines()
def test_has_lines(passwd):
assert len(passwd) >= 1
The file ``f`` will be closed after the test finished execution
because the Python ``file`` object supports finalization when
the ``with`` statement ends.
.. note::
Prior to version 2.10, in order to use a ``yield`` statement to execute teardown code one
had to mark a fixture using the ``yield_fixture`` marker. From 2.10 onward, normal
fixtures can use ``yield`` directly so the ``yield_fixture`` decorator is no longer needed
and considered deprecated.
.. note::
As historical note, another way to write teardown code is
by accepting a ``request`` object into your fixture function and can call its
``request.addfinalizer`` one or multiple times::
# content of conftest.py
import smtplib
import pytest
@pytest.fixture(scope="module")
def smtp(request):
smtp = smtplib.SMTP("smtp.gmail.com")
def fin():
print ("teardown smtp")
smtp.close()
request.addfinalizer(fin)
return smtp # provide the fixture value
The ``fin`` function will execute when the last test using
the fixture in the module has finished execution.
This method is still fully supported, but ``yield`` is recommended from 2.10 onward because
it is considered simpler and better describes the natural code flow.
.. _`request-context`:
@@ -301,21 +342,18 @@ read an optional server URL from the test module which uses our fixture::
def smtp(request):
server = getattr(request.module, "smtpserver", "smtp.gmail.com")
smtp = smtplib.SMTP(server)
def fin():
print ("finalizing %s (%s)" % (smtp, server))
smtp.close()
request.addfinalizer(fin)
return smtp
yield smtp
print ("finalizing %s (%s)" % (smtp, server))
smtp.close()
We use the ``request.module`` attribute to optionally obtain an
``smtpserver`` attribute from the test module. If we just execute
again, nothing much has changed::
$ py.test -s -q --tb=no
$ pytest -s -q --tb=no
FFfinalizing <smtplib.SMTP object at 0xdeadbeef> (smtp.gmail.com)
2 failed in 0.12 seconds
.
2 failed, 1 passed in 0.12 seconds
Let's quickly create another test module that actually sets the
server URL in its module namespace::
@@ -329,7 +367,7 @@ server URL in its module namespace::
Running it::
$ py.test -qq --tb=short test_anothersmtp.py
$ pytest -qq --tb=short test_anothersmtp.py
F
======= FAILURES ========
_______ test_showhelo ________
@@ -343,7 +381,7 @@ from the module namespace.
.. _`fixture-parametrize`:
Parametrizing a fixture
Parametrizing fixtures
-----------------------------------------------------------------
Fixture functions can be parametrized in which case they will be called
@@ -366,11 +404,9 @@ through the special :py:class:`request <FixtureRequest>` object::
params=["smtp.gmail.com", "mail.python.org"])
def smtp(request):
smtp = smtplib.SMTP(request.param)
def fin():
print ("finalizing %s" % smtp)
smtp.close()
request.addfinalizer(fin)
return smtp
yield smtp
print ("finalizing %s" % smtp)
smtp.close()
The main change is the declaration of ``params`` with
:py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values
@@ -378,7 +414,7 @@ for each of which the fixture function will execute and can access
a value via ``request.param``. No test function code needs to change.
So let's just do another run::
$ py.test -q test_module.py
$ pytest -q test_module.py
FFFF
======= FAILURES ========
_______ test_ehlo[smtp.gmail.com] ________
@@ -478,11 +514,11 @@ return ``None`` then pytest's auto-generated ID will be used.
Running the above tests results in the following test IDs being used::
$ py.test --collect-only
$ pytest --collect-only
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 10 items
collected 11 items
<Module 'test_anothersmtp.py'>
<Function 'test_showhelo[smtp.gmail.com]'>
<Function 'test_showhelo[mail.python.org]'>
@@ -496,6 +532,8 @@ Running the above tests results in the following test IDs being used::
<Function 'test_noop[smtp.gmail.com]'>
<Function 'test_ehlo[mail.python.org]'>
<Function 'test_noop[mail.python.org]'>
<Module 'test_yield2.py'>
<Function 'test_has_lines'>
======= no tests ran in 0.12 seconds ========
@@ -529,9 +567,9 @@ and instantiate an object ``app`` where we stick the already defined
Here we declare an ``app`` fixture which receives the previously defined
``smtp`` fixture and instantiates an ``App`` object with it. Let's run it::
$ py.test -v test_appsetup.py
$ pytest -v test_appsetup.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 2 items
@@ -567,7 +605,7 @@ first execute with one instance and then finalizers are called
before the next fixture instance is created. Among other things,
this eases testing of applications which create and use global state.
The following example uses two parametrized funcargs, one of which is
The following example uses two parametrized fixture, one of which is
scoped on a per-module basis, and all the functions perform ``print`` calls
to show the setup/teardown flow::
@@ -577,55 +615,80 @@ to show the setup/teardown flow::
@pytest.fixture(scope="module", params=["mod1", "mod2"])
def modarg(request):
param = request.param
print ("create", param)
def fin():
print ("fin %s" % param)
return param
print (" SETUP modarg %s" % param)
yield param
print (" TEARDOWN modarg %s" % param)
@pytest.fixture(scope="function", params=[1,2])
def otherarg(request):
return request.param
param = request.param
print (" SETUP otherarg %s" % param)
yield param
print (" TEARDOWN otherarg %s" % param)
def test_0(otherarg):
print (" test0", otherarg)
print (" RUN test0 with otherarg %s" % otherarg)
def test_1(modarg):
print (" test1", modarg)
print (" RUN test1 with modarg %s" % modarg)
def test_2(otherarg, modarg):
print (" test2", otherarg, modarg)
print (" RUN test2 with otherarg %s and modarg %s" % (otherarg, modarg))
Let's run the tests in verbose mode and with looking at the print-output::
$ py.test -v -s test_module.py
$ pytest -v -s test_module.py
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5
cachedir: .cache
rootdir: $REGENDOC_TMPDIR, inifile:
collecting ... collected 8 items
test_module.py::test_0[1] test0 1
test_module.py::test_0[1] SETUP otherarg 1
RUN test0 with otherarg 1
PASSED TEARDOWN otherarg 1
test_module.py::test_0[2] SETUP otherarg 2
RUN test0 with otherarg 2
PASSED TEARDOWN otherarg 2
test_module.py::test_1[mod1] SETUP modarg mod1
RUN test1 with modarg mod1
PASSED
test_module.py::test_0[2] test0 2
PASSED
test_module.py::test_1[mod1] create mod1
test1 mod1
PASSED
test_module.py::test_2[1-mod1] test2 1 mod1
PASSED
test_module.py::test_2[2-mod1] test2 2 mod1
PASSED
test_module.py::test_1[mod2] create mod2
test1 mod2
PASSED
test_module.py::test_2[1-mod2] test2 1 mod2
PASSED
test_module.py::test_2[2-mod2] test2 2 mod2
test_module.py::test_2[1-mod1] SETUP otherarg 1
RUN test2 with otherarg 1 and modarg mod1
PASSED TEARDOWN otherarg 1
test_module.py::test_2[2-mod1] SETUP otherarg 2
RUN test2 with otherarg 2 and modarg mod1
PASSED TEARDOWN otherarg 2
test_module.py::test_1[mod2] TEARDOWN modarg mod1
SETUP modarg mod2
RUN test1 with modarg mod2
PASSED
test_module.py::test_2[1-mod2] SETUP otherarg 1
RUN test2 with otherarg 1 and modarg mod2
PASSED TEARDOWN otherarg 1
test_module.py::test_2[2-mod2] SETUP otherarg 2
RUN test2 with otherarg 2 and modarg mod2
PASSED TEARDOWN otherarg 2
TEARDOWN modarg mod2
======= 8 passed in 0.12 seconds ========
You can see that the parametrized module-scoped ``modarg`` resource caused
an ordering of test execution that lead to the fewest possible "active" resources. The finalizer for the ``mod1`` parametrized resource was executed
before the ``mod2`` resource was setup.
You can see that the parametrized module-scoped ``modarg`` resource caused an
ordering of test execution that lead to the fewest possible "active" resources.
The finalizer for the ``mod1`` parametrized resource was executed before the
``mod2`` resource was setup.
In particular notice that test_0 is completely independent and finishes first.
Then test_1 is executed with ``mod1``, then test_2 with ``mod1``, then test_1
with ``mod2`` and finally test_2 with ``mod2``.
The ``otherarg`` parametrized resource (having function scope) was set up before
and teared down after every test that used it.
.. _`usefixtures`:
@@ -675,7 +738,7 @@ will be required for the execution of each test method, just as if
you specified a "cleandir" function argument to each of them. Let's run it
to verify our fixture is activated and the tests pass::
$ py.test -q
$ pytest -q
..
2 passed in 0.12 seconds
@@ -740,7 +803,8 @@ self-contained implementation of this idea::
@pytest.fixture(autouse=True)
def transact(self, request, db):
db.begin(request.function.__name__)
request.addfinalizer(db.rollback)
yield
db.rollback()
def test_method1(self, db):
assert db.intransaction == ["test_method1"]
@@ -755,12 +819,16 @@ class-level ``usefixtures`` decorator.
If we run it, we get two passing tests::
$ py.test -q
$ pytest -q
..
2 passed in 0.12 seconds
Here is how autouse fixtures work in other scopes:
- autouse fixtures obey the ``scope=`` keyword-argument: if an autouse fixture
has ``scope='session'`` it will only be run once, no matter where it is
defined. ``scope='class'`` means it will be run once per class, etc.
- if an autouse fixture is defined in a test module, all its test
functions automatically use it.
@@ -780,10 +848,11 @@ active. The canonical way to do that is to put the transact definition
into a conftest.py file **without** using ``autouse``::
# content of conftest.py
@pytest.fixture()
@pytest.fixture
def transact(self, request, db):
db.begin()
request.addfinalizer(db.rollback)
yield
db.rollback()
and then e.g. have a TestClass using it by declaring the need::
@@ -796,6 +865,7 @@ All test methods in this TestClass will use the transaction fixture while
other test classes or functions in the module will not use it unless
they also add a ``transact`` reference.
Shifting (visibility of) fixture functions
----------------------------------------------------

View File

@@ -172,17 +172,17 @@ to do this with parametrization as ``pytest_runtest_setup()`` is called
during test execution and parametrization happens at collection time.
It follows that pytest_configure/session/runtest_setup are often not
appropriate for implementing common fixture needs. Therefore,
appropriate for implementing common fixture needs. Therefore,
pytest-2.3 introduces :ref:`autouse fixtures` which fully
integrate with the generic :ref:`fixture mechanism <fixture>`
integrate with the generic :ref:`fixture mechanism <fixture>`
and obsolete many prior uses of pytest hooks.
funcargs/fixture discovery now happens at collection time
---------------------------------------------------------------------
pytest-2.3 takes care to discover fixture/funcarg factories
at collection time. This is more efficient especially for large test suites.
Moreover, a call to "py.test --collect-only" should be able to in the future
Since pytest-2.3, discovery of fixture/funcarg factories are taken care of
at collection time. This is more efficient especially for large test suites.
Moreover, a call to "pytest --collect-only" should be able to in the future
show a lot of setup-information and thus presents a nice method to get an
overview of fixture management in your project.

View File

@@ -32,7 +32,7 @@ class Writer:
def pytest_funcarg__a(request):
with Writer("request") as writer:
writer.docmethod(request.getfuncargvalue)
writer.docmethod(request.getfixturevalue)
writer.docmethod(request.cached_setup)
writer.docmethod(request.addfinalizer)
writer.docmethod(request.applymarker)

View File

@@ -11,7 +11,7 @@ Installation and Getting Started
`colorama (Windows) <http://pypi.python.org/pypi/colorama>`_,
`argparse (py26) <http://pypi.python.org/pypi/argparse>`_.
**documentation as PDF**: `download latest <http://pytest.org/latest/pytest.pdf>`_
**documentation as PDF**: `download latest <https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
.. _`getstarted`:
.. _installation:
@@ -26,8 +26,8 @@ Installation options::
To check your installation has installed the correct version::
$ py.test --version
This is pytest version 2.8.5, imported from $PYTHON_PREFIX/lib/python3.4/site-packages/pytest.py
$ pytest --version
This is pytest version 3.0.0, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py
If you get an error checkout :ref:`installation issues`.
@@ -47,9 +47,9 @@ Let's create a first test file with a simple test function::
That's it. You can execute the test function now::
$ py.test
$ pytest
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 1 items
@@ -102,7 +102,7 @@ use the ``raises`` helper::
Running it with, this time in "quiet" reporting mode::
$ py.test -q test_sysexit.py
$ pytest -q test_sysexit.py
.
1 passed in 0.12 seconds
@@ -127,7 +127,7 @@ The two tests are found because of the standard :ref:`test discovery`.
There is no need to subclass anything. We can simply
run the module by passing its filename::
$ py.test -q test_class.py
$ pytest -q test_class.py
.F
======= FAILURES ========
_______ TestClass.test_two ________
@@ -137,7 +137,8 @@ run the module by passing its filename::
def test_two(self):
x = "hello"
> assert hasattr(x, 'check')
E assert hasattr('hello', 'check')
E assert False
E + where False = hasattr('hello', 'check')
test_class.py:8: AssertionError
1 failed, 1 passed in 0.12 seconds
@@ -163,7 +164,7 @@ We list the name ``tmpdir`` in the test function signature and
``pytest`` will lookup and call a fixture factory to create the resource
before performing the test function call. Let's just run it::
$ py.test -q test_tmpdir.py
$ pytest -q test_tmpdir.py
F
======= FAILURES ========
_______ test_needsfiles ________
@@ -185,7 +186,7 @@ was created. More info at :ref:`tmpdir handling`.
You can find out what kind of builtin :ref:`fixtures` exist by typing::
py.test --fixtures # shows builtin and custom fixtures
pytest --fixtures # shows builtin and custom fixtures
Where to go next
-------------------------------------
@@ -193,45 +194,9 @@ Where to go next
Here are a few suggestions where to go next:
* :ref:`cmdline` for command line invocation examples
* :ref:`good practises <goodpractises>` for virtualenv, test layout, genscript support
* :ref:`good practices <goodpractices>` for virtualenv, test layout
* :ref:`fixtures` for providing a functional baseline to your tests
* :ref:`apiref` for documentation and examples on using ``pytest``
* :ref:`plugins` managing and writing plugins
.. _`installation issues`:
Known Installation issues
------------------------------
easy_install or pip not found?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.. _`install pip`: http://www.pip-installer.org/en/latest/index.html
`Install pip`_ for a state of the art python package installer.
Install `setuptools`_ to get ``easy_install`` which allows to install
``.egg`` binary format packages in addition to source-based ones.
py.test not found on Windows despite installation?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.. _`Python for Windows`: http://www.imladris.com/Scripts/PythonForWindows.html
- **Windows**: If "easy_install" or "py.test" are not found
you need to add the Python script path to your ``PATH``, see here:
`Python for Windows`_. You may alternatively use an `ActivePython install`_
which does this for you automatically.
.. _`ActivePython install`: http://www.activestate.com/activepython/downloads
.. _`Jython does not create command line launchers`: http://bugs.jython.org/issue1491
- **Jython2.5.1 on Windows XP**: `Jython does not create command line launchers`_
so ``py.test`` will not work correctly. You may install py.test on
CPython and type ``py.test --genscript=mytest`` and then use
``jython mytest`` to run your tests with Jython using ``pytest``.
:ref:`examples` for more complex examples
.. include:: links.inc

View File

@@ -1,5 +1,5 @@
.. highlightlang:: python
.. _`goodpractises`:
.. _`goodpractices`:
Good Integration Practices
=================================================
@@ -72,17 +72,17 @@ Important notes relating to both schemes:
- With inlined tests you might put ``__init__.py`` into test
directories and make them installable as part of your application.
Using the ``py.test --pyargs mypkg`` invocation pytest will
Using the ``pytest --pyargs mypkg`` invocation pytest will
discover where mypkg is installed and collect tests from there.
With the "external" test you can still distribute tests but they
will not be installed or become importable.
Typically you can run tests by pointing to test directories or modules::
py.test tests/test_app.py # for external test dirs
py.test mypkg/test/test_app.py # for inlined test dirs
py.test mypkg # run tests in all below test directories
py.test # run all tests below current dir
pytest tests/test_app.py # for external test dirs
pytest mypkg/test/test_app.py # for inlined test dirs
pytest mypkg # run tests in all below test directories
pytest # run all tests below current dir
...
Because of the above ``editable install`` mode you can change your
@@ -153,7 +153,9 @@ against your source code checkout, helping to detect packaging
glitches.
Continuous integration services such as Jenkins_ can make use of the
``--junitxml=PATH`` option to create a JUnitXML file and generate reports.
``--junitxml=PATH`` option to create a JUnitXML file and generate reports (e.g.
by publishing the results in a nice format with the `Jenkins xUnit Plugin
<https://wiki.jenkins-ci.org/display/JENKINS/xUnit+Plugin>`_).
Integrating with setuptools / ``python setup.py test`` / ``pytest-runner``
@@ -191,9 +193,27 @@ If you now type::
this will execute your tests using ``pytest-runner``. As this is a
standalone version of ``pytest`` no prior installation whatsoever is
required for calling the test command. You can also pass additional
arguments to py.test such as your test directory or other
arguments to pytest such as your test directory or other
options using ``--addopts``.
You can also specify other pytest-ini options in your ``setup.cfg`` file
by putting them into a ``[tool:pytest]`` section:
.. code-block:: ini
[tool:pytest]
addopts = --verbose
python_files = testing/*/*.py
.. note::
Prior to 3.0, the supported section name was ``[pytest]``. Due to how
this may collide with some distutils commands, the recommended
section name for ``setup.cfg`` files is now ``[tool:pytest]``.
Note that for ``pytest.ini`` and ``tox.ini`` files the section
name is ``[pytest]``.
Manual Integration
^^^^^^^^^^^^^^^^^^
@@ -209,7 +229,7 @@ your own setuptools Test command for invoking pytest.
class PyTest(TestCommand):
user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]
user_options = [('pytest-args=', 'a', "Arguments to pass to pytest")]
def initialize_options(self):
TestCommand.initialize_options(self)
@@ -238,41 +258,7 @@ using the ``--pytest-args`` or ``-a`` command-line option. For example::
python setup.py test -a "--durations=5"
is equivalent to running ``py.test --durations=5``.
.. _standalone:
.. _`genscript method`:
(deprecated) Create a pytest standalone script
-----------------------------------------------
.. deprecated:: 2.8
.. note::
``genscript`` has been deprecated because:
* It cannot support plugins, rendering its usefulness extremely limited;
* Tooling has become much better since ``genscript`` was introduced;
* It is possible to build a zipped ``pytest`` application without the
shortcomings above.
There's no planned version in which this command will be removed
at the moment of this writing, but its use is discouraged for new
applications.
If you are a maintainer or application developer and want people
who don't deal with python much to easily run tests you may generate
a standalone ``pytest`` script::
py.test --genscript=runtests.py
This generates a ``runtests.py`` script which is a fully functional basic
``pytest`` script, running unchanged under Python2 and Python3.
You can tell people to download the script and then e.g. run it like this::
python runtests.py
is equivalent to running ``pytest --durations=5``.
.. include:: links.inc

BIN
doc/en/img/freiburg2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@@ -6,10 +6,11 @@ pytest: helps you write better programs
**a mature full-featured Python testing tool**
- runs on Posix/Windows, Python 2.6-3.5, PyPy and (possibly still) Jython-2.5.1
- runs on Posix/Windows, Python 2.6, 2.7 and 3.3-3.5, PyPy and (possibly still) Jython-2.5.1
- free and open source software, distributed under the terms of the :ref:`MIT license <license>`
- **well tested** with more than a thousand tests against itself
- **strict backward compatibility policy** for safe pytest upgrades
- :ref:`comprehensive online <toc>` and `PDF documentation <pytest.pdf>`_
- :ref:`comprehensive online <toc>` and `PDF documentation <https://media.readthedocs.org/pdf/pytest/latest/pytest.pdf>`_
- many :ref:`third party plugins <extplugins>` and :ref:`builtin helpers <pytest helpers>`,
- used in :ref:`many small and large projects and organisations <projects>`
- comes with many :ref:`tested examples <examples>`
@@ -40,7 +41,7 @@ pytest: helps you write better programs
- multi-paradigm: pytest can run ``nose``, ``unittest`` and
``doctest`` style test suites, including running testcases made for
Django and trial
- supports :ref:`good integration practises <goodpractises>`
- supports :ref:`good integration practices <goodpractices>`
- supports extended :ref:`xUnit style setup <xunitsetup>`
- supports domain-specific :ref:`non-python tests`
- supports generating `test coverage reports
@@ -56,5 +57,3 @@ pytest: helps you write better programs
.. _`easy`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html

32
doc/en/license.rst Normal file
View File

@@ -0,0 +1,32 @@
.. _license:
License
-------
Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
::
The MIT License (MIT)
Copyright (c) 2004-2016 Holger Krekel and others
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE

View File

@@ -6,7 +6,7 @@
.. _`pytest_nose`: plugin/nose.html
.. _`reStructured Text`: http://docutils.sourceforge.net
.. _`Python debugger`: http://docs.python.org/lib/module-pdb.html
.. _nose: https://nose.readthedocs.org/en/latest/
.. _nose: https://nose.readthedocs.io/en/latest/
.. _pytest: http://pypi.python.org/pypi/pytest
.. _mercurial: http://mercurial.selenic.com/wiki/
.. _`setuptools`: http://pypi.python.org/pypi/setuptools
@@ -18,4 +18,4 @@
.. _hudson: http://hudson-ci.org/
.. _jenkins: http://jenkins-ci.org/
.. _tox: http://testrun.org/tox
.. _pylib: http://py.readthedocs.org/en/latest/
.. _pylib: https://py.readthedocs.io/en/latest/

View File

@@ -6,7 +6,7 @@ Monkeypatching/mocking modules and environments
Sometimes tests need to invoke functionality which depends
on global settings or which invokes code which cannot be easily
tested such as network access. The ``monkeypatch`` function argument
tested such as network access. The ``monkeypatch`` fixture
helps you to safely set/delete an attribute, dictionary item or
environment variable or to modify ``sys.path`` for importing.
See the `monkeypatch blog post`_ for some introduction material
@@ -14,6 +14,7 @@ and a discussion of its motivation.
.. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/
Simple example: monkeypatching functions
---------------------------------------------------
@@ -53,27 +54,31 @@ This autouse fixture will be executed for each test function and it
will delete the method ``request.session.Session.request``
so that any attempts within tests to create http requests will fail.
example: setting an attribute on some class
------------------------------------------------------
example: setting an environment variable for the test session
-------------------------------------------------------------
If you need to patch out ``os.getcwd()`` to return an artificial
value::
If you would like for an environment variable to be
configured for the entire test session, you can add this to your
top-level ``conftest.py`` file:
def test_some_interaction(monkeypatch):
monkeypatch.setattr("os.getcwd", lambda: "/")
.. code-block:: python
which is equivalent to the long form::
# content of conftest.py
@pytest.fixture(scope='session', autouse=True)
def enable_debugging(monkeypatch):
monkeypatch.setenv("DEBUGGING_VERBOSITY", "4")
def test_some_interaction(monkeypatch):
import os
monkeypatch.setattr(os, "getcwd", lambda: "/")
This auto-use fixture will set the ``DEBUGGING_VERBOSITY`` environment variable for
the entire test session.
Note that the ability to use a ``monkeypatch`` fixture from a ``session``-scoped
fixture was added in pytest-3.0.
Method reference of the monkeypatch function argument
-----------------------------------------------------
Method reference of the monkeypatch fixture
-------------------------------------------
.. autoclass:: monkeypatch
.. autoclass:: MonkeyPatch
:members: setattr, replace, delattr, setitem, delitem, setenv, delenv, syspath_prepend, chdir, undo
``monkeypatch.setattr/delattr/delitem/delenv()`` all

View File

@@ -13,7 +13,7 @@ Usage
After :ref:`installation` type::
python setup.py develop # make sure tests can import our package
py.test # instead of 'nosetests'
pytest # instead of 'nosetests'
and you should be able to run your nose style tests and
make use of pytest's capabilities.
@@ -24,7 +24,7 @@ Supported nose Idioms
* setup and teardown at module/class/method level
* SkipTest exceptions and markers
* setup/teardown decorators
* yield-based tests and their setup
* ``yield``-based tests and their setup
* ``__test__`` attribute on modules/classes/functions
* general usage of nose utilities
@@ -46,10 +46,17 @@ Unsupported idioms / known issues
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
by extending sys.path/import semantics. pytest does not do that
but there is discussion in `issue268 <https://github.com/pytest-dev/pytest/issues/268>`_ for adding some support. Note that
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.org/en/latest/differences.html#test-discovery-and-loading>`_.
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.io/en/latest/differences.html#test-discovery-and-loading>`_.
- nose-style doctests are not collected and executed correctly,
also doctest fixtures don't work.
- no nose-configuration is recognized
- no nose-configuration is recognized.
- ``yield``-based methods don't support ``setup`` properly because
the ``setup`` method is always called in the same class instance.
There are no plans to fix this currently because ``yield``-tests
are deprecated in pytest 3.0, with ``pytest.mark.parametrize``
being the recommended alternative.

View File

@@ -5,10 +5,9 @@ Getting started basics
.. toctree::
:maxdepth: 2
index
getting-started
usage
goodpractises
goodpractices
projects
faq

View File

@@ -41,21 +41,21 @@ to an expected output::
# content of test_expectation.py
import pytest
@pytest.mark.parametrize("input,expected", [
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
("2+4", 6),
("6*9", 42),
])
def test_eval(input, expected):
assert eval(input) == expected
def test_eval(test_input, expected):
assert eval(test_input) == expected
Here, the ``@parametrize`` decorator defines three different ``(input,expected)``
Here, the ``@parametrize`` decorator defines three different ``(test_input,expected)``
tuples so that the ``test_eval`` function will run three times using
them in turn::
$ py.test
$ pytest
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 3 items
@@ -64,15 +64,15 @@ them in turn::
======= FAILURES ========
_______ test_eval[6*9-42] ________
input = '6*9', expected = 42
test_input = '6*9', expected = 42
@pytest.mark.parametrize("input,expected", [
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
("2+4", 6),
("6*9", 42),
])
def test_eval(input, expected):
> assert eval(input) == expected
def test_eval(test_input, expected):
> assert eval(test_input) == expected
E assert 54 == 42
E + where 54 = eval('6*9')
@@ -91,19 +91,19 @@ for example with the builtin ``mark.xfail``::
# content of test_expectation.py
import pytest
@pytest.mark.parametrize("input,expected", [
@pytest.mark.parametrize("test_input,expected", [
("3+5", 8),
("2+4", 6),
pytest.mark.xfail(("6*9", 42)),
])
def test_eval(input, expected):
assert eval(input) == expected
def test_eval(test_input, expected):
assert eval(test_input) == expected
Let's run this::
$ py.test
$ pytest
======= test session starts ========
platform linux -- Python 3.4.3, pytest-2.8.5, py-1.4.31, pluggy-0.3.1
platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:
collected 3 items
@@ -142,7 +142,7 @@ Sometimes you may want to implement your own parametrization scheme
or implement some dynamism for determining the parameters or scope
of a fixture. For this, you can use the ``pytest_generate_tests`` hook
which is called when collecting a test function. Through the passed in
`metafunc` object you can inspect the requesting test context and, most
``metafunc`` object you can inspect the requesting test context and, most
importantly, you can call ``metafunc.parametrize()`` to cause
parametrization.
@@ -171,13 +171,13 @@ command line option and the parametrization of our test function::
If we now pass two stringinput values, our test will run twice::
$ py.test -q --stringinput="hello" --stringinput="world" test_strings.py
$ pytest -q --stringinput="hello" --stringinput="world" test_strings.py
..
2 passed in 0.12 seconds
Let's also run with a stringinput that will lead to a failing test::
$ py.test -q --stringinput="!" test_strings.py
$ pytest -q --stringinput="!" test_strings.py
F
======= FAILURES ========
_______ test_valid_string[!] ________
@@ -186,8 +186,9 @@ Let's also run with a stringinput that will lead to a failing test::
def test_valid_string(stringinput):
> assert stringinput.isalpha()
E assert <built-in method isalpha of str object at 0xdeadbeef>()
E + where <built-in method isalpha of str object at 0xdeadbeef> = '!'.isalpha
E assert False
E + where False = <built-in method isalpha of str object at 0xdeadbeef>()
E + where <built-in method isalpha of str object at 0xdeadbeef> = '!'.isalpha
test_strings.py:3: AssertionError
1 failed in 0.12 seconds
@@ -198,10 +199,10 @@ If you don't specify a stringinput it will be skipped because
``metafunc.parametrize()`` will be called with an empty parameter
list::
$ py.test -q -rs test_strings.py
$ pytest -q -rs test_strings.py
s
======= short test summary info ========
SKIP [1] $PYTHON_PREFIX/lib/python3.4/site-packages/_pytest/python.py:1417: got empty parameter set, function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:1
SKIP [1] test_strings.py:1: got empty parameter set ['stringinput'], function test_valid_string at $REGENDOC_TMPDIR/test_strings.py:1
1 skipped in 0.12 seconds
For further examples, you might want to look at :ref:`more

View File

@@ -56,10 +56,10 @@ Here is a little annotated list for some popular plugins:
check source code with pyflakes.
* `oejskit <http://pypi.python.org/pypi/oejskit>`_:
a plugin to run javascript unittests in life browsers.
a plugin to run javascript unittests in live browsers.
To see a complete list of all plugins with their latest testing
status against different py.test and Python versions, please visit
status against different pytest and Python versions, please visit
`plugincompat <http://plugincompat.herokuapp.com/>`_.
You may also discover more plugins through a `pytest- pypi.python.org search`_.
@@ -90,7 +90,7 @@ Finding out which plugins are active
If you want to find out which plugins are active in your
environment you can type::
py.test --traceconfig
pytest --trace-config
and will get an extended test header which shows activated plugins
and their names. It will also print local plugins aka
@@ -103,7 +103,7 @@ Deactivating / unregistering a plugin by name
You can prevent plugins from loading or unregister them::
py.test -p no:NAME
pytest -p no:NAME
This means that any subsequent try to activate/load the named
plugin will not work.
@@ -138,7 +138,6 @@ in the `pytest repository <https://github.com/pytest-dev/pytest>`_.
_pytest.capture
_pytest.config
_pytest.doctest
_pytest.genscript
_pytest.helpconfig
_pytest.junitxml
_pytest.mark

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