diff --git a/AUTHORS b/AUTHORS index 40f77e083..2e487e772 100644 --- a/AUTHORS +++ b/AUTHORS @@ -48,7 +48,6 @@ Eduardo Schettino Elizaveta Shashkova Endre Galaczi Eric Hunsberger -Eric Hunsberger Eric Siegerman Erik M. Bray Feng Ma @@ -81,6 +80,7 @@ Lukas Bednar Maciek Fijalkowski Maho Marc Schlaich +Marcin Bachry Mark Abramowitz Markus Unterwaditzer Martijn Faassen diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 48262f8c9..bcf1984c9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,7 +10,7 @@ * -3.0.1.dev +3.0.2.dev ========= * @@ -21,6 +21,33 @@ * +3.0.1 +===== + +* Fix regression when ``importorskip`` is used at module level (`#1822`_). + Thanks `@jaraco`_ and `@The-Compiler`_ for the report and `@nicoddemus`_ for the PR. + +* Fix parametrization scope when session fixtures are used in conjunction + with normal parameters in the same call (`#1832`_). + Thanks `@The-Compiler`_ for the report, `@Kingdread`_ and `@nicoddemus`_ for the PR. + +* Fix internal error when parametrizing tests or fixtures using an empty ``ids`` argument (`#1849`_). + Thanks `@OPpuolitaival`_ for the report and `@nicoddemus`_ for the PR. + +* Fix loader error when running ``pytest`` embedded in a zipfile. + Thanks `@mbachry`_ for the PR. + + +.. _@Kingdread: https://github.com/Kingdread +.. _@mbachry: https://github.com/mbachry +.. _@OPpuolitaival: https://github.com/OPpuolitaival + +.. _#1822: https://github.com/pytest-dev/pytest/issues/1822 +.. _#1832: https://github.com/pytest-dev/pytest/issues/1832 +.. _#1849: https://github.com/pytest-dev/pytest/issues/1849 + +>>>>>>> master + 3.0.0 ===== @@ -323,10 +350,6 @@ time or change existing behaviors in order to make them less surprising/more use identify bugs in ``conftest.py`` files (`#1516`_). Thanks `@txomon`_ for the PR. -* Add an 'E' to the first line of error messages from FixtureLookupErrorRepr. - Fixes `#717`_. Thanks `@blueyed`_ for reporting, `@eolo999`_ for the PR - and `@tomviner`_ for his guidance during EuroPython2016 sprint. - * Text documents without any doctests no longer appear as "skipped". Thanks `@graingert`_ for reporting and providing a full PR (`#1580`_). @@ -1225,7 +1248,7 @@ time or change existing behaviors in order to make them less surprising/more use dep). Thanks Charles Cloud for analysing the issue. - fix conftest related fixture visibility issue: when running with a - CWD outside a test package pytest would get fixture discovery wrong. + CWD outside of a test package pytest would get fixture discovery wrong. Thanks to Wolfgang Schnerring for figuring out a reproducable example. - Introduce pytest_enter_pdb hook (needed e.g. by pytest_timeout to cancel the diff --git a/HOWTORELEASE.rst b/HOWTORELEASE.rst index afc0c3bc3..e6f1973b6 100644 --- a/HOWTORELEASE.rst +++ b/HOWTORELEASE.rst @@ -3,90 +3,83 @@ 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). -1. Bump version numbers in _pytest/__init__.py (setup.py reads it) +2. Check and finalize ``CHANGELOG.rst``. -2. Check and finalize CHANGELOG +3. Write ``doc/en/announce/release-VERSION.txt`` and include + it in ``doc/en/announce/index.txt``. Run this command to list names of authors involved:: -3. Write doc/en/announce/release-VERSION.txt and include - it in doc/en/announce/index.txt:: + git log $(git describe --abbrev=0 --tags)..HEAD --format='%aN' | sort -u - git log 2.8.2..HEAD --format='%aN' | sort -u # lists the names of authors involved +4. Regenerate the docs examples using tox:: -4. Use devpi for uploading a release tarball to a staging area:: + tox -e regen + +5. At this point, open a PR named ``release-X`` so others can help find regressions or provide suggestions. + +6. Use devpi for uploading a release tarball to a staging area:: devpi use https://devpi.net/USER/dev devpi upload --formats sdist,bdist_wheel -5. Run from multiple machines:: +7. Run from multiple machines:: devpi use https://devpi.net/USER/dev devpi test pytest==VERSION -6. Check that tests pass for relevant combinations with:: + Alternatively, you can use `devpi-cloud-tester `_ to test + the package on AppVeyor and Travis (follow instructions on the ``README``). + +8. Check that tests pass for relevant combinations with:: devpi list pytest or look at failures with "devpi list -f pytest". -7. Regenerate the docs examples using tox, and check for regressions:: - - tox -e regen - git diff - - -8. Build the docs, you need a virtualenv with py and sphinx - installed:: - - cd doc/en - make html - - Commit any changes before tagging the release. - -9. Tag the release:: - - git tag VERSION - git push - -10. Upload the docs using doc/en/Makefile:: - - cd doc/en - make install # or "installall" if you have LaTeX installed for PDF - - This requires ssh-login permission on pytest.org because it uses - rsync. - Note that the ``install`` target of ``doc/en/Makefile`` defines where the - rsync goes to, typically to the "latest" section of pytest.org. - - If you are making a minor release (e.g. 5.4), you also need to manually - create a symlink for "latest":: - - ssh pytest-dev@pytest.org - ln -s 5.4 latest - - Browse to pytest.org to verify. - -11. Publish to pypi:: +9. Feeling confident? Publish to pypi:: devpi push pytest==VERSION pypi:NAME - where NAME is the name of pypi.python.org as configured in your ``~/.pypirc`` - file `for devpi `_. + where NAME is the name of pypi.python.org as configured in your ``~/.pypirc`` + file `for devpi `_. +10. Tag the release:: -12. Send release announcement to mailing lists: + git tag VERSION + git push origin VERSION - - pytest-dev - - testing-in-python + Make sure ```` is **exactly** the git hash at the time the package was created. + +11. Send release announcement to mailing lists: + + - pytest-dev@python.org + - testing-in-python@lists.idyll.org - python-announce-list@python.org + And announce the release on Twitter, making sure to add the hashtag ``#pytest``. + +12. **After the release** + + a. **patch release (2.8.3)**: + + 1. Checkout ``master``. + 2. Update version number in ``_pytest/__init__.py`` to ``"2.8.4.dev"``. + 3. Create a new section in ``CHANGELOG.rst`` titled ``2.8.4.dev`` and add a few bullet points as placeholders for new entries. + 4. Commit and push. + + b. **minor release (2.9.0)**: + + 1. Merge ``features`` into ``master``. + 2. Checkout ``master``. + 3. Follow the same steps for a **patch release** above, using the next patch release: ``2.9.1.dev``. + 4. Commit ``master``. + 5. Checkout ``features`` and merge with ``master`` (should be a fast-forward at this point). + 6. Update version number in ``_pytest/__init__.py`` to the next minor release: ``"2.10.0.dev"``. + 7. Create a new section in ``CHANGELOG.rst`` titled ``2.10.0.dev``, above ``2.9.1.dev``, and add a few bullet points as placeholders for new entries. + 8. Commit ``features``. + 9. Push ``master`` and ``features``. + + c. **major release (3.0.0)**: same steps as that of a **minor release** -13. **after the release** Bump the version number in ``_pytest/__init__.py``, - 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 master branch and do a pull request against it -15. merge from master to features diff --git a/ISSUES.txt b/ISSUES.txt deleted file mode 100644 index 081d727e8..000000000 --- a/ISSUES.txt +++ /dev/null @@ -1,365 +0,0 @@ - - -recorder = monkeypatch.function(".......") -------------------------------------------------------------- -tags: nice feature - -Like monkeypatch.replace but sets a mock-like call recorder: - - recorder = monkeypatch.function("os.path.abspath") - recorder.set_return("/hello") - os.path.abspath("hello") - call, = recorder.calls - assert call.args.path == "hello" - assert call.returned == "/hello" - ... - -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 - -extend metafunc.parametrize to directly support indirection, example: - - def setupdb(request, config): - # setup "resource" based on test request and the values passed - # in to parametrize. setupfunc is called for each such value. - # you may use request.addfinalizer() or request.cached_setup ... - return dynamic_setup_database(val) - - @pytest.mark.parametrize("db", ["pg", "mysql"], setupfunc=setupdb) - def test_heavy_functional_test(db): - ... - -There would be no need to write or explain funcarg factories and -their special __ syntax. - -The examples and improvements should also show how to put the parametrize -decorator to a class, to a module or even to a directory. For the directory -part a conftest.py content like this:: - - pytestmark = [ - @pytest.mark.parametrize_setup("db", ...), - ] - -probably makes sense in order to keep the declarative nature. This mirrors -the marker-mechanism with respect to a test module but puts it to a directory -scale. - -When doing larger scoped parametrization it probably becomes necessary -to allow parametrization to be ignored if the according parameter is not -used (currently any parametrized argument that is not present in a function will cause a ValueError). Example: - - @pytest.mark.parametrize("db", ..., mustmatch=False) - -means to not raise an error but simply ignore the parametrization -if the signature of a decorated function does not match. XXX is it -not sufficient to always allow non-matches? - - -allow parametrized attributes on classes --------------------------------------------------- - -tags: wish 2.4 - -example: - - @pytest.mark.parametrize_attr("db", setupfunc, [1,2,3], scope="class") - @pytest.mark.parametrize_attr("tmp", setupfunc, scope="...") - class TestMe: - def test_hello(self): - access self.db ... - -this would run the test_hello() function three times with three -different values for self.db. This could also work with unittest/nose -style tests, i.e. it leverages existing test suites without needing -to rewrite them. Together with the previously mentioned setup_test() -maybe the setupfunc could be omitted? - -optimizations ---------------------------------------------------------------- -tags: 2.4 core - -- look at ihook optimization such that all lookups for - hooks relating to the same fspath are cached. - -fix start/finish partial finailization problem ---------------------------------------------------------------- -tags: bug core - -if a configure/runtest_setup/sessionstart/... hook invocation partially -fails the sessionfinishes is not called. Each hook implementation -should better be repsonsible for registering a cleanup/finalizer -appropriately to avoid this issue. Moreover/Alternatively, we could -record which implementations of a hook succeeded and only call their -teardown. - - -relax requirement to have tests/testing contain an __init__ ----------------------------------------------------------------- -tags: feature -bb: http://bitbucket.org/hpk42/py-trunk/issue/64 - -A local test run of a "tests" directory may work -but a remote one fail because the tests directory -does not contain an "__init__.py". Either give -an error or make it work without the __init__.py -i.e. port the nose-logic of unloading a test module. - -customize test function collection -------------------------------------------------------- -tags: feature - -- introduce pytest.mark.nocollect for not considering a function for - test collection at all. maybe also introduce a pytest.mark.test to - explicitly mark a function to become a tested one. Lookup JUnit ways - of tagging tests. - -introduce pytest.mark.importorskip -------------------------------------------------------- -tags: feature - -in addition to the imperative pytest.importorskip also introduce -a pytest.mark.importorskip so that the test count is more correct. - - -introduce pytest.mark.platform -------------------------------------------------------- -tags: feature - -Introduce nice-to-spell platform-skipping, examples: - - @pytest.mark.platform("python3") - @pytest.mark.platform("not python3") - @pytest.mark.platform("win32 and not python3") - @pytest.mark.platform("darwin") - @pytest.mark.platform("not (jython and win32)") - @pytest.mark.platform("not (jython and win32)", xfail=True) - -etc. Idea is to allow Python expressions which can operate -on common spellings for operating systems and python -interpreter versions. - -pytest.mark.xfail signature change -------------------------------------------------------- -tags: feature - -change to pytest.mark.xfail(reason, (optional)condition) -to better implement the word meaning. It also signals -better that we always have some kind of an implementation -reason that can be formualated. -Compatibility? how to introduce a new name/keep compat? - -allow to non-intrusively apply skipfs/xfail/marks ---------------------------------------------------- -tags: feature - -use case: mark a module or directory structures -to be skipped on certain platforms (i.e. no import -attempt will be made). - -consider introducing a hook/mechanism that allows to apply marks -from conftests or plugins. (See extended parametrization) - - -explicit referencing of conftest.py files ------------------------------------------ -tags: feature - -allow to name conftest.py files (in sub directories) that should -be imported early, as to include command line options. - -improve central pytest ini file -------------------------------- -tags: feature - -introduce more declarative configuration options: -- (to-be-collected test directories) -- required plugins -- test func/class/file matching patterns -- skip/xfail (non-intrusive) -- pytest.ini and tox.ini and setup.cfg configuration in the same file - -new documentation ----------------------------------- -tags: feature - -- logo pytest -- examples for unittest or functional testing -- resource management for functional testing -- patterns: page object - -have imported module mismatch honour relative paths --------------------------------------------------------- -tags: bug - -With 1.1.1 pytest fails at least on windows if an import -is relative and compared against an absolute conftest.py -path. Normalize. - -consider globals: pytest.ensuretemp and config --------------------------------------------------------------- -tags: experimental-wish - -consider deprecating pytest.ensuretemp and pytest.config -to further reduce pytest globality. Also consider -having pytest.config and ensuretemp coming from -a plugin rather than being there from the start. - - -consider pytest_addsyspath hook ------------------------------------------ -tags: wish - -pytest could call a new pytest_addsyspath() in order to systematically -allow manipulation of sys.path and to inhibit it via --no-addsyspath -in order to more easily run against installed packages. - -Alternatively it could also be done via the config object -and pytest_configure. - - - -deprecate global pytest.config usage ----------------------------------------------------------------- -tags: feature - -pytest.ensuretemp and pytest.config are probably the last -objects containing global state. Often using them is not -necessary. This is about trying to get rid of them, i.e. -deprecating them and checking with PyPy's usages as well -as others. - -remove deprecated bits in collect.py -------------------------------------------------------------------- -tags: feature - -In an effort to further simplify code, review and remove deprecated bits -in collect.py. Probably good: -- inline consider_file/dir methods, no need to have them - subclass-overridable because of hooks - -implement fslayout decorator ---------------------------------- -tags: feature - -Improve the way how tests can work with pre-made examples, -keeping the layout close to the test function: - -@pytest.mark.fslayout(""" - conftest.py: - # empty - tests/ - test_%(NAME)s: # becomes test_run1.py - def test_function(self): - pass -""") -def test_run(pytester, fslayout): - p = fslayout.findone("test_*.py") - result = pytester.runpytest(p) - assert result.ret == 0 - assert result.passed == 1 - -Another idea is to allow to define a full scenario including the run -in one content string:: - - runscenario(""" - test_{TESTNAME}.py: - import pytest - @pytest.mark.xfail - def test_that_fails(): - assert 0 - - @pytest.mark.skipif("True") - def test_hello(): - pass - - conftest.py: - import pytest - def pytest_runsetup_setup(item): - pytest.skip("abc") - - runpytest -rsxX - *SKIP*{TESTNAME}* - *1 skipped* - """) - -This could be run with at least three different ways to invoke pytest: -through the shell, through "python -m pytest" and inlined. As inlined -would be the fastest it could be run first (or "--fast" mode). - - -Create isolate plugin ---------------------- -tags: feature - -The idea is that you can e.g. import modules in a test and afterwards -sys.modules, sys.meta_path etc would be reverted. It can go further -then just importing however, e.g. current working directory, file -descriptors, ... - -This would probably be done by marking:: - - @pytest.mark.isolate(importing=True, cwd=True, fds=False) - def test_foo(): - ... - -With the possibility of doing this globally in an ini-file. - - -fnmatch for test names ----------------------- -tags: feature-wish - -various testsuites use suffixes instead of prefixes for test classes -also it lends itself to bdd style test names:: - - class UserBehaviour: - def anonymous_should_not_have_inbox(user): - ... - def registred_should_have_inbox(user): - .. - -using the following in pytest.ini:: - - [pytest] - python_classes = Test *Behaviour *Test - python_functions = test *_should_* - - -mechanism for running named parts of tests with different reporting behaviour ------------------------------------------------------------------------------- -tags: feature-wish-incomplete - -a few use-cases come to mind: - -* fail assertions and record that without stopping a complete test - - * this is in particular hepfull if a small bit of a test is known to fail/xfail:: - - def test_fun(): - with pytest.section('fdcheck, marks=pytest.mark.xfail_if(...)): - breaks_on_windows() - -* divide functional/acceptance tests into sections -* provide a different mechanism for generators, maybe something like:: - - def pytest_runtest_call(item) - if not generator: - ... - prepare_check = GeneratorCheckprepare() - - gen = item.obj(**fixtures) - for check in gen - id, call = prepare_check(check) - # bubble should only prevent exception propagation after a failure - # the whole test should still fail - # there might be need for a lower level api and taking custom markers into account - with pytest.section(id, bubble=False): - call() - - diff --git a/MANIFEST.in b/MANIFEST.in index 266a9184d..079b225ec 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,6 +4,7 @@ include AUTHORS include README.rst include CONTRIBUTING.rst +include HOWTORELEASE.rst include tox.ini include setup.py @@ -29,6 +30,3 @@ recursive-exclude * *.pyc *.pyo exclude appveyor/install.ps1 exclude appveyor.yml exclude appveyor - -exclude ISSUES.txt -exclude HOWTORELEASE.rst diff --git a/_pytest/main.py b/_pytest/main.py index 48f5bb6f2..5771a1699 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -687,7 +687,7 @@ class Session(FSCollector): # This method is sometimes invoked when AssertionRewritingHook, which # does not define a get_filename method, is already in place: try: - path = loader.get_filename() + path = loader.get_filename(x) except AttributeError: # Retrieve path from AssertionRewritingHook: path = loader.modules[x][0].co_filename diff --git a/_pytest/mark.py b/_pytest/mark.py index d8b60def3..40c998c3e 100644 --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -283,6 +283,21 @@ class MarkDecorator: return self.__class__(self.name, args=args, kwargs=kw) +def extract_argvalue(maybe_marked_args): + # TODO: incorrect mark data, the old code wanst able to collect lists + # individual parametrized argument sets can be wrapped in a series + # of markers in which case we unwrap the values and apply the mark + # at Function init + newmarks = {} + argval = maybe_marked_args + while isinstance(argval, MarkDecorator): + newmark = MarkDecorator(argval.markname, + argval.args[:-1], argval.kwargs) + newmarks[newmark.markname] = newmark + argval = argval.args[-1] + return argval, newmarks + + class MarkInfo: """ Marking object created by :class:`MarkDecorator` instances. """ def __init__(self, name, args, kwargs): diff --git a/_pytest/python.py b/_pytest/python.py index 9e9c78897..75d139df3 100644 --- a/_pytest/python.py +++ b/_pytest/python.py @@ -5,10 +5,11 @@ import inspect import sys import collections import math +from itertools import count import py import pytest -from _pytest.mark import MarkDecorator, MarkerError +from _pytest.mark import MarkerError import _pytest @@ -431,10 +432,12 @@ class Module(pytest.File, PyCollector): "Make sure your test modules/packages have valid Python names." % (self.fspath, exc or exc_class) ) - except _pytest.runner.Skipped: + except _pytest.runner.Skipped as e: + if e.allow_module_level: + raise raise self.CollectError( - "Using @pytest.skip outside a test (e.g. as a test function " - "decorator) is not allowed. Use @pytest.mark.skip or " + "Using @pytest.skip outside of a test (e.g. as a test " + "function decorator) is not allowed. Use @pytest.mark.skip or " "@pytest.mark.skipif instead." ) self.config.pluginmanager.consider_module(mod) @@ -774,19 +777,14 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): to set a dynamic scope using test context or configuration. """ from _pytest.fixtures import scopes - # individual parametrized argument sets can be wrapped in a series - # of markers in which case we unwrap the values and apply the mark - # at Function init - newkeywords = {} + from _pytest.mark import extract_argvalue + unwrapped_argvalues = [] - for i, argval in enumerate(argvalues): - while isinstance(argval, MarkDecorator): - newmark = MarkDecorator(argval.markname, - argval.args[:-1], argval.kwargs) - newmarks = newkeywords.setdefault(i, {}) - newmarks[newmark.markname] = newmark - argval = argval.args[-1] + newkeywords = [] + for maybe_marked_args in argvalues: + argval, newmarks = extract_argvalue(maybe_marked_args) unwrapped_argvalues.append(argval) + newkeywords.append(newmarks) argvalues = unwrapped_argvalues if not isinstance(argnames, (tuple, list)): @@ -801,18 +799,11 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): newmark = pytest.mark.skip( reason="got empty parameter set %r, function %s at %s:%d" % ( argnames, self.function.__name__, fs, lineno)) - newmarks = newkeywords.setdefault(0, {}) - newmarks[newmark.markname] = newmark + newkeywords = [{newmark.markname: newmark}] if scope is None: - if self._arg2fixturedefs: - # Takes the most narrow scope from used fixtures - fixtures_scopes = [fixturedef[0].scope for fixturedef in self._arg2fixturedefs.values()] - for scope in reversed(scopes): - if scope in fixtures_scopes: - break - else: - scope = 'function' + scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) + scopenum = scopes.index(scope) valtypes = {} for arg in argnames: @@ -846,12 +837,12 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): ids = idmaker(argnames, argvalues, idfn, ids, self.config) newcalls = [] for callspec in self._calls or [CallSpec2(self)]: - for param_index, valset in enumerate(argvalues): + elements = zip(ids, argvalues, newkeywords, count()) + for a_id, valset, keywords, param_index in elements: assert len(valset) == len(argnames) newcallspec = callspec.copy(self) - newcallspec.setmulti(valtypes, argnames, valset, ids[param_index], - newkeywords.get(param_index, {}), scopenum, - param_index) + newcallspec.setmulti(valtypes, argnames, valset, a_id, + keywords, scopenum, param_index) newcalls.append(newcallspec) self._calls = newcalls @@ -892,6 +883,30 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): self._calls.append(cs) +def _find_parametrized_scope(argnames, arg2fixturedefs, indirect): + """Find the most appropriate scope for a parametrized call based on its arguments. + + When there's at least one direct argument, always use "function" scope. + + When a test function is parametrized and all its arguments are indirect + (e.g. fixtures), return the most narrow scope based on the fixtures used. + + Related to issue #1832, based on code posted by @Kingdread. + """ + from _pytest.fixtures import scopes + indirect_as_list = isinstance(indirect, (list, tuple)) + all_arguments_are_fixtures = indirect is True or \ + indirect_as_list and len(indirect) == argnames + if all_arguments_are_fixtures: + fixturedefs = arg2fixturedefs or {} + used_scopes = [fixturedef[0].scope for name, fixturedef in fixturedefs.items()] + if used_scopes: + # Takes the most narrow scope from used fixtures + for scope in reversed(scopes): + if scope in used_scopes: + return scope + + return 'function' def _idval(val, argname, idx, idfn, config=None): @@ -921,7 +936,7 @@ def _idval(val, argname, idx, idfn, config=None): return str(argname)+str(idx) def _idvalset(idx, valset, argnames, idfn, ids, config=None): - if ids is None or ids[idx] is None: + if ids is None or (idx >= len(ids) or ids[idx] is None): this_id = [_idval(val, argname, idx, idfn, config) for val, argname in zip(valset, argnames)] return "-".join(this_id) diff --git a/_pytest/runner.py b/_pytest/runner.py index 525a2b00f..d1a155415 100644 --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -492,10 +492,16 @@ class Skipped(OutcomeException): # in order to have Skipped exception printing shorter/nicer __module__ = 'builtins' + def __init__(self, msg=None, pytrace=True, allow_module_level=False): + OutcomeException.__init__(self, msg=msg, pytrace=pytrace) + self.allow_module_level = allow_module_level + + class Failed(OutcomeException): """ raised from an explicit call to pytest.fail() """ __module__ = 'builtins' + class Exit(KeyboardInterrupt): """ raised for immediate program exits (no tracebacks/summaries)""" def __init__(self, msg="unknown reason"): @@ -546,7 +552,7 @@ def importorskip(modname, minversion=None): # Do not raise chained exception here(#1485) should_skip = True if should_skip: - skip("could not import %r" %(modname,)) + raise Skipped("could not import %r" %(modname,), allow_module_level=True) mod = sys.modules[modname] if minversion is None: return mod @@ -555,10 +561,11 @@ def importorskip(modname, minversion=None): try: from pkg_resources import parse_version as pv except ImportError: - skip("we have a required version for %r but can not import " - "no pkg_resources to parse version strings." %(modname,)) + raise Skipped("we have a required version for %r but can not import " + "pkg_resources to parse version strings." % (modname,), + allow_module_level=True) if verattr is None or pv(verattr) < pv(minversion): - skip("module %r has __version__ %r, required is: %r" %( - modname, verattr, minversion)) + raise Skipped("module %r has __version__ %r, required is: %r" %( + modname, verattr, minversion), allow_module_level=True) return mod diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index 1afee3813..8613582b3 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -7,6 +7,8 @@ Release announcements sprint2016 + release-3.0.1 + release-3.0.0 release-2.9.2 release-2.9.1 release-2.9.0 diff --git a/doc/en/announce/release-2.6.3.rst b/doc/en/announce/release-2.6.3.rst index 13fae31b8..c1d0ad278 100644 --- a/doc/en/announce/release-2.6.3.rst +++ b/doc/en/announce/release-2.6.3.rst @@ -41,7 +41,7 @@ Changes 2.6.3 dep). Thanks Charles Cloud for analysing the issue. - fix conftest related fixture visibility issue: when running with a - CWD outside a test package pytest would get fixture discovery wrong. + CWD outside of a test package pytest would get fixture discovery wrong. Thanks to Wolfgang Schnerring for figuring out a reproducable example. - Introduce pytest_enter_pdb hook (needed e.g. by pytest_timeout to cancel the diff --git a/doc/en/announce/release-3.0.0.rst b/doc/en/announce/release-3.0.0.rst index f031840fb..4bf1e8534 100644 --- a/doc/en/announce/release-3.0.0.rst +++ b/doc/en/announce/release-3.0.0.rst @@ -6,7 +6,7 @@ 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 +This release contains a lot of bugs fixes 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. @@ -76,7 +76,7 @@ Thanks to all who contributed to this release, among them: Happy testing, -The py.test Development Team +The Pytest Development Team [1] http://blog.pytest.org/2016/pytest-development-sprint/ [2] http://blog.pytest.org/2016/whats-new-in-pytest-30/ diff --git a/doc/en/announce/release-3.0.1.rst b/doc/en/announce/release-3.0.1.rst new file mode 100644 index 000000000..9fb38047b --- /dev/null +++ b/doc/en/announce/release-3.0.1.rst @@ -0,0 +1,26 @@ +pytest-3.0.1 +============ + +pytest 3.0.1 has just been released to PyPI. + +This release fixes some regressions reported in version 3.0.0, being a +drop-in replacement. To upgrade: + + pip install --upgrade pytest + +The changelog is available at http://doc.pytest.org/en/latest/changelog.html. + +Thanks to all who contributed to this release, among them: + + Adam Chainz + Andrew Svetlov + Bruno Oliveira + Daniel Hahler + Dmitry Dygalo + Florian Bruhin + Marcin Bachry + Ronny Pfannschmidt + matthiasha + +Happy testing, +The py.test Development Team diff --git a/doc/en/assert.rst b/doc/en/assert.rst index 4ddc96056..40fdebdc7 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -26,7 +26,7 @@ you will see the return value of the function call:: $ pytest test_assert1.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 1 items @@ -170,7 +170,7 @@ if you run this module:: $ pytest test_assert2.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 1 items diff --git a/doc/en/cache.rst b/doc/en/cache.rst index e7784c953..d364f19de 100644 --- a/doc/en/cache.rst +++ b/doc/en/cache.rst @@ -80,7 +80,7 @@ If you then run it with ``--lf``:: $ pytest --lf ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 run-last-failure: rerun last 2 failures rootdir: $REGENDOC_TMPDIR, inifile: collected 50 items @@ -122,7 +122,7 @@ of ``FF`` and dots):: $ pytest --ff ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 run-last-failure: rerun last 2 failures first rootdir: $REGENDOC_TMPDIR, inifile: collected 50 items @@ -227,7 +227,7 @@ You can always peek at the content of the cache using the $ py.test --cache-show ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: cachedir: $REGENDOC_TMPDIR/.cache ------------------------------- cache values ------------------------------- diff --git a/doc/en/capture.rst b/doc/en/capture.rst index 05ed216e9..e4514b17d 100644 --- a/doc/en/capture.rst +++ b/doc/en/capture.rst @@ -64,7 +64,7 @@ of the failing function and hide the other one:: $ pytest ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items diff --git a/doc/en/doctest.rst b/doc/en/doctest.rst index 35a87ad79..fa194ddae 100644 --- a/doc/en/doctest.rst +++ b/doc/en/doctest.rst @@ -48,7 +48,7 @@ then you can just invoke ``pytest`` without command line options:: $ pytest ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini collected 1 items diff --git a/doc/en/example/layout1/setup.cfg b/doc/en/example/layout1/setup.cfg deleted file mode 100644 index 02d3750ee..000000000 --- a/doc/en/example/layout1/setup.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[pytest] -testfilepatterns = - ${topdir}/tests/unit/test_${basename} - ${topdir}/tests/functional/*.py diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index b4bb74377..e622b993c 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -31,7 +31,7 @@ You can then restrict a test run to only run tests marked with ``webtest``:: $ pytest -v -m webtest ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 cachedir: .cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items @@ -45,7 +45,7 @@ Or the inverse, running all tests except the webtest ones:: $ pytest -v -m "not webtest" ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 cachedir: .cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items @@ -66,7 +66,7 @@ tests based on their module, class, method, or function name:: $ pytest -v test_server.py::TestClass::test_method ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 cachedir: .cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 5 items @@ -79,7 +79,7 @@ You can also select on the class:: $ pytest -v test_server.py::TestClass ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 cachedir: .cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items @@ -92,7 +92,7 @@ Or select multiple nodes:: $ pytest -v test_server.py::TestClass test_server.py::test_send_http ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 cachedir: .cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 8 items @@ -130,7 +130,7 @@ select tests based on their names:: $ pytest -v -k http # running with the above defined example module ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 cachedir: .cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items @@ -144,7 +144,7 @@ And you can also run all tests except the ones that match the keyword:: $ pytest -k "not send_http" -v ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 cachedir: .cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items @@ -160,7 +160,7 @@ Or to select "http" and "quick" tests:: $ pytest -k "http or quick" -v ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 cachedir: .cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 4 items @@ -352,7 +352,7 @@ the test needs:: $ pytest -E stage2 ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 1 items @@ -364,7 +364,7 @@ and here is one that specifies exactly the environment needed:: $ pytest -E stage1 ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 1 items @@ -485,7 +485,7 @@ then you will see two test skipped and two executed tests as expected:: $ pytest -rs # this option reports skip reasons ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items @@ -499,7 +499,7 @@ Note that if you specify a platform via the marker-command line option like this $ pytest -m linux2 ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items @@ -551,7 +551,7 @@ We can now use the ``-m option`` to select one set:: $ pytest -m interface --tb=short ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items @@ -573,7 +573,7 @@ or to select both "event" and "interface" tests:: $ pytest -m "interface or event" --tb=short ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items diff --git a/doc/en/example/nonpython.rst b/doc/en/example/nonpython.rst index 49d476f63..fd6d3b333 100644 --- a/doc/en/example/nonpython.rst +++ b/doc/en/example/nonpython.rst @@ -27,7 +27,7 @@ now execute the test specification:: nonpython $ pytest test_simple.yml ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR/nonpython, inifile: collected 2 items @@ -59,7 +59,7 @@ consulted when reporting in ``verbose`` mode:: nonpython $ pytest -v ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 cachedir: .cache rootdir: $REGENDOC_TMPDIR/nonpython, inifile: collecting ... collected 2 items @@ -81,7 +81,7 @@ interesting to just look at the collection tree:: nonpython $ pytest --collect-only ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR/nonpython, inifile: collected 2 items diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index 06d6ae767..70bf92c42 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -130,7 +130,7 @@ objects, they are still using the default pytest representation:: $ pytest test_time.py --collect-only ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 6 items @@ -181,7 +181,7 @@ this is a fully self-contained example which you can run with:: $ pytest test_scenarios.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items @@ -194,7 +194,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia $ pytest --collect-only test_scenarios.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items @@ -259,7 +259,7 @@ Let's first see how it looks like at collection time:: $ pytest test_backends.py --collect-only ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items @@ -320,7 +320,7 @@ The result of this test will be successful:: $ pytest test_indirect_list.py --collect-only ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 1 items @@ -447,7 +447,7 @@ If you run this with reporting for skips enabled:: $ pytest -rs test_module.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items diff --git a/doc/en/example/pythoncollection.rst b/doc/en/example/pythoncollection.rst index 8d778e24d..d86e08bab 100644 --- a/doc/en/example/pythoncollection.rst +++ b/doc/en/example/pythoncollection.rst @@ -117,7 +117,7 @@ then the test collection looks like this:: $ pytest --collect-only ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini collected 2 items @@ -163,7 +163,7 @@ You can always peek at the collection tree without running tests like this:: . $ pytest --collect-only pythoncollection.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini collected 3 items @@ -230,7 +230,7 @@ will be left out:: $ pytest --collect-only ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini collected 0 items diff --git a/doc/en/example/reportingdemo.rst b/doc/en/example/reportingdemo.rst index b6f4d34b3..45c4a8e8b 100644 --- a/doc/en/example/reportingdemo.rst +++ b/doc/en/example/reportingdemo.rst @@ -13,7 +13,7 @@ get on the terminal - we are working on that): assertion $ pytest failure_demo.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR/assertion, inifile: collected 42 items @@ -361,7 +361,7 @@ get on the terminal - we are working on that): > int(s) E ValueError: invalid literal for int() with base 10: 'qwe' - <0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python.py:1174>:1: ValueError + <0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python.py:1189>:1: ValueError _______ TestRaises.test_raises_doesnt ________ self = diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 713b63612..8c6a58b2e 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -108,7 +108,7 @@ directory with the above conftest.py:: $ pytest ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 0 items @@ -156,7 +156,7 @@ and when running it will see a skipped "slow" test:: $ pytest -rs # "-rs" means report details on the little 's' ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items @@ -170,7 +170,7 @@ Or run it including the ``slow`` marked test:: $ pytest --runslow ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items @@ -284,7 +284,7 @@ which will add the string to the test header accordingly:: $ pytest ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 project deps: mylib-1.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 0 items @@ -308,7 +308,7 @@ which will add info only when run with "--v":: $ pytest -v ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 cachedir: .cache info1: did you know that ... did you? @@ -321,7 +321,7 @@ and nothing when run plainly:: $ pytest ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 0 items @@ -354,7 +354,7 @@ Now we can profile which test functions execute the slowest:: $ pytest --durations=3 ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 3 items @@ -416,7 +416,7 @@ If we run this:: $ pytest -rx ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 4 items @@ -487,7 +487,7 @@ We can run this:: $ pytest ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 7 items @@ -591,7 +591,7 @@ and run them:: $ pytest test_module.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items @@ -681,7 +681,7 @@ and run it:: $ pytest -s test_module.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 3 items diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 07146e982..4f0669ec5 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -70,7 +70,7 @@ marked ``smtp`` fixture function. Running the test looks like this:: $ pytest test_smtpsimple.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 1 items @@ -188,7 +188,7 @@ inspect what is going on and can now run the tests:: $ pytest test_module.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items @@ -516,7 +516,7 @@ Running the above tests results in the following test IDs being used:: $ pytest --collect-only ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 11 items @@ -569,7 +569,7 @@ Here we declare an ``app`` fixture which receives the previously defined $ pytest -v test_appsetup.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 cachedir: .cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 2 items @@ -638,7 +638,7 @@ Let's run the tests in verbose mode and with looking at the print-output:: $ pytest -v -s test_module.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.5 cachedir: .cache rootdir: $REGENDOC_TMPDIR, inifile: collecting ... collected 8 items diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index cea8c0eb8..d5f304aef 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -27,7 +27,7 @@ Installation options:: To check your installation has installed the correct version:: $ pytest --version - This is pytest version 3.0.0, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py + This is pytest version 3.0.1, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py If you get an error checkout :ref:`installation issues`. @@ -49,7 +49,7 @@ That's it. You can execute the test function now:: $ pytest ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 1 items diff --git a/doc/en/parametrize.rst b/doc/en/parametrize.rst index 2cb264fb9..2d0ceef04 100644 --- a/doc/en/parametrize.rst +++ b/doc/en/parametrize.rst @@ -55,7 +55,7 @@ them in turn:: $ pytest ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 3 items @@ -103,7 +103,7 @@ Let's run this:: $ pytest ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 3 items diff --git a/doc/en/skipping.rst b/doc/en/skipping.rst index 85b4fad22..fdc4bfe60 100644 --- a/doc/en/skipping.rst +++ b/doc/en/skipping.rst @@ -224,7 +224,7 @@ Running it with the report-on-xfail option gives this output:: example $ pytest -rx xfail_demo.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR/example, inifile: collected 7 items diff --git a/doc/en/tmpdir.rst b/doc/en/tmpdir.rst index 3445bc12f..597731dcf 100644 --- a/doc/en/tmpdir.rst +++ b/doc/en/tmpdir.rst @@ -29,7 +29,7 @@ Running this would result in a passed test except for the last $ pytest test_tmpdir.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 1 items diff --git a/doc/en/unittest.rst b/doc/en/unittest.rst index cd4e01d2a..c9f8598cd 100644 --- a/doc/en/unittest.rst +++ b/doc/en/unittest.rst @@ -88,7 +88,7 @@ the ``self.db`` values in the traceback:: $ pytest test_unittest_db.py ======= test session starts ======== - platform linux -- Python 3.5.2, pytest-3.0.0, py-1.4.31, pluggy-0.3.1 + platform linux -- Python 3.5.2, pytest-3.0.1, py-1.4.31, pluggy-0.3.1 rootdir: $REGENDOC_TMPDIR, inifile: collected 2 items diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 74533e273..03c4c78b0 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -293,7 +293,7 @@ can use like this:: pass """) result = testdir.runpytest("--verbose") - result.fnmatch_lines(""" + result.stdout.fnmatch_lines(""" test_example* """) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 82b131dcc..b03f7fe4c 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -763,3 +763,21 @@ class TestDurationWithFixture: * call *test_1* """) + +def test_zipimport_hook(testdir, tmpdir): + """Test package loader is being used correctly (see #1837).""" + zipapp = pytest.importorskip('zipapp') + testdir.tmpdir.join('app').ensure(dir=1) + testdir.makepyfile(**{ + 'app/foo.py': """ + import pytest + def main(): + pytest.main(['--pyarg', 'foo']) + """, + }) + target = tmpdir.join('foo.zip') + zipapp.create_archive(str(testdir.tmpdir.join('app')), str(target), main='foo:main') + result = testdir.runpython(target) + assert result.ret == 0 + result.stderr.fnmatch_lines(['*not found*foo*']) + assert 'INTERNALERROR>' not in result.stdout.str() diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 9f1887515..61ace1867 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -889,6 +889,33 @@ class TestMetafuncFunctional: "*test_function*advanced*FAILED", ]) + def test_fixture_parametrized_empty_ids(self, testdir): + """Fixtures parametrized with empty ids cause an internal error (#1849).""" + testdir.makepyfile(''' + import pytest + + @pytest.fixture(scope="module", ids=[], params=[]) + def temp(request): + return request.param + + def test_temp(temp): + pass + ''') + result = testdir.runpytest() + result.stdout.fnmatch_lines(['* 1 skipped *']) + + def test_parametrized_empty_ids(self, testdir): + """Tests parametrized with empty ids cause an internal error (#1849).""" + testdir.makepyfile(''' + import pytest + + @pytest.mark.parametrize('temp', [], ids=list()) + def test_temp(temp): + pass + ''') + result = testdir.runpytest() + result.stdout.fnmatch_lines(['* 1 skipped *']) + def test_parametrize_with_identical_ids_get_unique_names(self, testdir): testdir.makepyfile(""" import pytest @@ -930,43 +957,6 @@ class TestMetafuncFunctional: reprec = testdir.inline_run() reprec.assertoutcome(passed=5) - def test_parametrize_issue634(self, testdir): - testdir.makepyfile(''' - import pytest - - @pytest.fixture(scope='module') - def foo(request): - print('preparing foo-%d' % request.param) - return 'foo-%d' % request.param - - - def test_one(foo): - pass - - - def test_two(foo): - pass - - - test_two.test_with = (2, 3) - - - def pytest_generate_tests(metafunc): - params = (1, 2, 3, 4) - if not 'foo' in metafunc.fixturenames: - return - - test_with = getattr(metafunc.function, 'test_with', None) - if test_with: - params = test_with - metafunc.parametrize('foo', params, indirect=True) - - ''') - result = testdir.runpytest("-s") - output = result.stdout.str() - assert output.count('preparing foo-2') == 1 - assert output.count('preparing foo-3') == 1 - def test_parametrize_issue323(self, testdir): testdir.makepyfile(""" import pytest @@ -1047,6 +1037,125 @@ class TestMetafuncFunctional: assert expectederror in failures[0].longrepr.reprcrash.message +class TestMetafuncFunctionalAuto: + """ + Tests related to automatically find out the correct scope for parametrized tests (#1832). + """ + + def test_parametrize_auto_scope(self, testdir): + testdir.makepyfile(''' + import pytest + + @pytest.fixture(scope='session', autouse=True) + def fixture(): + return 1 + + @pytest.mark.parametrize('animal', ["dog", "cat"]) + def test_1(animal): + assert animal in ('dog', 'cat') + + @pytest.mark.parametrize('animal', ['fish']) + def test_2(animal): + assert animal == 'fish' + + ''') + result = testdir.runpytest() + result.stdout.fnmatch_lines(['* 3 passed *']) + + def test_parametrize_auto_scope_indirect(self, testdir): + testdir.makepyfile(''' + import pytest + + @pytest.fixture(scope='session') + def echo(request): + return request.param + + @pytest.mark.parametrize('animal, echo', [("dog", 1), ("cat", 2)], indirect=['echo']) + def test_1(animal, echo): + assert animal in ('dog', 'cat') + assert echo in (1, 2, 3) + + @pytest.mark.parametrize('animal, echo', [('fish', 3)], indirect=['echo']) + def test_2(animal, echo): + assert animal == 'fish' + assert echo in (1, 2, 3) + ''') + result = testdir.runpytest() + result.stdout.fnmatch_lines(['* 3 passed *']) + + def test_parametrize_auto_scope_override_fixture(self, testdir): + testdir.makepyfile(''' + import pytest + + @pytest.fixture(scope='session', autouse=True) + def animal(): + return 'fox' + + @pytest.mark.parametrize('animal', ["dog", "cat"]) + def test_1(animal): + assert animal in ('dog', 'cat') + ''') + result = testdir.runpytest() + result.stdout.fnmatch_lines(['* 2 passed *']) + + def test_parametrize_all_indirects(self, testdir): + testdir.makepyfile(''' + import pytest + + @pytest.fixture() + def animal(request): + return request.param + + @pytest.fixture(scope='session') + def echo(request): + return request.param + + @pytest.mark.parametrize('animal, echo', [("dog", 1), ("cat", 2)], indirect=True) + def test_1(animal, echo): + assert animal in ('dog', 'cat') + assert echo in (1, 2, 3) + + @pytest.mark.parametrize('animal, echo', [("fish", 3)], indirect=True) + def test_2(animal, echo): + assert animal == 'fish' + assert echo in (1, 2, 3) + ''') + result = testdir.runpytest() + result.stdout.fnmatch_lines(['* 3 passed *']) + + def test_parametrize_issue634(self, testdir): + testdir.makepyfile(''' + import pytest + + @pytest.fixture(scope='module') + def foo(request): + print('preparing foo-%d' % request.param) + return 'foo-%d' % request.param + + def test_one(foo): + pass + + def test_two(foo): + pass + + test_two.test_with = (2, 3) + + def pytest_generate_tests(metafunc): + params = (1, 2, 3, 4) + if not 'foo' in metafunc.fixturenames: + return + + test_with = getattr(metafunc.function, 'test_with', None) + if test_with: + params = test_with + metafunc.parametrize('foo', params, indirect=True) + ''') + result = testdir.runpytest("-s") + output = result.stdout.str() + assert output.count('preparing foo-2') == 1 + assert output.count('preparing foo-3') == 1 + + class TestMarkersWithParametrization: pytestmark = pytest.mark.issue308 def test_simple_mark(self, testdir): diff --git a/testing/test_runner.py b/testing/test_runner.py index a4fe80912..e88a548d7 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -571,6 +571,19 @@ def test_importorskip_dev_module(monkeypatch): pytest.fail("spurious skip") +def test_importorskip_module_level(testdir): + """importorskip must be able to skip entire modules when used at module level""" + testdir.makepyfile(''' + import pytest + foobarbaz = pytest.importorskip("foobarbaz") + + def test_foo(): + pass + ''') + result = testdir.runpytest() + result.stdout.fnmatch_lines(['*collected 0 items / 1 skipped*']) + + def test_pytest_cmdline_main(testdir): p = testdir.makepyfile(""" import pytest diff --git a/testing/test_skipping.py b/testing/test_skipping.py index b11f66858..12b18ca33 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -967,5 +967,5 @@ def test_module_level_skip_error(testdir): """) result = testdir.runpytest() result.stdout.fnmatch_lines( - "*Using @pytest.skip outside a test * is not allowed*" + "*Using @pytest.skip outside of a test * is not allowed*" ) diff --git a/tox.ini b/tox.ini index 55b8440df..3c5621315 100644 --- a/tox.ini +++ b/tox.ini @@ -41,7 +41,7 @@ basepython = python2.7 deps = flake8 restructuredtext_lint commands = flake8 pytest.py _pytest testing - rst-lint CHANGELOG.rst + rst-lint CHANGELOG.rst HOWTORELEASE.rst [testenv:py27-xdist] deps=pytest-xdist>=1.13