From commits-noreply at bitbucket.org Mon Oct 1 09:23:59 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 01 Oct 2012 07:23:59 -0000 Subject: [py-svn] commit/pytest: hpk42: remove unneccessary internal __request__ funcarg. Message-ID: <20121001072359.28067.82599@bitbucket03.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/8a116c6b207a/ changeset: 8a116c6b207a user: hpk42 date: 2012-10-01 09:23:39 summary: remove unneccessary internal __request__ funcarg. affected #: 2 files diff -r 1f79503ce1f8a68185c7f8b2900546fb84606adb -r 8a116c6b207a0b4fa75eb690fee7b2a8c07631ce _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -519,11 +519,8 @@ def fillfuncargs(function): - """ fill missing funcargs. """ - #if not getattr(function, "_args", None) is not None: - # request = FuncargRequest(pyfuncitem=function) - # request._fillfuncargs() - if getattr(function, "_args", None) is None: + """ fill missing funcargs for a test function. """ + if getattr(function, "_args", None) is None: # not a yielded function try: request = function._request except AttributeError: @@ -941,11 +938,6 @@ return property(provide, None, None, func.__doc__) return decoratescope -def pytest_funcarg__request(__request__): - return __request__ - -#def pytest_funcarg__testcontext(__request__): -# return __request__ class FuncargRequest: """ A request for function arguments from a test or setup function. @@ -963,7 +955,6 @@ self.scope = "function" self.getparent = pyfuncitem.getparent self._funcargs = self._pyfuncitem.funcargs.copy() - self._funcargs["__request__"] = self self._name2factory = {} self.funcargmanager = pyfuncitem.session.funcargmanager self._currentarg = None @@ -1078,9 +1069,6 @@ def _fillfuncargs(self): item = self._pyfuncitem funcargnames = getattr(item, "funcargnames", self.funcargnames) - if funcargnames: - assert not getattr(item, '_args', None), ( - "yielded functions cannot have funcargs") for argname in funcargnames: if argname not in item.funcargs: item.funcargs[argname] = self.getfuncargvalue(argname) @@ -1128,23 +1116,28 @@ def getfuncargvalue(self, argname): - """ (deprecated) Retrieve a function argument by name for this test + """ Retrieve a function argument by name for this test function invocation. This allows one function argument factory to call another function argument factory. If there are two funcarg factories for the same test function argument the first factory may use ``getfuncargvalue`` to call the second one and do something additional with the resource. - **Note**, however, that starting with - pytest-2.3 it is easier and better to directly state the needed - funcarg in the factory signature. This will also work seemlessly + **Note**, however, that starting with pytest-2.3 it is usually + easier and better to directly use the needed funcarg in the + factory function signature. This will also work seemlessly with parametrization and the new resource setup optimizations. """ try: return self._funcargs[argname] except KeyError: pass - factorydeflist = self._getfaclist(argname) + try: + factorydeflist = self._getfaclist(argname) + except FuncargLookupError: + if argname == "request": + return self + raise factorydef = factorydeflist.pop() self._factorystack.append(factorydef) try: @@ -1338,10 +1331,6 @@ if facdeflist is not None: for facdef in facdeflist: merge(facdef.funcargnames) - try: - funcargnames.remove("__request__") - except ValueError: - pass return funcargnames, arg2facdeflist def pytest_generate_tests(self, metafunc): diff -r 1f79503ce1f8a68185c7f8b2900546fb84606adb -r 8a116c6b207a0b4fa75eb690fee7b2a8c07631ce testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1758,7 +1758,6 @@ "*LookupError: no factory found for argument 'missing'", ]) - def test_factory_setup_as_classes(self, testdir): testdir.makepyfile(""" import pytest @@ -1779,6 +1778,19 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=1) + def test_request_can_be_overridden(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.factory() + def request(request): + request.a = 1 + return request + def test_request(request): + assert request.a == 1 + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) + class TestResourceIntegrationFunctional: def test_parametrize_with_ids(self, testdir): @@ -2661,18 +2673,7 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=3) -def test_request_can_be_overridden(testdir): - testdir.makepyfile(""" - import pytest - @pytest.factory() - def request(request): - request.a = 1 - return request - def test_request(request): - assert request.a == 1 - """) - reprec = testdir.inline_run() - reprec.assertoutcome(passed=1) + def test_setup_funcarg_order(testdir): testdir.makepyfile(""" Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 1 10:15:08 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 01 Oct 2012 08:15:08 -0000 Subject: [py-svn] commit/pytest: hpk42: fixes to against python3.3 Message-ID: <20121001081508.14208.15290@bitbucket22.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/3414d67a4601/ changeset: 3414d67a4601 user: hpk42 date: 2012-10-01 10:14:54 summary: fixes to against python3.3 affected #: 5 files diff -r 8a116c6b207a0b4fa75eb690fee7b2a8c07631ce -r 3414d67a4601e6ef427a6b4fac09ffd21c387e31 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ Changes between 2.2.4 and 2.3.0.dev ----------------------------------- +- fix python3.3 compat, mostly reporting bits that previously depended + on dict ordering - introduce a generic "markers" object on Nodes and a request.node attribute pointing to the scope-specific collection node of a request. node.markers allows reading and manipulating of MarkInfo objects diff -r 8a116c6b207a0b4fa75eb690fee7b2a8c07631ce -r 3414d67a4601e6ef427a6b4fac09ffd21c387e31 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -652,6 +652,7 @@ for i, valset in enumerate(argvalues): assert len(valset) == len(argnames) newcallspec = callspec.copy(self) + #print ("setmulti %r id %r" % (argnames, ids[i])) newcallspec.setmulti(valtype, argnames, valset, ids[i], scopenum) newcalls.append(newcallspec) @@ -1113,8 +1114,6 @@ self._addfinalizer(finalizer, scope=scope) return val - - def getfuncargvalue(self, argname): """ Retrieve a function argument by name for this test function invocation. This allows one function argument factory @@ -1408,11 +1407,13 @@ def getsetuplist(self, node): nodeid = node.nodeid l = [] - allargnames = set() + allargnames = [] for setupcall in self.setuplist: if nodeid.startswith(setupcall.baseid): l.append(setupcall) - allargnames.update(setupcall.funcargnames) + for arg in setupcall.funcargnames: + if arg not in allargnames: + allargnames.append(arg) l.sort(key=lambda x: x.scopenum) return l, allargnames diff -r 8a116c6b207a0b4fa75eb690fee7b2a8c07631ce -r 3414d67a4601e6ef427a6b4fac09ffd21c387e31 _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -440,7 +440,7 @@ def summary_stats(self): session_duration = py.std.time.time() - self._sessionstarttime - keys = "failed passed skipped deselected".split() + keys = "failed passed skipped deselected xfailed xpassed".split() for key in self.stats.keys(): if key not in keys: keys.append(key) diff -r 8a116c6b207a0b4fa75eb690fee7b2a8c07631ce -r 3414d67a4601e6ef427a6b4fac09ffd21c387e31 testing/acceptance_test.py --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -71,7 +71,7 @@ "*---configure", "*---unconfigure", ]) - + def test_config_preparse_plugin_option(self, testdir): testdir.makepyfile(pytest_xyz=""" @@ -357,13 +357,13 @@ @pytest.mark.skipif("sys.version_info < (2,5)") def test_python_minus_m_invocation_ok(self, testdir): p1 = testdir.makepyfile("def test_hello(): pass") - res = testdir.run(py.std.sys.executable, "-m", "py.test", str(p1)) + res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1)) assert res.ret == 0 @pytest.mark.skipif("sys.version_info < (2,5)") def test_python_minus_m_invocation_fail(self, testdir): p1 = testdir.makepyfile("def test_fail(): 0/0") - res = testdir.run(py.std.sys.executable, "-m", "py.test", str(p1)) + res = testdir.run(py.std.sys.executable, "-m", "pytest", str(p1)) assert res.ret == 1 @pytest.mark.skipif("sys.version_info < (2,5)") diff -r 8a116c6b207a0b4fa75eb690fee7b2a8c07631ce -r 3414d67a4601e6ef427a6b4fac09ffd21c387e31 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -2391,7 +2391,7 @@ def test_3(self): pass """) - result = testdir.runpytest("-v") + result = testdir.runpytest("-vs") result.stdout.fnmatch_lines(""" test_class_ordering.py:4: TestClass2.test_1[1-a] PASSED test_class_ordering.py:4: TestClass2.test_1[2-a] PASSED Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 1 10:31:12 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 01 Oct 2012 08:31:12 -0000 Subject: [py-svn] commit/pytest: hpk42: add py33 to tox.ini, report pypy-1.9 as working as well Message-ID: <20121001083112.14209.44509@bitbucket22.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/df938be9a30f/ changeset: df938be9a30f user: hpk42 date: 2012-10-01 10:31:04 summary: add py33 to tox.ini, report pypy-1.9 as working as well affected #: 2 files diff -r 3414d67a4601e6ef427a6b4fac09ffd21c387e31 -r df938be9a30f4c74bd4eda6c6cf09068330e88f2 setup.py --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ Platforms: Linux, Win32, OSX -Interpreters: Python versions 2.4 through to 3.2, Jython 2.5.1 and PyPy-1.6/1.7 +Interpreters: Python versions 2.4 through to 3.3, Jython 2.5.1 and PyPy-1.9 Bugs and issues: http://bitbucket.org/hpk42/pytest/issues/ @@ -70,4 +70,4 @@ return {'console_scripts': l} if __name__ == '__main__': - main() \ No newline at end of file + main() diff -r 3414d67a4601e6ef427a6b4fac09ffd21c387e31 -r df938be9a30f4c74bd4eda6c6cf09068330e88f2 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] distshare={homedir}/.tox/distshare -envlist=py26,py27,py31,py32,py27-xdist,py25,py24 +envlist=py26,py27,py31,py32,py33,py27-xdist,py25,py24 indexserver= pypi = http://pypi.python.org/simple testrun = http://pypi.testrun.org Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 4 11:51:20 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 04 Oct 2012 09:51:20 -0000 Subject: [py-svn] commit/pytest: hpk42: avoid pyc file issues by parametrizing the test instead of rewriting conftest.py files Message-ID: <20121004095120.7835.89942@bitbucket16.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/45009f2213bc/ changeset: 45009f2213bc user: hpk42 date: 2012-10-04 11:51:14 summary: avoid pyc file issues by parametrizing the test instead of rewriting conftest.py files affected #: 1 file diff -r df938be9a30f4c74bd4eda6c6cf09068330e88f2 -r 45009f2213bc36057470967ff709fd4a77b5257d testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -14,7 +14,8 @@ finally: monkeypatch.undo() - def test_configure_per_fspath(self, testdir): + @pytest.mark.parametrize("mode", "no fd sys".split()) + def test_configure_per_fspath(self, testdir, mode): config = testdir.parseconfig(testdir.tmpdir) capman = CaptureManager() hasfd = hasattr(os, 'dup') @@ -23,13 +24,12 @@ else: assert capman._getmethod(config, None) == "sys" - for name in ('no', 'fd', 'sys'): - if not hasfd and name == 'fd': - continue - sub = testdir.tmpdir.mkdir("dir" + name) - sub.ensure("__init__.py") - sub.join("conftest.py").write('option_capture = %r' % name) - assert capman._getmethod(config, sub.join("test_hello.py")) == name + if not hasfd and mode == 'fd': + return + sub = testdir.tmpdir.mkdir("dir" + mode) + sub.ensure("__init__.py") + sub.join("conftest.py").write('option_capture = %r' % mode) + assert capman._getmethod(config, sub.join("test_hello.py")) == mode @needsosdup @pytest.mark.multi(method=['no', 'fd', 'sys']) Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Oct 5 14:35:27 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 05 Oct 2012 12:35:27 -0000 Subject: [py-svn] commit/pytest: 6 new changesets Message-ID: <20121005123527.7497.5629@bitbucket22.managed.contegix.com> 6 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/b5230e47ebef/ changeset: b5230e47ebef user: hpk42 date: 2012-10-05 10:21:35 summary: merge factories/funcargs and setup functions into the new "fixture" document affected #: 5 files diff -r 45009f2213bc36057470967ff709fd4a77b5257d -r b5230e47ebefaf111624dadc34a7852418dadf3f doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -269,7 +269,11 @@ # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {} # 'http://docs.python.org/': None} +intersphinx_mapping = {'python': ('http://docs.python.org/', None), + 'lib': ("http://docs.python.org/library/", None), + } + + def setup(app): #from sphinx.ext.autodoc import cut_lines #app.connect('autodoc-process-docstring', cut_lines(4, what=['module'])) diff -r 45009f2213bc36057470967ff709fd4a77b5257d -r b5230e47ebefaf111624dadc34a7852418dadf3f doc/en/fixture.txt --- /dev/null +++ b/doc/en/fixture.txt @@ -0,0 +1,681 @@ +.. _xunitsetup: +.. _setup: +.. _fixture: +.. _`fixture functions`: +.. _`@pytest.fixture`: + +pytest fixtures: modular, re-useable, flexible +======================================================== + +.. versionadded:: 2.0,2.3 + +.. _`funcargs`: funcargs.html +.. _`test parametrization`: funcargs.html#parametrizing-tests +.. _`unittest plugin`: plugin/unittest.html +.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit +.. _`general purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software +.. _`django`: https://www.djangoproject.com/ +.. _`pytest-django`: https://pypi.python.org/pytest-django +.. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection#Definition + +pytest allows to provide and use test fixtures in a modular and flexible +manner, offering major improvements over the classic xUnit style of +setup/teardown functions. The `general purpose of test fixtures`_ is to +provide a fixed baseline upon which tests can reliably and +repeatedly execute. With pytest, fixtures are implemented by +**fixture functions** which may return a fixture object, put extra +attributes on test classes or perform side effects. The name of a +fixture function is significant and is used for invoking or activating it. + +**Test functions can receive fixture objects by naming them as an input +argument.** For each argument name, a matching fixture +function will provide a fixture object. This mechanism has been +introduced with pytest-2.0 and is also called the **funcarg mechanism**. +It allows test functions to easily receive and work against specific +pre-initialized application objects without having to care about the +details of setup/cleanup procedures. This mechanism is a prime example of +`dependency injection`_ where fixture functions take the role of the +*injector* and test functions are the *consumers* of fixture objects. +With pytest-2.3 this mechanism has been much improved to help with +sharing and parametrizing fixtures across test runs. + +**Test classes, modules or whole projects can declare a need for +one or more fixtures**. All required fixture functions will execute +before a test from the specifying context executes. They will +typically not provide a fixture object but rather perform side effects +like reading or preparing default config settings and pre-initializing +an application. For example, the Django_ project requires database +initialization to be able to import from and use its model objects. +Plugins like `pytest-django`_ provide baseline fixtures which your +project can then easily depend or extend on. + +**Fixtures can be shared throughout a test session, module or class.**. +By means of a "scope" declaration on a fixture function, it will +only be invoked once per the specified scope. Sharing expensive application +object setups between tests typically helps to speed up test runs. +Typical examples are the setup of test databases or establishing +required subprocesses or network connections. + +**Fixture functions have controlled visilibity** which depends on where they +are defined. If they are defined on a test class, only its test methods +may use it. A fixture defined in a module can only be used +from that test module. A fixture defined in a conftest.py file +can only be used by the tests below the directory of that file. +Lastly plugins can define fixtures which are available across all +projects. + +**Fixture functions can interact with the requesting testcontext**. By +accepting a special ``request`` object, fixture functions can introspect +the function, class or module for which they are invoked and can +optionally register cleanup functions which are called when the last +test finished execution. A good example is `pytest-timeout`_ which +allows to limit the execution time of a test, and will read the +according parameter from a test function or from project-wide setting. + +**Fixture functions can be parametrized** in which case they will be called +multiple times, each time executing the set of dependent tests, i. e. the +tests that depend on this fixture. Test functions do usually not need +to be aware of their re-running. Fixture parametrization helps to +write functional tests for components which themselves can be +configured in multiple ways. + + +Basic funcarg fixture example +----------------------------------------------------------- + +.. versionadded:: 2.3 + + +Let's look at a simple self-contained test module containing a module +visible fixture function and a test function using the provided fixture:: + + # content of ./test_simplefactory.py + import pytest + + @pytest.fixture + def myfuncarg(): + return 42 + + def test_function(myfuncarg): + assert myfuncarg == 17 + +Here, the ``test_function`` needs an object named ``myfuncarg`` and thus +py.test will discover and call the ``@pytest.fixture`` marked ``myfuncarg`` +factory function. Running the tests looks like this:: + + $ py.test test_simplefactory.py + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 + plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov + collecting ... collected 1 items + + test_simplefactory.py F + + ================================= FAILURES ================================= + ______________________________ test_function _______________________________ + + myfuncarg = 42 + + def test_function(myfuncarg): + > assert myfuncarg == 17 + E assert 42 == 17 + + test_simplefactory.py:8: AssertionError + ========================= 1 failed in 0.01 seconds ========================= + +This shows that the test function was called with a ``myfuncarg`` +argument value of ``42`` and the assert fails as expected. Here is +how py.test comes to call the test function this way: + +1. py.test :ref:`finds ` the ``test_function`` because + of the ``test_`` prefix. The test function needs a function argument + named ``myfuncarg``. A matching factory function is discovered by + looking for a fixture function named ``myfuncarg``. + +2. ``myfuncarg()`` is called to create a value ``42``. + +3. ``test_function(42)`` is now called and results in the above + reported exception because of the assertion mismatch. + +Note that if you misspell a function argument or want +to use one that isn't available, you'll see an error +with a list of available function arguments. + +.. Note:: + + You can always issue:: + + py.test --fixtures test_simplefactory.py + + to see available fixtures. + + In versions prior to 2.3 there was no @pytest.fixture marker + and you had to instead use a magic ``pytest_funcarg__NAME`` prefix + for the fixture factory. This remains and will remain supported + but is not advertised as the primary means of declaring fixture + functions. + + +Creating and using a session-shared fixture +----------------------------------------------------------------- + +.. regendoc:wipe + +Here is a simple example of a fixture function creating a shared +``smtplib.SMTP`` connection fixture which test functions from +test modules below the directory of a ``conftest.py`` file may use:: + + # content of conftest.py + import pytest + import smtplib + + @pytest.fixture(scope="session") + def smtp(): + return smtplib.SMTP("merlinux.eu") + +The name of the fixture is ``smtp`` and you can access its result by +listing the name ``smtp`` as an input parameter in any test or setup +function:: + + # content of test_module.py + def test_ehlo(smtp): + response = smtp.ehlo() + assert response[0] == 250 + assert "merlinux" in response[1] + assert 0 # for demo purposes + + def test_noop(smtp): + response = smtp.noop() + assert response[0] == 250 + assert 0 # for demo purposes + +We deliberately insert failing ``assert 0`` statements in order to +inspect what is going on and can now run the tests:: + + $ py.test -q test_module.py + collecting ... collected 2 items + FF + ================================= FAILURES ================================= + ________________________________ test_ehlo _________________________________ + + smtp = + + def test_ehlo(smtp): + response = smtp.ehlo() + assert response[0] == 250 + assert "merlinux" in response[1] + > assert 0 # for demo purposes + E assert 0 + + test_module.py:5: AssertionError + ________________________________ test_noop _________________________________ + + smtp = + + def test_noop(smtp): + response = smtp.noop() + assert response[0] == 250 + > assert 0 # for demo purposes + E assert 0 + + test_module.py:10: AssertionError + 2 failed in 0.26 seconds + +you see the two ``assert 0`` failing and can also see that +the same (session-scoped) object was passed into the two test functions +because pytest shows the incoming arguments in the traceback. + +Adding a finalizer to a fixture +-------------------------------------------------------- + +Further extending the ``smtp`` example, we now want to properly +close a smtp server connection after the last test using it +has been run. We can do this by calling the ``request.addfinalizer()`` +helper:: + + # content of conftest.py + import pytest + import smtplib + + @pytest.fixture(scope="session") + def smtp(request): + smtp = smtplib.SMTP("merlinux.eu") + def fin(): + print ("finalizing %s" % smtp) + smtp.close() + request.addfinalizer(fin) + return smtp + +The registered ``fin`` function will be called when the last test +using it has executed:: + + $ py.test -s -q --tb=no + collecting ... collected 4 items + FFFF + 4 failed in 6.40 seconds + finalizing + +We see that the ``smtp`` instance is finalized after all +tests executed. If we had specified ``scope='function'`` +then fixture setup and cleanup would occur around each +single test. + +Parametrizing a session-shared funcarg resource +----------------------------------------------------------------- + +Extending the previous example, we can flag the fixture to create +two ``smtp`` fixture instances which will cause all tests using the +fixture to run twice. The fixture function gets +access to each parameter through the special `request`_ object:: + + # content of conftest.py + import pytest + import smtplib + + @pytest.fixture(scope="session", + params=["merlinux.eu", "mail.python.org"]) + def smtp(request): + smtp = smtplib.SMTP(request.param) + def fin(): + print ("finalizing %s" % smtp) + smtp.close() + request.addfinalizer(fin) + return smtp + +The main change is the declaration of ``params``, a list of values +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 + collecting ... collected 4 items + FFFF + ================================= FAILURES ================================= + __________________________ test_ehlo[merlinux.eu] __________________________ + + smtp = + + def test_ehlo(smtp): + response = smtp.ehlo() + assert response[0] == 250 + assert "merlinux" in response[1] + > assert 0 # for demo purposes + E assert 0 + + test_module.py:5: AssertionError + __________________________ test_noop[merlinux.eu] __________________________ + + smtp = + + def test_noop(smtp): + response = smtp.noop() + assert response[0] == 250 + > assert 0 # for demo purposes + E assert 0 + + test_module.py:10: AssertionError + ________________________ test_ehlo[mail.python.org] ________________________ + + smtp = + + def test_ehlo(smtp): + response = smtp.ehlo() + assert response[0] == 250 + > assert "merlinux" in response[1] + E assert 'merlinux' in 'mail.python.org\nSIZE 10240000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN' + + test_module.py:4: AssertionError + ________________________ test_noop[mail.python.org] ________________________ + + smtp = + + def test_noop(smtp): + response = smtp.noop() + assert response[0] == 250 + > assert 0 # for demo purposes + E assert 0 + + test_module.py:10: AssertionError + 4 failed in 6.17 seconds + +We now get four failures because we are running the two tests twice with +different ``smtp`` fixture instances. Note that with the +``mail.python.org`` connection the second test fails in ``test_ehlo`` +because it expects a specific server string. + +We also see that the two ``smtp`` instances are finalized appropriately. + +Looking at test collection without running tests +------------------------------------------------------ + +You can also look at the tests which pytest collects without running them:: + + $ py.test --collectonly + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 + plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov + collecting ... collected 4 items + + + + + + + ============================= in 0.01 seconds ============================= + +Our fixture parameters show up in the test id of the test functions. +Note that pytest orders your test run by resource usage, minimizing +the number of active resources at any given time. + + +.. _`interdependent fixtures`: + +Interdepdendent fixtures +---------------------------------------------------------- + +You can not only use fixtures in test functions but fixture functions +can use other fixtures themselves. This contributes to a modular design +of your fixtures and allows re-use of framework-specific fixtures across +many projects. As a simple example, we can extend the previous example +and instantiate an object ``app`` where we stick the already defined +``smtp`` resource into it:: + + # content of test_appsetup.py + + import pytest + + class App: + def __init__(self, smtp): + self.smtp = smtp + + @pytest.fixture(scope="module") + def app(smtp): + return App(smtp) + + def test_smtp_exists(app): + assert app.smtp + +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 + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python + cachedir: /home/hpk/tmp/doc-exec-423/.cache + plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov + collecting ... collected 2 items + + test_appsetup.py:12: test_exists[merlinux.eu] PASSED + test_appsetup.py:12: test_exists[mail.python.org] PASSED + + ========================= 2 passed in 6.82 seconds ========================= + +Due to the parametrization of ``smtp`` the test will run twice with two +different ``App`` instances and respective smtp servers. There is no +need for the ``app`` fixture to be aware of the ``smtp`` parametrization +as pytest will fully analyse the fixture dependency graph. Note also, +that the ``app`` fixture has a scope of ``module`` but uses a +session-scoped ``smtp``: it is fine for fixtures to use "broader" scoped +fixtures but not the other way round: A session-scoped fixture could +not use a module-scoped one in a meaningful way. + +.. _`automatic per-resource grouping`: + +Automatic grouping of tests by fixture instances +---------------------------------------------------------- + +.. regendoc: wipe + +pytest minimizes the number of active fixtures during test runs. +If you have a parametrized fixture, then all the tests using it will +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 +scoped on a per-module basis, and all the functions perform ``print`` call s +to show the flow of calls:: + + # content of test_module.py + import pytest + + @pytest.fixture(scope="module", params=["mod1", "mod2"]) + def modarg(request): + param = request.param + print "create", param + def fin(): + print "fin", param + request.addfinalizer(fin) + return param + + @pytest.fixture(scope="function", params=[1,2]) + def otherarg(request): + return request.param + + def test_0(otherarg): + print " test0", otherarg + def test_1(modarg): + print " test1", modarg + def test_2(otherarg, modarg): + print " test2", otherarg, modarg + +Let's run the tests in verbose mode and with looking at the print-output:: + + $ py.test -v -s test_module.py + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python + cachedir: /home/hpk/tmp/doc-exec-423/.cache + plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov + collecting ... collected 8 items + + test_module.py:16: test_0[1] PASSED + test_module.py:16: test_0[2] PASSED + test_module.py:18: test_1[mod1] PASSED + test_module.py:20: test_2[1-mod1] PASSED + test_module.py:20: test_2[2-mod1] PASSED + test_module.py:18: test_1[mod2] PASSED + test_module.py:20: test_2[1-mod2] PASSED + test_module.py:20: test_2[2-mod2] PASSED + + ========================= 8 passed in 0.02 seconds ========================= + test0 1 + test0 2 + create mod1 + test1 mod1 + test2 1 mod1 + test2 2 mod1 + fin mod1 + create mod2 + test1 mod2 + test2 1 mod2 + test2 2 mod2 + fin mod2 + +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. + + +Marking test classes, modules, projects with required fixtures +---------------------------------------------------------------------- + +.. regendoc:wipe + +Sometimes test functions do not directly get access to a fixture object. +For example, each test in a test class may require to operate with an +empty directory as the current working directory. Here is how you can +can use the standard :ref:`tempfile ` and pytest fixtures +to achieve it. We separate the creation of the fixture into +a conftest.py file:: + + # content of conftest.py + + import pytest + import tempfile + import os + + @pytest.fixture() + def cleandir(): + newpath = tempfile.mkdtemp() + os.chdir(newpath) + +and declare its use in a test module via a ``needs`` marker:: + + # content of test_setenv.py + import os + import pytest + + @pytest.mark.needsfixtures("cleandir") + class TestDirectoryInit: + def test_cwd_starts_empty(self): + assert os.listdir(os.getcwd()) == [] + with open("myfile", "w") as f: + f.write("hello") + + def test_cwd_again_starts_empty(self): + assert os.listdir(os.getcwd()) == [] + +Due to the ``needs`` class marker, the ``cleandir`` fixture +will be required for the execution of each of the test methods, just as if +you specified a "cleandir" function argument to each of them. Let's run it +to verify our fixture is activated:: + + $ py.test -q + collecting ... collected 2 items + . + 2 passed in 0.01 seconds + +You may specify the need for multiple fixtures:: + + @pytest.mark.needsfixtures("cleandir", "anotherfixture") + +and you may specify fixture needs at the test module level, using +a generic feature of the mark mechanism:: + + pytestmark = pytest.mark.needsfixtures("cleandir") + +Lastly you can put fixtures required by all tests in your project +into an ini-file:: + + # content of pytest.ini + + [pytest] + needsfixtures = cleandir + +Implicit fixtures at class/module/directory/global level +---------------------------------------------------------------------- + +.. regendoc:wipe + +Occasionally, you may want to have fixtures get invoked automatically +without any ``needs`` reference. Also, if you are used to the classical +xUnit setup/teardown functions you may have gotten used to fixture +functions executing always. As a practical example, +suppose we have a database fixture which has a begin/rollback/commit +architecture and we want to surround each test method by a transaction +and a rollback. Here is a dummy self-contained implementation:: + + # content of test_db_transact.py + + import pytest + + @pytest.fixture(scope="module") + class db: + def __init__(self): + self.intransaction = False + def begin(self): + self.intransaction = True + def rollback(Self): + self.intransaction = False + + class TestClass: + @pytest.fixture(auto=True) + def transact(self, request, db): + db.begin() + request.addfinalizer(db.rollback) + + def test_method1(self, db): + assert db.intransaction + + def test_method2(self): + pass + +The class-level ``transact`` fixture is marked with *auto=true* which will +mark all test methods in the class as needing the fixture. + +Here is how this maps to module, project and cross-project scopes: + +- if an automatic fixture was defined in a test module, all its test + functions would automatically invoke it. + +- if defined in a conftest.py file then all tests in all test + modules belows its directory will invoke the fixture. + +- lastly, and **please use that with care**: if you define an automatic + fixture in a plugin, it will be invoked for all tests in all projects + where the plugin is installed. This can be useful if a fixture only + anyway works in the presence of certain settings in the ini-file. Such + a global fixture should thus quickly determine if it should do + any work and avoid expensive imports or computation otherwise. + +Note that the above ``transact`` fixture may very well be something that +you want to make available in your project without having each test function +in your project automatically using it. The canonical way to do that is to put +the transact definition into a conftest.py file without using ``auto``:: + + # content of conftest.py + @pytest.fixture() + def transact(self, request, db): + db.begin() + request.addfinalizer(db.rollback) + +and then have a TestClass using it by declaring the need:: + + @pytest.mark.needsfixtures("transact") + class TestClass: + def test_method1(self): + ... + +While all test methods in this TestClass will thus use the transaction +fixture, other test classes will not unless they state the need. + +.. currentmodule:: _pytest.python + +.. _`@pytest.fixture`: + +``@pytest.fixture``: marking a fixture function +-------------------------------------------------------------- + +The ``@pytest.fixture`` marker allows to + +* mark a function as a factory for fixtures, useable by test and other + fixture functions + +* declare a scope which determines the level of caching, i.e. how often + the factory will be called. Valid scopes are ``session``, ``module``, + ``class`` and ``function``. + +* define a list of parameters in order to run dependent tests multiple + times with different fixtures + +.. _`request`: + +``request``: interacting with test invocation context +-------------------------------------------------------------- + +The ``request`` object may be received by fixture functions +and provides methods to: + +* to inspect attributes of the requesting test context, such as + ``function``, ``cls``, ``module``, ``session`` and the pytest + ``config`` object. A request object passed to a parametrized factory + will also carry a ``request.param`` object (A parametrized factory and + all of its dependent tests will be called with each of the factory-specified + ``params``). + +* to add finalizers/teardowns to be invoked when the last + test of the requesting test context executes + +.. autoclass:: _pytest.python.FuncargRequest() + :members: + diff -r 45009f2213bc36057470967ff709fd4a77b5257d -r b5230e47ebefaf111624dadc34a7852418dadf3f doc/en/funcargs.txt --- a/doc/en/funcargs.txt +++ b/doc/en/funcargs.txt @@ -159,366 +159,6 @@ funcarg factories starts at test classes, then test modules, then ``conftest.py`` files and finally builtin and 3-rd party plugins. -Creating and using a session-shared funcarg ------------------------------------------------------------------ - -.. regendoc:wipe - -.. versionadded:: 2.3 - -The `@pytest.factory`_ marker allows to - -* mark a function as a factory for resources, useable by test and setup - functions - -* define parameters in order to run tests multiple times with - different resource instances - -* declare a scope which determines the level of caching, i.e. how often - the factory will be called. Valid scopes are ``session``, ``module``, - ``class`` and ``function``. - -Here is a simple example of a factory creating a shared ``smtplib.SMTP`` -connection resource which test functions then may use across the whole -test session:: - - # content of conftest.py - import pytest - import smtplib - - @pytest.factory(scope="session") - def smtp(request): - return smtplib.SMTP("merlinux.eu") - -The name of the factory is ``smtp`` (the factory function name) -and you can access its result by listing the name ``smtp`` as -an input parameter in any test or setup function:: - - # content of test_module.py - def test_ehlo(smtp): - response = smtp.ehlo() - assert response[0] == 250 - assert "merlinux" in response[1] - assert 0 # for demo purposes - - def test_noop(smtp): - response = smtp.noop() - assert response[0] == 250 - assert 0 # for demo purposes - -We deliberately insert failing ``assert 0`` statements in order to -inspect what is going on and can now run the tests:: - - $ py.test -q test_module.py - collecting ... collected 2 items - FF - ================================= FAILURES ================================= - ________________________________ test_ehlo _________________________________ - - smtp = - - def test_ehlo(smtp): - response = smtp.ehlo() - assert response[0] == 250 - assert "merlinux" in response[1] - > assert 0 # for demo purposes - E assert 0 - - test_module.py:5: AssertionError - ________________________________ test_noop _________________________________ - - smtp = - - def test_noop(smtp): - response = smtp.noop() - assert response[0] == 250 - > assert 0 # for demo purposes - E assert 0 - - test_module.py:10: AssertionError - 2 failed in 0.26 seconds - -you see the two ``assert 0`` failing and can also see that -the same (session-scoped) object was passed into the two test functions. - - -Parametrizing a session-shared funcarg resource ------------------------------------------------------------------ - -Extending the previous example, we can flag the factory to create -two ``smtp`` values which will cause all tests using it to -run twice with two different values. The factory function gets -access to each parameter through the special `request`_ object:: - - # content of conftest.py - import pytest - import smtplib - - @pytest.factory(scope="session", - params=["merlinux.eu", "mail.python.org"]) - def smtp(request): - return smtplib.SMTP(request.param) - -The main change is the definition of a ``params`` list in the -``factory``-marker and the ``request.param`` access within the -factory function. No test function code needs to change. -So let's just do another run:: - - $ py.test -q - collecting ... collected 4 items - FFFF - ================================= FAILURES ================================= - __________________________ test_ehlo[merlinux.eu] __________________________ - - smtp = - - def test_ehlo(smtp): - response = smtp.ehlo() - assert response[0] == 250 - assert "merlinux" in response[1] - > assert 0 # for demo purposes - E assert 0 - - test_module.py:5: AssertionError - __________________________ test_noop[merlinux.eu] __________________________ - - smtp = - - def test_noop(smtp): - response = smtp.noop() - assert response[0] == 250 - > assert 0 # for demo purposes - E assert 0 - - test_module.py:10: AssertionError - ________________________ test_ehlo[mail.python.org] ________________________ - - smtp = - - def test_ehlo(smtp): - response = smtp.ehlo() - assert response[0] == 250 - > assert "merlinux" in response[1] - E assert 'merlinux' in 'mail.python.org\nSIZE 10240000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN' - - test_module.py:4: AssertionError - ________________________ test_noop[mail.python.org] ________________________ - - smtp = - - def test_noop(smtp): - response = smtp.noop() - assert response[0] == 250 - > assert 0 # for demo purposes - E assert 0 - - test_module.py:10: AssertionError - 4 failed in 6.17 seconds - -We get four failures because we are running the two tests twice with -different ``smtp`` instantiations as defined on the factory. -Note that with the ``mail.python.org`` connection the second test -fails in ``test_ehlo`` because it expects a specific server string. - -Adding a finalizer to the parametrized resource --------------------------------------------------------- - -Further extending the ``smtp`` example, we now want to properly -close a smtp server connection after the last test using it -has been run. We can do this by calling the ``request.addfinalizer()`` -helper:: - - # content of conftest.py - import pytest - import smtplib - - @pytest.factory(scope="session", - params=["merlinux.eu", "mail.python.org"]) - def smtp(request): - smtp = smtplib.SMTP(request.param) - def fin(): - print ("finalizing %s" % smtp) - smtp.close() - request.addfinalizer(fin) - return smtp - -We also add a print call and then run py.test without the default -output capturing and disabled traceback reporting:: - - $ py.test -s -q --tb=no - collecting ... collected 4 items - FFFF - 4 failed in 6.40 seconds - finalizing - finalizing - -We see that the two ``smtp`` instances are finalized appropriately. - -Looking at test collection without running tests ------------------------------------------------------- - -You can also look at what tests pytest collects without running them:: - - $ py.test --collectonly - =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 4 items - - - - - - - ============================= in 0.01 seconds ============================= - -Note that pytest orders your test run by resource usage, minimizing -the number of active resources at any given time. - - -.. _`interdependent resources`: - -Interdepdendent resources ----------------------------------------------------------- - -You can not only use funcargs in test functions but also in their factories -themselves. Extending the previous example we can instantiate another -object ``app`` and stick the ``smtp`` resource into it like this:: - - # content of test_appsetup.py - - import pytest - - @pytest.factory(scope="module") - class app: - def __init__(self, smtp): - self.smtp = smtp - - def test_exists(app): - assert app.smtp - -Here we declare an ``app`` class to be a factory and have its init-method -use the previously defined ``smtp`` resource. Let's run it:: - - $ py.test -v test_appsetup.py - =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python - cachedir: /home/hpk/tmp/doc-exec-423/.cache - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 2 items - - test_appsetup.py:12: test_exists[merlinux.eu] PASSED - test_appsetup.py:12: test_exists[mail.python.org] PASSED - - ========================= 2 passed in 6.82 seconds ========================= - -Due to the parametrization of ``smtp`` the test will run twice with two -different ``App`` instances and respective smtp servers. There is no -need for the ``app`` factory to be aware of the parametrization. Note -that the ``app`` factory has a scope of ``module`` but uses the -session-scoped ``smtp`` object: it is fine for factories to use -"broader" scoped resources but not the other way round. A -session-scoped resource could not use a module-scoped resource in a -meaningful way. - -.. _`automatic per-resource grouping`: - -Grouping tests by resource parameters ----------------------------------------------------------- - -.. regendoc: wipe - -pytest minimizes the number of active resources during test runs. -If you have a parametrized resource, then all the tests using one -resource instance will execute one after another. Then any finalizers -are called for that resource instance and then the next parametrized -resource instance is created and its tests are run. 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 -scoped on a per-module basis, and all the functions perform ``print`` call s -to show the flow of calls:: - - # content of test_module.py - import pytest - - @pytest.factory(scope="module", params=["mod1", "mod2"]) - def modarg(request): - param = request.param - print "create", param - def fin(): - print "fin", param - request.addfinalizer(fin) - return param - - @pytest.factory(scope="function", params=[1,2]) - def otherarg(request): - return request.param - - def test_0(otherarg): - print " test0", otherarg - def test_1(modarg): - print " test1", modarg - def test_2(otherarg, modarg): - print " test2", otherarg, modarg - -Let's run the tests in verbose mode and with looking at the print-output:: - - $ py.test -v -s test_module.py - =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python - cachedir: /home/hpk/tmp/doc-exec-423/.cache - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 8 items - - test_module.py:16: test_0[1] PASSED - test_module.py:16: test_0[2] PASSED - test_module.py:18: test_1[mod1] PASSED - test_module.py:20: test_2[1-mod1] PASSED - test_module.py:20: test_2[2-mod1] PASSED - test_module.py:18: test_1[mod2] PASSED - test_module.py:20: test_2[1-mod2] PASSED - test_module.py:20: test_2[2-mod2] PASSED - - ========================= 8 passed in 0.02 seconds ========================= - test0 1 - test0 2 - create mod1 - test1 mod1 - test2 1 mod1 - test2 2 mod1 - fin mod1 - create mod2 - test1 mod2 - test2 1 mod2 - test2 2 mod2 - fin mod2 - -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. - -.. currentmodule:: _pytest.python -.. _`request`: - -``request``: interacting with test invocation context -------------------------------------------------------- - -The ``request`` object may be received by `@pytest.factory`_ or -:ref:`@pytest.setup ` marked functions and provides methods - -* to inspect attributes of the requesting test context, such as - ``function``, ``cls``, ``module``, ``session`` and the pytest - ``config`` object. A request object passed to a parametrized factory - will also carry a ``request.param`` object (A parametrized factory and - all of its dependent tests will be called with each of the factory-specified - ``params``). - -* to add finalizers/teardowns to be invoked when the last - test of the requesting test context executes - -.. autoclass:: _pytest.python.FuncargRequest() - :members: .. _`test generators`: diff -r 45009f2213bc36057470967ff709fd4a77b5257d -r b5230e47ebefaf111624dadc34a7852418dadf3f doc/en/setup.txt --- a/doc/en/setup.txt +++ b/doc/en/setup.txt @@ -1,212 +1,12 @@ -.. _xunitsetup: -.. _setup: -.. _`setup functions`: -.. _`@pytest.setup`: -``@setup`` functions or: xunit on steroids +Page has moved to fixture ======================================================== -.. versionadded:: 2.3 +During development prior to the pytest-2.3 release the name +``pytest.setup`` was used but before the release it was renamed +to :ref:`pytest.fixture` mainly to avoid the misconception that there +should be a ``pytest.teardown`` as well. -.. _`funcargs`: funcargs.html -.. _`test parametrization`: funcargs.html#parametrizing-tests -.. _`unittest plugin`: plugin/unittest.html -.. _`xUnit`: http://en.wikipedia.org/wiki/XUnit +Please refer to :ref:`pytest.fixture` for information on the new +fixture functions. -Python, Java and many other languages support a so called xUnit_ style -of resource setup. This typically involves the call of a ``setup`` -("fixture") method before running a test function and ``teardown`` after -it has finished. Unlike :ref:`injected resources ` setup -functions work indirectly by causing global side effects or -setting test case attributes which test methods can then access. - -pytest originally introduced in 2005 a scope-specific model of detecting -setup and teardown functions on a per-module, class or function basis. -The Python unittest package and nose have subsequently incorporated them. -This model remains supported by pytest as :ref:`old-style xunit`. - -Moreover, pytest-2.3 introduces a new ``pytest.setup()`` decorator -to mark functions as setup functions which allow to implement everything -you can do with the old-style and much more. Specifically setup functions: - -- can receive :ref:`resources through funcargs `, -- fully interoperate with parametrized resources, -- can be defined in a plugin or :ref:`conftest.py ` file and get called - on a per-session, per-module, per-class or per-function basis, -- can access the :ref:`request ` for which the setup is called, -- can precisely control teardown by registering one or multiple - teardown functions as soon as they have performed some actions - which need undoing, eliminating the no need for a separate - "teardown" decorator. -- allow to separate different setup concerns even if they - happen to work in the same scope - -All of these features are now demonstrated by little examples. - -.. _`new_setup`: -.. _`@pytest.setup`: - -basic per-function setup -------------------------------- - -.. regendoc:wipe - -Suppose you want to have a clean directory with a single -file entry for each test function in a module and have -the test execute with this directory as current working dir:: - - # content of test_funcdir.py - import pytest - import os - - @pytest.setup() - def mydir(tmpdir): - tmpdir.join("myfile").write("example content") - old = tmpdir.chdir() - - def test_function1(): - assert os.path.exists("myfile") - f = open("anotherfile", "w") - f.write("") - f.close() - - def test_function2(): - assert os.path.exists("myfile") - assert not os.path.exists("anotherfile") - -Our ``mydir`` setup function is executed on a per-function basis, -the default scope used by the ``pytest.setup`` decorator. -It accesses the ``tmpdir`` resource which provides a new empty -directory path object. The ``test_function2`` here checks that -it executes with a fresh directory and that it -does not see the previously created ``anotherfile``. We can -thus expect two passing tests:: - - $ py.test -v - =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev7 -- /home/hpk/venv/1/bin/python - cachedir: /home/hpk/tmp/doc-exec-410/.cache - plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov - collecting ... collected 2 items - - test_funcdir.py:9: test_function1 PASSED - test_funcdir.py:15: test_function2 PASSED - - ========================= 2 passed in 0.26 seconds ========================= - -per-function setup, for every function of a project ------------------------------------------------------------- - -If you want to define a setup per-function but want to apply -it to every function in your project you don't need to duplicate -the setup-definition into each test module. Instead you can put -it into a ``conftest.py`` file into the root of your project:: - - # content of conftest.py - import pytest - import os - - @pytest.setup() - def cleandir(tmpdir): - old = tmpdir.chdir() - -The ``cleandir`` setup function will be called for every test function -below the directory tree where ``conftest.py`` resides. In this -case it just uses the builtin ``tmpdir`` resource to change to the -empty directory ahead of running a test. - -test modules accessing a global resource -------------------------------------------------------- - -.. note:: - - Relying on `global state is considered bad programming practise `_ but when you work with an application - that relies on it you often have no choice. - -If you want test modules to access a global resource, -you can stick the resource to the module globals in -a per-module setup function. We use a :ref:`resource factory -<@pytest.factory>` to create our global resource:: - - # content of conftest.py - import pytest - - class GlobalResource: - def __init__(self): - pass - - @pytest.factory(scope="session") - def globresource(): - return GlobalResource() - - @pytest.setup(scope="module") - def setresource(request, globresource): - request.module.globresource = globresource - -Now any test module can access ``globresource`` as a module global:: - - # content of test_glob.py - - def test_1(): - print ("test_1 %s" % globresource) - def test_2(): - print ("test_2 %s" % globresource) - -Let's run this module without output-capturing:: - - $ py.test -qs test_glob.py - collecting ... collected 2 items - .. - 2 passed in 0.02 seconds - test_1 - test_2 - -The two tests see the same global ``globresource`` object. - -Parametrizing the global resource -+++++++++++++++++++++++++++++++++++++++++++++++++ - -We extend the previous example and add parametrization to the globresource -factory and also add a finalizer:: - - # content of conftest.py - - import pytest - - class GlobalResource: - def __init__(self, param): - self.param = param - - @pytest.factory(scope="session", params=[1,2]) - def globresource(request): - g = GlobalResource(request.param) - def fin(): - print "finalizing", g - request.addfinalizer(fin) - return g - - @pytest.setup(scope="module") - def setresource(request, globresource): - request.module.globresource = globresource - -And then re-run our test module:: - - $ py.test -qs test_glob.py - collecting ... collected 4 items - .... - 4 passed in 0.02 seconds - test_1 - test_2 - finalizing - test_1 - test_2 - finalizing - -We are now running the two tests twice with two different global resource -instances. Note that the tests are ordered such that only -one instance is active at any given time: the finalizer of -the first globresource instance is called before the second -instance is created and sent to the setup functions. - - - diff -r 45009f2213bc36057470967ff709fd4a77b5257d -r b5230e47ebefaf111624dadc34a7852418dadf3f doc/en/xunit_setup.txt --- a/doc/en/xunit_setup.txt +++ b/doc/en/xunit_setup.txt @@ -1,16 +1,16 @@ -.. _`old-style xunit`: +.. _`classic xunit`: -Old-style xunit-style setup +classic xunit-style setup ======================================== .. note:: This section describes the old way how you can implement setup and teardown on a per-module/class/function basis. It remains fully - supported but it is recommended to rather use :ref:`@setup functions - ` or :ref:`injected resources ` for implementing your - setup needs. + supported but it is recommended to rather use :ref:`fixture functions + ` or :ref:`funcargs ` for implementing your + needs to prepare and fix the test state for your tests. Module level setup/teardown -------------------------------------- https://bitbucket.org/hpk42/pytest/changeset/de544d09912b/ changeset: de544d09912b user: hpk42 date: 2012-10-05 10:21:35 summary: rename pytest.factory usages into pytest.fixture ones affected #: 8 files diff -r b5230e47ebefaf111624dadc34a7852418dadf3f -r de544d09912bf791c6635f023ce153348e38e961 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -10,12 +10,12 @@ import _pytest cutdir = py.path.local(_pytest.__file__).dirpath() -class FactoryMarker: +class FixtureFunctionMarker: def __init__(self, scope, params): self.scope = scope self.params = params def __call__(self, function): - function._pytestfactory = self + function._pytestfixturefunction = self return function class SetupMarker: @@ -26,18 +26,32 @@ return function # XXX a test fails when scope="function" how it should be, investigate -def factory(scope=None, params=None): - """ return a decorator to mark functions as resource factories. +def fixture(scope=None, params=None): + """ return a decorator to mark a fixture factory function. - :arg scope: the scope for which this resource is shared, one of + The name of the fixture function can be referenced in a test context + to cause activation ahead of running tests. Test modules or classes + can use the pytest.mark.needsfixtures(fixturename) marker to specify + needed fixtures. Test functions can use fixture names as input arguments + in which case the object returned from the fixture function will be + injected. + + :arg scope: the scope for which this fixture is shared, one of "function", "class", "module", "session". Defaults to "function". :arg params: an optional list of parameters which will cause multiple - invocations of tests depending on the resource. + invocations of the fixture functions and their dependent + tests. """ - return FactoryMarker(scope, params) + return FixtureFunctionMarker(scope, params) def setup(scope="function"): - """ return a decorator to mark functions as setup functions. + """ return a decorator to mark a function as providing a fixture for + a testcontext. A fixture function is executed for each scope and may + receive funcargs which allows it to initialise and provide implicit + test state. A fixture function may receive the "testcontext" object + and register a finalizer via "testcontext.addfinalizer(finalizer)" + which will be called when the last test in the testcontext has + executed. :arg scope: the scope for which the setup function will be active, one of "function", "class", "module", "session". @@ -112,7 +126,7 @@ def pytest_namespace(): raises.Exception = pytest.fail.Exception return { - 'factory': factory, + 'fixture': fixture, 'setup': setup, 'raises' : raises, 'collect': { @@ -1377,9 +1391,9 @@ continue # resource factories either have a pytest_funcarg__ prefix # or are "funcarg" marked - marker = getattr(obj, "_pytestfactory", None) + marker = getattr(obj, "_pytestfixturefunction", None) if marker is not None: - if not isinstance(marker, FactoryMarker): + if not isinstance(marker, FixtureFunctionMarker): # magic globals with __getattr__ # give us something thats wrong for that case continue diff -r b5230e47ebefaf111624dadc34a7852418dadf3f -r de544d09912bf791c6635f023ce153348e38e961 doc/en/example/costlysetup/conftest.py --- a/doc/en/example/costlysetup/conftest.py +++ b/doc/en/example/costlysetup/conftest.py @@ -1,7 +1,7 @@ import pytest - at pytest.factory("session") + at pytest.fixture("session") def setup(request): setup = CostlySetup() request.addfinalizer(setup.finalize) diff -r b5230e47ebefaf111624dadc34a7852418dadf3f -r de544d09912bf791c6635f023ce153348e38e961 doc/en/example/multipython.py --- a/doc/en/example/multipython.py +++ b/doc/en/example/multipython.py @@ -5,12 +5,12 @@ import py, pytest pythonlist = ['python2.4', 'python2.5', 'python2.6', 'python2.7', 'python2.8'] - at pytest.factory(params=pythonlist) + at pytest.fixture(params=pythonlist) def python1(request, tmpdir): picklefile = tmpdir.join("data.pickle") return Python(request.param, picklefile) - at pytest.factory(params=pythonlist) + at pytest.fixture(params=pythonlist) def python2(request, python1): return Python(request.param, python1.picklefile) diff -r b5230e47ebefaf111624dadc34a7852418dadf3f -r de544d09912bf791c6635f023ce153348e38e961 doc/en/faq.txt --- a/doc/en/faq.txt +++ b/doc/en/faq.txt @@ -125,7 +125,7 @@ .. note:: - With pytest-2.3 you can use the :ref:`@pytest.factory` decorator + With pytest-2.3 you can use the :ref:`@pytest.fixture` decorator to mark a function as a funcarg factory. .. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration @@ -146,7 +146,7 @@ policy - in real-world examples some combinations often should not run. -However, with pytest-2.3 you can use the :ref:`@pytest.factory` decorator +However, with pytest-2.3 you can use the :ref:`@pytest.fixture` decorator and specify ``params`` so that all tests depending on the factory-created resource will run multiple times with different parameters. diff -r b5230e47ebefaf111624dadc34a7852418dadf3f -r de544d09912bf791c6635f023ce153348e38e961 doc/en/funcarg_compare.txt --- a/doc/en/funcarg_compare.txt +++ b/doc/en/funcarg_compare.txt @@ -63,9 +63,9 @@ Direct scoping of funcarg factories -------------------------------------------------------- -Instead of calling cached_setup(), you can use the :ref:`@pytest.factory <@pytest.factory>` decorator and directly state the scope:: +Instead of calling cached_setup(), you can use the :ref:`@pytest.fixture <@pytest.fixture>` decorator and directly state the scope:: - @pytest.factory(scope="session") + @pytest.fixture(scope="session") def db(request): # factory will only be invoked once per session - db = DataBase() @@ -87,7 +87,7 @@ parametrization, i.e. calling a test multiple times with different value sets. pytest-2.3 introduces a decorator for use on the factory itself:: - @pytest.factory(params=["mysql", "pg"]) + @pytest.fixture(params=["mysql", "pg"]) def db(request): ... # use request.param @@ -104,7 +104,7 @@ Of course it's perfectly fine to combine parametrization and scoping:: - @pytest.factory(scope="session", params=["mysql", "pg"]) + @pytest.fixture(scope="session", params=["mysql", "pg"]) def db(request): if request.param == "mysql": db = MySQL() @@ -125,7 +125,7 @@ denotes the name under which the resource can be accessed as a function argument:: - @pytest.factory() + @pytest.fixture() def db(request): ... diff -r b5230e47ebefaf111624dadc34a7852418dadf3f -r de544d09912bf791c6635f023ce153348e38e961 doc/en/funcargs.txt --- a/doc/en/funcargs.txt +++ b/doc/en/funcargs.txt @@ -51,7 +51,7 @@ Concretely, there are three main means of funcarg management: -* a `@pytest.factory`_ marker to define resource factories, +* a `@pytest.fixture`_ marker to define resource factories, their scoping and parametrization. Factories can themselves receive resources through their function arguments, easing the setup of `interdependent resources`_. Factories can use @@ -77,9 +77,9 @@ setup statically into the test source code, leading to duplicate and hard-to change fixtures. -.. _`@pytest.factory`: +.. _`@pytest.fixture`: -``@pytest.factory``: Creating parametrized, scoped resources +``@pytest.fixture``: Creating parametrized, scoped resources ===================================================================== Basic funcarg injection example @@ -91,7 +91,7 @@ # content of ./test_simplefactory.py import pytest - @pytest.factory() + @pytest.fixture() def myfuncarg(): return 42 @@ -99,7 +99,7 @@ assert myfuncarg == 17 Here, the ``test_function`` needs an object named ``myfuncarg`` and thus -py.test will discover and call the ``@pytest.factory`` marked ``myfuncarg`` +py.test will discover and call the ``@pytest.fixture`` marked ``myfuncarg`` factory function. Running the tests looks like this:: $ py.test test_simplefactory.py @@ -168,7 +168,7 @@ Parametrizing test functions ========================================================================== -While the `@pytest.factory`_ decorator allows to define parametrization +While the `@pytest.fixture`_ decorator allows to define parametrization of funcarg resources at the factory-level, there are also means to define parametrization at test functions directly: @@ -365,18 +365,18 @@ * previously funcarg factories were specified with a special ``pytest_funcarg__NAME`` prefix instead of using the - ``@pytest.factory`` decorator. + ``@pytest.fixture`` decorator. * Factories received a `request`_ object which managed caching through ``request.cached_setup()`` calls and allowed using other funcargs via ``request.getfuncargvalue()`` calls. These intricate APIs made it hard to do proper parametrization and implement resource caching. The - new ``@pytest.factory`` decorator allows to simply declare the scope + new ``@pytest.fixture`` decorator allows to simply declare the scope and let pytest figure things out for you. * if you used parametrization and funcarg factories which made use of ``request.cached_setup()`` it is recommeneded to invest a few minutes - and simplify your funcarg factory code to use the `@pytest.factory`_ + and simplify your funcarg factory code to use the `@pytest.fixture`_ decorator instead. This will also allow to take advantage of the `automatic per-resource grouping`_ of tests. diff -r b5230e47ebefaf111624dadc34a7852418dadf3f -r de544d09912bf791c6635f023ce153348e38e961 doc/en/unittest.txt --- a/doc/en/unittest.txt +++ b/doc/en/unittest.txt @@ -82,7 +82,7 @@ import pytest import unittest - @pytest.factory() + @pytest.fixture() def db(): class DummyDB: x = 1 diff -r b5230e47ebefaf111624dadc34a7852418dadf3f -r de544d09912bf791c6635f023ce153348e38e961 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -744,7 +744,7 @@ def test_accessmarker_function(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def markers(request): return request.node.markers @pytest.mark.XYZ @@ -758,7 +758,7 @@ def test_accessmarker_dynamic(self, testdir): testdir.makeconftest(""" import pytest - @pytest.factory() + @pytest.fixture() def markers(request): return request.node.markers @@ -1674,11 +1674,11 @@ def test_receives_funcargs(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def arg1(): return 1 - @pytest.factory() + @pytest.fixture() def arg2(arg1): return arg1 + 1 @@ -1694,11 +1694,11 @@ def test_receives_funcargs_scope_mismatch(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory(scope="function") + @pytest.fixture(scope="function") def arg1(): return 1 - @pytest.factory(scope="module") + @pytest.fixture(scope="module") def arg2(arg1): return arg1 + 1 @@ -1717,12 +1717,12 @@ testdir.makepyfile(""" import pytest l = [] - @pytest.factory(params=[1,2]) + @pytest.fixture(params=[1,2]) def arg1(request): l.append(1) return request.param - @pytest.factory() + @pytest.fixture() def arg2(arg1): return arg1 + 1 @@ -1739,11 +1739,11 @@ testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def fail(missing): return - @pytest.factory() + @pytest.fixture() def call_fail(fail): return @@ -1764,7 +1764,7 @@ class arg1: def __init__(self, request): self.x = 1 - arg1 = pytest.factory()(arg1) + arg1 = pytest.fixture()(arg1) class MySetup: def __init__(self, request, arg1): @@ -1781,7 +1781,7 @@ def test_request_can_be_overridden(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def request(request): request.a = 1 return request @@ -1863,7 +1863,7 @@ def perfunction(request, tmpdir): pass - @pytest.factory() + @pytest.fixture() def arg1(tmpdir): pass @pytest.setup() @@ -1918,7 +1918,7 @@ def enabled(parentnode, markers): return "needsdb" in markers - @pytest.factory(params=[1,2]) + @pytest.fixture(params=[1,2]) def db(request): return request.param @@ -1959,7 +1959,7 @@ testdir.makepyfile(""" import pytest l = [] - @pytest.factory(scope="module") + @pytest.fixture(scope="module") def arg(): l.append(1) return 0 @@ -1984,7 +1984,7 @@ testdir.makepyfile(""" import pytest l = [] - @pytest.factory(params=[1,2]) + @pytest.fixture(params=[1,2]) def arg(request): return request.param @@ -2010,7 +2010,7 @@ l = [] - @pytest.factory(scope="session", params=[1,2]) + @pytest.fixture(scope="session", params=[1,2]) def arg(request): return request.param @@ -2036,11 +2036,11 @@ l = [] - @pytest.factory(scope="function", params=[1,2]) + @pytest.fixture(scope="function", params=[1,2]) def farg(request): return request.param - @pytest.factory(scope="class", params=list("ab")) + @pytest.fixture(scope="class", params=list("ab")) def carg(request): return request.param @@ -2117,7 +2117,7 @@ def test_parametrize(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory(params=["a", "b", "c"]) + @pytest.fixture(params=["a", "b", "c"]) def arg(request): return request.param l = [] @@ -2133,7 +2133,7 @@ testdir.makepyfile(""" import pytest l = [] - @pytest.factory(scope="module") + @pytest.fixture(scope="module") def arg(): l.append(1) return 1 @@ -2155,7 +2155,7 @@ testdir.makepyfile(""" import pytest l = [] - @pytest.factory(scope="module") + @pytest.fixture(scope="module") def arg(): l.append(1) return 1 @@ -2178,7 +2178,7 @@ import pytest finalized = [] created = [] - @pytest.factory(scope="module") + @pytest.fixture(scope="module") def arg(request): created.append(1) assert request.scope == "module" @@ -2217,14 +2217,14 @@ import pytest finalized = [] created = [] - @pytest.factory(scope="function") + @pytest.fixture(scope="function") def arg(request): pass """) testdir.makepyfile( test_mod1=""" import pytest - @pytest.factory(scope="session") + @pytest.fixture(scope="session") def arg(request): %s def test_1(arg): @@ -2239,14 +2239,14 @@ def test_register_only_with_mark(self, testdir): testdir.makeconftest(""" import pytest - @pytest.factory() + @pytest.fixture() def arg(): return 1 """) testdir.makepyfile( test_mod1=""" import pytest - @pytest.factory() + @pytest.fixture() def arg(arg): return arg + 1 def test_1(arg): @@ -2258,7 +2258,7 @@ def test_parametrize_and_scope(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory(scope="module", params=["a", "b", "c"]) + @pytest.fixture(scope="module", params=["a", "b", "c"]) def arg(request): return request.param l = [] @@ -2276,13 +2276,13 @@ def test_scope_mismatch(self, testdir): testdir.makeconftest(""" import pytest - @pytest.factory(scope="function") + @pytest.fixture(scope="function") def arg(request): pass """) testdir.makepyfile(""" import pytest - @pytest.factory(scope="session") + @pytest.fixture(scope="session") def arg(arg): pass def test_mismatch(arg): @@ -2298,7 +2298,7 @@ testdir.makepyfile(""" import pytest - @pytest.factory(scope="module", params=[1, 2]) + @pytest.fixture(scope="module", params=[1, 2]) def arg(request): return request.param @@ -2317,10 +2317,10 @@ testdir.makeconftest(""" import pytest - @pytest.factory(scope="session", params="s1 s2".split()) + @pytest.fixture(scope="session", params="s1 s2".split()) def sarg(): pass - @pytest.factory(scope="module", params="m1 m2".split()) + @pytest.fixture(scope="module", params="m1 m2".split()) def marg(): pass """) @@ -2365,11 +2365,11 @@ l = [] - @pytest.factory(scope="function", params=[1,2]) + @pytest.fixture(scope="function", params=[1,2]) def farg(request): return request.param - @pytest.factory(scope="class", params=list("ab")) + @pytest.fixture(scope="class", params=list("ab")) def carg(request): return request.param @@ -2411,14 +2411,14 @@ testdir.makepyfile(""" import pytest - @pytest.factory(scope="function", params=[1, 2]) + @pytest.fixture(scope="function", params=[1, 2]) def arg(request): param = request.param request.addfinalizer(lambda: l.append("fin:%s" % param)) l.append("create:%s" % param) return request.param - @pytest.factory(scope="module", params=["mod1", "mod2"]) + @pytest.fixture(scope="module", params=["mod1", "mod2"]) def modarg(request): param = request.param request.addfinalizer(lambda: l.append("fin:%s" % param)) @@ -2455,7 +2455,7 @@ testdir.makepyfile(""" import pytest - @pytest.factory(scope="module", params=[1, 2]) + @pytest.fixture(scope="module", params=[1, 2]) def arg(request): request.config.l = l # to access from outer x = request.param @@ -2484,7 +2484,7 @@ testdir.makepyfile(""" import pytest - @pytest.factory(scope="function", params=[1, 2]) + @pytest.fixture(scope="function", params=[1, 2]) def arg(request): x = request.param request.addfinalizer(lambda: l.append("fin%s" % x)) @@ -2506,7 +2506,7 @@ testdir.makepyfile(""" import pytest - @pytest.factory(scope="module", params=[1, 2]) + @pytest.fixture(scope="module", params=[1, 2]) def arg(request): return request.param @@ -2562,7 +2562,7 @@ def test_funcarg(self, testdir, scope, ok, error): testdir.makepyfile(""" import pytest - @pytest.factory(scope=%r) + @pytest.fixture(scope=%r) def arg(request): for x in %r: assert hasattr(request, x) @@ -2582,7 +2582,7 @@ def test_subfactory_missing_funcarg(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def gen(qwe123): return 1 def test_something(gen): @@ -2618,7 +2618,7 @@ def test_newstyle_with_request(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def arg(request): pass def test_1(arg): @@ -2630,7 +2630,7 @@ def test_setupcontext_no_param(self, testdir): testdir.makepyfile(""" import pytest - @pytest.factory(params=[1,2]) + @pytest.fixture(params=[1,2]) def arg(request): return request.param @@ -2683,7 +2683,7 @@ @pytest.setup() def fix1(): l.append(1) - @pytest.factory() + @pytest.fixture() def arg1(): l.append(2) def test_hello(arg1): @@ -2696,10 +2696,10 @@ def test_request_funcargnames(testdir): testdir.makepyfile(""" import pytest - @pytest.factory() + @pytest.fixture() def arg1(): pass - @pytest.factory() + @pytest.fixture() def farg(arg1): pass @pytest.setup() https://bitbucket.org/hpk42/pytest/changeset/70b4758381a0/ changeset: 70b4758381a0 user: hpk42 date: 2012-10-05 10:21:35 summary: internally unify setup and fixture code, making setup a shortcut to fixture(autoactive=True) affected #: 4 files diff -r de544d09912bf791c6635f023ce153348e38e961 -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 _pytest/nose.py --- a/_pytest/nose.py +++ b/_pytest/nose.py @@ -41,7 +41,7 @@ def call_optional(obj, name): method = getattr(obj, name, None) - if method is not None and not hasattr(method, "_pytestsetup"): + if method is not None and not hasattr(method, "_pytestfixturefunction"): # If there's any problems allow the exception to raise rather than # silently ignoring them method() diff -r de544d09912bf791c6635f023ce153348e38e961 -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -11,22 +11,18 @@ cutdir = py.path.local(_pytest.__file__).dirpath() class FixtureFunctionMarker: - def __init__(self, scope, params): + def __init__(self, scope, params, autoactive=False): self.scope = scope self.params = params + self.autoactive = autoactive + def __call__(self, function): function._pytestfixturefunction = self return function -class SetupMarker: - def __init__(self, scope): - self.scope = scope - def __call__(self, function): - function._pytestsetup = self - return function # XXX a test fails when scope="function" how it should be, investigate -def fixture(scope=None, params=None): +def fixture(scope=None, params=None, autoactive=False): """ return a decorator to mark a fixture factory function. The name of the fixture function can be referenced in a test context @@ -42,8 +38,11 @@ invocations of the fixture functions and their dependent tests. """ - return FixtureFunctionMarker(scope, params) + return FixtureFunctionMarker(scope, params, autoactive=autoactive) +defaultfuncargprefixmarker = fixture() + +# XXX remove in favour of fixture(autoactive=True) def setup(scope="function"): """ return a decorator to mark a function as providing a fixture for a testcontext. A fixture function is executed for each scope and may @@ -57,7 +56,7 @@ of "function", "class", "module", "session". Defaults to "function". """ - return SetupMarker(scope) + return FixtureFunctionMarker(scope, params=None, autoactive=True) def cached_property(f): """returns a cached property that is calculated by function f. @@ -163,7 +162,10 @@ testfunction(*pyfuncitem._args) else: funcargs = pyfuncitem.funcargs - testfunction(**funcargs) + testargs = {} + for arg in getfuncargnames(testfunction): + testargs[arg] = funcargs[arg] + testfunction(**testargs) def pytest_collect_file(path, parent): ext = path.ext @@ -666,7 +668,6 @@ for i, valset in enumerate(argvalues): assert len(valset) == len(argnames) newcallspec = callspec.copy(self) - #print ("setmulti %r id %r" % (argnames, ids[i])) newcallspec.setmulti(valtype, argnames, valset, ids[i], scopenum) newcalls.append(newcallspec) @@ -879,14 +880,19 @@ #req._discoverfactories() if callobj is not _dummy: self.obj = callobj - startindex = int(self.cls is not None) - self.funcargnames = getfuncargnames(self.obj, startindex=startindex) + self.funcargnames = self._getfixturenames() + for name, val in (py.builtin._getfuncdict(self.obj) or {}).items(): setattr(self.markers, name, val) if keywords: for name, val in keywords.items(): setattr(self.markers, name, val) + def _getfixturenames(self): + startindex = int(self.cls is not None) + return (self.session.funcargmanager._autofixtures + + getfuncargnames(self.obj, startindex=startindex)) + @property def function(self): "underlying python 'function' object" @@ -913,8 +919,8 @@ def setup(self): super(Function, self).setup() - if hasattr(self, "_request"): - self._request._callsetup() + #if hasattr(self, "_request"): + # self._request._callsetup() fillfuncargs(self) def __eq__(self, other): @@ -1054,6 +1060,7 @@ """add finalizer/teardown function to be called after the last test within the requesting test context finished execution. """ + # XXX usually this method is shadowed by factorydef specific ones self._addfinalizer(finalizer, scope=self.scope) def _addfinalizer(self, finalizer, scope): @@ -1084,13 +1091,11 @@ def _fillfuncargs(self): item = self._pyfuncitem funcargnames = getattr(item, "funcargnames", self.funcargnames) + for argname in funcargnames: if argname not in item.funcargs: item.funcargs[argname] = self.getfuncargvalue(argname) - def _callsetup(self): - self.funcargmanager.ensure_setupcalls(self) - def cached_setup(self, setup, teardown=None, scope="module", extrakey=None): """ (deprecated) Return a testing resource managed by ``setup`` & ``teardown`` calls. ``scope`` and ``extrakey`` determine when the @@ -1154,20 +1159,19 @@ factorydef = factorydeflist.pop() self._factorystack.append(factorydef) try: - return self._getfuncargvalue(factorydef) + result = self._getfuncargvalue(factorydef) + self._funcargs[argname] = result + return result finally: self._factorystack.pop() def _getfuncargvalue(self, factorydef): - # collect funcargs from the factory - newnames = factorydef.funcargnames + if factorydef.active: + return factorydef.cached_result + + # prepare request scope and param attributes before + # calling into factory argname = factorydef.argname - factory_kwargs = {} - def fillfactoryargs(): - for newname in newnames: - val = self.getfuncargvalue(newname) - factory_kwargs[newname] = val - node = self._pyfuncitem mp = monkeypatch() mp.setattr(self, '_currentarg', argname) @@ -1177,9 +1181,7 @@ pass else: mp.setattr(self, 'param', param, raising=False) - scope = factorydef.scope - funcargfactory = factorydef.func if scope is not None: __tracebackhide__ = True if scopemismatch(self.scope, scope): @@ -1191,17 +1193,16 @@ (scope, argname, self.scope, "\n".join(lines)))) __tracebackhide__ = False mp.setattr(self, "scope", scope) - kwargs = {} - if hasattr(self, "param"): - kwargs["extrakey"] = param - fillfactoryargs() - val = self.cached_setup(lambda: funcargfactory(**factory_kwargs), - scope=scope, **kwargs) - else: - fillfactoryargs() - val = funcargfactory(**factory_kwargs) + + # prepare finalization according to scope + self.session._setupstate.addfinalizer(factorydef.finish, self.node) + self.funcargmanager.addargfinalizer(factorydef.finish, argname) + for subargname in factorydef.funcargnames: # XXX all deps? + self.funcargmanager.addargfinalizer(factorydef.finish, subargname) + mp.setattr(self, "addfinalizer", factorydef.addfinalizer) + # finally perform the factory call + val = factorydef.execute(request=self) mp.undo() - self._funcargs[argname] = val return val def _factorytraceback(self): @@ -1291,8 +1292,8 @@ self.arg2facspec = {} self._seenplugins = set() self._holderobjseen = set() - self.setuplist = [] self._arg2finish = {} + self._autofixtures = [] session.config.pluginmanager.register(self, "funcmanage") ### XXX this hook should be called for historic events like pytest_configure @@ -1325,13 +1326,11 @@ # so that the caller can reuse it and does not have to re-discover # factories again for each funcargname parentid = parentnode.nodeid - funcargnames = list(funcargnames) - _, setupargs = self.getsetuplist(parentnode) + funcargnames = self._autofixtures + list(funcargnames) def merge(otherlist): for arg in otherlist: if arg not in funcargnames: funcargnames.append(arg) - merge(setupargs) arg2facdeflist = {} lastlen = -1 while lastlen != len(funcargnames): @@ -1365,6 +1364,9 @@ cs1 = item.callspec except AttributeError: return + + # determine which fixtures are not needed anymore for the next test + keylist = [] for name in cs1.params: try: if name in nextitem.callspec.params and \ @@ -1372,8 +1374,13 @@ continue except AttributeError: pass - key = (name, cs1.params[name]) - item.session._setupstate._callfinalizers(key) + key = (-cs1._arg2scopenum[name], name, cs1.params[name]) + keylist.append(key) + + # sort by scope (function scope first, then higher ones) + keylist.sort() + for (scopenum, name, param) in keylist: + item.session._setupstate._callfinalizers((name, param)) l = self._arg2finish.get(name) if l is not None: for fin in l: @@ -1382,55 +1389,36 @@ def _parsefactories(self, holderobj, nodeid, unittest=False): if holderobj in self._holderobjseen: return - #print "parsefactories", holderobj self._holderobjseen.add(holderobj) for name in dir(holderobj): - #print "check", holderobj, name obj = getattr(holderobj, name) if not callable(obj): continue - # resource factories either have a pytest_funcarg__ prefix - # or are "funcarg" marked + # fixture functions have a pytest_funcarg__ prefix + # or are "@pytest.fixture" marked marker = getattr(obj, "_pytestfixturefunction", None) - if marker is not None: - if not isinstance(marker, FixtureFunctionMarker): - # magic globals with __getattr__ - # give us something thats wrong for that case + if marker is None: + if not name.startswith(self._argprefix): continue + marker = defaultfuncargprefixmarker + name = name[len(self._argprefix):] + elif not isinstance(marker, FixtureFunctionMarker): + # magic globals with __getattr__ might have got us a wrong + # fixture attribute + continue + else: assert not name.startswith(self._argprefix) - argname = name - scope = marker.scope - params = marker.params - elif name.startswith(self._argprefix): - argname = name[len(self._argprefix):] - scope = None - params = None - else: - # no funcargs. check if we have a setup function. - setup = getattr(obj, "_pytestsetup", None) - if setup is not None: - scope = setup.scope - sf = SetupCall(self, nodeid, obj, scope, unittest) - self.setuplist.append(sf) - continue - faclist = self.arg2facspec.setdefault(argname, []) - factorydef = FactoryDef(self, nodeid, argname, obj, scope, params) + factorydef = FactoryDef(self, nodeid, name, obj, + marker.scope, marker.params, + unittest=unittest) + faclist = self.arg2facspec.setdefault(name, []) faclist.append(factorydef) - ### check scope/params mismatch? - - def getsetuplist(self, node): - nodeid = node.nodeid - l = [] - allargnames = [] - for setupcall in self.setuplist: - if nodeid.startswith(setupcall.baseid): - l.append(setupcall) - for arg in setupcall.funcargnames: - if arg not in allargnames: - allargnames.append(arg) - l.sort(key=lambda x: x.scopenum) - return l, allargnames - + if marker.autoactive: + # make sure the self._autofixtures list is always sorted + # by scope, scopenum 0 is session + self._autofixtures.append(name) + self._autofixtures.sort( + key=lambda x: self.arg2facspec[x][-1].scopenum) def getfactorylist(self, argname, nodeid): try: @@ -1438,20 +1426,17 @@ except KeyError: return None else: - return self._matchfactories(factorydeflist, nodeid) + return list(self._matchfactories(factorydeflist, nodeid)) def _matchfactories(self, factorydeflist, nodeid): - l = [] for factorydef in factorydeflist: - #print "check", basepath, nodeid if nodeid.startswith(factorydef.baseid): - l.append(factorydef) - return l + yield factorydef def _raiselookupfailed(self, argname, function, nodeid, getfactb=None): available = [] for name, facdef in self.arg2facspec.items(): - faclist = self._matchfactories(facdef, nodeid) + faclist = list(self._matchfactories(facdef, nodeid)) if faclist: available.append(name) msg = "LookupError: no factory found for argument %r" % (argname,) @@ -1460,36 +1445,6 @@ lines = getfactb and getfactb() or [] raise FuncargLookupError(function, msg, lines) - def ensure_setupcalls(self, request): - setuplist, allnames = self.getsetuplist(request._pyfuncitem) - for setupcall in setuplist: - if setupcall.active: - continue - request._factorystack.append(setupcall) - mp = monkeypatch() - try: - mp.setattr(request, "scope", setupcall.scope) - kwargs = {} - for name in setupcall.funcargnames: - kwargs[name] = request.getfuncargvalue(name) - scope = setupcall.scope or "function" - scol = setupcall.scopeitem = request._getscopeitem(scope) - self.session._setupstate.addfinalizer(setupcall.finish, scol) - for argname in setupcall.funcargnames: # XXX all deps? - self.addargfinalizer(setupcall.finish, argname) - req = kwargs.get("request", None) - if req is not None: - mp.setattr(req, "addfinalizer", setupcall.addfinalizer) - # for unittest-setup methods we need to provide - # the correct instance - posargs = () - if setupcall.unittest: - posargs = (request.instance,) - setupcall.execute(posargs, kwargs) - finally: - mp.undo() - request._factorystack.remove(setupcall) - def addargfinalizer(self, finalizer, argname): l = self._arg2finish.setdefault(argname, []) l.append(finalizer) @@ -1501,28 +1456,24 @@ except ValueError: pass - -class SetupCall: - """ a container/helper for managing calls to setup functions. """ - def __init__(self, funcargmanager, baseid, func, scope, unittest): +class FactoryDef: + """ A container for a factory definition. """ + def __init__(self, funcargmanager, baseid, argname, func, scope, params, + unittest=False): self.funcargmanager = funcargmanager self.baseid = baseid self.func = func + self.argname = argname + self.scope = scope + self.scopenum = scopes.index(scope or "function") + self.params = params startindex = unittest and 1 or None self.funcargnames = getfuncargnames(func, startindex=startindex) - self.scope = scope - self.scopenum = scopes.index(scope) + self.unittest = unittest self.active = False - self.unittest= unittest self._finalizer = [] - def execute(self, posargs, kwargs): - assert not self.active - self.active = True - self.func(*posargs, **kwargs) - def addfinalizer(self, finalizer): - assert self.active self._finalizer.append(finalizer) def finish(self): @@ -1532,17 +1483,23 @@ # check neccesity of next commented call self.funcargmanager.removefinalizer(self.finish) self.active = False + #print "finished", self + #del self.cached_result -class FactoryDef: - """ A container for a factory definition. """ - def __init__(self, funcargmanager, baseid, argname, func, scope, params): - self.funcargmanager = funcargmanager - self.baseid = baseid - self.func = func - self.argname = argname - self.scope = scope - self.params = params - self.funcargnames = getfuncargnames(func) + def execute(self, request): + kwargs = {} + for newname in self.funcargnames: + kwargs[newname] = request.getfuncargvalue(newname) + if self.unittest: + result = self.func(request.instance, **kwargs) + else: + result = self.func(**kwargs) + self.active = True + self.cached_result = result + return result + + def __repr__(self): + return "" % (self.argname, self.scope) def getfuncargnames(function, startindex=None): # XXX merge with main.py's varnames @@ -1634,5 +1591,5 @@ def xunitsetup(obj, name): meth = getattr(obj, name, None) if meth is not None: - if not hasattr(meth, "_pytestsetup"): + if not hasattr(meth, "_pytestfixturefunction"): return meth diff -r de544d09912bf791c6635f023ce153348e38e961 -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 _pytest/unittest.py --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -52,6 +52,9 @@ class TestCaseFunction(pytest.Function): _excinfo = None + def _getfixturenames(self): + return list(self.session.funcargmanager._autofixtures) + def setup(self): self._testcase = self.parent.obj(self.name) self._obj = getattr(self._testcase, self.name) @@ -62,7 +65,7 @@ if hasattr(self._testcase, 'setup_method'): self._testcase.setup_method(self._obj) if hasattr(self, "_request"): - self._request._callsetup() + self._request._fillfuncargs() def teardown(self): if hasattr(self._testcase, 'teardown_method'): diff -r de544d09912bf791c6635f023ce153348e38e961 -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -276,8 +276,10 @@ assert hasattr(modcol.obj, 'test_func') def test_function_equality(self, testdir, tmpdir): + from _pytest.python import FuncargManager config = testdir.parseconfigure() session = testdir.Session(config) + session.funcargmanager = FuncargManager(session) def func1(): pass def func2(): @@ -576,21 +578,18 @@ assert item.funcargs['other'] == 42 def test_funcarg_lookup_modulelevel(self, testdir): - modcol = testdir.getmodulecol(""" + testdir.makepyfile(""" def pytest_funcarg__something(request): return request.function.__name__ class TestClass: def test_method(self, something): - pass + assert something == "test_method" def test_func(something): - pass + assert something == "test_func" """) - item1, item2 = testdir.genitems([modcol]) - funcargs.fillfuncargs(item1) - assert item1.funcargs['something'] == "test_method" - funcargs.fillfuncargs(item2) - assert item2.funcargs['something'] == "test_func" + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2) def test_funcarg_lookup_classlevel(self, testdir): p = testdir.makepyfile(""" @@ -1881,16 +1880,9 @@ def test_parsefactories_conftest(self, testdir): testdir.makepyfile(""" def test_check_setup(item, fm): - setupcalls, allnames = fm.getsetuplist(item) - assert len(setupcalls) == 2 - assert setupcalls[0].func.__name__ == "perfunction" - assert "request" in setupcalls[0].funcargnames - assert "tmpdir" in setupcalls[0].funcargnames - assert setupcalls[1].func.__name__ == "perfunction2" - assert "request" not in setupcalls[1].funcargnames - assert "arg1" in setupcalls[1].funcargnames - assert "tmpdir" not in setupcalls[1].funcargnames - #assert "tmpdir" in setupcalls[1].depfuncargs + assert len(fm._autofixtures) == 2 + assert "perfunction2" in fm._autofixtures + assert "perfunction" in fm._autofixtures """) reprec = testdir.inline_run("-s") reprec.assertoutcome(passed=1) @@ -2098,8 +2090,8 @@ class TestClass: @pytest.setup(scope="class") def addteardown(self, item, request): + l.append("setup-%d" % item) request.addfinalizer(lambda: l.append("teardown-%d" % item)) - l.append("setup-%d" % item) def test_step1(self, item): l.append("step1-%d" % item) def test_step2(self, item): @@ -2436,17 +2428,18 @@ l.append("test4") def test_5(): assert len(l) == 12 * 3 - import pprint - pprint.pprint(l) - assert l == [ + expected = [ 'create:1', 'test1', 'fin:1', 'create:2', 'test1', 'fin:2', 'create:mod1', 'test2', 'create:1', 'test3', 'fin:1', 'create:2', 'test3', 'fin:2', 'create:1', - 'test4', 'fin:1', 'create:2', 'test4', 'fin:mod1', - 'fin:2', 'create:mod2', 'test2', 'create:1', 'test3', + 'test4', 'fin:1', 'create:2', 'test4', 'fin:2', + 'fin:mod1', 'create:mod2', 'test2', 'create:1', 'test3', 'fin:1', 'create:2', 'test3', 'fin:2', 'create:1', - 'test4', 'fin:1', 'create:2', 'test4', 'fin:mod2', - 'fin:2'] + 'test4', 'fin:1', 'create:2', 'test4', 'fin:2', + 'fin:mod2'] + import pprint + pprint.pprint(list(zip(l, expected))) + assert l == expected """) reprec = testdir.inline_run("-v") reprec.assertoutcome(passed=12+1) @@ -2707,7 +2700,7 @@ pass def test_function(request, farg): assert set(request.funcargnames) == \ - set(["tmpdir", "arg1", "request", "farg"]) + set(["tmpdir", "sarg", "arg1", "request", "farg"]) """) reprec = testdir.inline_run() reprec.assertoutcome(passed=1) https://bitbucket.org/hpk42/pytest/changeset/8d87ff808393/ changeset: 8d87ff808393 user: hpk42 date: 2012-10-05 14:24:44 summary: rename a number of internal and externally visible variables to use the fixture name rather than funcargs. Introduce .funcargnames compatibility attribute for backward compat. affected #: 33 files diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e ISSUES.txt --- a/ISSUES.txt +++ b/ISSUES.txt @@ -71,7 +71,7 @@ and the documentation needs to change along to mention this new way of doing things. -impl note: probably Request._fillfuncargs would be called from the +impl note: probably Request._fillfixtures would be called from the python plugins own pytest_runtest_setup(item) and would call item.getresource(X) for all X in the funcargs of a function. diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e _pytest/capture.py --- a/_pytest/capture.py +++ b/_pytest/capture.py @@ -189,7 +189,7 @@ """ if "capfd" in request._funcargs: raise request.raiseerror(error_capsysfderror) - return CaptureFuncarg(py.io.StdCapture) + return CaptureFixture(py.io.StdCapture) def pytest_funcarg__capfd(request): """enables capturing of writes to file descriptors 1 and 2 and makes @@ -200,9 +200,9 @@ request.raiseerror(error_capsysfderror) if not hasattr(os, 'dup'): pytest.skip("capfd funcarg needs os.dup") - return CaptureFuncarg(py.io.StdCaptureFD) + return CaptureFixture(py.io.StdCaptureFD) -class CaptureFuncarg: +class CaptureFixture: def __init__(self, captureclass): self.capture = captureclass(now=False) diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e _pytest/helpconfig.py --- a/_pytest/helpconfig.py +++ b/_pytest/helpconfig.py @@ -80,7 +80,7 @@ tw.line() ; tw.line() #tw.sep("=") tw.line("to see available markers type: py.test --markers") - tw.line("to see available funcargs type: py.test --funcargs") + tw.line("to see available fixtures type: py.test --fixtures") return tw.line("conftest.py options:") diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -308,8 +308,8 @@ pass def _repr_failure_py(self, excinfo, style=None): - fm = self.session.funcargmanager - if excinfo.errisinstance(fm.FuncargLookupError): + fm = self.session._fixturemanager + if excinfo.errisinstance(fm.FixtureLookupError): function = excinfo.value.function factblines = excinfo.value.factblines if function is not None: @@ -317,7 +317,7 @@ lines, _ = inspect.getsourcelines(function) for i, line in enumerate(lines): if line.strip().startswith('def'): - return fm.FuncargLookupErrorRepr(fspath, + return fm.FixtureLookupErrorRepr(fspath, lineno, lines[:i+1], str(excinfo.value.msg), factblines) if self.config.option.fulltrace: diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -44,18 +44,7 @@ # XXX remove in favour of fixture(autoactive=True) def setup(scope="function"): - """ return a decorator to mark a function as providing a fixture for - a testcontext. A fixture function is executed for each scope and may - receive funcargs which allows it to initialise and provide implicit - test state. A fixture function may receive the "testcontext" object - and register a finalizer via "testcontext.addfinalizer(finalizer)" - which will be called when the last test in the testcontext has - executed. - - :arg scope: the scope for which the setup function will be active, one - of "function", "class", "module", "session". - Defaults to "function". - """ + """ alias for fixture(scope, autoactive=True) """ return FixtureFunctionMarker(scope, params=None, autoactive=True) def cached_property(f): @@ -85,9 +74,9 @@ def pytest_addoption(parser): group = parser.getgroup("general") - group.addoption('--funcargs', - action="store_true", dest="showfuncargs", default=False, - help="show available function arguments, sorted by plugin") + group.addoption('--fixtures', '--fixtures', + action="store_true", dest="showfixtures", default=False, + help="show available fixtures, sorted by plugin appearance") parser.addini("python_files", type="args", default=('test_*.py', '*_test.py'), help="glob-style file patterns for Python test module discovery") @@ -97,8 +86,8 @@ help="prefixes for Python test function and method discovery") def pytest_cmdline_main(config): - if config.option.showfuncargs: - showfuncargs(config) + if config.option.showfixtures: + showfixtures(config) return 0 @@ -119,7 +108,7 @@ ) def pytest_sessionstart(session): - session.funcargmanager = FuncargManager(session) + session._fixturemanager = FixtureManager(session) @pytest.mark.trylast def pytest_namespace(): @@ -131,10 +120,11 @@ 'collect': { 'Module': Module, 'Class': Class, 'Instance': Instance, 'Function': Function, 'Generator': Generator, - '_fillfuncargs': fillfuncargs} + '_fillfuncargs': fillfixtures} } -def pytest_funcarg__pytestconfig(request): + at fixture() +def pytestconfig(request): """ the pytest config object with access to command line opts.""" return request.config @@ -146,12 +136,12 @@ testfunction(*pyfuncitem._args) else: try: - funcargnames = pyfuncitem.funcargnames + fixturenames = pyfuncitem.fixturenames except AttributeError: funcargs = pyfuncitem.funcargs else: funcargs = {} - for name in funcargnames: + for name in fixturenames: funcargs[name] = pyfuncitem.funcargs[name] testfunction(**funcargs) @@ -352,7 +342,7 @@ return self._memoizedcall('_obj', self._importtestmodule) def collect(self): - self.session.funcargmanager._parsefactories(self.obj, self.nodeid) + self.session._fixturemanager._parsefactories(self.obj, self.nodeid) return super(Module, self).collect() def _importtestmodule(self): @@ -425,7 +415,7 @@ return obj def collect(self): - self.session.funcargmanager._parsefactories(self.obj, self.nodeid) + self.session._fixturemanager._parsefactories(self.obj, self.nodeid) return super(Instance, self).collect() def newinstance(self): @@ -534,14 +524,14 @@ -def fillfuncargs(function): +def fillfixtures(function): """ fill missing funcargs for a test function. """ if getattr(function, "_args", None) is None: # not a yielded function try: request = function._request except AttributeError: - request = function._request = FuncargRequest(function) - request._fillfuncargs() + request = function._request = FixtureRequest(function) + request._fillfixtures() _notexists = object() @@ -605,21 +595,30 @@ self._globalparam = param -class Metafunc: +class FuncargnamesCompatAttr: + """ helper class so that Metafunc, Function and FixtureRequest + don't need to each define the "funcargnames" compatibility attribute. + """ + @property + def funcargnames(self): + """ alias attribute for ``fixturenames`` for pre-2.3 compatibility""" + return self.fixturenames + +class Metafunc(FuncargnamesCompatAttr): def __init__(self, function, config=None, cls=None, module=None, parentnode=None): self.config = config self.module = module self.function = function self.parentnode = parentnode - self.parentid = getattr(parentnode, "nodeid", "") + self._parentid = getattr(parentnode, "nodeid", "") argnames = getfuncargnames(function, startindex=int(cls is not None)) if parentnode is not None: - fm = parentnode.session.funcargmanager - self.funcargnames, self._arg2facdeflist = fm.getallfuncargnames( + fm = parentnode.session._fixturemanager + self.fixturenames, self._arg2fixturedeflist = fm.getfixtureclosure( argnames, parentnode) else: - self.funcargnames = argnames + self.fixturenames = argnames self.cls = cls self.module = module self._calls = [] @@ -631,7 +630,7 @@ """ Add new invocations to the underlying test function using the list of argvalues for the given argnames. Parametrization is performed during the collection phase. If you need to setup expensive resources - you may pass indirect=True and implement a funcarg factory which can + you may pass indirect=True and implement a fixture function which can perform the expensive setup just before a test is actually run. :arg argnames: an argument name or a list of argument names @@ -640,7 +639,7 @@ values for the list of argument names. :arg indirect: if True each argvalue corresponding to an argument will - be passed as request.param to its respective funcarg factory so + be passed as request.param to its respective fixture function so that it can perform more expensive setups during the setup phase of a test rather than at collection time. @@ -657,7 +656,7 @@ if not indirect: #XXX should we also check for the opposite case? for arg in argnames: - if arg not in self.funcargnames: + if arg not in self.fixturenames: raise ValueError("%r has no argument %r" %(self.function, arg)) valtype = indirect and "params" or "funcargs" if not ids: @@ -686,13 +685,13 @@ :arg id: used for reporting and identification purposes. If you don't supply an `id` an automatic unique id will be generated. - :arg param: a parameter which will be exposed to a later funcarg factory + :arg param: a parameter which will be exposed to a later fixture function invocation through the ``request.param`` attribute. """ assert funcargs is None or isinstance(funcargs, dict) if funcargs is not None: for name in funcargs: - if name not in self.funcargnames: + if name not in self.fixturenames: pytest.fail("funcarg %r not used in this function." % name) else: funcargs = {} @@ -722,11 +721,11 @@ return "-".join(l) -def showfuncargs(config): +def showfixtures(config): from _pytest.main import wrap_session - return wrap_session(config, _showfuncargs_main) + return wrap_session(config, _showfixtures_main) -def _showfuncargs_main(config, session): +def _showfixtures_main(config, session): session.perform_collect() if session.items: plugins = session.items[0].getplugins() @@ -735,7 +734,7 @@ curdir = py.path.local() tw = py.io.TerminalWriter() verbose = config.getvalue("verbose") - argprefix = session.funcargmanager._argprefix + argprefix = session._fixturemanager._argprefix for plugin in plugins: available = [] for name, factory in vars(plugin).items(): @@ -854,7 +853,7 @@ # the basic py.test Function item # _dummy = object() -class Function(FunctionMixin, pytest.Item): +class Function(FunctionMixin, pytest.Item, FuncargnamesCompatAttr): """ a Function Item is responsible for setting up and executing a Python test function. """ @@ -876,11 +875,11 @@ self.param = callspec.param else: self.funcargs = {} - self._request = req = FuncargRequest(self) + self._request = req = FixtureRequest(self) #req._discoverfactories() if callobj is not _dummy: self.obj = callobj - self.funcargnames = self._getfixturenames() + self.fixturenames = self._getfuncargnames() for name, val in (py.builtin._getfuncdict(self.obj) or {}).items(): setattr(self.markers, name, val) @@ -888,9 +887,10 @@ for name, val in keywords.items(): setattr(self.markers, name, val) - def _getfixturenames(self): + + def _getfuncargnames(self): startindex = int(self.cls is not None) - return (self.session.funcargmanager._autofixtures + + return (self.session._fixturemanager._autofixtures + getfuncargnames(self.obj, startindex=startindex)) @property @@ -921,7 +921,7 @@ super(Function, self).setup() #if hasattr(self, "_request"): # self._request._callsetup() - fillfuncargs(self) + fillfixtures(self) def __eq__(self, other): try: @@ -960,8 +960,8 @@ return decoratescope -class FuncargRequest: - """ A request for function arguments from a test or setup function. +class FixtureRequest(FuncargnamesCompatAttr): + """ A request for fixtures from a test or setup function. A request object gives access to attributes of the requesting test context. It has an optional ``param`` attribute in case @@ -976,38 +976,38 @@ self.scope = "function" self.getparent = pyfuncitem.getparent self._funcargs = self._pyfuncitem.funcargs.copy() - self._name2factory = {} - self.funcargmanager = pyfuncitem.session.funcargmanager + self._arg2fixturedeflist = {} + self._fixturemanager = pyfuncitem.session._fixturemanager self._currentarg = None - self.parentid = pyfuncitem.parent.nodeid - self.funcargnames, self._arg2facdeflist_ = \ - self.funcargmanager.getallfuncargnames( + self._parentid = pyfuncitem.parent.nodeid + self.fixturenames, self._arg2fixturedeflist_ = \ + self._fixturemanager.getfixtureclosure( getfuncargnames(self.function), # XXX _pyfuncitem... pyfuncitem.parent) - self._factorystack = [] + self._fixturestack = [] @property def node(self): """ underlying collection node (depends on request scope)""" return self._getscopeitem(self.scope) - def _getfaclist(self, argname): - facdeflist = self._name2factory.get(argname, None) - getfactb = None + def _getfixturedeflist(self, argname): + fixturedeflist = self._arg2fixturedeflist.get(argname, None) + getfixturetb = None function = None - if facdeflist is None: - if self._factorystack: - function = self._factorystack[-1].func - getfactb = lambda: self._factorystack[:-1] + if fixturedeflist is None: + if self._fixturestack: + function = self._fixturestack[-1].func + getfixturetb = lambda: self._fixturestack[:-1] else: function = self.function - facdeflist = self.funcargmanager.getfactorylist( - argname, self.parentid) - self._name2factory[argname] = facdeflist - if not facdeflist: - self.funcargmanager._raiselookupfailed(argname, function, - self.parentid, getfactb) - return facdeflist + fixturedeflist = self._fixturemanager.getfixturedeflist( + argname, self._parentid) + self._arg2fixturedeflist[argname] = fixturedeflist + if not fixturedeflist: + self._fixturemanager._raiselookupfailed(argname, function, + self._parentid, getfixturetb) + return fixturedeflist @property def config(self): @@ -1060,7 +1060,7 @@ """add finalizer/teardown function to be called after the last test within the requesting test context finished execution. """ - # XXX usually this method is shadowed by factorydef specific ones + # XXX usually this method is shadowed by fixturedef specific ones self._addfinalizer(finalizer, scope=self.scope) def _addfinalizer(self, finalizer, scope): @@ -1084,15 +1084,15 @@ self.node.applymarker(marker) def raiseerror(self, msg): - """ raise a FuncargLookupError with the given message. """ - raise self.funcargmanager.FuncargLookupError(self.function, msg) + """ raise a FixtureLookupError with the given message. """ + raise self._fixturemanager.FixtureLookupError(self.function, msg) - def _fillfuncargs(self): + def _fillfixtures(self): item = self._pyfuncitem - funcargnames = getattr(item, "funcargnames", self.funcargnames) + fixturenames = getattr(item, "fixturenames", self.fixturenames) - for argname in funcargnames: + for argname in fixturenames: if argname not in item.funcargs: item.funcargs[argname] = self.getfuncargvalue(argname) @@ -1100,9 +1100,9 @@ """ (deprecated) Return a testing resource managed by ``setup`` & ``teardown`` calls. ``scope`` and ``extrakey`` determine when the ``teardown`` function will be called so that subsequent calls to - ``setup`` would recreate the resource. With pytest-2.3 you + ``setup`` would recreate the resource. With pytest-2.3 you often do not need ``cached_setup()`` as you can directly declare a scope - on a funcarg factory and register a finalizer through + on a fixture function and register a finalizer through ``request.addfinalizer()``. :arg teardown: function receiving a previously setup resource. @@ -1151,27 +1151,27 @@ except KeyError: pass try: - factorydeflist = self._getfaclist(argname) - except FuncargLookupError: + fixturedeflist = self._getfixturedeflist(argname) + except FixtureLookupError: if argname == "request": return self raise - factorydef = factorydeflist.pop() - self._factorystack.append(factorydef) + fixturedef = fixturedeflist.pop() + self._fixturestack.append(fixturedef) try: - result = self._getfuncargvalue(factorydef) + result = self._getfuncargvalue(fixturedef) self._funcargs[argname] = result return result finally: - self._factorystack.pop() + self._fixturestack.pop() - def _getfuncargvalue(self, factorydef): - if factorydef.active: - return factorydef.cached_result + def _getfuncargvalue(self, fixturedef): + if fixturedef.active: + return fixturedef.cached_result # prepare request scope and param attributes before # calling into factory - argname = factorydef.argname + argname = fixturedef.argname node = self._pyfuncitem mp = monkeypatch() mp.setattr(self, '_currentarg', argname) @@ -1181,7 +1181,7 @@ pass else: mp.setattr(self, 'param', param, raising=False) - scope = factorydef.scope + scope = fixturedef.scope if scope is not None: __tracebackhide__ = True if scopemismatch(self.scope, scope): @@ -1195,20 +1195,20 @@ mp.setattr(self, "scope", scope) # prepare finalization according to scope - self.session._setupstate.addfinalizer(factorydef.finish, self.node) - self.funcargmanager.addargfinalizer(factorydef.finish, argname) - for subargname in factorydef.funcargnames: # XXX all deps? - self.funcargmanager.addargfinalizer(factorydef.finish, subargname) - mp.setattr(self, "addfinalizer", factorydef.addfinalizer) + self.session._setupstate.addfinalizer(fixturedef.finish, self.node) + self._fixturemanager.addargfinalizer(fixturedef.finish, argname) + for subargname in fixturedef.fixturenames: # XXX all deps? + self._fixturemanager.addargfinalizer(fixturedef.finish, subargname) + mp.setattr(self, "addfinalizer", fixturedef.addfinalizer) # finally perform the factory call - val = factorydef.execute(request=self) + val = fixturedef.execute(request=self) mp.undo() return val def _factorytraceback(self): lines = [] - for factorydef in self._factorystack: - factory = factorydef.func + for fixturedef in self._fixturestack: + factory = fixturedef.func fs, lineno = getfslineno(factory) p = self._pyfuncitem.session.fspath.bestrelpath(fs) args = inspect.formatargspec(*inspect.getargspec(factory)) @@ -1232,10 +1232,10 @@ raise ValueError("unknown finalization scope %r" %(scope,)) def __repr__(self): - return "" %(self._pyfuncitem) + return "" %(self._pyfuncitem) class ScopeMismatchError(Exception): - """ A funcarg factory tries to access a funcargvalue/factory + """ A fixture function tries to use a different fixture function which which has a lower scope (e.g. a Session one calls a function one) """ @@ -1249,14 +1249,14 @@ new_kwargs[name] = kwargs[name] return new_kwargs -class FuncargLookupError(LookupError): +class FixtureLookupError(LookupError): """ could not find a factory. """ def __init__(self, function, msg, factblines=None): self.function = function self.msg = msg self.factblines = factblines -class FuncargLookupErrorRepr(TerminalRepr): +class FixtureLookupErrorRepr(TerminalRepr): def __init__(self, filename, firstlineno, deflines, errorstring, factblines): self.deflines = deflines self.errorstring = errorstring @@ -1268,10 +1268,10 @@ tw.line() if self.factblines: tw.line(' dependency of:') - for factorydef in self.factblines: + for fixturedef in self.factblines: tw.line(' %s in %s' % ( - factorydef.argname, - factorydef.baseid, + fixturedef.argname, + fixturedef.baseid, )) tw.line() for line in self.deflines: @@ -1281,15 +1281,15 @@ tw.line() tw.line("%s:%d" % (self.filename, self.firstlineno+1)) -class FuncargManager: +class FixtureManager: _argprefix = "pytest_funcarg__" - FuncargLookupError = FuncargLookupError - FuncargLookupErrorRepr = FuncargLookupErrorRepr + FixtureLookupError = FixtureLookupError + FixtureLookupErrorRepr = FixtureLookupErrorRepr def __init__(self, session): self.session = session self.config = session.config - self.arg2facspec = {} + self.arg2fixturedeflist = {} self._seenplugins = set() self._holderobjseen = set() self._arg2finish = {} @@ -1319,41 +1319,44 @@ for plugin in plugins: self.pytest_plugin_registered(plugin) - def getallfuncargnames(self, funcargnames, parentnode): - # collect the closure of all funcargs, starting with - # funcargnames as the initial set - # we populate and return a arg2facdeflist mapping - # so that the caller can reuse it and does not have to re-discover - # factories again for each funcargname + def getfixtureclosure(self, fixturenames, parentnode): + # collect the closure of all funcargs, starting with the given + # fixturenames as the initial set. As we have to visit all + # factory definitions anyway, we also return a arg2fixturedeflist + # mapping so that the caller can reuse it and does not have + # to re-discover fixturedefs again for each fixturename + # (discovering matching fixtures for a given name/node is expensive) + parentid = parentnode.nodeid - funcargnames = self._autofixtures + list(funcargnames) + fixturenames_closure = list(self._autofixtures) def merge(otherlist): for arg in otherlist: - if arg not in funcargnames: - funcargnames.append(arg) - arg2facdeflist = {} + if arg not in fixturenames_closure: + fixturenames_closure.append(arg) + merge(fixturenames) + arg2fixturedeflist = {} lastlen = -1 - while lastlen != len(funcargnames): - lastlen = len(funcargnames) - for argname in list(funcargnames): - if argname in arg2facdeflist: + while lastlen != len(fixturenames_closure): + lastlen = len(fixturenames_closure) + for argname in fixturenames_closure: + if argname in arg2fixturedeflist: continue - facdeflist = self.getfactorylist(argname, parentid) - arg2facdeflist[argname] = facdeflist - if facdeflist is not None: - for facdef in facdeflist: - merge(facdef.funcargnames) - return funcargnames, arg2facdeflist + fixturedeflist = self.getfixturedeflist(argname, parentid) + arg2fixturedeflist[argname] = fixturedeflist + if fixturedeflist is not None: + for fixturedef in fixturedeflist: + merge(fixturedef.fixturenames) + return fixturenames_closure, arg2fixturedeflist def pytest_generate_tests(self, metafunc): - for argname in metafunc.funcargnames: - faclist = metafunc._arg2facdeflist[argname] + for argname in metafunc.fixturenames: + faclist = metafunc._arg2fixturedeflist[argname] if faclist is None: - continue # will raise FuncargLookupError at setup time - for facdef in faclist: - if facdef.params is not None: - metafunc.parametrize(argname, facdef.params, indirect=True, - scope=facdef.scope) + continue # will raise FixtureLookupError at setup time + for fixturedef in faclist: + if fixturedef.params is not None: + metafunc.parametrize(argname, fixturedef.params, indirect=True, + scope=fixturedef.scope) def pytest_collection_modifyitems(self, items): # separate parametrized setups @@ -1394,7 +1397,7 @@ obj = getattr(holderobj, name) if not callable(obj): continue - # fixture functions have a pytest_funcarg__ prefix + # fixture functions have a pytest_funcarg__ prefix (pre-2.3 style) # or are "@pytest.fixture" marked marker = getattr(obj, "_pytestfixturefunction", None) if marker is None: @@ -1408,42 +1411,42 @@ continue else: assert not name.startswith(self._argprefix) - factorydef = FactoryDef(self, nodeid, name, obj, + fixturedef = FixtureDef(self, nodeid, name, obj, marker.scope, marker.params, unittest=unittest) - faclist = self.arg2facspec.setdefault(name, []) - faclist.append(factorydef) + faclist = self.arg2fixturedeflist.setdefault(name, []) + faclist.append(fixturedef) if marker.autoactive: # make sure the self._autofixtures list is always sorted # by scope, scopenum 0 is session self._autofixtures.append(name) self._autofixtures.sort( - key=lambda x: self.arg2facspec[x][-1].scopenum) + key=lambda x: self.arg2fixturedeflist[x][-1].scopenum) - def getfactorylist(self, argname, nodeid): + def getfixturedeflist(self, argname, nodeid): try: - factorydeflist = self.arg2facspec[argname] + fixturedeflist = self.arg2fixturedeflist[argname] except KeyError: return None else: - return list(self._matchfactories(factorydeflist, nodeid)) + return list(self._matchfactories(fixturedeflist, nodeid)) - def _matchfactories(self, factorydeflist, nodeid): - for factorydef in factorydeflist: - if nodeid.startswith(factorydef.baseid): - yield factorydef + def _matchfactories(self, fixturedeflist, nodeid): + for fixturedef in fixturedeflist: + if nodeid.startswith(fixturedef.baseid): + yield fixturedef - def _raiselookupfailed(self, argname, function, nodeid, getfactb=None): + def _raiselookupfailed(self, argname, function, nodeid, getfixturetb=None): available = [] - for name, facdef in self.arg2facspec.items(): - faclist = list(self._matchfactories(facdef, nodeid)) + for name, fixturedef in self.arg2fixturedeflist.items(): + faclist = list(self._matchfactories(fixturedef, nodeid)) if faclist: available.append(name) msg = "LookupError: no factory found for argument %r" % (argname,) msg += "\n available funcargs: %s" %(", ".join(available),) - msg += "\n use 'py.test --funcargs [testpath]' for help on them." - lines = getfactb and getfactb() or [] - raise FuncargLookupError(function, msg, lines) + msg += "\n use 'py.test --fixtures [testpath]' for help on them." + lines = getfixturetb and getfixturetb() or [] + raise FixtureLookupError(function, msg, lines) def addargfinalizer(self, finalizer, argname): l = self._arg2finish.setdefault(argname, []) @@ -1456,11 +1459,11 @@ except ValueError: pass -class FactoryDef: +class FixtureDef: """ A container for a factory definition. """ - def __init__(self, funcargmanager, baseid, argname, func, scope, params, + def __init__(self, fixturenanager, baseid, argname, func, scope, params, unittest=False): - self.funcargmanager = funcargmanager + self._fixturemanager = fixturenanager self.baseid = baseid self.func = func self.argname = argname @@ -1468,7 +1471,7 @@ self.scopenum = scopes.index(scope or "function") self.params = params startindex = unittest and 1 or None - self.funcargnames = getfuncargnames(func, startindex=startindex) + self.fixturenames = getfuncargnames(func, startindex=startindex) self.unittest = unittest self.active = False self._finalizer = [] @@ -1481,14 +1484,14 @@ func = self._finalizer.pop() func() # check neccesity of next commented call - self.funcargmanager.removefinalizer(self.finish) + self._fixturemanager.removefinalizer(self.finish) self.active = False #print "finished", self #del self.cached_result def execute(self, request): kwargs = {} - for newname in self.funcargnames: + for newname in self.fixturenames: kwargs[newname] = request.getfuncargvalue(newname) if self.unittest: result = self.func(request.instance, **kwargs) @@ -1499,7 +1502,7 @@ return result def __repr__(self): - return "" % (self.argname, self.scope) + return "" % (self.argname, self.scope) def getfuncargnames(function, startindex=None): # XXX merge with main.py's varnames diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e _pytest/unittest.py --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -21,7 +21,7 @@ class UnitTestCase(pytest.Class): def collect(self): - self.session.funcargmanager._parsefactories(self.obj, self.nodeid, + self.session._fixturemanager._parsefactories(self.obj, self.nodeid, unittest=True) loader = py.std.unittest.TestLoader() module = self.getparent(pytest.Module).obj @@ -52,8 +52,8 @@ class TestCaseFunction(pytest.Function): _excinfo = None - def _getfixturenames(self): - return list(self.session.funcargmanager._autofixtures) + def _getfuncargnames(self): + return list(self.session._fixturemanager._autofixtures) def setup(self): self._testcase = self.parent.obj(self.name) @@ -65,7 +65,7 @@ if hasattr(self._testcase, 'setup_method'): self._testcase.setup_method(self._obj) if hasattr(self, "_request"): - self._request._fillfuncargs() + self._request._fillfixtures() def teardown(self): if hasattr(self._testcase, 'teardown_method'): diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/en/builtin.txt --- a/doc/en/builtin.txt +++ b/doc/en/builtin.txt @@ -25,7 +25,7 @@ You can ask for available builtin or project-custom :ref:`function arguments ` by typing:: - $ py.test --funcargs + $ py.test --fixtures =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 plugins: xdist, bugzilla, cache, oejskit, pep8, cov diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/en/example/assertion/failure_demo.py --- a/doc/en/example/assertion/failure_demo.py +++ b/doc/en/example/assertion/failure_demo.py @@ -15,7 +15,7 @@ assert param1 * 2 < param2 def pytest_generate_tests(metafunc): - if 'param1' in metafunc.funcargnames: + if 'param1' in metafunc.fixturenames: metafunc.addcall(funcargs=dict(param1=3, param2=6)) class TestFailing(object): diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/en/example/attic.txt --- a/doc/en/example/attic.txt +++ b/doc/en/example/attic.txt @@ -13,9 +13,9 @@ help="run (slow) acceptance tests") def pytest_funcarg__accept(request): - return AcceptFuncarg(request) + return AcceptFixture(request) - class AcceptFuncarg: + class AcceptFixture: def __init__(self, request): if not request.config.option.acceptance: pytest.skip("specify -A to run acceptance tests") @@ -39,7 +39,7 @@ If you run this test without specifying a command line option the test will get skipped with an appropriate message. Otherwise you can start to add convenience and test support methods -to your AcceptFuncarg and drive running of tools or +to your AcceptFixture and drive running of tools or applications and provide ways to do assertions about the output. diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/en/example/parametrize.txt --- a/doc/en/example/parametrize.txt +++ b/doc/en/example/parametrize.txt @@ -84,7 +84,7 @@ help="run all combinations") def pytest_generate_tests(metafunc): - if 'param1' in metafunc.funcargnames: + if 'param1' in metafunc.fixturenames: if metafunc.config.option.all: end = 5 else: @@ -213,7 +213,7 @@ # content of conftest.py def pytest_generate_tests(metafunc): - if 'db' in metafunc.funcargnames: + if 'db' in metafunc.fixturenames: metafunc.parametrize("db", ['d1', 'd2'], indirect=True) class DB1: diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/en/faq.txt --- a/doc/en/faq.txt +++ b/doc/en/faq.txt @@ -126,11 +126,11 @@ .. note:: With pytest-2.3 you can use the :ref:`@pytest.fixture` decorator - to mark a function as a funcarg factory. + to mark a function as a fixture function. .. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration -Can I yield multiple values from a funcarg factory function? +Can I yield multiple values from a fixture function function? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ There are two conceptual reasons why yielding from a factory function diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -676,6 +676,6 @@ * to add finalizers/teardowns to be invoked when the last test of the requesting test context executes -.. autoclass:: _pytest.python.FuncargRequest() +.. autoclass:: _pytest.python.FixtureRequest() :members: diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/en/funcarg_compare.txt --- a/doc/en/funcarg_compare.txt +++ b/doc/en/funcarg_compare.txt @@ -54,7 +54,7 @@ 4. there is no way how you can make use of funcarg factories in xUnit setup methods. -5. A non-parametrized funcarg factory cannot use a parametrized +5. A non-parametrized fixture function cannot use a parametrized funcarg resource if it isn't stated in the test function signature. All of these limitations are addressed with pytest-2.3 and its diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/en/funcargs.txt --- a/doc/en/funcargs.txt +++ b/doc/en/funcargs.txt @@ -29,7 +29,7 @@ The basic mechanism for injecting objects is called the *funcarg mechanism* because objects are injected when a test or setup **function** states it as an **argument**. The injected argument -is created by a call to a registered **funcarg factory** for each argument +is created by a call to a registered **fixture function** for each argument name. This mechanism is an example of `Dependency Injection`_ and helps to de-couple test code from the setup of required objects: at test writing time you do not need to care for the details of @@ -38,7 +38,7 @@ is invoked multiple times with differently configured resource instances. -Funcarg dependency injection allows to organise test resources +Fixture dependency injection allows to organise test resources in a modular explicit way so that test functions state their needs in their signature. pytest additionally offers powerful xunit-style :ref:`setup functions ` for the cases where you need @@ -144,7 +144,7 @@ You can always issue:: - py.test --funcargs test_simplefactory.py + py.test --fixtures test_simplefactory.py to see available function arguments. @@ -289,7 +289,7 @@ help="run all combinations") def pytest_generate_tests(metafunc): - if 'param1' in metafunc.funcargnames: + if 'param1' in metafunc.fixturenames: if metafunc.config.option.all: end = 5 else: @@ -336,7 +336,7 @@ according to test configuration or values specified in the class or module where a test function is defined: -``metafunc.funcargnames``: set of required function arguments for given function +``metafunc.fixturenames``: set of required function arguments for given function ``metafunc.function``: underlying python test function @@ -346,6 +346,8 @@ ``metafunc.config``: access to command line opts and general config +``metafunc.funcargnames``: alias for ``fixturenames``, for pre-2.3 compatibility + .. automethod:: Metafunc.parametrize .. automethod:: Metafunc.addcall(funcargs=None,id=_notexists,param=_notexists) @@ -360,7 +362,7 @@ Compatibility notes ============================================================ -**Funcargs** were originally introduced to pytest-2.0. In pytest-2.3 +**Fixtures** were originally introduced to pytest-2.0. In pytest-2.3 the mechanism was extended and refined: * previously funcarg factories were specified with a special @@ -376,14 +378,14 @@ * if you used parametrization and funcarg factories which made use of ``request.cached_setup()`` it is recommeneded to invest a few minutes - and simplify your funcarg factory code to use the `@pytest.fixture`_ + and simplify your fixture function code to use the `@pytest.fixture`_ decorator instead. This will also allow to take advantage of the `automatic per-resource grouping`_ of tests. .. note:: Throughout the pytest documents the ``pytest_funcarg__NAME`` way of - defining a funcarg factory is often termed "old-style". Their + defining a fixture function is often termed "old-style". Their use remains fully supported and existing code using it should run unmodified. diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/en/getting-started.txt --- a/doc/en/getting-started.txt +++ b/doc/en/getting-started.txt @@ -186,7 +186,7 @@ You can find out what kind of builtin :ref:`funcargs` exist by typing:: - py.test --funcargs # shows builtin and custom function arguments + py.test --fixtures # shows builtin and custom function arguments Where to go next ------------------------------------- diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/en/test/plugin/terminal.txt --- a/doc/en/test/plugin/terminal.txt +++ b/doc/en/test/plugin/terminal.txt @@ -24,7 +24,7 @@ traceback print mode (long/short/line/no). ``--fulltrace`` don't cut any tracebacks (default is to cut). -``--funcargs`` +``--fixtures`` show available function arguments, sorted by plugin Start improving this plugin in 30 seconds diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/en/usage.txt --- a/doc/en/usage.txt +++ b/doc/en/usage.txt @@ -26,7 +26,7 @@ :: py.test --version # shows where pytest was imported from - py.test --funcargs # show available builtin function arguments + py.test --fixtures # show available builtin function arguments py.test -h | --help # show help on command line and config file options diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/ja/builtin.txt --- a/doc/ja/builtin.txt +++ b/doc/ja/builtin.txt @@ -47,7 +47,7 @@ ????????????????????????????????? :ref:`????? ` ???????? - | $ py.test --funcargs + | $ py.test --fixtures | ====================== test session starts ======================= | platform linux2 -- Python 2.7.1 -- pytest-2.2.4 | collected 0 items @@ -98,7 +98,7 @@ | ======================== in 0.00 seconds ======================== .. - $ py.test --funcargs + $ py.test --fixtures =========================== test session starts ============================ platform linux2 -- Python 2.7.1 -- pytest-2.2.4 collected 0 items diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/ja/example/assertion/failure_demo.py --- a/doc/ja/example/assertion/failure_demo.py +++ b/doc/ja/example/assertion/failure_demo.py @@ -15,7 +15,7 @@ assert param1 * 2 < param2 def pytest_generate_tests(metafunc): - if 'param1' in metafunc.funcargnames: + if 'param1' in metafunc.fixturenames: metafunc.addcall(funcargs=dict(param1=3, param2=6)) class TestFailing(object): diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/ja/example/attic.txt --- a/doc/ja/example/attic.txt +++ b/doc/ja/example/attic.txt @@ -13,9 +13,9 @@ help="run (slow) acceptance tests") def pytest_funcarg__accept(request): - return AcceptFuncarg(request) + return AcceptFixture(request) - class AcceptFuncarg: + class AcceptFixture: def __init__(self, request): if not request.config.option.acceptance: pytest.skip("specify -A to run acceptance tests") @@ -39,7 +39,7 @@ If you run this test without specifying a command line option the test will get skipped with an appropriate message. Otherwise you can start to add convenience and test support methods -to your AcceptFuncarg and drive running of tools or +to your AcceptFixture and drive running of tools or applications and provide ways to do assertions about the output. diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/ja/example/multipython.py --- a/doc/ja/example/multipython.py +++ b/doc/ja/example/multipython.py @@ -11,7 +11,7 @@ # over the python interpreters of our list above - the actual # setup and lookup of interpreters in the python1/python2 factories # respectively. - for arg in metafunc.funcargnames: + for arg in metafunc.fixturenames: if arg in ("python1", "python2"): metafunc.parametrize(arg, pythonlist, indirect=True) diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/ja/example/parametrize.txt --- a/doc/ja/example/parametrize.txt +++ b/doc/ja/example/parametrize.txt @@ -117,7 +117,7 @@ help="run all combinations") def pytest_generate_tests(metafunc): - if 'param1' in metafunc.funcargnames: + if 'param1' in metafunc.fixturenames: if metafunc.config.option.all: end = 5 else: @@ -266,7 +266,7 @@ # conftest.py ??? def pytest_generate_tests(metafunc): - if 'db' in metafunc.funcargnames: + if 'db' in metafunc.fixturenames: metafunc.parametrize("db", ['d1', 'd2'], indirect=True) class DB1: diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/ja/faq.txt --- a/doc/ja/faq.txt +++ b/doc/ja/faq.txt @@ -153,7 +153,7 @@ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .. - Can I yield multiple values from a funcarg factory function? + Can I yield multiple values from a fixture function function? ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .. diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/ja/funcargs.txt --- a/doc/ja/funcargs.txt +++ b/doc/ja/funcargs.txt @@ -157,7 +157,7 @@ ???????????:: - py.test --funcargs test_simplefactory.py + py.test --fixtures test_simplefactory.py .. to see available function arguments (which you can also @@ -171,7 +171,7 @@ .. _`xUnit style`: xunit_setup.html -.. _`funcarg factory`: +.. _`fixture function`: .. _factory: funcarg **request** ?????? @@ -182,24 +182,24 @@ ============================================= .. - Each funcarg factory receives a **request** object tied to a specific test - function call. A request object is passed to a funcarg factory and provides + Each fixture function receives a **request** object tied to a specific test + function call. A request object is passed to a fixture function and provides access to test configuration and context: funcarg ?????????????????????????????? **request** ??????????????request ??????? funcarg ????????????????????????????????????: -.. autoclass:: _pytest.python.FuncargRequest() +.. autoclass:: _pytest.python.FixtureRequest() :members: function,cls,module,keywords,config .. _`useful caching and finalization helpers`: -.. automethod:: FuncargRequest.addfinalizer +.. automethod:: FixtureRequest.addfinalizer -.. automethod:: FuncargRequest.cached_setup +.. automethod:: FixtureRequest.cached_setup -.. automethod:: FuncargRequest.applymarker +.. automethod:: FixtureRequest.applymarker -.. automethod:: FuncargRequest.getfuncargvalue +.. automethod:: FixtureRequest.getfuncargvalue .. _`test generators`: @@ -236,7 +236,7 @@ # test_example.py ??? def pytest_generate_tests(metafunc): - if "numiter" in metafunc.funcargnames: + if "numiter" in metafunc.fixturenames: metafunc.parametrize("numiter", range(10)) def test_func(numiter): @@ -329,9 +329,9 @@ metafunc ??????? ``pytest_generate_tests`` ????????????????????????????????????????????????????????????????????????????????: .. - ``metafunc.funcargnames``: set of required function arguments for given function + ``metafunc.fixturenames``: set of required function arguments for given function -``metafunc.funcargnames``: ??????????????? +``metafunc.fixturenames``: ??????????????? .. ``metafunc.function``: underlying python test function diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/ja/getting-started.txt --- a/doc/ja/getting-started.txt +++ b/doc/ja/getting-started.txt @@ -265,7 +265,7 @@ ????? :ref:`funcargs` ????????????????????:: - py.test --funcargs # ????/??????????????? + py.test --fixtures # ????/??????????????? .. Where to go next diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/ja/test/plugin/terminal.txt --- a/doc/ja/test/plugin/terminal.txt +++ b/doc/ja/test/plugin/terminal.txt @@ -24,7 +24,7 @@ traceback print mode (long/short/line/no). ``--fulltrace`` don't cut any tracebacks (default is to cut). -``--funcargs`` +``--fixtures`` show available function arguments, sorted by plugin Start improving this plugin in 30 seconds diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e doc/ja/usage.txt --- a/doc/ja/usage.txt +++ b/doc/ja/usage.txt @@ -43,7 +43,7 @@ :: py.test --version # pytest ?????????????? - py.test --funcargs # ????????????????? + py.test --fixtures # ????????????????? py.test -h | --help # ?????????????????????????? .. diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e testing/conftest.py --- a/testing/conftest.py +++ b/testing/conftest.py @@ -64,7 +64,7 @@ for name, l in multi.kwargs.items(): for val in l: metafunc.addcall(funcargs={name: val}) - elif 'anypython' in metafunc.funcargnames: + elif 'anypython' in metafunc.fixturenames: for name in ('python2.4', 'python2.5', 'python2.6', 'python2.7', 'python3.1', 'pypy', 'jython'): metafunc.addcall(id=name, param=name) diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e testing/test_capture.py --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -363,7 +363,7 @@ assert 'operation on closed file' not in result.stderr.str() -class TestCaptureFuncarg: +class TestCaptureFixture: def test_std_functional(self, testdir): reprec = testdir.inline_runsource(""" def test_hello(capsys): diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e testing/test_conftest.py --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -2,7 +2,7 @@ from _pytest.config import Conftest def pytest_generate_tests(metafunc): - if "basedir" in metafunc.funcargnames: + if "basedir" in metafunc.fixturenames: metafunc.addcall(param="global") metafunc.addcall(param="inpackage") diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e testing/test_helpconfig.py --- a/testing/test_helpconfig.py +++ b/testing/test_helpconfig.py @@ -22,7 +22,7 @@ *setup.cfg* *minversion* *to see*markers*py.test --markers* - *to see*funcargs*py.test --funcargs* + *to see*fixtures*py.test --fixtures* """) def test_collectattr(): diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1,6 +1,6 @@ import pytest, py, sys from _pytest import python as funcargs -from _pytest.python import FuncargLookupError +from _pytest.python import FixtureLookupError class TestModule: def test_failing_import(self, testdir): @@ -276,10 +276,10 @@ assert hasattr(modcol.obj, 'test_func') def test_function_equality(self, testdir, tmpdir): - from _pytest.python import FuncargManager + from _pytest.python import FixtureManager config = testdir.parseconfigure() session = testdir.Session(config) - session.funcargmanager = FuncargManager(session) + session._fixturemanager = FixtureManager(session) def func1(): pass def func2(): @@ -542,10 +542,10 @@ pass assert funcargs.getfuncargnames(A) == ["x"] -class TestFillFuncArgs: +class TestFillFixtures: def test_fillfuncargs_exposed(self): - # used by oejskit - assert pytest._fillfuncargs == funcargs.fillfuncargs + # used by oejskit, kept for compatibility + assert pytest._fillfuncargs == funcargs.fillfixtures def test_funcarg_lookupfails(self, testdir): testdir.makepyfile(""" @@ -572,7 +572,7 @@ def test_func(some, other): pass """) - funcargs.fillfuncargs(item) + funcargs.fillfixtures(item) assert len(item.funcargs) == 2 assert item.funcargs['some'] == "test_func" assert item.funcargs['other'] == 42 @@ -611,7 +611,7 @@ def pytest_funcarg__something(request): pass def test_func(something): pass """) - req = funcargs.FuncargRequest(item) + req = funcargs.FixtureRequest(item) assert req.function == item.obj assert req.keywords == item.keywords assert hasattr(req.module, 'test_func') @@ -632,7 +632,7 @@ assert req.cls.__name__ == "TestB" assert req.instance.__class__ == req.cls - def XXXtest_request_contains_funcarg_name2factory(self, testdir): + def XXXtest_request_contains_funcarg_arg2fixturedeflist(self, testdir): modcol = testdir.getmodulecol(""" def pytest_funcarg__something(request): pass @@ -642,9 +642,9 @@ """) item1, = testdir.genitems([modcol]) assert item1.name == "test_method" - name2factory = funcargs.FuncargRequest(item1)._name2factory - assert len(name2factory) == 1 - assert name2factory[0].__name__ == "pytest_funcarg__something" + arg2fixturedeflist = funcargs.FixtureRequest(item1)._arg2fixturedeflist + assert len(arg2fixturedeflist) == 1 + assert arg2fixturedeflist[0].__name__ == "pytest_funcarg__something" def test_getfuncargvalue_recursive(self, testdir): testdir.makeconftest(""" @@ -669,7 +669,7 @@ def test_func(something): pass """) req = item._request - pytest.raises(FuncargLookupError, req.getfuncargvalue, "notexists") + pytest.raises(FixtureLookupError, req.getfuncargvalue, "notexists") val = req.getfuncargvalue("something") assert val == 1 val = req.getfuncargvalue("something") @@ -717,7 +717,7 @@ def test_request_getmodulepath(self, testdir): modcol = testdir.getmodulecol("def test_somefunc(): pass") item, = testdir.genitems([modcol]) - req = funcargs.FuncargRequest(item) + req = funcargs.FixtureRequest(item) assert req.fspath == modcol.fspath class TestMarking: @@ -731,7 +731,7 @@ def test_func2(self, something): pass """) - req1 = funcargs.FuncargRequest(item1) + req1 = funcargs.FixtureRequest(item1) assert 'xfail' not in item1.keywords req1.applymarker(pytest.mark.xfail) assert 'xfail' in item1.keywords @@ -814,7 +814,7 @@ def test_request_cachedsetup_extrakey(self, testdir): item1 = testdir.getitem("def test_func(): pass") - req1 = funcargs.FuncargRequest(item1) + req1 = funcargs.FixtureRequest(item1) l = ["hello", "world"] def setup(): return l.pop() @@ -829,7 +829,7 @@ def test_request_cachedsetup_cache_deletion(self, testdir): item1 = testdir.getitem("def test_func(): pass") - req1 = funcargs.FuncargRequest(item1) + req1 = funcargs.FixtureRequest(item1) l = [] def setup(): l.append("setup") @@ -906,14 +906,14 @@ def test_no_funcargs(self, testdir): def function(): pass metafunc = funcargs.Metafunc(function) - assert not metafunc.funcargnames + assert not metafunc.fixturenames repr(metafunc._calls) def test_function_basic(self): def func(arg1, arg2="qwe"): pass metafunc = funcargs.Metafunc(func) - assert len(metafunc.funcargnames) == 1 - assert 'arg1' in metafunc.funcargnames + assert len(metafunc.fixturenames) == 1 + assert 'arg1' in metafunc.fixturenames assert metafunc.function is func assert metafunc.cls is None @@ -1032,7 +1032,7 @@ def test_parametrize_functional(self, testdir): testdir.makepyfile(""" def pytest_generate_tests(metafunc): - assert "test_parametrize_functional" in metafunc.parentid + assert "test_parametrize_functional" in metafunc._parentid metafunc.parametrize('x', [1,2], indirect=True) metafunc.parametrize('y', [2]) def pytest_funcarg__x(request): @@ -1170,7 +1170,7 @@ def test_addcall_with_two_funcargs_generators(self, testdir): testdir.makeconftest(""" def pytest_generate_tests(metafunc): - assert "arg1" in metafunc.funcargnames + assert "arg1" in metafunc.fixturenames metafunc.addcall(funcargs=dict(arg1=1, arg2=2)) """) p = testdir.makepyfile(""" @@ -1213,7 +1213,7 @@ def test_noself_in_method(self, testdir): p = testdir.makepyfile(""" def pytest_generate_tests(metafunc): - assert 'xyz' not in metafunc.funcargnames + assert 'xyz' not in metafunc.fixturenames class TestHello: def test_hello(xyz): @@ -1228,7 +1228,7 @@ def test_generate_plugin_and_module(self, testdir): testdir.makeconftest(""" def pytest_generate_tests(metafunc): - assert "arg1" in metafunc.funcargnames + assert "arg1" in metafunc.fixturenames metafunc.addcall(id="world", param=(2,100)) """) p = testdir.makepyfile(""" @@ -1342,7 +1342,7 @@ def test_parametrize_on_setup_arg(self, testdir): p = testdir.makepyfile(""" def pytest_generate_tests(metafunc): - assert "arg1" in metafunc.funcargnames + assert "arg1" in metafunc.fixturenames metafunc.parametrize("arg1", [1], indirect=True) def pytest_funcarg__arg1(request): @@ -1403,7 +1403,7 @@ clscol = rep.result[0] clscol.obj = lambda arg1: None clscol.funcargs = {} - funcargs.fillfuncargs(clscol) + funcargs.fillfixtures(clscol) assert clscol.funcargs['arg1'] == 42 @@ -1488,7 +1488,7 @@ """ def test_show_funcarg(testdir): - result = testdir.runpytest("--funcargs") + result = testdir.runpytest("--fixtures") result.stdout.fnmatch_lines([ "*tmpdir*", "*temporary directory*", @@ -1669,7 +1669,7 @@ ]) -class TestFuncargFactory: +class TestFixtureFactory: def test_receives_funcargs(self, testdir): testdir.makepyfile(""" import pytest @@ -1809,7 +1809,7 @@ "*test_function*advanced*FAILED", ]) -class TestFuncargManager: +class TestFixtureManager: def pytest_funcarg__testdir(self, request): testdir = request.getfuncargvalue("testdir") testdir.makeconftest(""" @@ -1817,7 +1817,7 @@ return "conftest" def pytest_funcarg__fm(request): - return request.funcargmanager + return request._fixturemanager def pytest_funcarg__item(request): return request._pyfuncitem @@ -1828,7 +1828,7 @@ testdir.makepyfile(""" def test_hello(item, fm): for name in ("fm", "hello", "item"): - faclist = fm.getfactorylist(name, item.nodeid) + faclist = fm.getfixturedeflist(name, item.nodeid) assert len(faclist) == 1 fac = faclist[0] assert fac.func.__name__ == "pytest_funcarg__" + name @@ -1844,7 +1844,7 @@ def pytest_funcarg__hello(self, request): return "class" def test_hello(self, item, fm): - faclist = fm.getfactorylist("hello", item.nodeid) + faclist = fm.getfixturedeflist("hello", item.nodeid) print (faclist) assert len(faclist) == 3 assert faclist[0].func(item._request) == "conftest" @@ -1870,7 +1870,7 @@ pass def pytest_funcarg__fm(request): - return request.funcargmanager + return request._fixturemanager def pytest_funcarg__item(request): return request._pyfuncitem @@ -1919,11 +1919,11 @@ pass def test_func1(request): - assert "db" not in request.funcargnames + assert "db" not in request.fixturenames @pytest.mark.needsdb def test_func2(request): - assert "db" in request.funcargnames + assert "db" in request.fixturenames """) reprec = testdir.inline_run("-s") reprec.assertoutcome(passed=2) @@ -2105,7 +2105,7 @@ reprec.assertoutcome(passed=5) -class TestFuncargMarker: +class TestFixtureMarker: def test_parametrize(self, testdir): testdir.makepyfile(""" import pytest @@ -2686,7 +2686,7 @@ reprec.assertoutcome(passed=1) -def test_request_funcargnames(testdir): +def test_request_fixturenames(testdir): testdir.makepyfile(""" import pytest @pytest.fixture() @@ -2699,8 +2699,23 @@ def sarg(tmpdir): pass def test_function(request, farg): - assert set(request.funcargnames) == \ + assert set(request.fixturenames) == \ set(["tmpdir", "sarg", "arg1", "request", "farg"]) """) reprec = testdir.inline_run() reprec.assertoutcome(passed=1) + +def test_funcargnames_compatattr(testdir): + testdir.makepyfile(""" + def pytest_generate_tests(metafunc): + assert metafunc.funcargnames == metafunc.fixturenames + def pytest_funcarg__fn(request): + assert request._pyfuncitem.funcargnames == \ + request._pyfuncitem.fixturenames + return request.funcargnames, request.fixturenames + + def test_hello(fn): + assert fn[0] == fn[1] + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) diff -r 70b4758381a0f1f453bb450ae349eedb3b0f1000 -r 8d87ff80839344ad785f4d07c081ccab5603325e testing/test_terminal.py --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -26,7 +26,7 @@ return l def pytest_generate_tests(metafunc): - if "option" in metafunc.funcargnames: + if "option" in metafunc.fixturenames: metafunc.addcall(id="default", funcargs={'option': Option(verbose=False)}) metafunc.addcall(id="verbose", https://bitbucket.org/hpk42/pytest/changeset/0d8d41c1f4c9/ changeset: 0d8d41c1f4c9 user: hpk42 date: 2012-10-05 14:24:45 summary: make the default non-error pass simpler and faster, refine error reporting by presenting "fixture" tracebacks affected #: 3 files diff -r 8d87ff80839344ad785f4d07c081ccab5603325e -r 0d8d41c1f4c9f0bfc11523b446cb3176cb31db70 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -310,16 +310,7 @@ def _repr_failure_py(self, excinfo, style=None): fm = self.session._fixturemanager if excinfo.errisinstance(fm.FixtureLookupError): - function = excinfo.value.function - factblines = excinfo.value.factblines - if function is not None: - fspath, lineno = getfslineno(function) - lines, _ = inspect.getsourcelines(function) - for i, line in enumerate(lines): - if line.strip().startswith('def'): - return fm.FixtureLookupErrorRepr(fspath, - lineno, lines[:i+1], - str(excinfo.value.msg), factblines) + return excinfo.value.formatrepr() if self.config.option.fulltrace: style="long" else: diff -r 8d87ff80839344ad785f4d07c081ccab5603325e -r 0d8d41c1f4c9f0bfc11523b446cb3176cb31db70 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -993,20 +993,12 @@ def _getfixturedeflist(self, argname): fixturedeflist = self._arg2fixturedeflist.get(argname, None) - getfixturetb = None - function = None if fixturedeflist is None: - if self._fixturestack: - function = self._fixturestack[-1].func - getfixturetb = lambda: self._fixturestack[:-1] - else: - function = self.function fixturedeflist = self._fixturemanager.getfixturedeflist( argname, self._parentid) self._arg2fixturedeflist[argname] = fixturedeflist if not fixturedeflist: - self._fixturemanager._raiselookupfailed(argname, function, - self._parentid, getfixturetb) + raise FixtureLookupError(argname, self) return fixturedeflist @property @@ -1085,8 +1077,7 @@ def raiseerror(self, msg): """ raise a FixtureLookupError with the given message. """ - raise self._fixturemanager.FixtureLookupError(self.function, msg) - + raise self._fixturemanager.FixtureLookupError(None, self, msg) def _fillfixtures(self): item = self._pyfuncitem @@ -1250,32 +1241,58 @@ return new_kwargs class FixtureLookupError(LookupError): - """ could not find a factory. """ - def __init__(self, function, msg, factblines=None): - self.function = function + """ could not return a requested Fixture (missing or invalid). """ + def __init__(self, argname, request, msg=None): + self.argname = argname + self.request = request + self.fixturestack = list(request._fixturestack) self.msg = msg - self.factblines = factblines + + def formatrepr(self): + tblines = [] + addline = tblines.append + stack = [self.request._pyfuncitem.obj] + stack.extend(map(lambda x: x.func, self.fixturestack)) + msg = self.msg + if msg is not None: + stack = stack[:-1] # the last fixture raise an error, let's present + # it at the requesting side + for function in stack: + fspath, lineno = getfslineno(function) + lines, _ = inspect.getsourcelines(function) + addline("file %s, line %s" % (fspath, lineno+1)) + for i, line in enumerate(lines): + line = line.rstrip() + addline(" " + line) + if line.lstrip().startswith('def'): + break + + if msg is None: + fm = self.request._fixturemanager + nodeid = self.request._parentid + available = [] + for name, fixturedef in fm.arg2fixturedeflist.items(): + faclist = list(fm._matchfactories(fixturedef, self.request._parentid)) + if faclist: + available.append(name) + msg = "fixture %r not found" % (self.argname,) + msg += "\n available fixtures: %s" %(", ".join(available),) + msg += "\n use 'py.test --fixtures [testpath]' for help on them." + + return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname) class FixtureLookupErrorRepr(TerminalRepr): - def __init__(self, filename, firstlineno, deflines, errorstring, factblines): - self.deflines = deflines + def __init__(self, filename, firstlineno, tblines, errorstring, argname): + self.tblines = tblines self.errorstring = errorstring self.filename = filename self.firstlineno = firstlineno - self.factblines = factblines + self.argname = argname def toterminal(self, tw): - tw.line() - if self.factblines: - tw.line(' dependency of:') - for fixturedef in self.factblines: - tw.line(' %s in %s' % ( - fixturedef.argname, - fixturedef.baseid, - )) - tw.line() - for line in self.deflines: - tw.line(" " + line.strip()) + #tw.line("FixtureLookupError: %s" %(self.argname), red=True) + for tbline in self.tblines: + tw.line(tbline.rstrip()) for line in self.errorstring.split("\n"): tw.line(" " + line.strip(), red=True) tw.line() @@ -1436,18 +1453,6 @@ if nodeid.startswith(fixturedef.baseid): yield fixturedef - def _raiselookupfailed(self, argname, function, nodeid, getfixturetb=None): - available = [] - for name, fixturedef in self.arg2fixturedeflist.items(): - faclist = list(self._matchfactories(fixturedef, nodeid)) - if faclist: - available.append(name) - msg = "LookupError: no factory found for argument %r" % (argname,) - msg += "\n available funcargs: %s" %(", ".join(available),) - msg += "\n use 'py.test --fixtures [testpath]' for help on them." - lines = getfixturetb and getfixturetb() or [] - raise FixtureLookupError(function, msg, lines) - def addargfinalizer(self, finalizer, argname): l = self._arg2finish.setdefault(argname, []) l.append(finalizer) diff -r 8d87ff80839344ad785f4d07c081ccab5603325e -r 0d8d41c1f4c9f0bfc11523b446cb3176cb31db70 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -559,7 +559,7 @@ assert result.ret != 0 result.stdout.fnmatch_lines([ "*def test_func(some)*", - "*LookupError*", + "*fixture*some*not found*", "*xyzsomething*", ]) @@ -1416,8 +1416,8 @@ result.stdout.fnmatch_lines([ "*ERROR*test_lookup_error*", "*def test_lookup_error(unknown):*", - "*LookupError: no factory found*unknown*", - "*available funcargs*", + "*fixture*unknown*not found*", + "*available fixtures*", "*1 error*", ]) assert "INTERNAL" not in result.stdout.str() @@ -1750,12 +1750,13 @@ pass """) result = testdir.runpytest() - result.stdout.fnmatch_lines([ - "*dependency of:*", - "*call_fail*", - "*def fail(*", - "*LookupError: no factory found for argument 'missing'", - ]) + result.stdout.fnmatch_lines(""" + *pytest.fixture()* + *def call_fail(fail)* + *pytest.fixture()* + *def fail* + *fixture*'missing'*not found* + """) def test_factory_setup_as_classes(self, testdir): testdir.makepyfile(""" @@ -2585,7 +2586,7 @@ assert result.ret != 0 result.stdout.fnmatch_lines([ "*def gen(qwe123):*", - "*no factory*qwe123*", + "*fixture*qwe123*not found*", "*1 error*", ]) @@ -2602,7 +2603,7 @@ assert result.ret != 0 result.stdout.fnmatch_lines([ "*def gen(qwe123):*", - "*no factory*qwe123*", + "*fixture*qwe123*not found*", "*1 error*", ]) https://bitbucket.org/hpk42/pytest/changeset/05bd8a8df91e/ changeset: 05bd8a8df91e user: hpk42 date: 2012-10-05 14:35:16 summary: bump version affected #: 2 files diff -r 0d8d41c1f4c9f0bfc11523b446cb3176cb31db70 -r 05bd8a8df91ee15eaf1ab7e9aa21e3265fe62151 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev17' +__version__ = '2.3.0.dev18' diff -r 0d8d41c1f4c9f0bfc11523b446cb3176cb31db70 -r 05bd8a8df91ee15eaf1ab7e9aa21e3265fe62151 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev17', + version='2.3.0.dev18', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Oct 5 19:20:50 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 05 Oct 2012 17:20:50 -0000 Subject: [py-svn] commit/pytest: hpk42: implement pytest.mark.usefixtures and ini-file usefixtures setting Message-ID: <20121005172050.29314.21431@bitbucket14.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/ac92c63e9b31/ changeset: ac92c63e9b31 user: hpk42 date: 2012-10-05 19:20:40 summary: implement pytest.mark.usefixtures and ini-file usefixtures setting and also refine fixture docs a bit - fixtures.txt should now mostly reflect the current state of the implementation affected #: 5 files diff -r 05bd8a8df91ee15eaf1ab7e9aa21e3265fe62151 -r ac92c63e9b31659f2b02cd0c7e37236497067e4c _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -77,6 +77,8 @@ group.addoption('--fixtures', '--fixtures', action="store_true", dest="showfixtures", default=False, help="show available fixtures, sorted by plugin appearance") + parser.addini("usefixtures", type="args", default=(), + help="list of default fixtures to be used with this project") parser.addini("python_files", type="args", default=('test_*.py', '*_test.py'), help="glob-style file patterns for Python test module discovery") @@ -879,7 +881,6 @@ #req._discoverfactories() if callobj is not _dummy: self.obj = callobj - self.fixturenames = self._getfuncargnames() for name, val in (py.builtin._getfuncdict(self.obj) or {}).items(): setattr(self.markers, name, val) @@ -887,11 +888,17 @@ for name, val in keywords.items(): setattr(self.markers, name, val) + # contstruct a list of all neccessary fixtures for this test function + if hasattr(self.markers, "usefixtures"): + usefixtures = list(self.markers.usefixtures.args) + else: + usefixtures = [] + self.fixturenames = (self.session._fixturemanager.getdefaultfixtures() + + usefixtures + self._getfuncargnames()) def _getfuncargnames(self): startindex = int(self.cls is not None) - return (self.session._fixturemanager._autofixtures + - getfuncargnames(self.obj, startindex=startindex)) + return getfuncargnames(self.obj, startindex=startindex) @property def function(self): @@ -1336,8 +1343,22 @@ for plugin in plugins: self.pytest_plugin_registered(plugin) + def getdefaultfixtures(self): + """ return a list of default fixture names (XXX for the given file path). """ + try: + return self._defaultfixtures + except AttributeError: + defaultfixtures = list(self.config.getini("usefixtures")) + # make sure the self._autofixtures list is sorted + # by scope, scopenum 0 is session + self._autofixtures.sort( + key=lambda x: self.arg2fixturedeflist[x][-1].scopenum) + defaultfixtures.extend(self._autofixtures) + self._defaultfixtures = defaultfixtures + return defaultfixtures + def getfixtureclosure(self, fixturenames, parentnode): - # collect the closure of all funcargs, starting with the given + # collect the closure of all fixtures , starting with the given # fixturenames as the initial set. As we have to visit all # factory definitions anyway, we also return a arg2fixturedeflist # mapping so that the caller can reuse it and does not have @@ -1345,7 +1366,7 @@ # (discovering matching fixtures for a given name/node is expensive) parentid = parentnode.nodeid - fixturenames_closure = list(self._autofixtures) + fixturenames_closure = list(self.getdefaultfixtures()) def merge(otherlist): for arg in otherlist: if arg not in fixturenames_closure: @@ -1434,11 +1455,11 @@ faclist = self.arg2fixturedeflist.setdefault(name, []) faclist.append(fixturedef) if marker.autoactive: - # make sure the self._autofixtures list is always sorted - # by scope, scopenum 0 is session self._autofixtures.append(name) - self._autofixtures.sort( - key=lambda x: self.arg2fixturedeflist[x][-1].scopenum) + try: + del self._defaultfixtures + except AttributeError: + pass def getfixturedeflist(self, argname, nodeid): try: diff -r 05bd8a8df91ee15eaf1ab7e9aa21e3265fe62151 -r ac92c63e9b31659f2b02cd0c7e37236497067e4c _pytest/unittest.py --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -53,7 +53,7 @@ _excinfo = None def _getfuncargnames(self): - return list(self.session._fixturemanager._autofixtures) + return [] def setup(self): self._testcase = self.parent.obj(self.name) diff -r 05bd8a8df91ee15eaf1ab7e9aa21e3265fe62151 -r ac92c63e9b31659f2b02cd0c7e37236497067e4c doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.3.0.dev9" +version = release = "2.3.0.dev18" import sys, os diff -r 05bd8a8df91ee15eaf1ab7e9aa21e3265fe62151 -r ac92c63e9b31659f2b02cd0c7e37236497067e4c doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -18,50 +18,54 @@ .. _`pytest-django`: https://pypi.python.org/pytest-django .. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection#Definition -pytest allows to provide and use test fixtures in a modular and flexible -manner, offering major improvements over the classic xUnit style of +pytest allows to create and use test fixtures in a modular and flexible +manner, offering dramatic improvements over the classic xUnit style of setup/teardown functions. The `general purpose of test fixtures`_ is to provide a fixed baseline upon which tests can reliably and -repeatedly execute. With pytest, fixtures are implemented by -**fixture functions** which may return a fixture object, put extra -attributes on test classes or perform side effects. The name of a -fixture function is significant and is used for invoking or activating it. +repeatedly execute. With pytest, fixtures have names and can be referenced +from test functions, modules, classes or whole projects. Fixtures are +implemented by **fixture functions** which may return a fixture object +or put extra attributes on test classes or perform global side effects +if needed. Fixtures can themselves access other fixtures, allowing a +**structured modular approach** to organising fixtures for an +application. **Test functions can receive fixture objects by naming them as an input argument.** For each argument name, a matching fixture -function will provide a fixture object. This mechanism has been +function will provide a fixture object. This mechanism was already introduced with pytest-2.0 and is also called the **funcarg mechanism**. It allows test functions to easily receive and work against specific pre-initialized application objects without having to care about the -details of setup/cleanup procedures. This mechanism is a prime example of +details of setup/cleanup procedures. It's a prime example of `dependency injection`_ where fixture functions take the role of the *injector* and test functions are the *consumers* of fixture objects. -With pytest-2.3 this mechanism has been much improved to help with -sharing and parametrizing fixtures across test runs. +With pytest-2.3 this mechanism has been generalized and improved as described +further in this document. **Test classes, modules or whole projects can declare a need for one or more fixtures**. All required fixture functions will execute -before a test from the specifying context executes. They will -typically not provide a fixture object but rather perform side effects -like reading or preparing default config settings and pre-initializing -an application. For example, the Django_ project requires database +before a test from the specifying context executes. You can use this +to make tests operate from a pre-initialized directory or with +certain environment variables or with pre-initialized applications. +For example, the Django_ project requires database initialization to be able to import from and use its model objects. -Plugins like `pytest-django`_ provide baseline fixtures which your -project can then easily depend or extend on. +For that, the `pytest-django`_ plugin provides fixtures which your +project can then easily depend or extend on, simply by referencing the +name of the particular fixture. **Fixtures can be shared throughout a test session, module or class.**. By means of a "scope" declaration on a fixture function, it will -only be invoked once per the specified scope. Sharing expensive application -object setups between tests typically helps to speed up test runs. +only be invoked once per the specified scope. This allows to reduce the number +of expensive application object setups and thus helps to speed up test runs. Typical examples are the setup of test databases or establishing required subprocesses or network connections. -**Fixture functions have controlled visilibity** which depends on where they +**Fixture functions have limited visilibity** which depends on where they are defined. If they are defined on a test class, only its test methods may use it. A fixture defined in a module can only be used from that test module. A fixture defined in a conftest.py file can only be used by the tests below the directory of that file. -Lastly plugins can define fixtures which are available across all +Lastly, plugins can define fixtures which are available across all projects. **Fixture functions can interact with the requesting testcontext**. By @@ -70,17 +74,17 @@ optionally register cleanup functions which are called when the last test finished execution. A good example is `pytest-timeout`_ which allows to limit the execution time of a test, and will read the -according parameter from a test function or from project-wide setting. +according parameter from a test function or from project-wide settings. **Fixture functions can be parametrized** in which case they will be called multiple times, each time executing the set of dependent tests, i. e. the tests that depend on this fixture. Test functions do usually not need to be aware of their re-running. Fixture parametrization helps to -write functional tests for components which themselves can be +write exhaustive functional tests for components which themselves can be configured in multiple ways. -Basic funcarg fixture example +Basic test function with fixtures ----------------------------------------------------------- .. versionadded:: 2.3 @@ -92,21 +96,22 @@ # content of ./test_simplefactory.py import pytest - @pytest.fixture + @pytest.fixture() def myfuncarg(): return 42 def test_function(myfuncarg): assert myfuncarg == 17 -Here, the ``test_function`` needs an object named ``myfuncarg`` and thus -py.test will discover and call the ``@pytest.fixture`` marked ``myfuncarg`` -factory function. Running the tests looks like this:: +Here, the ``test_function`` needs a very simple fixture ``myfuncarg`` which +it wants to compare against a specific value. py.test will discover and call +the ``@pytest.fixture`` marked ``myfuncarg`` fixture function. Running the +tests looks like this:: $ py.test test_simplefactory.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev18 + plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout collecting ... collected 1 items test_simplefactory.py F @@ -129,7 +134,7 @@ 1. py.test :ref:`finds ` the ``test_function`` because of the ``test_`` prefix. The test function needs a function argument - named ``myfuncarg``. A matching factory function is discovered by + named ``myfuncarg``. A matching fixture function is discovered by looking for a fixture function named ``myfuncarg``. 2. ``myfuncarg()`` is called to create a value ``42``. @@ -150,7 +155,7 @@ to see available fixtures. In versions prior to 2.3 there was no @pytest.fixture marker - and you had to instead use a magic ``pytest_funcarg__NAME`` prefix + and you had to use a magic ``pytest_funcarg__NAME`` prefix for the fixture factory. This remains and will remain supported but is not advertised as the primary means of declaring fixture functions. @@ -163,7 +168,7 @@ Here is a simple example of a fixture function creating a shared ``smtplib.SMTP`` connection fixture which test functions from -test modules below the directory of a ``conftest.py`` file may use:: +any test module inside the directory of a ``conftest.py`` file may use:: # content of conftest.py import pytest @@ -178,6 +183,7 @@ function:: # content of test_module.py + def test_ehlo(smtp): response = smtp.ehlo() assert response[0] == 250 @@ -198,7 +204,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -210,7 +216,7 @@ test_module.py:5: AssertionError ________________________________ test_noop _________________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -219,19 +225,23 @@ E assert 0 test_module.py:10: AssertionError - 2 failed in 0.26 seconds + 2 failed in 0.15 seconds you see the two ``assert 0`` failing and can also see that the same (session-scoped) object was passed into the two test functions because pytest shows the incoming arguments in the traceback. + Adding a finalizer to a fixture -------------------------------------------------------- Further extending the ``smtp`` example, we now want to properly close a smtp server connection after the last test using it -has been run. We can do this by calling the ``request.addfinalizer()`` -helper:: +has been run. We can do this by changing the fixture function +to accept the special :ref:`request` object, representing the +requesting test context. After calling the ``request.addfinalizer()`` +helper pytest will make sure that the finalizer function is called +after the last test using the ``smtp`` resource has finished. # content of conftest.py import pytest @@ -250,10 +260,10 @@ using it has executed:: $ py.test -s -q --tb=no - collecting ... collected 4 items - FFFF - 4 failed in 6.40 seconds - finalizing + collecting ... collected 2 items + FF + 2 failed in 0.21 seconds + finalizing We see that the ``smtp`` instance is finalized after all tests executed. If we had specified ``scope='function'`` @@ -293,7 +303,7 @@ ================================= FAILURES ================================= __________________________ test_ehlo[merlinux.eu] __________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -305,7 +315,7 @@ test_module.py:5: AssertionError __________________________ test_noop[merlinux.eu] __________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -316,7 +326,7 @@ test_module.py:10: AssertionError ________________________ test_ehlo[mail.python.org] ________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -327,7 +337,7 @@ test_module.py:4: AssertionError ________________________ test_noop[mail.python.org] ________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -336,7 +346,7 @@ E assert 0 test_module.py:10: AssertionError - 4 failed in 6.17 seconds + 4 failed in 6.62 seconds We now get four failures because we are running the two tests twice with different ``smtp`` fixture instances. Note that with the @@ -352,8 +362,8 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev18 + plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout collecting ... collected 4 items @@ -400,15 +410,15 @@ $ py.test -v test_appsetup.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python - cachedir: /home/hpk/tmp/doc-exec-423/.cache - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev18 -- /home/hpk/venv/1/bin/python + cachedir: /tmp/doc-exec-6/.cache + plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout collecting ... collected 2 items - test_appsetup.py:12: test_exists[merlinux.eu] PASSED - test_appsetup.py:12: test_exists[mail.python.org] PASSED + test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED + test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED - ========================= 2 passed in 6.82 seconds ========================= + ========================= 2 passed in 0.14 seconds ========================= Due to the parametrization of ``smtp`` the test will run twice with two different ``App`` instances and respective smtp servers. There is no @@ -463,9 +473,9 @@ $ py.test -v -s test_module.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python - cachedir: /home/hpk/tmp/doc-exec-423/.cache - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev18 -- /home/hpk/venv/1/bin/python + cachedir: /tmp/doc-exec-6/.cache + plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout collecting ... collected 8 items test_module.py:16: test_0[1] PASSED @@ -525,7 +535,7 @@ import os import pytest - @pytest.mark.needsfixtures("cleandir") + @pytest.mark.usefixtures("cleandir") class TestDirectoryInit: def test_cwd_starts_empty(self): assert os.listdir(os.getcwd()) == [] @@ -535,24 +545,24 @@ def test_cwd_again_starts_empty(self): assert os.listdir(os.getcwd()) == [] -Due to the ``needs`` class marker, the ``cleandir`` fixture +Due to the ``usefixtures`` marker, the ``cleandir`` fixture will be required for the execution of each of the test methods, just as if you specified a "cleandir" function argument to each of them. Let's run it to verify our fixture is activated:: $ py.test -q collecting ... collected 2 items - . - 2 passed in 0.01 seconds + .. + 2 passed in 0.02 seconds You may specify the need for multiple fixtures:: - @pytest.mark.needsfixtures("cleandir", "anotherfixture") + @pytest.mark.usefixtures("cleandir", "anotherfixture") and you may specify fixture needs at the test module level, using a generic feature of the mark mechanism:: - pytestmark = pytest.mark.needsfixtures("cleandir") + pytestmark = pytest.mark.usefixtures("cleandir") Lastly you can put fixtures required by all tests in your project into an ini-file:: @@ -560,20 +570,20 @@ # content of pytest.ini [pytest] - needsfixtures = cleandir + usefixtures = cleandir -Implicit fixtures at class/module/directory/global level + +autoactive fixtures at class/module/directory/global level ---------------------------------------------------------------------- .. regendoc:wipe Occasionally, you may want to have fixtures get invoked automatically -without any ``needs`` reference. Also, if you are used to the classical -xUnit setup/teardown functions you may have gotten used to fixture -functions executing always. As a practical example, +without any ``usefixtures`` or funcargs reference. As a practical example, suppose we have a database fixture which has a begin/rollback/commit -architecture and we want to surround each test method by a transaction -and a rollback. Here is a dummy self-contained implementation:: +architecture and we want to automatically surround each test method by a +transaction and a rollback. Here is a dummy self-contained implementation +of this idea:: # content of test_db_transact.py @@ -582,46 +592,55 @@ @pytest.fixture(scope="module") class db: def __init__(self): - self.intransaction = False - def begin(self): - self.intransaction = True - def rollback(Self): - self.intransaction = False + self.intransaction = [] + def begin(self, name): + self.intransaction.append(name) + def rollback(self): + self.intransaction.pop() class TestClass: - @pytest.fixture(auto=True) + @pytest.fixture(autoactive=True) def transact(self, request, db): - db.begin() + db.begin(request.function.__name__) request.addfinalizer(db.rollback) def test_method1(self, db): - assert db.intransaction + assert db.intransaction == ["test_method1"] - def test_method2(self): - pass + def test_method2(self, db): + assert db.intransaction == ["test_method2"] -The class-level ``transact`` fixture is marked with *auto=true* which will -mark all test methods in the class as needing the fixture. +The class-level ``transact`` fixture is marked with *autoactive=true* which implies +that all test methods in the class will use this fixture without a need to +specify it. -Here is how this maps to module, project and cross-project scopes: +If we run it, we get two passing tests:: -- if an automatic fixture was defined in a test module, all its test - functions would automatically invoke it. + $ py.test -q + collecting ... collected 2 items + .. + 2 passed in 0.02 seconds -- if defined in a conftest.py file then all tests in all test - modules belows its directory will invoke the fixture. +And here is how autoactive fixtures work in other scopes: -- lastly, and **please use that with care**: if you define an automatic +- if an autoactive fixture is defined in a test module, all its test + functions automatically use it. + +- if an autoactive fixture is defined in a conftest.py file then all tests in + all test modules belows its directory will invoke the fixture. + +- lastly, and **please use that with care**: if you define an autoactive fixture in a plugin, it will be invoked for all tests in all projects where the plugin is installed. This can be useful if a fixture only - anyway works in the presence of certain settings in the ini-file. Such - a global fixture should thus quickly determine if it should do + anyway works in the presence of certain settings e. g. in the ini-file. Such + a global fixture should always quickly determine if it should do any work and avoid expensive imports or computation otherwise. Note that the above ``transact`` fixture may very well be something that -you want to make available in your project without having each test function -in your project automatically using it. The canonical way to do that is to put -the transact definition into a conftest.py file without using ``auto``:: +you want to make available in your project but which requires an explicit using +reference to have it activated. The canonical way to do that is to put +the transact definition into a conftest.py file without using +``autoactive``:: # content of conftest.py @pytest.fixture() @@ -631,13 +650,13 @@ and then have a TestClass using it by declaring the need:: - @pytest.mark.needsfixtures("transact") + @pytest.mark.usefixtures("transact") class TestClass: def test_method1(self): ... -While all test methods in this TestClass will thus use the transaction -fixture, other test classes will not unless they state the need. +While all test methods in this TestClass will use the transaction +fixture, other test classes or function will not do so without a marker or funcarg. .. currentmodule:: _pytest.python diff -r 05bd8a8df91ee15eaf1ab7e9aa21e3265fe62151 -r ac92c63e9b31659f2b02cd0c7e37236497067e4c testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1791,6 +1791,51 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=1) + def test_usefixtures_marker(self, testdir): + testdir.makepyfile(""" + import pytest + + l = [] + + @pytest.fixture(scope="class") + def myfix(request): + request.cls.hello = "world" + l.append(1) + + class TestClass: + def test_one(self): + assert self.hello == "world" + assert len(l) == 1 + def test_two(self): + assert self.hello == "world" + assert len(l) == 1 + pytest.mark.usefixtures("myfix")(TestClass) + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2) + + def test_usefixtures_ini(self, testdir): + testdir.makeini(""" + [pytest] + usefixtures = myfix + """) + testdir.makeconftest(""" + import pytest + + @pytest.fixture(scope="class") + def myfix(request): + request.cls.hello = "world" + + """) + testdir.makepyfile(""" + class TestClass: + def test_one(self): + assert self.hello == "world" + def test_two(self): + assert self.hello == "world" + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2) class TestResourceIntegrationFunctional: def test_parametrize_with_ids(self, testdir): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Oct 5 23:45:01 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 05 Oct 2012 21:45:01 -0000 Subject: [py-svn] commit/pytest: flub: Use updated names Message-ID: <20121005214501.3960.38075@bitbucket22.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/fe752ae6e18c/ changeset: fe752ae6e18c user: flub date: 2012-10-05 23:44:18 summary: Use updated names affected #: 1 file diff -r ac92c63e9b31659f2b02cd0c7e37236497067e4c -r fe752ae6e18c5108e77a822e8f0e9d3a3ef4f5b7 doc/en/funcarg_compare.txt --- a/doc/en/funcarg_compare.txt +++ b/doc/en/funcarg_compare.txt @@ -118,10 +118,10 @@ factory function. -No ``pytest_funcarg__`` prefix when using @factory decorator +No ``pytest_funcarg__`` prefix when using @fixture decorator ------------------------------------------------------------------- -When using the ``@factory`` decorator the name of the function +When using the ``@fixture`` decorator the name of the function denotes the name under which the resource can be accessed as a function argument:: Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Oct 6 11:36:18 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 06 Oct 2012 09:36:18 -0000 Subject: [py-svn] commit/pytest: hpk42: fix issue197 - in case a function is parametrized with zero arguments, Message-ID: <20121006093618.26737.15427@bitbucket21.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/b10528b9cd1a/ changeset: b10528b9cd1a user: hpk42 date: 2012-10-06 11:34:06 summary: fix issue197 - in case a function is parametrized with zero arguments, skip it during setup affected #: 3 files diff -r fe752ae6e18c5108e77a822e8f0e9d3a3ef4f5b7 -r b10528b9cd1acc11edca5172a9d8520069acd246 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ Changes between 2.2.4 and 2.3.0.dev ----------------------------------- +- fix issue193 skip test functions with were parametrized with empty + parameter sets - fix python3.3 compat, mostly reporting bits that previously depended on dict ordering - introduce a generic "markers" object on Nodes and a request.node diff -r fe752ae6e18c5108e77a822e8f0e9d3a3ef4f5b7 -r b10528b9cd1acc11edca5172a9d8520069acd246 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -95,11 +95,11 @@ def pytest_generate_tests(metafunc): try: - param = metafunc.function.parametrize + markers = metafunc.function.parametrize except AttributeError: return - for p in param: - metafunc.parametrize(*p.args, **p.kwargs) + for marker in markers: + metafunc.parametrize(*marker.args, **marker.kwargs) def pytest_configure(config): config.addinivalue_line("markers", @@ -190,7 +190,7 @@ if is_generator(obj): return Generator(name, parent=collector) else: - return collector._genfunctions(name, obj) + return list(collector._genfunctions(name, obj)) def is_generator(func): try: @@ -313,16 +313,14 @@ plugins = self.getplugins() + extra gentesthook.pcall(plugins, metafunc=metafunc) Function = self._getcustomclass("Function") - l = [] if not metafunc._calls: - l.append(Function(name, parent=self)) - for callspec in metafunc._calls: - subname = "%s[%s]" %(name, callspec.id) - function = Function(name=subname, parent=self, - callspec=callspec, callobj=funcobj, - keywords={callspec.id:True}) - l.append(function) - return l + yield Function(name, parent=self) + else: + for callspec in metafunc._calls: + subname = "%s[%s]" %(name, callspec.id) + yield Function(name=subname, parent=self, + callspec=callspec, callobj=funcobj, + keywords={callspec.id:True}) def transfer_markers(funcobj, cls, mod): # XXX this should rather be code in the mark plugin or the mark @@ -584,6 +582,8 @@ if valtype == "funcargs": self.params[arg] = id self._arg2scopenum[arg] = scopenum + if val == _notexists: + self._emptyparamspecified = True self._idlist.append(id) def setall(self, funcargs, id, param): @@ -652,6 +652,9 @@ if not isinstance(argnames, (tuple, list)): argnames = (argnames,) argvalues = [(val,) for val in argvalues] + if not argvalues: + argvalues = [(_notexists,) * len(argnames)] + if scope is None: scope = "function" scopenum = scopes.index(scope) @@ -659,7 +662,8 @@ #XXX should we also check for the opposite case? for arg in argnames: if arg not in self.fixturenames: - raise ValueError("%r has no argument %r" %(self.function, arg)) + raise ValueError("%r uses no fixture %r" %( + self.function, arg)) valtype = indirect and "params" or "funcargs" if not ids: idmaker = IDMaker() @@ -925,6 +929,16 @@ self.ihook.pytest_pyfunc_call(pyfuncitem=self) def setup(self): + # check if parametrization happend with an empty list + try: + empty = self.callspec._emptyparamspecified + except AttributeError: + pass + else: + fs, lineno = self._getfslineno() + pytest.skip("got empty parameter set, function %s at %s:%d" %( + self.function.__name__, fs, lineno)) + super(Function, self).setup() #if hasattr(self, "_request"): # self._request._callsetup() diff -r fe752ae6e18c5108e77a822e8f0e9d3a3ef4f5b7 -r b10528b9cd1acc11edca5172a9d8520069acd246 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -303,6 +303,16 @@ assert f1 == f1_b assert not f1 != f1_b + def test_issue197_parametrize_emptyset(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.mark.parametrize('arg', []) + def test_function(arg): + pass + """) + reprec = testdir.inline_run() + reprec.assertoutcome(skipped=1) + def test_function_equality_with_callspec(self, testdir, tmpdir): items = testdir.getitems(""" import pytest Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Oct 6 21:07:11 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 06 Oct 2012 19:07:11 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20121006190711.30884.86902@bitbucket01.managed.contegix.com> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/f99a77d3329d/ changeset: f99a77d3329d user: hpk42 date: 2012-10-06 21:01:13 summary: allow metafunc.parametrize(scope=...) calls to override the scope of a Fixture function definition. This is useful for cases where you want to dynamically set scope and parametrization for a fixture instead of statically declaring it on the fixture function. affected #: 2 files diff -r b10528b9cd1acc11edca5172a9d8520069acd246 -r f99a77d3329dc609b84368cf8811626e10c05378 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -628,7 +628,7 @@ self._arg2scopenum = {} def parametrize(self, argnames, argvalues, indirect=False, ids=None, - scope="function"): + scope=None): """ Add new invocations to the underlying test function using the list of argvalues for the given argnames. Parametrization is performed during the collection phase. If you need to setup expensive resources @@ -648,6 +648,11 @@ :arg ids: list of string ids each corresponding to the argvalues so that they are part of the test id. If no ids are provided they will be generated automatically from the argvalues. + + :arg scope: if specified: denotes the scope of the parameters. + The scope is used for sorting tests by parameters. It will + also override any fixture-function defined scope, allowing + to set a dynamic scope from test context and configuration. """ if not isinstance(argnames, (tuple, list)): argnames = (argnames,) @@ -656,7 +661,7 @@ argvalues = [(_notexists,) * len(argnames)] if scope is None: - scope = "function" + scope = "subfunction" scopenum = scopes.index(scope) if not indirect: #XXX should we also check for the opposite case? @@ -893,9 +898,9 @@ setattr(self.markers, name, val) # contstruct a list of all neccessary fixtures for this test function - if hasattr(self.markers, "usefixtures"): + try: usefixtures = list(self.markers.usefixtures.args) - else: + except AttributeError: usefixtures = [] self.fixturenames = (self.session._fixturemanager.getdefaultfixtures() + usefixtures + self._getfuncargnames()) @@ -938,10 +943,7 @@ fs, lineno = self._getfslineno() pytest.skip("got empty parameter set, function %s at %s:%d" %( self.function.__name__, fs, lineno)) - super(Function, self).setup() - #if hasattr(self, "_request"): - # self._request._callsetup() fillfixtures(self) def __eq__(self, other): @@ -1103,7 +1105,6 @@ def _fillfixtures(self): item = self._pyfuncitem fixturenames = getattr(item, "fixturenames", self.fixturenames) - for argname in fixturenames: if argname not in item.funcargs: item.funcargs[argname] = self.getfuncargvalue(argname) @@ -1146,7 +1147,7 @@ return val def getfuncargvalue(self, argname): - """ Retrieve a function argument by name for this test + """ Retrieve a fixture function argument by name for this test function invocation. This allows one function argument factory to call another function argument factory. If there are two funcarg factories for the same test function argument the first @@ -1181,8 +1182,8 @@ if fixturedef.active: return fixturedef.cached_result - # prepare request scope and param attributes before - # calling into factory + # prepare request _currentarg and param attributes before + # calling into fixture function argname = fixturedef.argname node = self._pyfuncitem mp = monkeypatch() @@ -1193,7 +1194,18 @@ pass else: mp.setattr(self, 'param', param, raising=False) + + # if a parametrize invocation set a scope it will override + # the static scope defined with the fixture function scope = fixturedef.scope + try: + paramscopenum = node.callspec._arg2scopenum[argname] + except (KeyError, AttributeError): + pass + else: + if paramscopenum != scopenum_subfunction: + scope = scopes[paramscopenum] + if scope is not None: __tracebackhide__ = True if scopemismatch(self.scope, scope): @@ -1251,7 +1263,8 @@ which has a lower scope (e.g. a Session one calls a function one) """ -scopes = "session module class function".split() +scopes = "session module class function subfunction".split() +scopenum_subfunction = scopes.index("subfunction") def scopemismatch(currentscope, newscope): return scopes.index(newscope) > scopes.index(currentscope) diff -r b10528b9cd1acc11edca5172a9d8520069acd246 -r f99a77d3329dc609b84368cf8811626e10c05378 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1370,6 +1370,28 @@ "*1 passed*" ]) + @pytest.mark.parametrize(("scope", "length"), + [("module", 2), ("function", 4)]) + def test_parametrize_scope_overrides(self, testdir, scope, length): + testdir.makepyfile(""" + import pytest + l = [] + def pytest_generate_tests(metafunc): + if "arg" in metafunc.funcargnames: + metafunc.parametrize("arg", [1,2], indirect=True, + scope=%r) + def pytest_funcarg__arg(request): + l.append(request.param) + return request.param + def test_hello(arg): + assert arg in (1,2) + def test_world(arg): + assert arg in (1,2) + def test_checklength(): + assert len(l) == %d + """ % (scope, length)) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=5) def test_conftest_funcargs_only_available_in_subdir(testdir): sub1 = testdir.mkpydir("sub1") https://bitbucket.org/hpk42/pytest/changeset/0a8334dc2fb3/ changeset: 0a8334dc2fb3 user: hpk42 date: 2012-10-06 21:03:55 summary: - allow to use fixtures directly, i.e. without () - also allow scope to be determined by a dynamic function affected #: 4 files diff -r f99a77d3329dc609b84368cf8811626e10c05378 -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev18' +__version__ = '2.3.0.dev19' diff -r f99a77d3329dc609b84368cf8811626e10c05378 -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -26,19 +26,27 @@ """ return a decorator to mark a fixture factory function. The name of the fixture function can be referenced in a test context - to cause activation ahead of running tests. Test modules or classes - can use the pytest.mark.needsfixtures(fixturename) marker to specify - needed fixtures. Test functions can use fixture names as input arguments - in which case the object returned from the fixture function will be - injected. + to cause its invocation ahead of running tests. Test modules or classes + can use the pytest.mark.usefixtures(fixturename) marker to specify + needed fixtures. Test functions can also use fixture names as input + arguments in which case the fixture instance returned from the fixture + function will be injected. :arg scope: the scope for which this fixture is shared, one of "function", "class", "module", "session". Defaults to "function". :arg params: an optional list of parameters which will cause multiple invocations of the fixture functions and their dependent tests. + + :arg autoactive: if True, the fixture func is activated for all tests that + can see it. If False (the default) then an explicit + reference is needed to activate the fixture. """ - return FixtureFunctionMarker(scope, params, autoactive=autoactive) + if hasattr(scope, "__call__") and params is None and autoactive == False: + # direct decoration + return FixtureFunctionMarker(None, params, autoactive)(scope) + else: + return FixtureFunctionMarker(scope, params, autoactive=autoactive) defaultfuncargprefixmarker = fixture() diff -r f99a77d3329dc609b84368cf8811626e10c05378 -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev18', + version='2.3.0.dev19', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r f99a77d3329dc609b84368cf8811626e10c05378 -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1701,7 +1701,20 @@ ]) -class TestFixtureFactory: +class TestFixtureUsages: + def test_noargfixturedec(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.fixture + def arg1(): + return 1 + + def test_func(arg1): + assert arg1 == 1 + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) + def test_receives_funcargs(self, testdir): testdir.makepyfile(""" import pytest Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Oct 7 13:07:07 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 07 Oct 2012 11:07:07 -0000 Subject: [py-svn] commit/pytest: hpk42: - fix doc references, refactor fixtures docs to more quickly start Message-ID: <20121007110707.30884.41681@bitbucket01.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/2af0387cac7c/ changeset: 2af0387cac7c user: hpk42 date: 2012-10-07 13:06:17 summary: - fix doc references, refactor fixtures docs to more quickly start with examples instead of big text blobgs - also silence -q and -qq reporting some more affected #: 34 files diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -21,22 +21,23 @@ return function -# XXX a test fails when scope="function" how it should be, investigate -def fixture(scope=None, params=None, autoactive=False): - """ return a decorator to mark a fixture factory function. +def fixture(scope="function", params=None, autoactive=False): + """ (return a) decorator to mark a fixture factory function. - The name of the fixture function can be referenced in a test context - to cause its invocation ahead of running tests. Test modules or classes - can use the pytest.mark.usefixtures(fixturename) marker to specify - needed fixtures. Test functions can also use fixture names as input + This decorator can be used (directly or with parameters) to define + a fixture function. The name of the fixture function can later be + referenced to cause its invocation ahead of running tests: test + modules or classes can use the pytest.mark.usefixtures(fixturename) + marker and test functions can directly use fixture names as input arguments in which case the fixture instance returned from the fixture function will be injected. :arg scope: the scope for which this fixture is shared, one of - "function", "class", "module", "session". Defaults to "function". + "function" (default), "class", "module", "session". + :arg params: an optional list of parameters which will cause multiple - invocations of the fixture functions and their dependent - tests. + invocations of the fixture function and all of the tests + using it. :arg autoactive: if True, the fixture func is activated for all tests that can see it. If False (the default) then an explicit @@ -992,7 +993,7 @@ class FixtureRequest(FuncargnamesCompatAttr): - """ A request for fixtures from a test or setup function. + """ A request for fixtures from a test or fixture function. A request object gives access to attributes of the requesting test context. It has an optional ``param`` attribute in case @@ -1019,7 +1020,7 @@ @property def node(self): - """ underlying collection node (depends on request scope)""" + """ underlying collection node (depends on current request scope)""" return self._getscopeitem(self.scope) def _getfixturedeflist(self, argname): @@ -1227,12 +1228,13 @@ mp.setattr(self, "scope", scope) # prepare finalization according to scope + # (XXX analyse exact finalizing mechanics / cleanup) self.session._setupstate.addfinalizer(fixturedef.finish, self.node) self._fixturemanager.addargfinalizer(fixturedef.finish, argname) for subargname in fixturedef.fixturenames: # XXX all deps? self._fixturemanager.addargfinalizer(fixturedef.finish, subargname) mp.setattr(self, "addfinalizer", fixturedef.addfinalizer) - # finally perform the factory call + # finally perform the fixture call val = fixturedef.execute(request=self) mp.undo() return val diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 _pytest/terminal.py --- a/_pytest/terminal.py +++ b/_pytest/terminal.py @@ -209,7 +209,7 @@ self.currentfspath = -2 def pytest_collection(self): - if not self.hasmarkup: + if not self.hasmarkup and self.config.option.verbose >=1: self.write("collecting ... ", bold=True) def pytest_collectreport(self, report): @@ -224,6 +224,9 @@ self.report_collect() def report_collect(self, final=False): + if self.config.option.verbose < 0: + return + errors = len(self.stats.get('error', [])) skipped = len(self.stats.get('skipped', [])) if final: @@ -455,8 +458,8 @@ msg = "%s in %.2f seconds" %(line, session_duration) if self.verbosity >= 0: self.write_sep("=", msg, bold=True) - else: - self.write_line(msg, bold=True) + #else: + # self.write_line(msg, bold=True) def summary_deselected(self): if 'deselected' in self.stats: diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/announce/index.txt --- a/doc/en/announce/index.txt +++ b/doc/en/announce/index.txt @@ -5,6 +5,7 @@ .. toctree:: :maxdepth: 2 + release-2.3.0 release-2.2.4 release-2.2.2 release-2.2.1 diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/apiref.txt --- a/doc/en/apiref.txt +++ b/doc/en/apiref.txt @@ -10,16 +10,15 @@ builtin.txt customize.txt assert.txt - funcargs.txt - funcarg_compare.txt - setup.txt + fixture.txt + parametrize.txt xunit_setup.txt capture.txt monkeypatch.txt xdist.txt tmpdir.txt + mark.txt skipping.txt - mark.txt recwarn.txt unittest.txt nose.txt diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/assert.txt --- a/doc/en/assert.txt +++ b/doc/en/assert.txt @@ -24,9 +24,8 @@ $ py.test test_assert1.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 - plugins: xdist, bugzilla, cache, oejskit, pep8, cov - collecting ... collected 1 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 1 items test_assert1.py F @@ -39,7 +38,7 @@ E + where 3 = f() test_assert1.py:5: AssertionError - ========================= 1 failed in 0.02 seconds ========================= + ========================= 1 failed in 0.01 seconds ========================= py.test has support for showing the values of the most common subexpressions including calls, attributes, comparisons, and binary and unary @@ -107,9 +106,8 @@ $ py.test test_assert2.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 - plugins: xdist, bugzilla, cache, oejskit, pep8, cov - collecting ... collected 1 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 1 items test_assert2.py F @@ -127,7 +125,7 @@ E '5' test_assert2.py:5: AssertionError - ========================= 1 failed in 0.02 seconds ========================= + ========================= 1 failed in 0.01 seconds ========================= Special comparisons are done for a number of cases: @@ -171,7 +169,6 @@ the conftest file:: $ py.test -q test_foocompare.py - collecting ... collected 1 items F ================================= FAILURES ================================= _______________________________ test_compare _______________________________ @@ -184,7 +181,6 @@ E vals: 1 != 2 test_foocompare.py:8: AssertionError - 1 failed in 0.02 seconds .. _assert-details: .. _`assert introspection`: diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/builtin.txt --- a/doc/en/builtin.txt +++ b/doc/en/builtin.txt @@ -17,7 +17,8 @@ .. automodule:: pytest :members: -.. _builtinresources: +.. _builtinfixtures: +.. _builtinfuncargs: Builtin resources / function arguments ----------------------------------------------------- @@ -27,11 +28,8 @@ $ py.test --fixtures =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 - plugins: xdist, bugzilla, cache, oejskit, pep8, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 collected 0 items - pytestconfig - the pytest config object with access to command line opts. capsys enables capturing of writes to sys.stdout/sys.stderr and makes captured output available via ``capsys.readouterr()`` method calls @@ -76,7 +74,5 @@ See http://docs.python.org/library/warnings.html for information on warning categories. - cov - A pytest funcarg that provides access to the underlying coverage object. - ============================= in 0.01 seconds ============================= + ============================= in 0.00 seconds ============================= diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/capture.txt --- a/doc/en/capture.txt +++ b/doc/en/capture.txt @@ -64,9 +64,8 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 - plugins: xdist, bugzilla, cache, oejskit, pep8, cov - collecting ... collected 2 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 2 items test_module.py .F @@ -79,8 +78,8 @@ test_module.py:9: AssertionError ----------------------------- Captured stdout ------------------------------ - setting up - ==================== 1 failed, 1 passed in 0.02 seconds ==================== + setting up + ==================== 1 failed, 1 passed in 0.01 seconds ==================== Accessing captured output from a test function --------------------------------------------------- diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.3.0.dev18" +version = release = "2.3.0.dev19" import sys, os @@ -70,6 +70,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['links.inc', '_build', 'naming20.txt', 'test/*', + "old_*", 'example/attic.txt', ] @@ -270,7 +271,7 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'python': ('http://docs.python.org/', None), - 'lib': ("http://docs.python.org/library/", None), +# 'lib': ("http://docs.python.org/2.7library/", None), } diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/contents.txt --- a/doc/en/contents.txt +++ b/doc/en/contents.txt @@ -17,12 +17,15 @@ plugins talks develop + funcarg_compare.txt announce/index .. toctree:: :hidden: changelog.txt - resources + funcargs example/resources_attic + setup.txt + example/remoteinterp.txt diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/doctest.txt --- a/doc/en/doctest.txt +++ b/doc/en/doctest.txt @@ -44,10 +44,9 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 - plugins: xdist, bugzilla, cache, oejskit, pep8, cov - collecting ... collected 1 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 1 items mymodule.py . - ========================= 1 passed in 0.07 seconds ========================= + ========================= 1 passed in 0.02 seconds ========================= diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/example/index.txt --- a/doc/en/example/index.txt +++ b/doc/en/example/index.txt @@ -16,7 +16,6 @@ reportingdemo.txt simple.txt - mysetup.txt parametrize.txt markers.txt pythoncollection.txt diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -26,9 +26,7 @@ $ py.test -v -m webtest =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python - cachedir: /home/hpk/tmp/doc-exec-426/.cache - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:3: test_send_http PASSED @@ -40,15 +38,13 @@ $ py.test -v -m "not webtest" =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 -- /home/hpk/venv/1/bin/python - cachedir: /home/hpk/tmp/doc-exec-426/.cache - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:6: test_something_quick PASSED ================= 1 tests deselected by "-m 'not webtest'" ================= - ================== 1 passed, 1 deselected in 0.01 seconds ================== + ================== 1 passed, 1 deselected in 0.00 seconds ================== Registering markers ------------------------------------- @@ -69,8 +65,6 @@ $ py.test --markers @pytest.mark.webtest: mark a test as a webtest. - @pytest.mark.timeout(timeout, method=None): Set a timeout and timeout method on just one test item. The first argument, *timeout*, is the timeout in seconds while the keyword, *method*, takes the same values as the --timeout_method option. - @pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has 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. @pytest.mark.xfail(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied. @@ -149,41 +143,38 @@ $ py.test -k send_http # running with the above defined examples =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 4 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 4 items test_server.py . =================== 3 tests deselected by '-ksend_http' ==================== - ================== 1 passed, 3 deselected in 0.02 seconds ================== + ================== 1 passed, 3 deselected in 0.01 seconds ================== And you can also run all tests except the ones that match the keyword:: $ py.test -k-send_http =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 4 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 4 items test_mark_classlevel.py .. test_server.py . =================== 1 tests deselected by '-k-send_http' =================== - ================== 3 passed, 1 deselected in 0.02 seconds ================== + ================== 3 passed, 1 deselected in 0.01 seconds ================== Or to only select the class:: $ py.test -kTestClass =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 4 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 4 items test_mark_classlevel.py .. =================== 2 tests deselected by '-kTestClass' ==================== - ================== 2 passed, 2 deselected in 0.02 seconds ================== + ================== 2 passed, 2 deselected in 0.01 seconds ================== .. _`adding a custom marker from a plugin`: @@ -230,33 +221,29 @@ $ py.test -E stage2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 1 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 1 items test_someenv.py s - ======================== 1 skipped in 0.01 seconds ========================= + ======================== 1 skipped in 0.00 seconds ========================= and here is one that specifies exactly the environment needed:: $ py.test -E stage1 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 1 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 1 items test_someenv.py . - ========================= 1 passed in 0.01 seconds ========================= + ========================= 1 passed in 0.00 seconds ========================= The ``--markers`` option always gives you a list of available markers:: $ py.test --markers @pytest.mark.env(name): mark test to run only on named environment - @pytest.mark.timeout(timeout, method=None): Set a timeout and timeout method on just one test item. The first argument, *timeout*, is the timeout in seconds while the keyword, *method*, takes the same values as the --timeout_method option. - @pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has 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. @pytest.mark.xfail(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied. @@ -304,12 +291,10 @@ Let's run this without capturing output and see what we get:: $ py.test -q -s - collecting ... collected 1 items glob args=('function',) kwargs={'x': 3} glob args=('class',) kwargs={'x': 2} glob args=('module',) kwargs={'x': 1} . - 1 passed in 0.01 seconds marking platform specific tests with pytest -------------------------------------------------------------- @@ -362,23 +347,21 @@ $ py.test -rs # this option reports skip reasons =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 4 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 4 items test_plat.py s.s. ========================= short test summary info ========================== - SKIP [2] /home/hpk/tmp/doc-exec-426/conftest.py:12: cannot run on platform linux2 + SKIP [2] /tmp/doc-exec-257/conftest.py:12: cannot run on platform linux2 - =================== 2 passed, 2 skipped in 0.02 seconds ==================== + =================== 2 passed, 2 skipped in 0.01 seconds ==================== Note that if you specify a platform via the marker-command line option like this:: $ py.test -m linux2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 4 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 4 items test_plat.py . diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/example/mysetup.txt --- a/doc/en/example/mysetup.txt +++ /dev/null @@ -1,142 +0,0 @@ - -.. highlightlang:: python - -.. _mysetup: - -Mysetup pattern: application specific test fixtures -========================================================== - -Here is a basic useful step-by-step example for managing and interacting -with application specific test setup. The goal is to have one place -where we have the glue and test support code for bootstrapping and -configuring application objects and allow test modules and test -functions to stay ignorant of involved details. - -Step 1: Implementing the test/app-specific ``mysetup`` pattern --------------------------------------------------------------- - -Let's write a simple test function using a ``mysetup`` funcarg:: - - # content of test_sample.py - def test_answer(mysetup): - app = mysetup.myapp() - answer = app.question() - assert answer == 42 - -To run this test py.test needs to find and call a factory to -obtain the required ``mysetup`` function argument. To make -an according factory findable we write down a specifically named factory -method in a :ref:`local plugin ` :: - - # content of conftest.py - from myapp import MyApp - - def pytest_funcarg__mysetup(request): # "mysetup" factory function - return MySetup() - - class MySetup: # instances of this are seen by test functions - def myapp(self): - return MyApp() - -To run the example we stub out a simple ``MyApp`` application object:: - - # content of myapp.py - class MyApp: - def question(self): - return 6 * 9 - -You can now run the test:: - - $ py.test test_sample.py - =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 - plugins: xdist, bugzilla, cache, oejskit, pep8, cov - collecting ... collected 1 items - - test_sample.py F - - ================================= FAILURES ================================= - _______________________________ test_answer ________________________________ - - mysetup = - - def test_answer(mysetup): - app = mysetup.myapp() - answer = app.question() - > assert answer == 42 - E assert 54 == 42 - - test_sample.py:4: AssertionError - ========================= 1 failed in 0.02 seconds ========================= - -This means that our ``mysetup`` object was successfully instantiated -and ``mysetup.app()`` returned an initialized ``MyApp`` instance. -We can ask it about the question and if you are confused as to what -the concrete question or answers actually mean, please see here_. - -.. _here: http://uncyclopedia.wikia.com/wiki/The_Hitchhiker's_Guide_to_the_Galaxy -.. _`tut-cmdlineoption`: - -Step 2: Checking a command line option and skipping tests ------------------------------------------------------------ - -To add a command line option we update the ``conftest.py`` of -the previous example to add a command line option -and to offer a new mysetup method:: - - # content of ./conftest.py - import pytest - from myapp import MyApp - - def pytest_funcarg__mysetup(request): # "mysetup" factory function - return MySetup(request) - - def pytest_addoption(parser): - parser.addoption("--ssh", action="store", default=None, - help="specify ssh host to run tests with") - - - class MySetup: - def __init__(self, request): - self.config = request.config - - def myapp(self): - return MyApp() - - def getsshconnection(self): - host = self.config.option.ssh - if host is None: - pytest.skip("specify ssh host with --ssh") - return execnet.SshGateway(host) - - -Now any test function can use the ``mysetup.getsshconnection()`` method -like this:: - - # content of test_ssh.py - class TestClass: - def test_function(self, mysetup): - conn = mysetup.getsshconnection() - # work with conn - -Running it yields:: - - $ py.test test_ssh.py -rs - =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 - plugins: xdist, bugzilla, cache, oejskit, pep8, cov - collecting ... collected 1 items - - test_ssh.py s - ========================= short test summary info ========================== - SKIP [1] /home/hpk/tmp/doc-exec-306/conftest.py:22: specify ssh host with --ssh - - ======================== 1 skipped in 0.02 seconds ========================= - -If you specify a command line option like ``py.test --ssh=python.org`` the test will execute as expected. - -Note that neither the ``TestClass`` nor the ``test_function`` need to -know anything about how to setup the test state. It is handled separately -in your "test setup glue" code in the ``conftest.py`` file. It is easy -to extend the ``mysetup`` object for further needs in the test code - and for use by any other test functions in the files and directories below the ``conftest.py`` file. - diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/example/nonpython.txt --- a/doc/en/example/nonpython.txt +++ b/doc/en/example/nonpython.txt @@ -27,18 +27,15 @@ nonpython $ py.test test_simple.yml =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 - plugins: xdist, bugzilla, cache, oejskit, pep8, cov - collecting ... collected 2 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 0 items / 1 errors - test_simple.yml .F - - ================================= FAILURES ================================= - ______________________________ usecase: hello ______________________________ - usecase execution failed - spec failed: 'some': 'other' - no further details known at this point. - ==================== 1 failed, 1 passed in 0.11 seconds ==================== + ================================== ERRORS ================================== + _____________________ ERROR collecting test_simple.yml _____________________ + conftest.py:11: in collect + > import yaml # we need a yaml parser, e.g. PyYAML + E ImportError: No module named yaml + ========================= 1 error in 0.00 seconds ========================== You get one dot for the passing ``sub1: sub1`` check and one failure. Obviously in the above ``conftest.py`` you'll want to implement a more @@ -57,31 +54,27 @@ nonpython $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 -- /home/hpk/venv/1/bin/python - cachedir: /home/hpk/p/pytest/doc/en/.cache - plugins: xdist, bugzilla, cache, oejskit, pep8, cov - collecting ... collected 2 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python + collecting ... collected 0 items / 1 errors - test_simple.yml:1: usecase: ok PASSED - test_simple.yml:1: usecase: hello FAILED - - ================================= FAILURES ================================= - ______________________________ usecase: hello ______________________________ - usecase execution failed - spec failed: 'some': 'other' - no further details known at this point. - ==================== 1 failed, 1 passed in 0.04 seconds ==================== + ================================== ERRORS ================================== + _____________________ ERROR collecting test_simple.yml _____________________ + conftest.py:11: in collect + > import yaml # we need a yaml parser, e.g. PyYAML + E ImportError: No module named yaml + ========================= 1 error in 0.01 seconds ========================== While developing your custom test collection and execution it's also interesting to just look at the collection tree:: nonpython $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 - plugins: xdist, bugzilla, cache, oejskit, pep8, cov - collecting ... collected 2 items - - - + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 0 items / 1 errors - ============================= in 0.04 seconds ============================= + ================================== ERRORS ================================== + _____________________ ERROR collecting test_simple.yml _____________________ + conftest.py:11: in collect + > import yaml # we need a yaml parser, e.g. PyYAML + E ImportError: No module named yaml + ========================= 1 error in 0.01 seconds ========================== diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/example/parametrize.txt --- a/doc/en/example/parametrize.txt +++ b/doc/en/example/parametrize.txt @@ -36,7 +36,6 @@ function is called three times. Let's run it:: $ py.test -q - collecting ... collected 3 items ..F ================================= FAILURES ================================= ____________________________ test_eval[6*9-42] _____________________________ @@ -54,7 +53,6 @@ E + where 54 = eval('6*9') test_expectation.py:8: AssertionError - 1 failed, 2 passed in 0.02 seconds As expected only one pair of input/output values fails the simple test function. @@ -94,15 +92,12 @@ This means that we only run 2 tests if we do not pass ``--all``:: $ py.test -q test_compute.py - collecting ... collected 2 items .. - 2 passed in 0.01 seconds We run only two computations, so we see two dots. let's run the full monty:: $ py.test -q --all - collecting ... collected 5 items ....F ================================= FAILURES ================================= _____________________________ test_compute[4] ______________________________ @@ -114,7 +109,6 @@ E assert 4 < 4 test_compute.py:3: AssertionError - 1 failed, 4 passed in 0.02 seconds As expected when running the full range of ``param1`` values we'll get an error on the last one. @@ -157,22 +151,20 @@ $ py.test test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev14 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 4 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 4 items test_scenarios.py .... - ========================= 4 passed in 0.02 seconds ========================= + ========================= 4 passed in 0.01 seconds ========================= If you just collect tests you'll also nicely see 'advanced' and 'basic' as variants for the test function:: $ py.test --collectonly test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev14 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 4 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 4 items @@ -233,24 +225,22 @@ $ py.test test_backends.py --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev14 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 2 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 2 items - ============================= in 0.01 seconds ============================= + ============================= in 0.00 seconds ============================= And then when we run the test:: $ py.test -q test_backends.py - collecting ... collected 2 items .F ================================= FAILURES ================================= _________________________ test_db_initialized[d2] __________________________ - db = + db = def test_db_initialized(db): # a dummy test @@ -259,7 +249,6 @@ E Failed: deliberately failing for demo purposes test_backends.py:6: Failed - 1 failed, 1 passed in 0.01 seconds The first invocation with ``db == "DB1"`` passed while the second with ``db == "DB2"`` failed. Our ``pytest_funcarg__db`` factory has instantiated each of the DB values during the setup phase while the ``pytest_generate_tests`` generated two according calls to the ``test_db_initialized`` during the collection phase. @@ -302,19 +291,17 @@ argument sets to use for each test function. Let's run it:: $ py.test -q - collecting ... collected 3 items F.. ================================= FAILURES ================================= ________________________ TestClass.test_equals[1-2] ________________________ - self = , a = 1, b = 2 + self = , a = 1, b = 2 def test_equals(self, a, b): > assert a == b E assert 1 == 2 test_parametrize.py:18: AssertionError - 1 failed, 2 passed in 0.02 seconds Indirect parametrization with multiple resources -------------------------------------------------------------- @@ -333,8 +320,6 @@ 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 - collecting ... collected 75 items ............sss............sss............sss............ssssssssssssssssss ========================= short test summary info ========================== SKIP [27] /home/hpk/p/pytest/doc/en/example/multipython.py:21: 'python2.8' not found - 48 passed, 27 skipped in 3.11 seconds diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/example/pythoncollection.txt --- a/doc/en/example/pythoncollection.txt +++ b/doc/en/example/pythoncollection.txt @@ -43,8 +43,8 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 - collecting ... collected 2 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 2 items @@ -82,8 +82,8 @@ . $ py.test --collectonly pythoncollection.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 - collecting ... collected 3 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 3 items @@ -135,8 +135,8 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.2.5.dev1 - collecting ... collected 1 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 1 items diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/example/remoteinterp.txt --- /dev/null +++ b/doc/en/example/remoteinterp.txt @@ -0,0 +1,198 @@ + +.. highlightlang:: python + +.. _myapp: + +Building an SSH connecting Application fixture +========================================================== + +The goal of this tutorial-example is to show how you can put efficient +test support and fixture code in one place, allowing test modules and +test functions to stay ignorant of importing, configuration or +setup/teardown details. + +The tutorial implements a simple ``RemoteInterpreter`` object that +allows evaluation of python expressions. We are going to use +the `execnet `_ package for the +underlying cross-python bridge functionality. + + +Step 1: Implementing a first test +-------------------------------------------------------------- + +Let's write a simple test function using a not yet defined ``interp`` fixture:: + + # content of test_remoteinterpreter.py + + def test_eval_simple(interp): + assert interp.eval("6*9") == 42 + +The test function needs an argument named `interp` and therefore pytest will +look for a :ref:`fixture function` that matches this name. We'll define it +in a :ref:`local plugin ` to make it available also to other +test modules:: + + # content of conftest.py + + from remoteinterpreter import RemoteInterpreter + + @pytest.fixture + def interp(request): + import execnet + gw = execnet.makegateway() + return RemoteInterpreter(gw) + +To run the example we furthermore need to implement a RemoteInterpreter +object which working with the injected execnet-gateway connection:: + + # content of remoteintepreter.py + + class RemoteInterpreter: + def __init__(self, gateway): + self.gateway = gateway + + def eval(self, expression): + # execnet open a "gateway" to the remote process + # which enables to remotely execute code and communicate + # to and fro via channels + ch = self.gateway.remote_exec("channel.send(%s)" % expression) + return ch.receive() + +That's it, we can now run the test:: + + $ py.test test_remoteinterpreter.py + Traceback (most recent call last): + File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in + load_entry_point('pytest==2.3.0.dev19', 'console_scripts', 'py.test')() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 469, in main + config = _prepareconfig(args, plugins) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig + pluginmanager=_pluginmanager, args=args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 422, in __call__ + return self._docall(methods, kwargs) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 433, in _docall + res = mc.execute() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute + res = method(**kwargs) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/helpconfig.py", line 25, in pytest_cmdline_parse + config = __multicall__.execute() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute + res = method(**kwargs) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 10, in pytest_cmdline_parse + config.parse(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 344, in parse + self._preparse(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 322, in _preparse + self._setinitialconftest(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 301, in _setinitialconftest + self._conftest.setinitial(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 160, in setinitial + self._try_load_conftest(anchor) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 166, in _try_load_conftest + self._path2confmods[None] = self.getconftestmodules(anchor) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 190, in getconftestmodules + clist[:0] = self.getconftestmodules(dp) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 189, in getconftestmodules + clist.append(self.importconftest(conftestpath)) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 218, in importconftest + self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport + __import__(modname) + File "/tmp/doc-exec-261/conftest.py", line 2, in + from remoteinterpreter import RemoteInterpreter + ImportError: No module named remoteinterpreter + +.. _`tut-cmdlineoption`: + +Step 2: Adding command line configuration +----------------------------------------------------------- + +To add a command line option we update the ``conftest.py`` of +the previous example and add a command line option which +is passed on to the MyApp object:: + + # content of ./conftest.py + import pytest + from myapp import MyApp + + def pytest_addoption(parser): # pytest hook called during initialisation + parser.addoption("--ssh", action="store", default=None, + help="specify ssh host to run tests with") + + @pytest.fixture + def mysetup(request): # "mysetup" factory function + return MySetup(request.config) + + class MySetup: + def __init__(self, config): + self.config = config + self.app = MyApp() + + def getsshconnection(self): + import execnet + host = self.config.option.ssh + if host is None: + pytest.skip("specify ssh host with --ssh") + return execnet.SshGateway(host) + + +Now any test function can use the ``mysetup.getsshconnection()`` method +like this:: + + # content of test_ssh.py + class TestClass: + def test_function(self, mysetup): + conn = mysetup.getsshconnection() + # work with conn + +Running it yields:: + + $ py.test -q test_ssh.py -rs + Traceback (most recent call last): + File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in + load_entry_point('pytest==2.3.0.dev19', 'console_scripts', 'py.test')() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 469, in main + config = _prepareconfig(args, plugins) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig + pluginmanager=_pluginmanager, args=args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 422, in __call__ + return self._docall(methods, kwargs) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 433, in _docall + res = mc.execute() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute + res = method(**kwargs) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/helpconfig.py", line 25, in pytest_cmdline_parse + config = __multicall__.execute() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute + res = method(**kwargs) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 10, in pytest_cmdline_parse + config.parse(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 344, in parse + self._preparse(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 322, in _preparse + self._setinitialconftest(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 301, in _setinitialconftest + self._conftest.setinitial(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 160, in setinitial + self._try_load_conftest(anchor) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 166, in _try_load_conftest + self._path2confmods[None] = self.getconftestmodules(anchor) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 190, in getconftestmodules + clist[:0] = self.getconftestmodules(dp) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 189, in getconftestmodules + clist.append(self.importconftest(conftestpath)) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 218, in importconftest + self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport + __import__(modname) + File "/tmp/doc-exec-261/conftest.py", line 2, in + from myapp import MyApp + ImportError: No module named myapp + +If you specify a command line option like ``py.test --ssh=python.org`` the test will execute as expected. + +Note that neither the ``TestClass`` nor the ``test_function`` need to +know anything about how to setup the test state. It is handled separately +in the ``conftest.py`` file. It is easy +to extend the ``mysetup`` object for further needs in the test code - and for use by any other test functions in the files and directories below the ``conftest.py`` file. + diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/example/reportingdemo.txt --- a/doc/en/example/reportingdemo.txt +++ b/doc/en/example/reportingdemo.txt @@ -13,8 +13,8 @@ assertion $ py.test failure_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 - collecting ... collected 39 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 39 items failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF @@ -30,7 +30,7 @@ failure_demo.py:15: AssertionError _________________________ TestFailing.test_simple __________________________ - self = + self = def test_simple(self): def f(): @@ -40,13 +40,13 @@ > assert f() == g() E assert 42 == 43 - E + where 42 = () - E + and 43 = () + E + where 42 = () + E + and 43 = () failure_demo.py:28: AssertionError ____________________ TestFailing.test_simple_multiline _____________________ - self = + self = def test_simple_multiline(self): otherfunc_multi( @@ -66,19 +66,19 @@ failure_demo.py:11: AssertionError ___________________________ TestFailing.test_not ___________________________ - self = + self = def test_not(self): def f(): return 42 > assert not f() E assert not 42 - E + where 42 = () + E + where 42 = () failure_demo.py:38: AssertionError _________________ TestSpecialisedExplanations.test_eq_text _________________ - self = + self = def test_eq_text(self): > assert 'spam' == 'eggs' @@ -89,7 +89,7 @@ failure_demo.py:42: AssertionError _____________ TestSpecialisedExplanations.test_eq_similar_text _____________ - self = + self = def test_eq_similar_text(self): > assert 'foo 1 bar' == 'foo 2 bar' @@ -102,7 +102,7 @@ failure_demo.py:45: AssertionError ____________ TestSpecialisedExplanations.test_eq_multiline_text ____________ - self = + self = def test_eq_multiline_text(self): > assert 'foo\nspam\nbar' == 'foo\neggs\nbar' @@ -115,7 +115,7 @@ failure_demo.py:48: AssertionError ______________ TestSpecialisedExplanations.test_eq_long_text _______________ - self = + self = def test_eq_long_text(self): a = '1'*100 + 'a' + '2'*100 @@ -132,7 +132,7 @@ failure_demo.py:53: AssertionError _________ TestSpecialisedExplanations.test_eq_long_text_multiline __________ - self = + self = def test_eq_long_text_multiline(self): a = '1\n'*100 + 'a' + '2\n'*100 @@ -156,7 +156,7 @@ failure_demo.py:58: AssertionError _________________ TestSpecialisedExplanations.test_eq_list _________________ - self = + self = def test_eq_list(self): > assert [0, 1, 2] == [0, 1, 3] @@ -166,7 +166,7 @@ failure_demo.py:61: AssertionError ______________ TestSpecialisedExplanations.test_eq_list_long _______________ - self = + self = def test_eq_list_long(self): a = [0]*100 + [1] + [3]*100 @@ -178,7 +178,7 @@ failure_demo.py:66: AssertionError _________________ TestSpecialisedExplanations.test_eq_dict _________________ - self = + self = def test_eq_dict(self): > assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2} @@ -191,7 +191,7 @@ failure_demo.py:69: AssertionError _________________ TestSpecialisedExplanations.test_eq_set __________________ - self = + self = def test_eq_set(self): > assert set([0, 10, 11, 12]) == set([0, 20, 21]) @@ -207,7 +207,7 @@ failure_demo.py:72: AssertionError _____________ TestSpecialisedExplanations.test_eq_longer_list ______________ - self = + self = def test_eq_longer_list(self): > assert [1,2] == [1,2,3] @@ -217,7 +217,7 @@ failure_demo.py:75: AssertionError _________________ TestSpecialisedExplanations.test_in_list _________________ - self = + self = def test_in_list(self): > assert 1 in [0, 2, 3, 4, 5] @@ -226,7 +226,7 @@ failure_demo.py:78: AssertionError __________ TestSpecialisedExplanations.test_not_in_text_multiline __________ - self = + self = def test_not_in_text_multiline(self): text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail' @@ -244,7 +244,7 @@ failure_demo.py:82: AssertionError ___________ TestSpecialisedExplanations.test_not_in_text_single ____________ - self = + self = def test_not_in_text_single(self): text = 'single foo line' @@ -257,7 +257,7 @@ failure_demo.py:86: AssertionError _________ TestSpecialisedExplanations.test_not_in_text_single_long _________ - self = + self = def test_not_in_text_single_long(self): text = 'head ' * 50 + 'foo ' + 'tail ' * 20 @@ -270,7 +270,7 @@ failure_demo.py:90: AssertionError ______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______ - self = + self = def test_not_in_text_single_long_term(self): text = 'head ' * 50 + 'f'*70 + 'tail ' * 20 @@ -289,7 +289,7 @@ i = Foo() > assert i.b == 2 E assert 1 == 2 - E + where 1 = .b + E + where 1 = .b failure_demo.py:101: AssertionError _________________________ test_attribute_instance __________________________ @@ -299,8 +299,8 @@ b = 1 > assert Foo().b == 2 E assert 1 == 2 - E + where 1 = .b - E + where = () + E + where 1 = .b + E + where = () failure_demo.py:107: AssertionError __________________________ test_attribute_failure __________________________ @@ -316,7 +316,7 @@ failure_demo.py:116: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - self = + self = def _get_b(self): > raise Exception('Failed to get attrib') @@ -332,15 +332,15 @@ b = 2 > assert Foo().b == Bar().b E assert 1 == 2 - E + where 1 = .b - E + where = () - E + and 2 = .b - E + where = () + E + where 1 = .b + E + where = () + E + and 2 = .b + E + where = () failure_demo.py:124: AssertionError __________________________ TestRaises.test_raises __________________________ - self = + self = def test_raises(self): s = 'qwe' @@ -352,10 +352,10 @@ > int(s) E ValueError: invalid literal for int() with base 10: 'qwe' - <0-codegen /home/hpk/p/pytest/_pytest/python.py:978>:1: ValueError + <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:838>:1: ValueError ______________________ TestRaises.test_raises_doesnt _______________________ - self = + self = def test_raises_doesnt(self): > raises(IOError, "int('3')") @@ -364,7 +364,7 @@ failure_demo.py:136: Failed __________________________ TestRaises.test_raise ___________________________ - self = + self = def test_raise(self): > raise ValueError("demo error") @@ -373,7 +373,7 @@ failure_demo.py:139: ValueError ________________________ TestRaises.test_tupleerror ________________________ - self = + self = def test_tupleerror(self): > a,b = [1] @@ -382,7 +382,7 @@ failure_demo.py:142: ValueError ______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______ - self = + self = def test_reinterpret_fails_with_print_for_the_fun_of_it(self): l = [1,2,3] @@ -395,7 +395,7 @@ l is [1, 2, 3] ________________________ TestRaises.test_some_error ________________________ - self = + self = def test_some_error(self): > if namenotexi: @@ -420,10 +420,10 @@ > assert 1 == 0 E assert 1 == 0 - <2-codegen 'abc-123' /home/hpk/p/pytest/doc/example/assertion/failure_demo.py:162>:2: AssertionError + <2-codegen 'abc-123' /home/hpk/p/pytest/doc/en/example/assertion/failure_demo.py:162>:2: AssertionError ____________________ TestMoreErrors.test_complex_error _____________________ - self = + self = def test_complex_error(self): def f(): @@ -452,7 +452,7 @@ failure_demo.py:5: AssertionError ___________________ TestMoreErrors.test_z1_unpack_error ____________________ - self = + self = def test_z1_unpack_error(self): l = [] @@ -462,7 +462,7 @@ failure_demo.py:179: ValueError ____________________ TestMoreErrors.test_z2_type_error _____________________ - self = + self = def test_z2_type_error(self): l = 3 @@ -472,19 +472,19 @@ failure_demo.py:183: TypeError ______________________ TestMoreErrors.test_startswith ______________________ - self = + self = def test_startswith(self): s = "123" g = "456" > assert s.startswith(g) - E assert ('456') - E + where = '123'.startswith + E assert ('456') + E + where = '123'.startswith failure_demo.py:188: AssertionError __________________ TestMoreErrors.test_startswith_nested ___________________ - self = + self = def test_startswith_nested(self): def f(): @@ -492,15 +492,15 @@ def g(): return "456" > assert f().startswith(g()) - E assert ('456') - E + where = '123'.startswith - E + where '123' = () - E + and '456' = () + E assert ('456') + E + where = '123'.startswith + E + where '123' = () + E + and '456' = () failure_demo.py:195: AssertionError _____________________ TestMoreErrors.test_global_func ______________________ - self = + self = def test_global_func(self): > assert isinstance(globf(42), float) @@ -510,18 +510,18 @@ failure_demo.py:198: AssertionError _______________________ TestMoreErrors.test_instance _______________________ - self = + self = def test_instance(self): self.x = 6*7 > assert self.x != 42 E assert 42 != 42 - E + where 42 = .x + E + where 42 = .x failure_demo.py:202: AssertionError _______________________ TestMoreErrors.test_compare ________________________ - self = + self = def test_compare(self): > assert globf(10) < 5 @@ -531,7 +531,7 @@ failure_demo.py:205: AssertionError _____________________ TestMoreErrors.test_try_finally ______________________ - self = + self = def test_try_finally(self): x = 1 @@ -540,4 +540,4 @@ E assert 1 == 0 failure_demo.py:210: AssertionError - ======================== 39 failed in 0.17 seconds ========================= + ======================== 39 failed in 0.15 seconds ========================= diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/example/simple.txt --- a/doc/en/example/simple.txt +++ b/doc/en/example/simple.txt @@ -22,20 +22,22 @@ For this to work we need to add a command line option and -provide the ``cmdopt`` through a :ref:`function argument ` factory:: +provide the ``cmdopt`` through a :ref:`fixture function `:: # content of conftest.py + import pytest + def pytest_addoption(parser): parser.addoption("--cmdopt", action="store", default="type1", help="my option: type1 or type2") - def pytest_funcarg__cmdopt(request): + @pytest.fixture + def cmdopt(request): return request.config.option.cmdopt -Let's run this without supplying our new command line option:: +Let's run this without supplying our new option:: $ py.test -q test_sample.py - collecting ... collected 1 items F ================================= FAILURES ================================= _______________________________ test_answer ________________________________ @@ -53,12 +55,10 @@ test_sample.py:6: AssertionError ----------------------------- Captured stdout ------------------------------ first - 1 failed in 0.01 seconds And now with supplying a command line option:: $ py.test -q --cmdopt=type2 - collecting ... collected 1 items F ================================= FAILURES ================================= _______________________________ test_answer ________________________________ @@ -76,14 +76,11 @@ test_sample.py:6: AssertionError ----------------------------- Captured stdout ------------------------------ second - 1 failed in 0.01 seconds -Ok, this completes the basic pattern. However, one often rather -wants to process command line options outside of the test and -rather pass in different or more complex objects. See the -next example or refer to :ref:`mysetup` for more information -on real-life examples. - +You can see that the command line option arrived in our test. This +completes the basic pattern. However, one often rather wants to process +command line options outside of the test and rather pass in different or +more complex objects. Dynamically adding command line options -------------------------------------------------------------- @@ -109,13 +106,10 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 - gw0 I / gw1 I / gw2 I / gw3 I - gw0 [0] / gw1 [0] / gw2 [0] / gw3 [0] + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 0 items - scheduling tests via LoadScheduling - - ============================= in 0.52 seconds ============================= + ============================= in 0.00 seconds ============================= .. _`excontrolskip`: @@ -156,12 +150,12 @@ $ py.test -rs # "-rs" means report details on the little 's' =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 - collecting ... collected 2 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 2 items test_module.py .s ========================= short test summary info ========================== - SKIP [1] /tmp/doc-exec-225/conftest.py:9: need --runslow option to run + SKIP [1] /tmp/doc-exec-264/conftest.py:9: need --runslow option to run =================== 1 passed, 1 skipped in 0.01 seconds ==================== @@ -169,8 +163,8 @@ $ py.test --runslow =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 - collecting ... collected 2 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 2 items test_module.py .. @@ -203,7 +197,6 @@ Let's run our little function:: $ py.test -q test_checkconfig.py - collecting ... collected 1 items F ================================= FAILURES ================================= ______________________________ test_something ______________________________ @@ -213,7 +206,6 @@ E Failed: not configured: 42 test_checkconfig.py:8: Failed - 1 failed in 0.01 seconds Detect if running from within a py.test run -------------------------------------------------------------- @@ -261,9 +253,9 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 project deps: mylib-1.1 - collecting ... collected 0 items + collected 0 items ============================= in 0.00 seconds ============================= @@ -284,7 +276,7 @@ $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 -- /home/hpk/venv/0/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python info1: did you know that ... did you? collecting ... collected 0 items @@ -295,8 +287,8 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 - collecting ... collected 0 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 0 items ============================= in 0.00 seconds ============================= @@ -327,13 +319,13 @@ $ py.test --durations=3 =========================== test session starts ============================ - platform linux2 -- Python 2.7.1 -- pytest-2.2.4 - collecting ... collected 3 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 3 items test_some_are_slow.py ... ========================= 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 call test_some_are_slow.py::test_funcfast ========================= 3 passed in 0.31 seconds ========================= diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -1,17 +1,12 @@ -.. _xunitsetup: -.. _setup: .. _fixture: +.. _fixtures: .. _`fixture functions`: -.. _`@pytest.fixture`: -pytest fixtures: modular, re-useable, flexible +pytest fixtures: modular, explicit, scalable ======================================================== -.. versionadded:: 2.0,2.3 +.. versionadded:: 2.0, 2.3 -.. _`funcargs`: funcargs.html -.. _`test parametrization`: funcargs.html#parametrizing-tests -.. _`unittest plugin`: plugin/unittest.html .. _`xUnit`: http://en.wikipedia.org/wiki/XUnit .. _`general purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software .. _`django`: https://www.djangoproject.com/ @@ -20,101 +15,59 @@ pytest allows to create and use test fixtures in a modular and flexible manner, offering dramatic improvements over the classic xUnit style of -setup/teardown functions. The `general purpose of test fixtures`_ is to -provide a fixed baseline upon which tests can reliably and -repeatedly execute. With pytest, fixtures have names and can be referenced -from test functions, modules, classes or whole projects. Fixtures are -implemented by **fixture functions** which may return a fixture object -or put extra attributes on test classes or perform global side effects -if needed. Fixtures can themselves access other fixtures, allowing a -**structured modular approach** to organising fixtures for an -application. +setup/teardown functions. The `general purpose of test fixtures`_ +is to provide a fixed baseline upon which tests can reliably +and repeatedly execute. With pytest, fixtures have names and can be +activated by referencing them from test functions, modules, classes or +whole projects. Fixtures are implemented by *fixture functions* which +have full access to the requesting test context and can use other +fixtures, allowing a modular and flexible approach to organising +and parametrizing fixtures for an application. Complemented by +pytest's generic :ref:`parametrize features `, pytest +fixtures help to write test suites that scale from simple to complex +with minimal effort. -**Test functions can receive fixture objects by naming them as an input -argument.** For each argument name, a matching fixture + +.. _`funcargs`: +.. _`funcarg mechanism`: +.. _`fixture function`: + +Fixtures as Function arguments +----------------------------------------- + +Test functions can receive fixture objects by naming them as an input +argument. For each argument name, a matching fixture function will provide a fixture object. This mechanism was already -introduced with pytest-2.0 and is also called the **funcarg mechanism**. +introduced with pytest-2.0 and is also called the *funcarg mechanism*. It allows test functions to easily receive and work against specific pre-initialized application objects without having to care about the details of setup/cleanup procedures. It's a prime example of `dependency injection`_ where fixture functions take the role of the *injector* and test functions are the *consumers* of fixture objects. -With pytest-2.3 this mechanism has been generalized and improved as described -further in this document. -**Test classes, modules or whole projects can declare a need for -one or more fixtures**. All required fixture functions will execute -before a test from the specifying context executes. You can use this -to make tests operate from a pre-initialized directory or with -certain environment variables or with pre-initialized applications. -For example, the Django_ project requires database -initialization to be able to import from and use its model objects. -For that, the `pytest-django`_ plugin provides fixtures which your -project can then easily depend or extend on, simply by referencing the -name of the particular fixture. +Let's look at a simple self-contained test module containing +a fixture and a test function using it:: -**Fixtures can be shared throughout a test session, module or class.**. -By means of a "scope" declaration on a fixture function, it will -only be invoked once per the specified scope. This allows to reduce the number -of expensive application object setups and thus helps to speed up test runs. -Typical examples are the setup of test databases or establishing -required subprocesses or network connections. - -**Fixture functions have limited visilibity** which depends on where they -are defined. If they are defined on a test class, only its test methods -may use it. A fixture defined in a module can only be used -from that test module. A fixture defined in a conftest.py file -can only be used by the tests below the directory of that file. -Lastly, plugins can define fixtures which are available across all -projects. - -**Fixture functions can interact with the requesting testcontext**. By -accepting a special ``request`` object, fixture functions can introspect -the function, class or module for which they are invoked and can -optionally register cleanup functions which are called when the last -test finished execution. A good example is `pytest-timeout`_ which -allows to limit the execution time of a test, and will read the -according parameter from a test function or from project-wide settings. - -**Fixture functions can be parametrized** in which case they will be called -multiple times, each time executing the set of dependent tests, i. e. the -tests that depend on this fixture. Test functions do usually not need -to be aware of their re-running. Fixture parametrization helps to -write exhaustive functional tests for components which themselves can be -configured in multiple ways. - - -Basic test function with fixtures ------------------------------------------------------------ - -.. versionadded:: 2.3 - - -Let's look at a simple self-contained test module containing a module -visible fixture function and a test function using the provided fixture:: - - # content of ./test_simplefactory.py + # content of ./test_fixturefuncarg.py import pytest - @pytest.fixture() + @pytest.fixture def myfuncarg(): return 42 def test_function(myfuncarg): - assert myfuncarg == 17 + assert myfuncarg == 17 # will fail -Here, the ``test_function`` needs a very simple fixture ``myfuncarg`` which -it wants to compare against a specific value. py.test will discover and call -the ``@pytest.fixture`` marked ``myfuncarg`` fixture function. Running the -tests looks like this:: +Here, the ``test_function`` needs the ``myfuncarg`` fixture value. pytest +will discover and call the ``@pytest.fixture`` marked ``myfuncarg`` +fixture function. Running the test looks like this:: - $ py.test test_simplefactory.py + $ py.test test_fixturefuncarg.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev18 - plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout - collecting ... collected 1 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 1 items - test_simplefactory.py F + test_fixturefuncarg.py F ================================= FAILURES ================================= ______________________________ test_function _______________________________ @@ -122,17 +75,17 @@ myfuncarg = 42 def test_function(myfuncarg): - > assert myfuncarg == 17 + > assert myfuncarg == 17 # will fail E assert 42 == 17 - test_simplefactory.py:8: AssertionError + test_fixturefuncarg.py:8: AssertionError ========================= 1 failed in 0.01 seconds ========================= This shows that the test function was called with a ``myfuncarg`` -argument value of ``42`` and the assert fails as expected. Here is +value of ``42`` and the assert fails as expected. Here is how py.test comes to call the test function this way: -1. py.test :ref:`finds ` the ``test_function`` because +1. pytest :ref:`finds ` the ``test_function`` because of the ``test_`` prefix. The test function needs a function argument named ``myfuncarg``. A matching fixture function is discovered by looking for a fixture function named ``myfuncarg``. @@ -164,6 +117,12 @@ Creating and using a session-shared fixture ----------------------------------------------------------------- +By means of a "scope" declaration, a fixture function will +only be invoked once per the specified scope. This allows to reduce the +number of expensive application object setups and thus helps to speed up +test runs. Typical examples are the setup of test databases or +establishing required subprocesses or network connections. + .. regendoc:wipe Here is a simple example of a fixture function creating a shared @@ -183,7 +142,7 @@ function:: # content of test_module.py - + def test_ehlo(smtp): response = smtp.ehlo() assert response[0] == 250 @@ -199,12 +158,11 @@ inspect what is going on and can now run the tests:: $ py.test -q test_module.py - collecting ... collected 2 items FF ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -213,10 +171,10 @@ > assert 0 # for demo purposes E assert 0 - test_module.py:5: AssertionError + test_module.py:6: AssertionError ________________________________ test_noop _________________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -224,32 +182,34 @@ > assert 0 # for demo purposes E assert 0 - test_module.py:10: AssertionError - 2 failed in 0.15 seconds + test_module.py:11: AssertionError you see the two ``assert 0`` failing and can also see that the same (session-scoped) object was passed into the two test functions because pytest shows the incoming arguments in the traceback. -Adding a finalizer to a fixture --------------------------------------------------------- +Fixtures can interact with the requesting test context +------------------------------------------------------------- -Further extending the ``smtp`` example, we now want to properly -close a smtp server connection after the last test using it -has been run. We can do this by changing the fixture function -to accept the special :ref:`request` object, representing the -requesting test context. After calling the ``request.addfinalizer()`` -helper pytest will make sure that the finalizer function is called -after the last test using the ``smtp`` resource has finished. +By using the special :ref:`request` object, fixture functions can introspect +the function, class or module for which they are invoked and can +optionally register cleanup functions which are called when the last +test finished execution. + +Further extending the previous ``smtp`` fixture example, let's try to +read the server URL from the module namespace, use module-scoping and +register a finalizer that closes the smtp connection after the last +test finished execution:: # content of conftest.py import pytest import smtplib - @pytest.fixture(scope="session") + @pytest.fixture(scope="module") def smtp(request): - smtp = smtplib.SMTP("merlinux.eu") + server = getattr(request.module, "smtpserver", "merlinux.eu") + smtp = smtplib.SMTP(server) def fin(): print ("finalizing %s" % smtp) smtp.close() @@ -260,19 +220,67 @@ using it has executed:: $ py.test -s -q --tb=no - collecting ... collected 2 items FF - 2 failed in 0.21 seconds - finalizing + finalizing -We see that the ``smtp`` instance is finalized after all -tests executed. If we had specified ``scope='function'`` -then fixture setup and cleanup would occur around each -single test. +We see that the ``smtp`` instance is finalized after the two +tests using it tests executed. If we had specified ``scope='function'`` +then fixture setup and cleanup would occur around each single test. +Note that the test module itself did not need to change! -Parametrizing a session-shared funcarg resource +Let's quickly create another test module that actually sets the +server URL and has a test to verify the fixture picks it up:: + + # content of test_anothersmtp.py + + smtpserver = "mail.python.org" # will be read by smtp fixture + + def test_showhelo(smtp): + assert 0, smtp.helo() + +Running it:: + + $ py.test -qq --tb=short test_anothersmtp.py + F + ================================= FAILURES ================================= + ______________________________ test_showhelo _______________________________ + test_anothersmtp.py:5: in test_showhelo + > assert 0, smtp.helo() + E AssertionError: (250, 'mail.python.org') + +**Test classes, modules or whole projects can make use of +one or more fixtures**. All required fixture functions will execute +before a test from the specifying context executes. As You can use this +to make tests operate from a pre-initialized directory or with +certain environment variables or with pre-configured global application +settings. + +For example, the Django_ project requires database +initialization to be able to import from and use its model objects. +For that, the `pytest-django`_ plugin provides fixtures which your +project can then easily depend or extend on, simply by referencing the +name of the particular fixture. + + +**Fixture functions have limited visilibity** which depends on where they +are defined. If they are defined on a test class, only its test methods +may use it. A fixture defined in a module can only be used +from that test module. A fixture defined in a conftest.py file +can only be used by the tests below the directory of that file. +Lastly, plugins can define fixtures which are available across all +projects. + + +Parametrizing a session-shared fixture ----------------------------------------------------------------- +**Fixture functions can be parametrized** in which case they will be called +multiple times, each time executing the set of dependent tests, i. e. the +tests that depend on this fixture. Test functions do usually not need +to be aware of their re-running. Fixture parametrization helps to +write exhaustive functional tests for components which themselves can be +configured in multiple ways. + Extending the previous example, we can flag the fixture to create two ``smtp`` fixture instances which will cause all tests using the fixture to run twice. The fixture function gets @@ -297,13 +305,12 @@ a value via ``request.param``. No test function code needs to change. So let's just do another run:: - $ py.test -q - collecting ... collected 4 items + $ py.test -q test_module.py FFFF ================================= FAILURES ================================= __________________________ test_ehlo[merlinux.eu] __________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -312,10 +319,10 @@ > assert 0 # for demo purposes E assert 0 - test_module.py:5: AssertionError + test_module.py:6: AssertionError __________________________ test_noop[merlinux.eu] __________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -323,10 +330,10 @@ > assert 0 # for demo purposes E assert 0 - test_module.py:10: AssertionError + test_module.py:11: AssertionError ________________________ test_ehlo[mail.python.org] ________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -334,10 +341,10 @@ > assert "merlinux" in response[1] E assert 'merlinux' in 'mail.python.org\nSIZE 10240000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN' - test_module.py:4: AssertionError + test_module.py:5: AssertionError ________________________ test_noop[mail.python.org] ________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -345,42 +352,17 @@ > assert 0 # for demo purposes E assert 0 - test_module.py:10: AssertionError - 4 failed in 6.62 seconds + test_module.py:11: AssertionError We now get four failures because we are running the two tests twice with different ``smtp`` fixture instances. Note that with the ``mail.python.org`` connection the second test fails in ``test_ehlo`` because it expects a specific server string. -We also see that the two ``smtp`` instances are finalized appropriately. - -Looking at test collection without running tests ------------------------------------------------------- - -You can also look at the tests which pytest collects without running them:: - - $ py.test --collectonly - =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev18 - plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout - collecting ... collected 4 items - - - - - - - ============================= in 0.01 seconds ============================= - -Our fixture parameters show up in the test id of the test functions. -Note that pytest orders your test run by resource usage, minimizing -the number of active resources at any given time. - .. _`interdependent fixtures`: -Interdepdendent fixtures +Using fixtures from a fixture function ---------------------------------------------------------- You can not only use fixtures in test functions but fixture functions @@ -410,15 +392,13 @@ $ py.test -v test_appsetup.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev18 -- /home/hpk/venv/1/bin/python - cachedir: /tmp/doc-exec-6/.cache - plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED - ========================= 2 passed in 0.14 seconds ========================= + ========================= 2 passed in 0.09 seconds ========================= Due to the parametrization of ``smtp`` the test will run twice with two different ``App`` instances and respective smtp servers. There is no @@ -429,6 +409,7 @@ fixtures but not the other way round: A session-scoped fixture could not use a module-scoped one in a meaningful way. + .. _`automatic per-resource grouping`: Automatic grouping of tests by fixture instances @@ -473,9 +454,7 @@ $ py.test -v -s test_module.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev18 -- /home/hpk/venv/1/bin/python - cachedir: /tmp/doc-exec-6/.cache - plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 8 items test_module.py:16: test_0[1] PASSED @@ -487,7 +466,7 @@ test_module.py:20: test_2[1-mod2] PASSED test_module.py:20: test_2[2-mod2] PASSED - ========================= 8 passed in 0.02 seconds ========================= + ========================= 8 passed in 0.01 seconds ========================= test0 1 test0 2 create mod1 @@ -505,18 +484,20 @@ 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. +.. _`usefixtures`: -Marking test classes, modules, projects with required fixtures + +using fixtures from classes, modules or projects ---------------------------------------------------------------------- .. regendoc:wipe -Sometimes test functions do not directly get access to a fixture object. -For example, each test in a test class may require to operate with an +Sometimes test functions do not directly need access to a fixture object. +For example, tests may require to operate with an empty directory as the current working directory. Here is how you can -can use the standard :ref:`tempfile ` and pytest fixtures -to achieve it. We separate the creation of the fixture into -a conftest.py file:: +can use the standard `tempfile `_ and pytest fixtures to +achieve it. We separate the creation of the fixture into a conftest.py +file:: # content of conftest.py @@ -529,7 +510,7 @@ newpath = tempfile.mkdtemp() os.chdir(newpath) -and declare its use in a test module via a ``needs`` marker:: +and declare its use in a test module via a ``usefixtures`` marker:: # content of test_setenv.py import os @@ -546,20 +527,18 @@ assert os.listdir(os.getcwd()) == [] Due to the ``usefixtures`` marker, the ``cleandir`` fixture -will be required for the execution of each of the test methods, just as if +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:: +to verify our fixture is activated and the tests pass:: $ py.test -q - collecting ... collected 2 items .. - 2 passed in 0.02 seconds -You may specify the need for multiple fixtures:: +You can specify multiple fixtures like this:: @pytest.mark.usefixtures("cleandir", "anotherfixture") -and you may specify fixture needs at the test module level, using +and you may specify fixture usage at the test module level, using a generic feature of the mark mechanism:: pytestmark = pytest.mark.usefixtures("cleandir") @@ -572,18 +551,19 @@ [pytest] usefixtures = cleandir +.. _`autoactive fixtures`: -autoactive fixtures at class/module/directory/global level +autoactive fixtures (xUnit setup on steroids) ---------------------------------------------------------------------- .. regendoc:wipe Occasionally, you may want to have fixtures get invoked automatically -without any ``usefixtures`` or funcargs reference. As a practical example, -suppose we have a database fixture which has a begin/rollback/commit -architecture and we want to automatically surround each test method by a -transaction and a rollback. Here is a dummy self-contained implementation -of this idea:: +without a `usefixtures`_ or `funcargs`_ reference. As a practical +example, suppose we have a database fixture which has a +begin/rollback/commit architecture and we want to automatically surround +each test method by a transaction and a rollback. Here is a dummy +self-contained implementation of this idea:: # content of test_db_transact.py @@ -617,9 +597,7 @@ If we run it, we get two passing tests:: $ py.test -q - collecting ... collected 2 items .. - 2 passed in 0.02 seconds And here is how autoactive fixtures work in other scopes: @@ -658,9 +636,20 @@ While all test methods in this TestClass will use the transaction fixture, other test classes or function will not do so without a marker or funcarg. +controlled visibility of fixture functions +---------------------------------------------------- + +If during implementing your tests you realize that you +want to use a fixture function from multiple test files you can move it +to a :ref:`conftest.py ` file or even separately installable +:ref:`plugins ` without changing test code. The discovery of +fixtures functions starts at test classes, then test modules, then +``conftest.py`` files and finally builtin and third party plugins. + .. currentmodule:: _pytest.python .. _`@pytest.fixture`: +.. _`pytest.fixture`: ``@pytest.fixture``: marking a fixture function -------------------------------------------------------------- diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/funcarg_compare.txt --- a/doc/en/funcarg_compare.txt +++ b/doc/en/funcarg_compare.txt @@ -1,18 +1,19 @@ .. _`funcargcompare`: -============================================================= pytest-2.3: reasoning for the new funcarg and setup functions ============================================================= **Target audience**: Reading this document requires basic knowledge of python testing, xUnit setup methods and the (previous) basic pytest funcarg mechanism, see http://pytest.org/2.2.4/funcargs.html +If you are new to pytest, then you can simply ignore this +section and read the other sections. .. currentmodule:: _pytest -Shortcomings of the previous pytest_funcarg__ mechanism -=========================================================== +Shortcomings of the previous ``pytest_funcarg__`` mechanism +-------------------------------------------------------------- The pre pytest-2.3 funcarg mechanism calls a factory each time a funcarg for a test function is required. If a factory wants to @@ -58,12 +59,15 @@ funcarg resource if it isn't stated in the test function signature. All of these limitations are addressed with pytest-2.3 and its -new facilities. +improved :ref:`fixture mechanism `. -Direct scoping of funcarg factories + +Direct scoping of fixture/funcarg factories -------------------------------------------------------- -Instead of calling cached_setup(), you can use the :ref:`@pytest.fixture <@pytest.fixture>` decorator and directly state the scope:: +Instead of calling cached_setup() with a cache scope, you can use the +:ref:`@pytest.fixture ` decorator and directly state +the scope:: @pytest.fixture(scope="session") def db(request): @@ -142,7 +146,7 @@ It is thus recommended to use the factory decorator. -solving per-session setup / the new @setup marker +solving per-session setup / autoactive fixtures -------------------------------------------------------------- pytest for a long time offered a pytest_configure and a pytest_sessionstart @@ -169,17 +173,44 @@ It follows that pytest_configure/session/runtest_setup are often not appropriate for implementing common fixture needs. Therefore, -pytest-2.3 introduces a new :ref:`@pytest.setup ` marker -for setup functions and it accepts an optional "scope" parameter. +pytest-2.3 introduces :ref:`autoactive fixtures` which fully +integrate with the generic :ref:`fixture mechanism ` +and obsolete many prior uses of pytest hooks. -See :ref:`setup` for more explanation and examples. - -funcarg and setup discovery now happens at collection time +funcargs/fixture discovery now happens at collection time --------------------------------------------------------------------- -pytest-2.3 takes care to discover funcarg factories and @setup methods +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 --collectonly" should be able to in the future show a lot of setup-information and thus presents a nice method to get an -overview of resource management in your project. +overview of fixture management in your project. +.. _`compatibility notes`: + +.. _`funcargscompat`: + +Conclusion and compatibility notes +--------------------------------------------------------- + +**Fixtures** were originally introduced to pytest-2.0. In pytest-2.3 +the mechanism was extended and refined: + +* previously funcarg factories were specified with a special + ``pytest_funcarg__NAME`` prefix instead of using the + ``@pytest.fixture`` decorator. + +* Factories received a :ref:`request ` object which managed caching through + ``request.cached_setup()`` calls and allowed using other funcargs via + ``request.getfuncargvalue()`` calls. These intricate APIs made it hard + to do proper parametrization and implement resource caching. The + new ``@pytest.fixture`` decorator allows to simply declare the scope + and let pytest figure things out for you. + +* if you used parametrization and funcarg factories which made use of + ``request.cached_setup()`` it is recommeneded to invest a few minutes + and simplify your fixture function code to use the :ref:`@pytest.fixture` + decorator instead. This will also allow to take advantage of + the automatic per-resource grouping of tests. + + diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/funcargs.txt --- a/doc/en/funcargs.txt +++ b/doc/en/funcargs.txt @@ -1,392 +1,14 @@ - -.. _resources: -.. _`funcargs`: -.. _`funcarg mechanism`: ======================================================= funcargs: resource injection and parametrization ======================================================= -.. note:: +pytest-2.3 introduces major refinements to fixture management +of which the funcarg mechanism introduced with pytest-2.0 remains +a core part. The documentation has been refactored as well +and you can read on here: - pytest-2.3 introduces major refinements to the test setup and funcarg - mechanisms introduced to pytest-2.0. All pre-2.3 usages remain - supported and several use cases, among them scoping and parametrization - of funcarg resources, are now easier to accomplish. For more background, - see `compatibility notes`_ and the detailed :ref:`reasoning for the new - funcarg and setup functions `. +- :ref:`fixtures` +- :ref:`parametrize` +- :ref:`funcargcompare` -.. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection - -Introduction -==================== - -pytest supports the injection of test resources into test and setup functions -and flexibly control their life cycle in relation to the overall test -execution. Moreover, tests can get executed multiple times if you have -different variants of test resources to test with. - -The basic mechanism for injecting objects is called the *funcarg -mechanism* because objects are injected when a test or setup -**function** states it as an **argument**. The injected argument -is created by a call to a registered **fixture function** for each argument -name. This mechanism is an example of `Dependency Injection`_ -and helps to de-couple test code from the setup of required -objects: at test writing time you do not need to care for the details of -where and how your required test resources are constructed, if they are -shared on a per-class, module or session basis, or if your test function -is invoked multiple times with differently configured resource -instances. - -Fixture dependency injection allows to organise test resources -in a modular explicit way so that test functions state their needs -in their signature. pytest additionally offers powerful xunit-style -:ref:`setup functions ` for the cases where you need -to create implicit test state that is not passed explicitely to test functions. - -When a test function is invoked multiple times with different arguments we -speak of **parametrized testing**. You can use it e. g. to repeatedly run test -functions against different database backends or to check that certain -inputs lead to certain outputs. - -Concretely, there are three main means of funcarg management: - -* a `@pytest.fixture`_ marker to define resource factories, - their scoping and parametrization. Factories can themselves - receive resources through their function arguments, easing - the setup of `interdependent resources`_. Factories can use - the special `request`_ object to access details from where - the factory or setup function is called and for registering finalizers. - -* a `@pytest.mark.parametrize`_ marker for executing test functions - multiple times with different argument sets, - -* a `pytest_generate_tests`_ plugin hook marker for implementing - your parametrization for a test function which may depend on - command line options, class/module attributes etc. - -Apart from making it easy to manage your own test resources -pytest also comes with some :ref:`builtinresources` which -you can use without defining them yourself. Third-party plugins -offer yet more domain-specific funcarg resources (for example the -`pytest-django plugin `_) so -that after plugin installation you can simply use them in -your test and setup functions. This all contributes to high -re-useability of test resource management and goes far beyond what can -be done with the classical xUnit style approach which encodes resource -setup statically into the test source code, leading to duplicate and -hard-to change fixtures. - -.. _`@pytest.fixture`: - -``@pytest.fixture``: Creating parametrized, scoped resources -===================================================================== - -Basic funcarg injection example ------------------------------------------------------------ - -Let's look at a simple self-contained test module using a factory -and a funcarg:: - - # content of ./test_simplefactory.py - import pytest - - @pytest.fixture() - def myfuncarg(): - return 42 - - def test_function(myfuncarg): - assert myfuncarg == 17 - -Here, the ``test_function`` needs an object named ``myfuncarg`` and thus -py.test will discover and call the ``@pytest.fixture`` marked ``myfuncarg`` -factory function. Running the tests looks like this:: - - $ py.test test_simplefactory.py - =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev11 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 1 items - - test_simplefactory.py F - - ================================= FAILURES ================================= - ______________________________ test_function _______________________________ - - myfuncarg = 42 - - def test_function(myfuncarg): - > assert myfuncarg == 17 - E assert 42 == 17 - - test_simplefactory.py:8: AssertionError - ========================= 1 failed in 0.01 seconds ========================= - -This shows that the test function was called with a ``myfuncarg`` -argument value of ``42`` and the assert fails as expected. Here is -how py.test comes to call the test function this way: - -1. py.test :ref:`finds ` the ``test_function`` because - of the ``test_`` prefix. The test function needs a function argument - named ``myfuncarg``. A matching factory function is discovered by - looking for a factory function named ``myfuncarg``. - -2. ``myfuncarg()`` is called to create a value ``42``. - -3. ``test_function(42)`` is now called and results in the above - reported exception because of the assertion mismatch. - -Note that if you misspell a function argument or want -to use one that isn't available, you'll see an error -with a list of available function arguments. - -.. Note:: - - You can always issue:: - - py.test --fixtures test_simplefactory.py - - to see available function arguments. - - -Location independency of funcarg factories ----------------------------------------------------- - -If during implementing your tests you realize that you -want to use a factory from multiple test files you can move it -to a :ref:`conftest.py ` file or even separately installable -:ref:`plugins ` without changing test code. The discovery of -funcarg factories starts at test classes, then test modules, then -``conftest.py`` files and finally builtin and 3-rd party plugins. - - - -.. _`test generators`: -.. _`parametrizing-tests`: -.. _`parametrized test functions`: - -Parametrizing test functions -========================================================================== - -While the `@pytest.fixture`_ decorator allows to define parametrization -of funcarg resources at the factory-level, there are also means to -define parametrization at test functions directly: - -* `@pytest.mark.parametrize`_ to provide multiple argument sets - for a particular test function or class. - -* `pytest_generate_tests`_ to implement your own custom parametrization - scheme or extensions. - -.. _`@pytest.mark.parametrize`: - -``@pytest.mark.parametrize``: parametrizing test functions ---------------------------------------------------------------------- - -.. regendoc: wipe - -.. versionadded:: 2.2 - -The builtin ``pytest.mark.parametrize`` decorator enables -parametrization of arguments for a test function. Here is a typical example -of a test function that wants check for expected output given a certain input:: - - # content of test_expectation.py - import pytest - @pytest.mark.parametrize(("input", "expected"), [ - ("3+5", 8), - ("2+4", 6), - ("6*9", 42), - ]) - def test_eval(input, expected): - assert eval(input) == expected - -The ``@parametrize`` decorator defines three different argument sets for the -two ``(input, output)`` arguments of ``test_eval`` function so the latter -will be run three times:: - - $ py.test -q - collecting ... collected 13 items - ....F........ - ================================= FAILURES ================================= - ____________________________ test_eval[6*9-42] _____________________________ - - input = '6*9', expected = 42 - - @pytest.mark.parametrize(("input", "expected"), [ - ("3+5", 8), - ("2+4", 6), - ("6*9", 42), - ]) - def test_eval(input, expected): - > assert eval(input) == expected - E assert 54 == 42 - E + where 54 = eval('6*9') - - test_expectation.py:8: AssertionError - 1 failed, 12 passed in 6.41 seconds - -As expected only one pair of input/output values fails the simple test function. -As usual you can see the ``input`` and ``output`` values in the traceback. - -Note that there are various ways how you can mark groups of functions, -see :ref:`mark`. - - -.. _`pytest_generate_tests`: - -Basic ``pytest_generate_tests`` example ---------------------------------------------- - -.. XXX - - > line 598 "Basic ``pytest_generate_tests`` example" - I think this is - > not a very basic example! I think it is copied from parametrize.txt - > page, where it might make more sense. Here is what I would consider a - > basic example. - > - > # code - > def isSquare(n): - > n = n ** 0.5 - > return int(n) == n - > - > # test file - > def pytest_generate_tests(metafunc): - > squares = [1, 4, 9, 16, 25, 36, 49] - > for n in range(1, 50): - > expected = n in squares - > if metafunc.function.__name__ == 'test_isSquare': - > metafunc.addcall(id=n, funcargs=dict(n=n, - > expected=expected)) - > - > - > def test_isSquare(n, expected): - > assert isSquare(n) == expected - - -.. XXX - consider adding more examples, also mixed (factory-parametrized/test-function-parametrized, see mail from Brianna) - -The ``pytest_generate_tests`` hook is typically used if you want -to go beyond what ``@pytest.mark.parametrize`` offers. For example, -let's say we want to execute a test with different computation -parameters and the parameter range shall be determined by a command -line argument. Let's first write a simple (do-nothing) computation test:: - - # content of test_compute.py - - def test_compute(param1): - assert param1 < 4 - -Now we add a ``conftest.py`` file containing the addition of a -command line option and the generation of tests depending on -that option:: - - # content of conftest.py - - def pytest_addoption(parser): - parser.addoption("--all", action="store_true", - help="run all combinations") - - def pytest_generate_tests(metafunc): - if 'param1' in metafunc.fixturenames: - if metafunc.config.option.all: - end = 5 - else: - end = 2 - metafunc.parametrize("param1", range(end)) - -This means that we only run two tests if no option is passed:: - - $ py.test -q test_compute.py - collecting ... collected 2 items - .. - 2 passed in 0.01 seconds - -And we run five tests if we add the ``--all`` option:: - - $ py.test -q --all test_compute.py - collecting ... collected 5 items - ....F - ================================= FAILURES ================================= - _____________________________ test_compute[4] ______________________________ - - param1 = 4 - - def test_compute(param1): - > assert param1 < 4 - E assert 4 < 4 - - test_compute.py:3: AssertionError - 1 failed, 4 passed in 0.02 seconds - -As expected when running the full range of ``param1`` values -we'll get an error on the last one. - -You might want to look at :ref:`more parametrization examples `. - - -.. _`metafunc object`: - -The **metafunc** object -------------------------------------------- - -metafunc objects are passed to the ``pytest_generate_tests`` hook. -They help to inspect a testfunction and to generate tests -according to test configuration or values specified -in the class or module where a test function is defined: - -``metafunc.fixturenames``: set of required function arguments for given function - -``metafunc.function``: underlying python test function - -``metafunc.cls``: class object where the test function is defined in or None. - -``metafunc.module``: the module object where the test function is defined in. - -``metafunc.config``: access to command line opts and general config - -``metafunc.funcargnames``: alias for ``fixturenames``, for pre-2.3 compatibility - -.. automethod:: Metafunc.parametrize -.. automethod:: Metafunc.addcall(funcargs=None,id=_notexists,param=_notexists) - - -.. regendoc:wipe - - -.. _`compatibility notes`: - -.. _`funcargscompat`: - -Compatibility notes -============================================================ - -**Fixtures** were originally introduced to pytest-2.0. In pytest-2.3 -the mechanism was extended and refined: - -* previously funcarg factories were specified with a special - ``pytest_funcarg__NAME`` prefix instead of using the - ``@pytest.fixture`` decorator. - -* Factories received a `request`_ object which managed caching through - ``request.cached_setup()`` calls and allowed using other funcargs via - ``request.getfuncargvalue()`` calls. These intricate APIs made it hard - to do proper parametrization and implement resource caching. The - new ``@pytest.fixture`` decorator allows to simply declare the scope - and let pytest figure things out for you. - -* if you used parametrization and funcarg factories which made use of - ``request.cached_setup()`` it is recommeneded to invest a few minutes - and simplify your fixture function code to use the `@pytest.fixture`_ - decorator instead. This will also allow to take advantage of - the `automatic per-resource grouping`_ of tests. - -.. note:: - - Throughout the pytest documents the ``pytest_funcarg__NAME`` way of - defining a fixture function is often termed "old-style". Their - use remains fully supported and existing code using it should run - unmodified. - - diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/getting-started.txt --- a/doc/en/getting-started.txt +++ b/doc/en/getting-started.txt @@ -1,7 +1,7 @@ Installation and Getting Started =================================== -**Pythons**: Python 2.4-3.2, Jython, PyPy +**Pythons**: Python 2.4-3.3, Jython, PyPy **Platforms**: Unix/Posix and Windows @@ -22,14 +22,7 @@ To check your installation has installed the correct version:: $ py.test --version - This is py.test version 2.3.0.dev2, imported from /home/hpk/p/pytest/pytest.pyc - setuptools registered plugins: - pytest-xdist-1.8 at /home/hpk/p/pytest-xdist/xdist/plugin.pyc - pytest-bugzilla-0.1 at /home/hpk/tmp/eanxgeek/pytest_bugzilla.pyc - pytest-cache-0.9 at /home/hpk/p/pytest-cache/pytest_cache.pyc - oejskit-0.9.0 at /home/hpk/p/js-infrastructure/oejskit/pytest_jstests.pyc - pytest-pep8-1.0.1 at /home/hpk/venv/1/local/lib/python2.7/site-packages/pytest_pep8.pyc - pytest-cov-1.6 at /home/hpk/venv/1/local/lib/python2.7/site-packages/pytest_cov.pyc + This is py.test version 2.3.0.dev19, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc If you get an error checkout :ref:`installation issues`. @@ -51,9 +44,8 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 - plugins: xdist, bugzilla, cache, oejskit, pep8, cov - collecting ... collected 1 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 1 items test_sample.py F @@ -66,7 +58,7 @@ E + where 4 = func(3) test_sample.py:5: AssertionError - ========================= 1 failed in 0.02 seconds ========================= + ========================= 1 failed in 0.01 seconds ========================= py.test found the ``test_answer`` function by following :ref:`standard test discovery rules `, basically detecting the ``test_`` prefixes. We got a failure report because our little ``func(3)`` call did not return ``5``. @@ -99,9 +91,7 @@ Running it with, this time in "quiet" reporting mode:: $ py.test -q test_sysexit.py - collecting ... collected 1 items . - 1 passed in 0.02 seconds .. todo:: For further ways to assert exceptions see the `raises` @@ -127,12 +117,11 @@ run the module by passing its filename:: $ py.test -q test_class.py - collecting ... collected 2 items .F ================================= FAILURES ================================= ____________________________ TestClass.test_two ____________________________ - self = + self = def test_two(self): x = "hello" @@ -140,7 +129,6 @@ E assert hasattr('hello', 'check') test_class.py:8: AssertionError - 1 failed, 1 passed in 0.02 seconds The first test passed, the second failed. Again we can easily see the intermediate values used in the assertion, helping us to @@ -150,9 +138,9 @@ -------------------------------------------------------------- For functional tests one often needs to create some files -and pass them to application objects. py.test provides -the versatile :ref:`funcarg mechanism` which allows to request -arbitrary resources, for example a unique temporary directory:: +and pass them to application objects. pytest provides +:ref:`builtinfixtures` which allow to request arbitrary +resources, for example a unique temporary directory:: # content of test_tmpdir.py def test_needsfiles(tmpdir): @@ -160,16 +148,15 @@ assert 0 We list the name ``tmpdir`` in the test function signature and -py.test will lookup and call a factory to create the resource +py.test 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 - collecting ... collected 1 items F ================================= FAILURES ================================= _____________________________ test_needsfiles ______________________________ - tmpdir = local('/home/hpk/tmp/pytest-2885/test_needsfiles0') + tmpdir = local('/tmp/pytest-168/test_needsfiles0') def test_needsfiles(tmpdir): print tmpdir @@ -178,15 +165,14 @@ test_tmpdir.py:3: AssertionError ----------------------------- Captured stdout ------------------------------ - /home/hpk/tmp/pytest-2885/test_needsfiles0 - 1 failed in 0.22 seconds + /tmp/pytest-168/test_needsfiles0 Before the test runs, a unique-per-test-invocation temporary directory was created. More info at :ref:`tmpdir handling`. -You can find out what kind of builtin :ref:`funcargs` exist by typing:: +You can find out what kind of builtin :ref:`fixtures` exist by typing:: - py.test --fixtures # shows builtin and custom function arguments + py.test --fixtures # shows builtin and custom fixtures Where to go next ------------------------------------- diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/goodpractises.txt --- a/doc/en/goodpractises.txt +++ b/doc/en/goodpractises.txt @@ -91,9 +91,6 @@ arguments to the subprocess-calls such as your test directory or other options. -.. _`test discovery`: -.. _`Python test discovery`: - Integration with setuptools/distribute test commands ---------------------------------------------------- @@ -129,6 +126,8 @@ this will download py.test if needed and then run py.test as you would expect it to. +.. _`test discovery`: +.. _`Python test discovery`: Conventions for Python test discovery ------------------------------------------------- diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/index.txt --- a/doc/en/index.txt +++ b/doc/en/index.txt @@ -1,11 +1,11 @@ -Welcome to pytest! +pytest: makes you a better programmer ============================================= -- **a mature full-featured testing tool** +- **a mature full-featured Python testing tool** - - runs on Posix/Windows, Python 2.4-3.2, PyPy and Jython-2.5.1 + - runs on Posix/Windows, Python 2.4-3.3, PyPy and Jython-2.5.1 - :ref:`comprehensive online ` and `PDF documentation `_ - continuously `tested on many Python interpreters `_ - used in :ref:`many projects and organisations `, in test @@ -25,12 +25,9 @@ - **supports functional testing and complex test setups** - - (new in 2.3) :ref:`easy test resource management, scoping and - parametrization ` - - (new in 2.3) :ref:`setup functions`. - - (new in 2.2) :ref:`durations` - - (much improved in 2.2) :ref:`marking and test selection ` - - (improved in 2.2) :ref:`parametrized test functions ` + - (new in 2.3) :ref:`modular parametrizeable fixtures ` + - :ref:`marking and test selection ` + - :ref:`parametrized test functions ` - advanced :ref:`skip and xfail` - can :ref:`distribute tests to multiple CPUs ` through :ref:`xdist plugin ` - can :ref:`continuously re-run failing tests ` diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/parametrize.txt --- /dev/null +++ b/doc/en/parametrize.txt @@ -0,0 +1,188 @@ + +.. _`test generators`: +.. _`parametrizing-tests`: +.. _`parametrized test functions`: +.. _`parametrize`: + +Parametrizing fixtures and test functions +========================================================================== + +While the :ref:`@pytest.fixture` decorator allows to define parametrization +at the level of fixture functions, there are two more parametrizations: + +* `@pytest.mark.parametrize`_ to provide multiple argument/fixture sets + for a particular test function or class. + +* `pytest_generate_tests`_ to implement your own custom parametrization + scheme or extensions. + +.. _`@pytest.mark.parametrize`: + +``@pytest.mark.parametrize``: parametrizing test functions +--------------------------------------------------------------------- + +.. regendoc: wipe + +.. versionadded:: 2.2 + +The builtin ``pytest.mark.parametrize`` decorator enables +parametrization of arguments for a test function. Here is a typical example +of a test function that wants check for expected output given a certain input:: + + # content of test_expectation.py + import pytest + @pytest.mark.parametrize(("input", "expected"), [ + ("3+5", 8), + ("2+4", 6), + ("6*9", 42), + ]) + def test_eval(input, expected): + assert eval(input) == expected + +The ``@parametrize`` decorator defines three different argument sets for the +two ``(input, output)`` arguments of ``test_eval`` function so the latter +will be run three times:: + + $ py.test -q + ..F + ================================= FAILURES ================================= + ____________________________ test_eval[6*9-42] _____________________________ + + input = '6*9', expected = 42 + + @pytest.mark.parametrize(("input", "expected"), [ + ("3+5", 8), + ("2+4", 6), + ("6*9", 42), + ]) + def test_eval(input, expected): + > assert eval(input) == expected + E assert 54 == 42 + E + where 54 = eval('6*9') + + test_expectation.py:8: AssertionError + +As expected only one pair of input/output values fails the simple test function. +As usual you can see the ``input`` and ``output`` values in the traceback. + +Note that there are various ways how you can mark groups of functions, +see :ref:`mark`. + + +.. _`pytest_generate_tests`: + +Basic ``pytest_generate_tests`` example +--------------------------------------------- + +.. XXX + + > line 598 "Basic ``pytest_generate_tests`` example" - I think this is + > not a very basic example! I think it is copied from parametrize.txt + > page, where it might make more sense. Here is what I would consider a + > basic example. + > + > # code + > def isSquare(n): + > n = n ** 0.5 + > return int(n) == n + > + > # test file + > def pytest_generate_tests(metafunc): + > squares = [1, 4, 9, 16, 25, 36, 49] + > for n in range(1, 50): + > expected = n in squares + > if metafunc.function.__name__ == 'test_isSquare': + > metafunc.addcall(id=n, funcargs=dict(n=n, + > expected=expected)) + > + > + > def test_isSquare(n, expected): + > assert isSquare(n) == expected + + +.. XXX + consider adding more examples, also mixed (factory-parametrized/test-function-parametrized, see mail from Brianna) + +The ``pytest_generate_tests`` hook is typically used if you want +to go beyond what ``@pytest.mark.parametrize`` offers. For example, +let's say we want to execute a test with different computation +parameters and the parameter range shall be determined by a command +line argument. Let's first write a simple (do-nothing) computation test:: + + # content of test_compute.py + + def test_compute(param1): + assert param1 < 4 + +Now we add a ``conftest.py`` file containing the addition of a +command line option and the generation of tests depending on +that option:: + + # content of conftest.py + + def pytest_addoption(parser): + parser.addoption("--all", action="store_true", + help="run all combinations") + + def pytest_generate_tests(metafunc): + if 'param1' in metafunc.fixturenames: + if metafunc.config.option.all: + end = 5 + else: + end = 2 + metafunc.parametrize("param1", range(end)) + +This means that we only run two tests if no option is passed:: + + $ py.test -q test_compute.py + .. + +And we run five tests if we add the ``--all`` option:: + + $ py.test -q --all test_compute.py + ....F + ================================= FAILURES ================================= + _____________________________ test_compute[4] ______________________________ + + param1 = 4 + + def test_compute(param1): + > assert param1 < 4 + E assert 4 < 4 + + test_compute.py:3: AssertionError + +As expected when running the full range of ``param1`` values +we'll get an error on the last one. + +You might want to look at :ref:`more parametrization examples `. + + +.. _`metafunc object`: + +The **metafunc** object +------------------------------------------- + +.. currentmodule:: _pytest.python + +metafunc objects are passed to the ``pytest_generate_tests`` hook. +They help to inspect a testfunction and to generate tests +according to test configuration or values specified +in the class or module where a test function is defined: + +``metafunc.fixturenames``: set of required function arguments for given function + +``metafunc.function``: underlying python test function + +``metafunc.cls``: class object where the test function is defined in or None. + +``metafunc.module``: the module object where the test function is defined in. + +``metafunc.config``: access to command line opts and general config + +``metafunc.funcargnames``: alias for ``fixturenames``, for pre-2.3 compatibility + +.. automethod:: Metafunc.parametrize +.. automethod:: Metafunc.addcall(funcargs=None,id=_notexists,param=_notexists) + + diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/setup.txt --- a/doc/en/setup.txt +++ b/doc/en/setup.txt @@ -1,12 +1,10 @@ -Page has moved to fixture +setup: is now an "autoactive fixture" ======================================================== During development prior to the pytest-2.3 release the name ``pytest.setup`` was used but before the release it was renamed -to :ref:`pytest.fixture` mainly to avoid the misconception that there -should be a ``pytest.teardown`` as well. +and moved to become part of the general fixture mechanism, +namely :ref:`autoactive fixtures` -Please refer to :ref:`pytest.fixture` for information on the new -fixture functions. diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/skipping.txt --- a/doc/en/skipping.txt +++ b/doc/en/skipping.txt @@ -130,9 +130,8 @@ example $ py.test -rx xfail_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 - plugins: xdist, bugzilla, cache, oejskit, pep8, cov - collecting ... collected 6 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 6 items xfail_demo.py xxxxxx ========================= short test summary info ========================== @@ -148,7 +147,7 @@ XFAIL xfail_demo.py::test_hello6 reason: reason - ======================== 6 xfailed in 0.04 seconds ========================= + ======================== 6 xfailed in 0.03 seconds ========================= .. _`evaluation of skipif/xfail conditions`: diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/talks.txt --- a/doc/en/talks.txt +++ b/doc/en/talks.txt @@ -18,12 +18,11 @@ - `pycon 2010 tutorial PDF`_ and `tutorial1 repository`_ -Function arguments: +Fixtures and Function arguments: -- :ref:`mysetup` -- `application setup in test functions with funcargs`_ +- :ref:`fixtures` - `monkey patching done right`_ (blog post, consult `monkeypatch - plugin`_ for actual 1.0 API) + plugin`_ for up-to-date API) Test parametrization: diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/tmpdir.txt --- a/doc/en/tmpdir.txt +++ b/doc/en/tmpdir.txt @@ -28,16 +28,15 @@ $ py.test test_tmpdir.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev2 - plugins: xdist, bugzilla, cache, oejskit, pep8, cov - collecting ... collected 1 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 1 items test_tmpdir.py F ================================= FAILURES ================================= _____________________________ test_create_file _____________________________ - tmpdir = local('/home/hpk/tmp/pytest-2886/test_create_file0') + tmpdir = local('/tmp/pytest-169/test_create_file0') def test_create_file(tmpdir): p = tmpdir.mkdir("sub").join("hello.txt") @@ -48,7 +47,7 @@ E assert 0 test_tmpdir.py:7: AssertionError - ========================= 1 failed in 0.23 seconds ========================= + ========================= 1 failed in 0.02 seconds ========================= .. _`base temporary directory`: diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/unittest.txt --- a/doc/en/unittest.txt +++ b/doc/en/unittest.txt @@ -1,7 +1,7 @@ .. _`unittest.TestCase`: -Support for unittest.TestCase +Support for unittest.TestCase / Integration of fixtures ===================================================================== py.test has limited support for running Python `unittest.py style`_ tests. @@ -24,9 +24,8 @@ $ py.test test_unittest.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev12 - plugins: xdist, bugzilla, cache, oejskit, cli, timeout, pep8, cov - collecting ... collected 1 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + collected 1 items test_unittest.py F @@ -43,20 +42,20 @@ test_unittest.py:8: AssertionError ----------------------------- Captured stdout ------------------------------ hello - ========================= 1 failed in 0.03 seconds ========================= + ========================= 1 failed in 0.01 seconds ========================= .. _`unittest.py style`: http://docs.python.org/library/unittest.html -Moreover, you can use the new :ref:`@pytest.setup functions <@pytest.setup>` -functions and make use of pytest's unique :ref:`funcarg mechanism` in your -test suite:: +Moreover, you can use pytest's new :ref:`autoactive fixtures` +functions, thereby connecting pytest's :ref:`fixture mechanism ` +with a setup/teardown style:: # content of test_unittest_funcargs.py import pytest import unittest class MyTest(unittest.TestCase): - @pytest.setup() + @pytest.fixture(autoactive=True) def chdir(self, tmpdir): tmpdir.chdir() # change to pytest-provided temporary directory tmpdir.join("samplefile.ini").write("# testdata") @@ -70,41 +69,66 @@ which the unittest-testcase method can now use:: $ py.test -q test_unittest_funcargs.py - collecting ... collected 1 items . - 1 passed in 0.28 seconds If you want to make a database attribute available on unittest.TestCases -instances, based on a marker, you can do it using :ref:`pytest.mark`` and -:ref:`setup functions`:: +instances, you can do it using :ref:`usefixtures` and a simple +:ref:`fixture function`:: # content of test_unittest_marked_db.py import pytest import unittest - @pytest.fixture() - def db(): + @pytest.fixture + def db(request): class DummyDB: - x = 1 - return DummyDB() + entries = [] + db = DummyDB() + if request.instance is not None: + request.instance.db = db + return db - @pytest.setup() - def stick_db_to_self(request, db): - if hasattr(request.node.markers, "needsdb"): - request.instance.db = db + @pytest.mark.usefixtures("db") + class MyTest(unittest.TestCase): + def test_append(self): + self.db.entries.append(1) - class MyTest(unittest.TestCase): - def test_method(self): - assert not hasattr(self, "db") + def test_method2(self): + # check we have a fresh instance + assert len(self.db.entries) == 0 - @pytest.mark.needsdb - def test_method2(self): - assert self.db.x == 1 - -Running it passes both tests, one of which will see a ``db`` attribute -because of the according ``needsdb`` marker:: +Running it passes both tests:: $ py.test -q test_unittest_marked_db.py - collecting ... collected 2 items .. - 2 passed in 0.03 seconds + +If you rather want to provide a class-cached "db" attribute, you +can write a slightly different fixture using a ``scope`` parameter +for the fixture decorator :: + + # content of test_unittest_class_db.py + import pytest + import unittest + + @pytest.fixture(scope="class") + def db_class(request): + class DummyDB: + entries = [] + db = DummyDB() + if request.cls is not None: + request.cls.db = db + return db + + @pytest.mark.usefixtures("db_class") + class MyTest(unittest.TestCase): + def test_append(self): + self.db.entries.append(1) + + def test_method2(self): + # check we DONT have a fresh instance + assert len(self.db.entries) == 1 + +Running it again passes both tests:: + + $ py.test -q test_unittest_class_db.py + .. diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/usage.txt --- a/doc/en/usage.txt +++ b/doc/en/usage.txt @@ -183,9 +183,7 @@ hook was invoked:: $ python myinvoke.py - collecting ... collected 0 items - in 0.01 seconds *** test run reporting finishing .. include:: links.inc diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 doc/en/xunit_setup.txt --- a/doc/en/xunit_setup.txt +++ b/doc/en/xunit_setup.txt @@ -1,16 +1,17 @@ .. _`classic xunit`: +.. _xunitsetup: classic xunit-style setup ======================================== .. note:: - This section describes the old way how you can implement setup and + This section describes the classic way how you can implement setup and teardown on a per-module/class/function basis. It remains fully - supported but it is recommended to rather use :ref:`fixture functions - ` or :ref:`funcargs ` for implementing your - needs to prepare and fix the test state for your tests. + supported but it is recommended to rather use the more flexible, + more modular and more scalable :ref:`fixture functions + ` for implementing for fixing test state for your tests. Module level setup/teardown -------------------------------------- diff -r 0a8334dc2fb38cb19b43962810b857de3bb1bd61 -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 tox.ini --- a/tox.ini +++ b/tox.ini @@ -43,13 +43,22 @@ [testenv:doc] basepython=python -changedir=doc +changedir=doc/en deps=:pypi:sphinx pytest commands= + make clean make html +[testenv:regen] +basepython=python +changedir=doc/en +deps=:pypi:sphinx + pytest +commands= + make regen + [testenv:py31] deps=:pypi:nose>=1.0 Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 8 08:34:36 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 08 Oct 2012 06:34:36 -0000 Subject: [py-svn] commit/pytest: hpk42: remove pytest.setup usage Message-ID: <20121008063436.14727.8792@bitbucket24.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/d56f9a2762a6/ changeset: d56f9a2762a6 user: hpk42 date: 2012-10-08 08:34:21 summary: remove pytest.setup usage affected #: 6 files diff -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 -r d56f9a2762a6a15e2024b419f0aed0e937bb7834 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev19' +__version__ = '2.3.0.dev20' diff -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 -r d56f9a2762a6a15e2024b419f0aed0e937bb7834 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -24,11 +24,11 @@ def fixture(scope="function", params=None, autoactive=False): """ (return a) decorator to mark a fixture factory function. - This decorator can be used (directly or with parameters) to define + This decorator can be used (with or or without parameters) to define a fixture function. The name of the fixture function can later be referenced to cause its invocation ahead of running tests: test modules or classes can use the pytest.mark.usefixtures(fixturename) - marker and test functions can directly use fixture names as input + marker. Test functions can directly use fixture names as input arguments in which case the fixture instance returned from the fixture function will be injected. @@ -45,17 +45,12 @@ """ if hasattr(scope, "__call__") and params is None and autoactive == False: # direct decoration - return FixtureFunctionMarker(None, params, autoactive)(scope) + return FixtureFunctionMarker("function", params, autoactive)(scope) else: return FixtureFunctionMarker(scope, params, autoactive=autoactive) defaultfuncargprefixmarker = fixture() -# XXX remove in favour of fixture(autoactive=True) -def setup(scope="function"): - """ alias for fixture(scope, autoactive=True) """ - return FixtureFunctionMarker(scope, params=None, autoactive=True) - def cached_property(f): """returns a cached property that is calculated by function f. taken from http://code.activestate.com/recipes/576563-cached-property/""" @@ -126,7 +121,6 @@ raises.Exception = pytest.fail.Exception return { 'fixture': fixture, - 'setup': setup, 'raises' : raises, 'collect': { 'Module': Module, 'Class': Class, 'Instance': Instance, diff -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 -r d56f9a2762a6a15e2024b419f0aed0e937bb7834 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev19', + version='2.3.0.dev20', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 -r d56f9a2762a6a15e2024b419f0aed0e937bb7834 testing/test_nose.py --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -28,7 +28,7 @@ from _pytest.nose import call_optional l = [] class A: - @pytest.setup() + @pytest.fixture( autoactive=True) def f(self): l.append(1) call_optional(A(), "f") diff -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 -r d56f9a2762a6a15e2024b419f0aed0e937bb7834 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -771,7 +771,7 @@ def markers(request): return request.node.markers - @pytest.setup(scope="class") + @pytest.fixture(scope="class", autoactive=True) def marking(request): request.applymarker(pytest.mark.XYZ("hello")) """) @@ -1814,7 +1814,7 @@ class MySetup: def __init__(self, request, arg1): request.instance.arg1 = arg1 - pytest.setup()(MySetup) + pytest.fixture(autoactive=True)(MySetup) class TestClass: def test_method(self): @@ -1949,14 +1949,14 @@ def pytest_funcarg__testdir(self, testdir): testdir.makeconftest(""" import pytest - @pytest.setup() + @pytest.fixture( autoactive=True) def perfunction(request, tmpdir): pass @pytest.fixture() def arg1(tmpdir): pass - @pytest.setup() + @pytest.fixture( autoactive=True) def perfunction2(arg1): pass @@ -1982,7 +1982,7 @@ testdir.makepyfile(""" import pytest class TestClass: - @pytest.setup() + @pytest.fixture(autoactive=True) def permethod(self, request): request.instance.funcname = request.function.__name__ def test_method1(self): @@ -2005,7 +2005,7 @@ def db(request): return request.param - @pytest.setup(enabled=enabled) + @pytest.fixture(enabled=enabled, autoactive=True) def createdb(db): pass @@ -2046,7 +2046,7 @@ def arg(): l.append(1) return 0 - @pytest.setup(scope="class") + @pytest.fixture(scope="class", autoactive=True) def something(arg): l.append(2) @@ -2071,7 +2071,7 @@ def arg(request): return request.param - @pytest.setup() + @pytest.fixture( autoactive=True) def something(arg): l.append(arg) @@ -2097,7 +2097,7 @@ def arg(request): return request.param - @pytest.setup(scope="function") + @pytest.fixture(scope="function", autoactive=True) def append(request, arg): if request.function.__name__ == "test_some": l.append(arg) @@ -2127,7 +2127,7 @@ def carg(request): return request.param - @pytest.setup(scope="function") + @pytest.fixture(scope="function", autoactive=True) def append(request, farg, carg): def fin(): l.append("fin_%s%s" % (carg, farg)) @@ -2153,13 +2153,13 @@ testdir.makepyfile(""" import pytest l = [] - @pytest.setup(scope="function") + @pytest.fixture(scope="function", autoactive=True) def fappend2(): l.append(2) - @pytest.setup(scope="class") + @pytest.fixture(scope="class", autoactive=True) def classappend3(): l.append(3) - @pytest.setup(scope="module") + @pytest.fixture(scope="module", autoactive=True) def mappend(): l.append(1) @@ -2179,7 +2179,7 @@ if metafunc.cls is not None: metafunc.parametrize("item", [1,2], scope="class") class TestClass: - @pytest.setup(scope="class") + @pytest.fixture(scope="class", autoactive=True) def addteardown(self, item, request): l.append("setup-%d" % item) request.addfinalizer(lambda: l.append("teardown-%d" % item)) @@ -2456,7 +2456,7 @@ def carg(request): return request.param - @pytest.setup(scope="function") + @pytest.fixture(scope="function", autoactive=True) def append(request, farg, carg): def fin(): l.append("fin_%s%s" % (carg, farg)) @@ -2594,7 +2594,7 @@ def arg(request): return request.param - @pytest.setup(scope="module") + @pytest.fixture(scope="module", autoactive=True) def mysetup(request, arg): request.addfinalizer(lambda: l.append("fin%s" % arg)) l.append("setup%s" % arg) @@ -2628,7 +2628,7 @@ def test_setup(self, testdir, scope, ok, error): testdir.makepyfile(""" import pytest - @pytest.setup(scope=%r) + @pytest.fixture(scope=%r, autoactive=True) def myscoped(request): for x in %r: assert hasattr(request, x) @@ -2683,7 +2683,7 @@ def test_setupfunc_missing_funcarg(self, testdir): testdir.makepyfile(""" import pytest - @pytest.setup() + @pytest.fixture( autoactive=True) def gen(qwe123): return 1 def test_something(): @@ -2718,7 +2718,7 @@ def arg(request): return request.param - @pytest.setup() + @pytest.fixture( autoactive=True) def mysetup(request, arg): assert not hasattr(request, "param") def test_1(arg): @@ -2731,10 +2731,10 @@ testdir.makepyfile(""" import pytest l = [] - @pytest.setup(scope='module') + @pytest.fixture(scope='module', autoactive=True) def setup_module(): l.append("module") - @pytest.setup() + @pytest.fixture( autoactive=True) def setup_function(): l.append("function") @@ -2742,10 +2742,10 @@ pass class TestClass: - @pytest.setup(scope="class") + @pytest.fixture(scope="class", autoactive=True) def setup_class(self): l.append("class") - @pytest.setup() + @pytest.fixture( autoactive=True) def setup_method(self): l.append("method") def test_method(self): @@ -2764,7 +2764,7 @@ import pytest l = [] - @pytest.setup() + @pytest.fixture( autoactive=True) def fix1(): l.append(1) @pytest.fixture() @@ -2786,7 +2786,7 @@ @pytest.fixture() def farg(arg1): pass - @pytest.setup() + @pytest.fixture( autoactive=True) def sarg(tmpdir): pass def test_function(request, farg): diff -r 2af0387cac7c59e8596b00daec3945f7ac094ac8 -r d56f9a2762a6a15e2024b419f0aed0e937bb7834 testing/test_unittest.py --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -497,10 +497,10 @@ import unittest import pytest class MyTestCase(unittest.TestCase): - @pytest.setup(scope="class") + @pytest.fixture(scope="class", autoactive=True) def perclass(self, request): request.cls.hello = "world" - @pytest.setup(scope="function") + @pytest.fixture(scope="function", autoactive=True) def perfunction(self, request): request.instance.funcname = request.function.__name__ Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 8 11:05:01 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 08 Oct 2012 09:05:01 -0000 Subject: [py-svn] commit/py: hpk42: addresses issue22 - improve heuristic for finding statement ranges Message-ID: <20121008090501.12827.46916@bitbucket01.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/605099c880b9/ changeset: 605099c880b9 user: hpk42 date: 2012-10-08 11:04:53 summary: addresses issue22 - improve heuristic for finding statement ranges affected #: 2 files diff -r f60c108f78fd973eb08e1933ff2130f339ea0dcf -r 605099c880b9cdefca0fbdb1f6eaced561fe04a2 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ - terminalwriter: default to encode to UTF8 if no encoding is defined on the output stream +- issue22: improve heuristic for finding the statementrange in exceptions Changes between 1.4.8 and 1.4.9 ================================================== diff -r f60c108f78fd973eb08e1933ff2130f339ea0dcf -r 605099c880b9cdefca0fbdb1f6eaced561fe04a2 py/_code/source.py --- a/py/_code/source.py +++ b/py/_code/source.py @@ -117,7 +117,6 @@ # 1. find the start of the statement from codeop import compile_command - end = None for start in range(lineno, -1, -1): if assertion: line = self.lines[start] @@ -127,8 +126,9 @@ if "assert" not in line and "raise" not in line: continue trylines = self.lines[start:lineno+1] - # quick hack to indent the source and get it as a string in one go - trylines.insert(0, 'if xxx:') + # 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: @@ -141,9 +141,7 @@ trysource = self[start:end] if trysource.isparseable(): return start, end - if end is None: - raise IndexError("no valid source range around line %d " % (lineno,)) - return start, end + raise IndexError("no valid source range around line %d " % (lineno,)) def getblockend(self, lineno): # XXX Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 8 11:22:45 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 08 Oct 2012 09:22:45 -0000 Subject: [py-svn] commit/pytest: hpk42: remove support for @pytest.fixture on classes, to be reserved for future use: Message-ID: <20121008092245.12827.25@bitbucket01.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/c03437aae5a1/ changeset: c03437aae5a1 user: hpk42 date: 2012-10-08 11:22:31 summary: remove support for @pytest.fixture on classes, to be reserved for future use: Fixture-classes could offer setup/teardown/addoption/configure methods and provide higher level support. Preliminary allowing it to work on classes may make introducing it harder. affected #: 3 files diff -r d56f9a2762a6a15e2024b419f0aed0e937bb7834 -r c03437aae5a16ebb7e2c67e89812acd72157dac9 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -17,6 +17,8 @@ self.autoactive = autoactive def __call__(self, function): + if inspect.isclass(function): + raise ValueError("class fixtures not supported (may be in the future)") function._pytestfixturefunction = self return function @@ -43,7 +45,7 @@ can see it. If False (the default) then an explicit reference is needed to activate the fixture. """ - if hasattr(scope, "__call__") and params is None and autoactive == False: + if py.builtin.callable(scope) and params is None and autoactive == False: # direct decoration return FixtureFunctionMarker("function", params, autoactive)(scope) else: @@ -1563,10 +1565,8 @@ def getfuncargnames(function, startindex=None): # XXX merge with main.py's varnames - if inspect.isclass(function): - function = function.__init__ - startindex = 1 - elif startindex is None: + #assert not inspect.isclass(function) + if startindex is None: startindex = inspect.ismethod(function) and 1 or 0 argnames = inspect.getargs(py.code.getrawcode(function))[0] defaults = getattr(function, 'func_defaults', diff -r d56f9a2762a6a15e2024b419f0aed0e937bb7834 -r c03437aae5a16ebb7e2c67e89812acd72157dac9 doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -569,8 +569,7 @@ import pytest - @pytest.fixture(scope="module") - class db: + class DB: def __init__(self): self.intransaction = [] def begin(self, name): @@ -578,6 +577,10 @@ def rollback(self): self.intransaction.pop() + @pytest.fixture(scope="module") + def db(): + return DB() + class TestClass: @pytest.fixture(autoactive=True) def transact(self, request, db): @@ -590,9 +593,9 @@ def test_method2(self, db): assert db.intransaction == ["test_method2"] -The class-level ``transact`` fixture is marked with *autoactive=true* which implies -that all test methods in the class will use this fixture without a need to -specify it. +The class-level ``transact`` fixture is marked with *autoactive=true* +which implies that all test methods in the class will use this fixture +without a need to specify it. If we run it, we get two passing tests:: @@ -614,11 +617,10 @@ a global fixture should always quickly determine if it should do any work and avoid expensive imports or computation otherwise. -Note that the above ``transact`` fixture may very well be something that -you want to make available in your project but which requires an explicit using -reference to have it activated. The canonical way to do that is to put -the transact definition into a conftest.py file without using -``autoactive``:: +Note that the above ``transact`` fixture may very well be a fixture that +you want to make available in your project without having it generally +active. The canonical way to do that is to put the transact definition +into a conftest.py file without using ``autoactive``:: # content of conftest.py @pytest.fixture() @@ -626,15 +628,16 @@ db.begin() request.addfinalizer(db.rollback) -and then have a TestClass using it by declaring the need:: +and then e.g. have a TestClass using it by declaring the need:: @pytest.mark.usefixtures("transact") class TestClass: def test_method1(self): ... -While all test methods in this TestClass will use the transaction -fixture, other test classes or function will not do so without a marker or funcarg. +All test methods in this TestClass will use the transaction fixture while +other test classes or functions will not do so unless they also add +a ``transact`` reference. controlled visibility of fixture functions ---------------------------------------------------- diff -r d56f9a2762a6a15e2024b419f0aed0e937bb7834 -r c03437aae5a16ebb7e2c67e89812acd72157dac9 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -547,10 +547,6 @@ if sys.version_info < (3,0): assert funcargs.getfuncargnames(A.f) == ['arg1'] - class A: - def __init__(self, x): - pass - assert funcargs.getfuncargnames(A) == ["x"] class TestFillFixtures: def test_fillfuncargs_exposed(self): @@ -1803,7 +1799,7 @@ *fixture*'missing'*not found* """) - def test_factory_setup_as_classes(self, testdir): + def test_factory_setup_as_classes_fails(self, testdir): testdir.makepyfile(""" import pytest class arg1: @@ -1811,17 +1807,10 @@ self.x = 1 arg1 = pytest.fixture()(arg1) - class MySetup: - def __init__(self, request, arg1): - request.instance.arg1 = arg1 - pytest.fixture(autoactive=True)(MySetup) - - class TestClass: - def test_method(self): - assert self.arg1.x == 1 """) reprec = testdir.inline_run() - reprec.assertoutcome(passed=1) + l = reprec.getfailedcollections() + assert len(l) == 1 def test_request_can_be_overridden(self, testdir): testdir.makepyfile(""" Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 8 13:21:56 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 08 Oct 2012 11:21:56 -0000 Subject: [py-svn] commit/pytest: hpk42: somewhat simplify pytest_generate_tests example Message-ID: <20121008112156.4453.22034@bitbucket02.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/025c93a014e7/ changeset: 025c93a014e7 user: hpk42 date: 2012-10-08 13:19:31 summary: somewhat simplify pytest_generate_tests example affected #: 3 files diff -r c03437aae5a16ebb7e2c67e89812acd72157dac9 -r 025c93a014e79f83e9f06091656df3c1652e9bef doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -262,7 +262,7 @@ name of the particular fixture. -**Fixture functions have limited visilibity** which depends on where they +Fixture functions have limited visilibity which depends on where they are defined. If they are defined on a test class, only its test methods may use it. A fixture defined in a module can only be used from that test module. A fixture defined in a conftest.py file @@ -270,11 +270,12 @@ Lastly, plugins can define fixtures which are available across all projects. +.. _`fixture-parametrize`: Parametrizing a session-shared fixture ----------------------------------------------------------------- -**Fixture functions can be parametrized** in which case they will be called +Fixture functions can be parametrized in which case they will be called multiple times, each time executing the set of dependent tests, i. e. the tests that depend on this fixture. Test functions do usually not need to be aware of their re-running. Fixture parametrization helps to diff -r c03437aae5a16ebb7e2c67e89812acd72157dac9 -r 025c93a014e79f83e9f06091656df3c1652e9bef doc/en/index.txt --- a/doc/en/index.txt +++ b/doc/en/index.txt @@ -7,7 +7,6 @@ - runs on Posix/Windows, Python 2.4-3.3, PyPy and Jython-2.5.1 - :ref:`comprehensive online ` and `PDF documentation `_ - - continuously `tested on many Python interpreters `_ - used in :ref:`many projects and organisations `, in test suites ranging from 10 to 10s of thousands of tests - comes with many :ref:`tested examples ` diff -r c03437aae5a16ebb7e2c67e89812acd72157dac9 -r 025c93a014e79f83e9f06091656df3c1652e9bef doc/en/parametrize.txt --- a/doc/en/parametrize.txt +++ b/doc/en/parametrize.txt @@ -7,14 +7,17 @@ Parametrizing fixtures and test functions ========================================================================== -While the :ref:`@pytest.fixture` decorator allows to define parametrization -at the level of fixture functions, there are two more parametrizations: +pytest supports test parametrization in several well-integrated ways: -* `@pytest.mark.parametrize`_ to provide multiple argument/fixture sets +- :ref:`@pytest.fixture` allows to define :ref:`parametrization + at the level of fixture functions `. + +* `@pytest.mark.parametrize`_ allows to define parametrization at the + function or class level, provides multiple argument/fixture sets for a particular test function or class. -* `pytest_generate_tests`_ to implement your own custom parametrization - scheme or extensions. +* `pytest_generate_tests`_ enables implementing your own custom + dynamic parametrization scheme or extensions. .. _`@pytest.mark.parametrize`: @@ -27,7 +30,8 @@ The builtin ``pytest.mark.parametrize`` decorator enables parametrization of arguments for a test function. Here is a typical example -of a test function that wants check for expected output given a certain input:: +of a test function that implements checking that a certain input leads +to an expected output:: # content of test_expectation.py import pytest @@ -74,89 +78,61 @@ Basic ``pytest_generate_tests`` example --------------------------------------------- -.. XXX +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 for every test function. Through the special `metafunc` +object you can inspect the requesting test context and, most importantly, +you can call ``metafunc.parametrize()`` to pass in parametrizatin. +For example, let's say we want to execute a test that takes some string +input and we want to pass that in with a command line option +``--stringinput=value``. Let's first write a simple test accepting +a ``stringinput`` fixture function argument:: - > line 598 "Basic ``pytest_generate_tests`` example" - I think this is - > not a very basic example! I think it is copied from parametrize.txt - > page, where it might make more sense. Here is what I would consider a - > basic example. - > - > # code - > def isSquare(n): - > n = n ** 0.5 - > return int(n) == n - > - > # test file - > def pytest_generate_tests(metafunc): - > squares = [1, 4, 9, 16, 25, 36, 49] - > for n in range(1, 50): - > expected = n in squares - > if metafunc.function.__name__ == 'test_isSquare': - > metafunc.addcall(id=n, funcargs=dict(n=n, - > expected=expected)) - > - > - > def test_isSquare(n, expected): - > assert isSquare(n) == expected + # content of test_strings.py - -.. XXX - consider adding more examples, also mixed (factory-parametrized/test-function-parametrized, see mail from Brianna) - -The ``pytest_generate_tests`` hook is typically used if you want -to go beyond what ``@pytest.mark.parametrize`` offers. For example, -let's say we want to execute a test with different computation -parameters and the parameter range shall be determined by a command -line argument. Let's first write a simple (do-nothing) computation test:: - - # content of test_compute.py - - def test_compute(param1): - assert param1 < 4 + def test_valid_string(stringinput): + assert stringinput.isalpha() Now we add a ``conftest.py`` file containing the addition of a -command line option and the generation of tests depending on -that option:: +command line option and the parametrization of our test function:: # content of conftest.py def pytest_addoption(parser): - parser.addoption("--all", action="store_true", - help="run all combinations") + parser.addoption("--stringinput", action="append", + help="list of stringinputs to pass to test functions") def pytest_generate_tests(metafunc): - if 'param1' in metafunc.fixturenames: - if metafunc.config.option.all: - end = 5 - else: - end = 2 - metafunc.parametrize("param1", range(end)) + if 'stringinput' in metafunc.fixturenames: + metafunc.parametrize("stringinput", + metafunc.config.option.stringinput) -This means that we only run two tests if no option is passed:: +If we now pass two stringinput values, our test will run twice:: - $ py.test -q test_compute.py + $ py.test -q --stringinput="hello" --stringinput="world" test_strings.py .. -And we run five tests if we add the ``--all`` option:: +Let's run with a stringinput that will lead to an error:: - $ py.test -q --all test_compute.py - ....F + $ py.test -q --stringinput="!" test_strings.py + F ================================= FAILURES ================================= - _____________________________ test_compute[4] ______________________________ + ___________________________ test_valid_string[!] ___________________________ - param1 = 4 + stringinput = '!' - def test_compute(param1): - > assert param1 < 4 - E assert 4 < 4 + def test_valid_string(stringinput): + > assert stringinput.isalpha() + E assert () + E + where = '!'.isalpha - test_compute.py:3: AssertionError + test_strings.py:3: AssertionError -As expected when running the full range of ``param1`` values -we'll get an error on the last one. +As expected our test function will error out. -You might want to look at :ref:`more parametrization examples `. - +For further examples, you might want to look at :ref:`more +parametrization examples `. .. _`metafunc object`: Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 8 13:48:54 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 08 Oct 2012 11:48:54 -0000 Subject: [py-svn] commit/pytest: hol...@merlinux.eu: Fix test for windows Message-ID: <20121008114854.21949.21077@bitbucket16.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/6fda63e086f5/ changeset: 6fda63e086f5 user: hol... at merlinux.eu date: 2012-10-08 13:42:31 summary: Fix test for windows affected #: 1 file diff -r 025c93a014e79f83e9f06091656df3c1652e9bef -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd testing/test_junitxml.py --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -383,16 +383,18 @@ for i in valid: assert chr(i) == bin_xml_escape(unichr(i)).uniobj -def test_logxml_path_expansion(): +def test_logxml_path_expansion(tmpdir, monkeypatch): from _pytest.junitxml import LogXML home_tilde = os.path.normpath(os.path.expanduser('~/test.xml')) - # this is here for when $HOME is not set correct - home_var = os.path.normpath(os.path.expandvars('$HOME/test.xml')) xml_tilde = LogXML('~/test.xml', None) assert xml_tilde.logfile == home_tilde + # this is here for when $HOME is not set correct + monkeypatch.setenv("HOME", tmpdir) + home_var = os.path.normpath(os.path.expandvars('$HOME/test.xml')) + xml_var = LogXML('$HOME/test.xml', None) assert xml_var.logfile == home_var Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Tue Oct 9 14:36:20 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 09 Oct 2012 12:36:20 -0000 Subject: [py-svn] commit/pytest: hpk42: try to move docs to a more releasable state, also refine Message-ID: <20121009123620.3931.66262@bitbucket03.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/3bf8975d2add/ changeset: 3bf8975d2add user: hpk42 date: 2012-10-09 14:35:17 summary: try to move docs to a more releasable state, also refine release announce and a few coding bits affected #: 26 files diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 _pytest/core.py --- a/_pytest/core.py +++ b/_pytest/core.py @@ -463,9 +463,13 @@ pluginmanager=_pluginmanager, args=args) def main(args=None, plugins=None): - """ returned exit code integer, after an in-process testing run - with the given command line arguments, preloading an optional list - of passed in plugin objects. """ + """ return exit code, after performing an in-process test run. + + :arg args: list of command line arguments. + + :arg plugins: list of plugin objects to be auto-registered during + initialization. + """ config = _prepareconfig(args, plugins) exitstatus = config.hook.pytest_cmdline_main(config=config) return exitstatus diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -637,27 +637,26 @@ """ Add new invocations to the underlying test function using the list of argvalues for the given argnames. Parametrization is performed during the collection phase. If you need to setup expensive resources - you may pass indirect=True and implement a fixture function which can - perform the expensive setup just before a test is actually run. + see about setting indirect=True to do it rather at test setup time. :arg argnames: an argument name or a list of argument names :arg argvalues: a list of values for the argname or a list of tuples of values for the list of argument names. - :arg indirect: if True each argvalue corresponding to an argument will - be passed as request.param to its respective fixture function so - that it can perform more expensive setups during the setup phase of - a test rather than at collection time. + :arg indirect: if True each argvalue corresponding to an argname will + be passed as request.param to its respective argname fixture + function so that it can perform more expensive setups during the + setup phase of a test rather than at collection time. :arg ids: list of string ids each corresponding to the argvalues so that they are part of the test id. If no ids are provided they will be generated automatically from the argvalues. - :arg scope: if specified: denotes the scope of the parameters. - The scope is used for sorting tests by parameters. It will - also override any fixture-function defined scope, allowing - to set a dynamic scope from test context and configuration. + :arg scope: if specified it denotes the scope of the parameters. + The scope is used for grouping tests by parameter instances. + It will also override any fixture-function defined scope, allowing + to set a dynamic scope using test context or configuration. """ if not isinstance(argnames, (tuple, list)): argnames = (argnames,) @@ -762,7 +761,7 @@ pluginname = plugin.__name__ for name, factory in available: loc = getlocation(factory, curdir) - if verbose: + if verbose > 0: funcargspec = "%s -- %s" %(name, loc,) else: funcargspec = name @@ -989,24 +988,25 @@ class FixtureRequest(FuncargnamesCompatAttr): - """ A request for fixtures from a test or fixture function. + """ A request for a fixture from a test or fixture function. - A request object gives access to attributes of the requesting - test context. It has an optional ``param`` attribute in case - of parametrization. + A request object gives access to the requesting test context + and has an optional ``param`` attribute in case + the fixture is parametrized indirectly. """ def __init__(self, pyfuncitem): self._pyfuncitem = pyfuncitem if hasattr(pyfuncitem, '_requestparam'): self.param = pyfuncitem._requestparam + #: fixture for which this request is being performed + self.fixturename = None #: Scope string, one of "function", "cls", "module", "session" self.scope = "function" self.getparent = pyfuncitem.getparent self._funcargs = self._pyfuncitem.funcargs.copy() self._arg2fixturedeflist = {} self._fixturemanager = pyfuncitem.session._fixturemanager - self._currentarg = None self._parentid = pyfuncitem.parent.nodeid self.fixturenames, self._arg2fixturedeflist_ = \ self._fixturemanager.getfixtureclosure( @@ -1087,7 +1087,7 @@ if scope != "function" and hasattr(self, "param"): # parametrized resources are sorted by param # so we rather store finalizers per (argname, param) - colitem = (self._currentarg, self.param) + colitem = (self.fixturename, self.param) else: colitem = self._getscopeitem(scope) self._pyfuncitem.session._setupstate.addfinalizer( @@ -1131,7 +1131,7 @@ """ if not hasattr(self.config, '_setupcache'): self.config._setupcache = {} # XXX weakref? - cachekey = (self._currentarg, self._getscopeitem(scope), extrakey) + cachekey = (self.fixturename, self._getscopeitem(scope), extrakey) cache = self.config._setupcache try: val = cache[cachekey] @@ -1152,17 +1152,13 @@ return val def getfuncargvalue(self, argname): - """ Retrieve a fixture function argument by name for this test - function invocation. This allows one function argument factory - to call another function argument factory. If there are two - funcarg factories for the same test function argument the first - factory may use ``getfuncargvalue`` to call the second one and - do something additional with the resource. + """ Dynamically retrieve a named fixture function argument. - **Note**, however, that starting with pytest-2.3 it is usually - easier and better to directly use the needed funcarg in the - factory function signature. This will also work seemlessly - with parametrization and the new resource setup optimizations. + As of pytest-2.3, it is easier and usually better to access other + fixture values by stating it as an input argument in the fixture + function. If you only can decide about using another fixture at test + setup time, you may use this function to retrieve it inside a fixture + function body. """ try: return self._funcargs[argname] @@ -1187,12 +1183,12 @@ if fixturedef.active: return fixturedef.cached_result - # prepare request _currentarg and param attributes before + # prepare request fixturename and param attributes before # calling into fixture function argname = fixturedef.argname node = self._pyfuncitem mp = monkeypatch() - mp.setattr(self, '_currentarg', argname) + mp.setattr(self, 'fixturename', argname) try: param = node.callspec.getparam(argname) except (AttributeError, ValueError): @@ -1246,7 +1242,6 @@ p, lineno, factory.__name__, args)) return lines - def _getscopeitem(self, scope): if scope == "function": return self._pyfuncitem @@ -1262,7 +1257,7 @@ raise ValueError("unknown finalization scope %r" %(scope,)) def __repr__(self): - return "" %(self._pyfuncitem) + return "" %(self.node) class ScopeMismatchError(Exception): """ A fixture function tries to use a different fixture function which diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 _pytest/runner.py --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -409,7 +409,9 @@ def fail(msg="", pytrace=True): """ explicitely fail an currently-executing test with the given Message. - if @pytrace is not True the msg represents the full failure information. + + :arg pytrace: if false the msg represents the full failure information + and no python traceback will be reported. """ __tracebackhide__ = True raise Failed(msg=msg, pytrace=pytrace) diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/assert.txt --- a/doc/en/assert.txt +++ b/doc/en/assert.txt @@ -24,7 +24,7 @@ $ py.test test_assert1.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 1 items test_assert1.py F @@ -55,6 +55,8 @@ See :ref:`assert-details` for more information on assertion introspection. +.. _`assertraises`: + Assertions about expected exceptions ------------------------------------------ @@ -106,7 +108,7 @@ $ py.test test_assert2.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 1 items test_assert2.py F diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/builtin.txt --- a/doc/en/builtin.txt +++ b/doc/en/builtin.txt @@ -1,35 +1,78 @@ .. _`pytest helpers`: -Pytest builtin helpers +Pytest API and builtin fixtures ================================================ -builtin pytest.* functions and helping objects ------------------------------------------------------ +This is a list of ``pytest.*`` API functions and fixtures. -You can always use an interactive Python prompt and type:: +For information on plugin hooks and objects, see :ref:`plugins`. + +For information on the ``pytest.mark`` mechanism, see :ref:`mark`. + +For the below objects, you can also interactively ask for help, e.g. by +typing on the Python interactive prompt something like:: import pytest help(pytest) -to get an overview on the globally available helpers. +.. currentmodule:: pytest -.. automodule:: pytest +Invoking pytest interactively +--------------------------------------------------- + +.. autofunction:: main + +More examples at :ref:`pytest.main-usage` + + +Helpers for assertions about Exceptions/Warnings +-------------------------------------------------------- + +.. autofunction:: raises + +Examples at :ref:`assertraises`. + +.. autofunction:: deprecated_call + +Raising a specific test outcome +-------------------------------------- + +You can use the following functions in your test, fixture or setup +functions to force a certain test outcome. Note that most often +you can rather use declarative marks, see :ref:`skipping`. + +.. autofunction:: fail +.. autofunction:: skip +.. autofunction:: importorskip +.. autofunction:: xfail +.. autofunction:: exit + +fixtures and requests +----------------------------------------------------- + +To mark a fixture function: + +.. autofunction:: fixture + +Tutorial at :ref:`fixtures`. + +The ``request`` object that can be used from fixture functions. + +.. autoclass:: _pytest.python.FixtureRequest() :members: + .. _builtinfixtures: .. _builtinfuncargs: -Builtin resources / function arguments ------------------------------------------------------ +Builtin fixtures/function arguments +----------------------------------------- You can ask for available builtin or project-custom -:ref:`function arguments ` by typing:: +:ref:`fixtures ` by typing:: - $ py.test --fixtures - =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 - collected 0 items + $ py.test -q --fixtures capsys enables capturing of writes to sys.stdout/sys.stderr and makes captured output available via ``capsys.readouterr()`` method calls @@ -75,4 +118,3 @@ on warning categories. - ============================= in 0.00 seconds ============================= diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/capture.txt --- a/doc/en/capture.txt +++ b/doc/en/capture.txt @@ -64,7 +64,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 2 items test_module.py .F @@ -78,7 +78,7 @@ test_module.py:9: AssertionError ----------------------------- Captured stdout ------------------------------ - setting up + setting up ==================== 1 failed, 1 passed in 0.01 seconds ==================== Accessing captured output from a test function diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.3.0.dev19" +version = release = "2.3.0.dev20" import sys, os @@ -71,7 +71,11 @@ # directories to ignore when looking for source files. exclude_patterns = ['links.inc', '_build', 'naming20.txt', 'test/*', "old_*", - 'example/attic.txt', + '*attic*', + '*/attic*', + 'funcargs.txt', + 'setup.txt', + 'example/remoteinterp.txt', ] diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/contents.txt --- a/doc/en/contents.txt +++ b/doc/en/contents.txt @@ -24,8 +24,4 @@ :hidden: changelog.txt - funcargs - example/resources_attic - setup.txt - example/remoteinterp.txt diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/doctest.txt --- a/doc/en/doctest.txt +++ b/doc/en/doctest.txt @@ -44,7 +44,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 1 items mymodule.py . diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -26,19 +26,19 @@ $ py.test -v -m webtest =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:3: test_send_http PASSED =================== 1 tests deselected by "-m 'webtest'" =================== - ================== 1 passed, 1 deselected in 0.01 seconds ================== + ================== 1 passed, 1 deselected in 0.00 seconds ================== Or the inverse, running all tests except the webtest ones:: $ py.test -v -m "not webtest" =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:6: test_something_quick PASSED @@ -143,7 +143,7 @@ $ py.test -k send_http # running with the above defined examples =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 4 items test_server.py . @@ -155,7 +155,7 @@ $ py.test -k-send_http =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 4 items test_mark_classlevel.py .. @@ -168,7 +168,7 @@ $ py.test -kTestClass =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 4 items test_mark_classlevel.py .. @@ -221,18 +221,18 @@ $ py.test -E stage2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 1 items test_someenv.py s - ======================== 1 skipped in 0.00 seconds ========================= + ======================== 1 skipped in 0.01 seconds ========================= and here is one that specifies exactly the environment needed:: $ py.test -E stage1 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 1 items test_someenv.py . @@ -347,12 +347,12 @@ $ py.test -rs # this option reports skip reasons =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 4 items test_plat.py s.s. ========================= short test summary info ========================== - SKIP [2] /tmp/doc-exec-257/conftest.py:12: cannot run on platform linux2 + SKIP [2] /tmp/doc-exec-189/conftest.py:12: cannot run on platform linux2 =================== 2 passed, 2 skipped in 0.01 seconds ==================== @@ -360,7 +360,7 @@ $ py.test -m linux2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 4 items test_plat.py . diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/example/nonpython.txt --- a/doc/en/example/nonpython.txt +++ b/doc/en/example/nonpython.txt @@ -27,7 +27,7 @@ nonpython $ py.test test_simple.yml =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 0 items / 1 errors ================================== ERRORS ================================== @@ -54,7 +54,7 @@ nonpython $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 0 items / 1 errors ================================== ERRORS ================================== @@ -69,7 +69,7 @@ nonpython $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 0 items / 1 errors ================================== ERRORS ================================== diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/example/parametrize.txt --- a/doc/en/example/parametrize.txt +++ b/doc/en/example/parametrize.txt @@ -151,7 +151,7 @@ $ py.test test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 4 items test_scenarios.py .... @@ -163,7 +163,7 @@ $ py.test --collectonly test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 4 items @@ -225,7 +225,7 @@ $ py.test test_backends.py --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 2 items @@ -240,7 +240,7 @@ ================================= FAILURES ================================= _________________________ test_db_initialized[d2] __________________________ - db = + db = def test_db_initialized(db): # a dummy test @@ -295,7 +295,7 @@ ================================= FAILURES ================================= ________________________ TestClass.test_equals[1-2] ________________________ - self = , a = 1, b = 2 + self = , a = 1, b = 2 def test_equals(self, a, b): > assert a == b diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/example/pythoncollection.txt --- a/doc/en/example/pythoncollection.txt +++ b/doc/en/example/pythoncollection.txt @@ -43,7 +43,7 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 2 items @@ -82,7 +82,7 @@ . $ py.test --collectonly pythoncollection.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 3 items @@ -135,7 +135,7 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 1 items diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/example/remoteinterp.txt --- a/doc/en/example/remoteinterp.txt +++ b/doc/en/example/remoteinterp.txt @@ -63,8 +63,8 @@ $ py.test test_remoteinterpreter.py Traceback (most recent call last): File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in - load_entry_point('pytest==2.3.0.dev19', 'console_scripts', 'py.test')() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 469, in main + load_entry_point('pytest==2.3.0.dev20', 'console_scripts', 'py.test')() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 473, in main config = _prepareconfig(args, plugins) File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig pluginmanager=_pluginmanager, args=args) @@ -98,7 +98,7 @@ self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport __import__(modname) - File "/tmp/doc-exec-261/conftest.py", line 2, in + File "/tmp/doc-exec-193/conftest.py", line 2, in from remoteinterpreter import RemoteInterpreter ImportError: No module named remoteinterpreter @@ -150,8 +150,8 @@ $ py.test -q test_ssh.py -rs Traceback (most recent call last): File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in - load_entry_point('pytest==2.3.0.dev19', 'console_scripts', 'py.test')() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 469, in main + load_entry_point('pytest==2.3.0.dev20', 'console_scripts', 'py.test')() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 473, in main config = _prepareconfig(args, plugins) File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig pluginmanager=_pluginmanager, args=args) @@ -185,7 +185,7 @@ self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport __import__(modname) - File "/tmp/doc-exec-261/conftest.py", line 2, in + File "/tmp/doc-exec-193/conftest.py", line 2, in from myapp import MyApp ImportError: No module named myapp diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/example/reportingdemo.txt --- a/doc/en/example/reportingdemo.txt +++ b/doc/en/example/reportingdemo.txt @@ -13,7 +13,7 @@ assertion $ py.test failure_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 39 items failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF @@ -30,7 +30,7 @@ failure_demo.py:15: AssertionError _________________________ TestFailing.test_simple __________________________ - self = + self = def test_simple(self): def f(): @@ -40,13 +40,13 @@ > assert f() == g() E assert 42 == 43 - E + where 42 = () - E + and 43 = () + E + where 42 = () + E + and 43 = () failure_demo.py:28: AssertionError ____________________ TestFailing.test_simple_multiline _____________________ - self = + self = def test_simple_multiline(self): otherfunc_multi( @@ -66,19 +66,19 @@ failure_demo.py:11: AssertionError ___________________________ TestFailing.test_not ___________________________ - self = + self = def test_not(self): def f(): return 42 > assert not f() E assert not 42 - E + where 42 = () + E + where 42 = () failure_demo.py:38: AssertionError _________________ TestSpecialisedExplanations.test_eq_text _________________ - self = + self = def test_eq_text(self): > assert 'spam' == 'eggs' @@ -89,7 +89,7 @@ failure_demo.py:42: AssertionError _____________ TestSpecialisedExplanations.test_eq_similar_text _____________ - self = + self = def test_eq_similar_text(self): > assert 'foo 1 bar' == 'foo 2 bar' @@ -102,7 +102,7 @@ failure_demo.py:45: AssertionError ____________ TestSpecialisedExplanations.test_eq_multiline_text ____________ - self = + self = def test_eq_multiline_text(self): > assert 'foo\nspam\nbar' == 'foo\neggs\nbar' @@ -115,7 +115,7 @@ failure_demo.py:48: AssertionError ______________ TestSpecialisedExplanations.test_eq_long_text _______________ - self = + self = def test_eq_long_text(self): a = '1'*100 + 'a' + '2'*100 @@ -132,7 +132,7 @@ failure_demo.py:53: AssertionError _________ TestSpecialisedExplanations.test_eq_long_text_multiline __________ - self = + self = def test_eq_long_text_multiline(self): a = '1\n'*100 + 'a' + '2\n'*100 @@ -156,7 +156,7 @@ failure_demo.py:58: AssertionError _________________ TestSpecialisedExplanations.test_eq_list _________________ - self = + self = def test_eq_list(self): > assert [0, 1, 2] == [0, 1, 3] @@ -166,7 +166,7 @@ failure_demo.py:61: AssertionError ______________ TestSpecialisedExplanations.test_eq_list_long _______________ - self = + self = def test_eq_list_long(self): a = [0]*100 + [1] + [3]*100 @@ -178,7 +178,7 @@ failure_demo.py:66: AssertionError _________________ TestSpecialisedExplanations.test_eq_dict _________________ - self = + self = def test_eq_dict(self): > assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2} @@ -191,7 +191,7 @@ failure_demo.py:69: AssertionError _________________ TestSpecialisedExplanations.test_eq_set __________________ - self = + self = def test_eq_set(self): > assert set([0, 10, 11, 12]) == set([0, 20, 21]) @@ -207,7 +207,7 @@ failure_demo.py:72: AssertionError _____________ TestSpecialisedExplanations.test_eq_longer_list ______________ - self = + self = def test_eq_longer_list(self): > assert [1,2] == [1,2,3] @@ -217,7 +217,7 @@ failure_demo.py:75: AssertionError _________________ TestSpecialisedExplanations.test_in_list _________________ - self = + self = def test_in_list(self): > assert 1 in [0, 2, 3, 4, 5] @@ -226,7 +226,7 @@ failure_demo.py:78: AssertionError __________ TestSpecialisedExplanations.test_not_in_text_multiline __________ - self = + self = def test_not_in_text_multiline(self): text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail' @@ -244,7 +244,7 @@ failure_demo.py:82: AssertionError ___________ TestSpecialisedExplanations.test_not_in_text_single ____________ - self = + self = def test_not_in_text_single(self): text = 'single foo line' @@ -257,7 +257,7 @@ failure_demo.py:86: AssertionError _________ TestSpecialisedExplanations.test_not_in_text_single_long _________ - self = + self = def test_not_in_text_single_long(self): text = 'head ' * 50 + 'foo ' + 'tail ' * 20 @@ -270,7 +270,7 @@ failure_demo.py:90: AssertionError ______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______ - self = + self = def test_not_in_text_single_long_term(self): text = 'head ' * 50 + 'f'*70 + 'tail ' * 20 @@ -289,7 +289,7 @@ i = Foo() > assert i.b == 2 E assert 1 == 2 - E + where 1 = .b + E + where 1 = .b failure_demo.py:101: AssertionError _________________________ test_attribute_instance __________________________ @@ -299,8 +299,8 @@ b = 1 > assert Foo().b == 2 E assert 1 == 2 - E + where 1 = .b - E + where = () + E + where 1 = .b + E + where = () failure_demo.py:107: AssertionError __________________________ test_attribute_failure __________________________ @@ -316,7 +316,7 @@ failure_demo.py:116: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - self = + self = def _get_b(self): > raise Exception('Failed to get attrib') @@ -332,15 +332,15 @@ b = 2 > assert Foo().b == Bar().b E assert 1 == 2 - E + where 1 = .b - E + where = () - E + and 2 = .b - E + where = () + E + where 1 = .b + E + where = () + E + and 2 = .b + E + where = () failure_demo.py:124: AssertionError __________________________ TestRaises.test_raises __________________________ - self = + self = def test_raises(self): s = 'qwe' @@ -352,10 +352,10 @@ > int(s) E ValueError: invalid literal for int() with base 10: 'qwe' - <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:838>:1: ValueError + <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:833>:1: ValueError ______________________ TestRaises.test_raises_doesnt _______________________ - self = + self = def test_raises_doesnt(self): > raises(IOError, "int('3')") @@ -364,7 +364,7 @@ failure_demo.py:136: Failed __________________________ TestRaises.test_raise ___________________________ - self = + self = def test_raise(self): > raise ValueError("demo error") @@ -373,7 +373,7 @@ failure_demo.py:139: ValueError ________________________ TestRaises.test_tupleerror ________________________ - self = + self = def test_tupleerror(self): > a,b = [1] @@ -382,7 +382,7 @@ failure_demo.py:142: ValueError ______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______ - self = + self = def test_reinterpret_fails_with_print_for_the_fun_of_it(self): l = [1,2,3] @@ -395,7 +395,7 @@ l is [1, 2, 3] ________________________ TestRaises.test_some_error ________________________ - self = + self = def test_some_error(self): > if namenotexi: @@ -423,7 +423,7 @@ <2-codegen 'abc-123' /home/hpk/p/pytest/doc/en/example/assertion/failure_demo.py:162>:2: AssertionError ____________________ TestMoreErrors.test_complex_error _____________________ - self = + self = def test_complex_error(self): def f(): @@ -452,7 +452,7 @@ failure_demo.py:5: AssertionError ___________________ TestMoreErrors.test_z1_unpack_error ____________________ - self = + self = def test_z1_unpack_error(self): l = [] @@ -462,7 +462,7 @@ failure_demo.py:179: ValueError ____________________ TestMoreErrors.test_z2_type_error _____________________ - self = + self = def test_z2_type_error(self): l = 3 @@ -472,19 +472,19 @@ failure_demo.py:183: TypeError ______________________ TestMoreErrors.test_startswith ______________________ - self = + self = def test_startswith(self): s = "123" g = "456" > assert s.startswith(g) - E assert ('456') - E + where = '123'.startswith + E assert ('456') + E + where = '123'.startswith failure_demo.py:188: AssertionError __________________ TestMoreErrors.test_startswith_nested ___________________ - self = + self = def test_startswith_nested(self): def f(): @@ -492,15 +492,15 @@ def g(): return "456" > assert f().startswith(g()) - E assert ('456') - E + where = '123'.startswith - E + where '123' = () - E + and '456' = () + E assert ('456') + E + where = '123'.startswith + E + where '123' = () + E + and '456' = () failure_demo.py:195: AssertionError _____________________ TestMoreErrors.test_global_func ______________________ - self = + self = def test_global_func(self): > assert isinstance(globf(42), float) @@ -510,18 +510,18 @@ failure_demo.py:198: AssertionError _______________________ TestMoreErrors.test_instance _______________________ - self = + self = def test_instance(self): self.x = 6*7 > assert self.x != 42 E assert 42 != 42 - E + where 42 = .x + E + where 42 = .x failure_demo.py:202: AssertionError _______________________ TestMoreErrors.test_compare ________________________ - self = + self = def test_compare(self): > assert globf(10) < 5 @@ -531,7 +531,7 @@ failure_demo.py:205: AssertionError _____________________ TestMoreErrors.test_try_finally ______________________ - self = + self = def test_try_finally(self): x = 1 @@ -540,4 +540,4 @@ E assert 1 == 0 failure_demo.py:210: AssertionError - ======================== 39 failed in 0.15 seconds ========================= + ======================== 39 failed in 0.16 seconds ========================= diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/example/simple.txt --- a/doc/en/example/simple.txt +++ b/doc/en/example/simple.txt @@ -106,7 +106,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 0 items ============================= in 0.00 seconds ============================= @@ -150,12 +150,12 @@ $ py.test -rs # "-rs" means report details on the little 's' =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 2 items test_module.py .s ========================= short test summary info ========================== - SKIP [1] /tmp/doc-exec-264/conftest.py:9: need --runslow option to run + SKIP [1] /tmp/doc-exec-195/conftest.py:9: need --runslow option to run =================== 1 passed, 1 skipped in 0.01 seconds ==================== @@ -163,7 +163,7 @@ $ py.test --runslow =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 2 items test_module.py .. @@ -253,7 +253,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 project deps: mylib-1.1 collected 0 items @@ -276,7 +276,7 @@ $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 -- /home/hpk/p/pytest/.tox/regen/bin/python info1: did you know that ... did you? collecting ... collected 0 items @@ -287,7 +287,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 0 items ============================= in 0.00 seconds ============================= @@ -319,7 +319,7 @@ $ py.test --durations=3 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 3 items test_some_are_slow.py ... diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -5,12 +5,12 @@ pytest fixtures: modular, explicit, scalable ======================================================== -.. versionadded:: 2.0, 2.3 +.. currentmodule:: _pytest.python + +.. versionadded:: 2.0/2.3 .. _`xUnit`: http://en.wikipedia.org/wiki/XUnit .. _`general purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software -.. _`django`: https://www.djangoproject.com/ -.. _`pytest-django`: https://pypi.python.org/pytest-django .. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection#Definition pytest allows to create and use test fixtures in a modular and flexible @@ -31,69 +31,73 @@ .. _`funcargs`: .. _`funcarg mechanism`: .. _`fixture function`: +.. _`@pytest.fixture`: +.. _`pytest.fixture`: -Fixtures as Function arguments +Fixtures as Function arguments (funcargs) ----------------------------------------- Test functions can receive fixture objects by naming them as an input -argument. For each argument name, a matching fixture -function will provide a fixture object. This mechanism was already -introduced with pytest-2.0 and is also called the *funcarg mechanism*. -It allows test functions to easily receive and work against specific -pre-initialized application objects without having to care about the -details of setup/cleanup procedures. It's a prime example of -`dependency injection`_ where fixture functions take the role of the -*injector* and test functions are the *consumers* of fixture objects. +argument. For each argument name, a fixture function with that name provides +a fixture object. Fixture functions are registered by marking them with +:py:func:`pytest.fixture`. Let's look at a simple self-contained test +module containing a fixture and a test function using it:: -Let's look at a simple self-contained test module containing -a fixture and a test function using it:: - - # content of ./test_fixturefuncarg.py + # content of ./test_smtpsimple.py import pytest @pytest.fixture - def myfuncarg(): - return 42 + def smtp(): + import smtplib + return smtplib.SMTP("merlinux.eu") - def test_function(myfuncarg): - assert myfuncarg == 17 # will fail + def test_ehlo(smtp): + response, msg = smtp.ehlo() + assert response == 250 + assert "merlinux" in msg + assert 0 # for demo purposes -Here, the ``test_function`` needs the ``myfuncarg`` fixture value. pytest -will discover and call the ``@pytest.fixture`` marked ``myfuncarg`` +Here, the ``test_function`` needs the ``smtp`` fixture value. pytest +will discover and call the ``@pytest.fixture`` marked ``smtp`` fixture function. Running the test looks like this:: - $ py.test test_fixturefuncarg.py + $ py.test test_smtpsimple.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout collected 1 items - test_fixturefuncarg.py F + test_smtpsimple.py F ================================= FAILURES ================================= - ______________________________ test_function _______________________________ + ________________________________ test_ehlo _________________________________ - myfuncarg = 42 + smtp = - def test_function(myfuncarg): - > assert myfuncarg == 17 # will fail - E assert 42 == 17 + def test_ehlo(smtp): + response, msg = smtp.ehlo() + assert response == 250 + assert "merlinux" in msg + > assert 0 # for demo purposes + E assert 0 - test_fixturefuncarg.py:8: AssertionError - ========================= 1 failed in 0.01 seconds ========================= + test_smtpsimple.py:12: AssertionError + ========================= 1 failed in 0.11 seconds ========================= -This shows that the test function was called with a ``myfuncarg`` -value of ``42`` and the assert fails as expected. Here is -how py.test comes to call the test function this way: +In the failure traceback we see that the test function was called with a +``smtp`` argument, the ``smtplib.SMTP()`` instance created by the fixture +function. The test function fails on our deliberate ``assert 0``. Here is +an exact protocol of how py.test comes to call the test function this way: -1. pytest :ref:`finds ` the ``test_function`` because +1. pytest :ref:`finds ` the ``test_ehlo`` because of the ``test_`` prefix. The test function needs a function argument - named ``myfuncarg``. A matching fixture function is discovered by - looking for a fixture function named ``myfuncarg``. + named ``smtp``. A matching fixture function is discovered by + looking for a fixture-marked function named ``smtp``. -2. ``myfuncarg()`` is called to create a value ``42``. +2. ``smtp()`` is called to create an instance. -3. ``test_function(42)`` is now called and results in the above - reported exception because of the assertion mismatch. +3. ``test_ehlo()`` is called and fails in the last + line of the test function. Note that if you misspell a function argument or want to use one that isn't available, you'll see an error @@ -113,21 +117,36 @@ but is not advertised as the primary means of declaring fixture functions. +Funcargs a prime example of dependency injection +--------------------------------------------------- -Creating and using a session-shared fixture +When injecting fixtures to test functions, pytest-2.0 introduced the +term "funcargs" or "funcarg mechanism" which continues to be present +also in pytest-2.3 docs. It now refers to the specific case of injecting +fixture values to test functions by arguments. With pytest-2.3 there are +more possibilities to use fixtures but "funcargs" probably will remain +as the main way of dealing with fixtures. + +As the following examples show in more detail, funcargs allow test +functions to easily receive and work against specific pre-initialized +application objects without having to care about import/setup/cleanup +details. It's a prime example of `dependency injection`_ where fixture +functions take the role of the *injector* and test functions are the +*consumers* of fixture objects. + +Working with a session-shared fixture ----------------------------------------------------------------- -By means of a "scope" declaration, a fixture function will -only be invoked once per the specified scope. This allows to reduce the -number of expensive application object setups and thus helps to speed up -test runs. Typical examples are the setup of test databases or -establishing required subprocesses or network connections. - .. regendoc:wipe -Here is a simple example of a fixture function creating a shared -``smtplib.SMTP`` connection fixture which test functions from -any test module inside the directory of a ``conftest.py`` file may use:: +Fixtures requiring network access depend on connectivity and are +usually time-expensive to create. Extending the previous example, we +can add a ``scope='session'`` parameter to the ``smtp`` fixture function +to cause it to only be invoked once per session. Multiple test +functions will thus only involve a once-per-test session creation of the +fixture instance. Also, we now split the creation of the fixture into a +``conftest.py`` file which will automatically loaded when running a test +module:: # content of conftest.py import pytest @@ -137,9 +156,9 @@ def smtp(): return smtplib.SMTP("merlinux.eu") -The name of the fixture is ``smtp`` and you can access its result by +The name of the fixture again is ``smtp`` and you can access its result by listing the name ``smtp`` as an input parameter in any test or setup -function:: +function (in or below the directory where ``conftest.py`` is located):: # content of test_module.py @@ -157,24 +176,29 @@ We deliberately insert failing ``assert 0`` statements in order to inspect what is going on and can now run the tests:: - $ py.test -q test_module.py - FF + $ py.test test_module.py + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout + collected 2 items + + test_module.py FF + ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() assert response[0] == 250 - assert "merlinux" in response[1] - > assert 0 # for demo purposes - E assert 0 + > assert "python" in response[1] + E assert 'python' in 'hq.merlinux.eu\nPIPELINING\nSIZE 25000000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN' - test_module.py:6: AssertionError + test_module.py:5: AssertionError ________________________________ test_noop _________________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -183,22 +207,25 @@ E assert 0 test_module.py:11: AssertionError + ========================= 2 failed in 0.12 seconds ========================= -you see the two ``assert 0`` failing and can also see that -the same (session-scoped) object was passed into the two test functions -because pytest shows the incoming arguments in the traceback. +You see the two ``assert 0`` failing and more importantly you can also see +that the same (session-scoped) ``smtp`` object was passed into the two +test functions because pytest shows the incoming argument values in the +traceback. As a result, the two test functions using ``smtp`` run as +quick as a single one because they reuse the same instance. Fixtures can interact with the requesting test context ------------------------------------------------------------- -By using the special :ref:`request` object, fixture functions can introspect -the function, class or module for which they are invoked and can -optionally register cleanup functions which are called when the last -test finished execution. +By accepting the special :py:class:`request ` argument, +fixture functions can introspect the function, class or module for which +they are invoked and can optionally register finalizing cleanup +functions which are called when the last test finished execution. Further extending the previous ``smtp`` fixture example, let's try to -read the server URL from the module namespace, use module-scoping and +read the server URL from the module namespace, also use module-caching and register a finalizer that closes the smtp connection after the last test finished execution:: @@ -221,7 +248,7 @@ $ py.test -s -q --tb=no FF - finalizing + finalizing We see that the ``smtp`` instance is finalized after the two tests using it tests executed. If we had specified ``scope='function'`` @@ -233,7 +260,7 @@ # content of test_anothersmtp.py - smtpserver = "mail.python.org" # will be read by smtp fixture + smtpserver = "merlinux.eu" # will be read by smtp fixture def test_showhelo(smtp): assert 0, smtp.helo() @@ -246,29 +273,9 @@ ______________________________ test_showhelo _______________________________ test_anothersmtp.py:5: in test_showhelo > assert 0, smtp.helo() - E AssertionError: (250, 'mail.python.org') + E AssertionError: (250, 'hq.merlinux.eu') -**Test classes, modules or whole projects can make use of -one or more fixtures**. All required fixture functions will execute -before a test from the specifying context executes. As You can use this -to make tests operate from a pre-initialized directory or with -certain environment variables or with pre-configured global application -settings. - -For example, the Django_ project requires database -initialization to be able to import from and use its model objects. -For that, the `pytest-django`_ plugin provides fixtures which your -project can then easily depend or extend on, simply by referencing the -name of the particular fixture. - - -Fixture functions have limited visilibity which depends on where they -are defined. If they are defined on a test class, only its test methods -may use it. A fixture defined in a module can only be used -from that test module. A fixture defined in a conftest.py file -can only be used by the tests below the directory of that file. -Lastly, plugins can define fixtures which are available across all -projects. +.. _`request`: :ref:pyclass:`_pytest.python.FixtureRequest` .. _`fixture-parametrize`: @@ -311,19 +318,18 @@ ================================= FAILURES ================================= __________________________ test_ehlo[merlinux.eu] __________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() assert response[0] == 250 - assert "merlinux" in response[1] - > assert 0 # for demo purposes - E assert 0 + > assert "python" in response[1] + E assert 'python' in 'hq.merlinux.eu\nPIPELINING\nSIZE 25000000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN' - test_module.py:6: AssertionError + test_module.py:5: AssertionError __________________________ test_noop[merlinux.eu] __________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -334,18 +340,19 @@ test_module.py:11: AssertionError ________________________ test_ehlo[mail.python.org] ________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() assert response[0] == 250 - > assert "merlinux" in response[1] - E assert 'merlinux' in 'mail.python.org\nSIZE 10240000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN' + assert "python" in response[1] + > assert 0 # for demo purposes + E assert 0 - test_module.py:5: AssertionError + test_module.py:6: AssertionError ________________________ test_noop[mail.python.org] ________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -355,15 +362,15 @@ test_module.py:11: AssertionError -We now get four failures because we are running the two tests twice with -different ``smtp`` fixture instances. Note that with the -``mail.python.org`` connection the second test fails in ``test_ehlo`` -because it expects a specific server string. +We see that our two test functions each ran twice, against the different +``smtp`` instances. Note also, that with the ``mail.python.org`` +connection the second test fails in ``test_ehlo`` because a +different server string is expected than what arrived. .. _`interdependent fixtures`: -Using fixtures from a fixture function +Modularity: using fixtures from a fixture function ---------------------------------------------------------- You can not only use fixtures in test functions but fixture functions @@ -393,13 +400,15 @@ $ py.test -v test_appsetup.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 -- /home/hpk/venv/1/bin/python + cachedir: /tmp/doc-exec-135/.cache + plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout collecting ... collected 2 items test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED - ========================= 2 passed in 0.09 seconds ========================= + ========================= 2 passed in 0.08 seconds ========================= Due to the parametrization of ``smtp`` the test will run twice with two different ``App`` instances and respective smtp servers. There is no @@ -425,8 +434,8 @@ this eases testing of applications which create and use global state. The following example uses two parametrized funcargs, one of which is -scoped on a per-module basis, and all the functions perform ``print`` call s -to show the flow of calls:: +scoped on a per-module basis, and all the functions perform ``print`` calls +to show the setup/teardown flow:: # content of test_module.py import pytest @@ -455,7 +464,9 @@ $ py.test -v -s test_module.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 -- /home/hpk/venv/1/bin/python + cachedir: /tmp/doc-exec-135/.cache + plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout collecting ... collected 8 items test_module.py:16: test_0[1] PASSED @@ -467,7 +478,7 @@ test_module.py:20: test_2[1-mod2] PASSED test_module.py:20: test_2[2-mod2] PASSED - ========================= 8 passed in 0.01 seconds ========================= + ========================= 8 passed in 0.02 seconds ========================= test0 1 test0 2 create mod1 @@ -494,9 +505,10 @@ .. regendoc:wipe Sometimes test functions do not directly need access to a fixture object. -For example, tests may require to operate with an -empty directory as the current working directory. Here is how you can -can use the standard `tempfile `_ and pytest fixtures to +For example, tests may require to operate with an empty directory as the +current working directory but otherwise do not care for the concrete +directory. Here is how you can can use the standard `tempfile +`_ and pytest fixtures to achieve it. We separate the creation of the fixture into a conftest.py file:: @@ -552,6 +564,7 @@ [pytest] usefixtures = cleandir + .. _`autoactive fixtures`: autoactive fixtures (xUnit setup on steroids) @@ -596,14 +609,15 @@ The class-level ``transact`` fixture is marked with *autoactive=true* which implies that all test methods in the class will use this fixture -without a need to specify it. +without a need to state it in the test function signature or with a +class-level ``usefixtures`` decorator. If we run it, we get two passing tests:: $ py.test -q .. -And here is how autoactive fixtures work in other scopes: +Here is how autoactive fixtures work in other scopes: - if an autoactive fixture is defined in a test module, all its test functions automatically use it. @@ -621,7 +635,7 @@ Note that the above ``transact`` fixture may very well be a fixture that you want to make available in your project without having it generally active. The canonical way to do that is to put the transact definition -into a conftest.py file without using ``autoactive``:: +into a conftest.py file **without** using ``autoactive``:: # content of conftest.py @pytest.fixture() @@ -637,10 +651,10 @@ ... All test methods in this TestClass will use the transaction fixture while -other test classes or functions will not do so unless they also add -a ``transact`` reference. +other test classes or functions in the module will not use it unless +they also add a ``transact`` reference. -controlled visibility of fixture functions +Shifting (visibility of) fixture functions ---------------------------------------------------- If during implementing your tests you realize that you @@ -650,44 +664,3 @@ fixtures functions starts at test classes, then test modules, then ``conftest.py`` files and finally builtin and third party plugins. -.. currentmodule:: _pytest.python - -.. _`@pytest.fixture`: -.. _`pytest.fixture`: - -``@pytest.fixture``: marking a fixture function --------------------------------------------------------------- - -The ``@pytest.fixture`` marker allows to - -* mark a function as a factory for fixtures, useable by test and other - fixture functions - -* declare a scope which determines the level of caching, i.e. how often - the factory will be called. Valid scopes are ``session``, ``module``, - ``class`` and ``function``. - -* define a list of parameters in order to run dependent tests multiple - times with different fixtures - -.. _`request`: - -``request``: interacting with test invocation context --------------------------------------------------------------- - -The ``request`` object may be received by fixture functions -and provides methods to: - -* to inspect attributes of the requesting test context, such as - ``function``, ``cls``, ``module``, ``session`` and the pytest - ``config`` object. A request object passed to a parametrized factory - will also carry a ``request.param`` object (A parametrized factory and - all of its dependent tests will be called with each of the factory-specified - ``params``). - -* to add finalizers/teardowns to be invoked when the last - test of the requesting test context executes - -.. autoclass:: _pytest.python.FixtureRequest() - :members: - diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/funcarg_compare.txt --- a/doc/en/funcarg_compare.txt +++ b/doc/en/funcarg_compare.txt @@ -102,8 +102,8 @@ This new way of parametrizing funcarg factories should in many cases allow to re-use already written factories because effectively -``request.param`` are already the parametrization attribute for test -functions/classes were parametrized via +``request.param`` was already used when test functions/classes were +parametrized via :py:func:`~_pytest.python.Metafunc.parametrize(indirect=True)` calls. Of course it's perfectly fine to combine parametrization and scoping:: @@ -193,18 +193,19 @@ Conclusion and compatibility notes --------------------------------------------------------- -**Fixtures** were originally introduced to pytest-2.0. In pytest-2.3 -the mechanism was extended and refined: +**funcargs** were originally introduced to pytest-2.0. In pytest-2.3 +the mechanism was extended and refined and is now described as +fixtures: * previously funcarg factories were specified with a special ``pytest_funcarg__NAME`` prefix instead of using the ``@pytest.fixture`` decorator. -* Factories received a :ref:`request ` object which managed caching through +* Factories received a ``request`` object which managed caching through ``request.cached_setup()`` calls and allowed using other funcargs via ``request.getfuncargvalue()`` calls. These intricate APIs made it hard to do proper parametrization and implement resource caching. The - new ``@pytest.fixture`` decorator allows to simply declare the scope + new :py:func:`pytest.fixture`` decorator allows to declare the scope and let pytest figure things out for you. * if you used parametrization and funcarg factories which made use of diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/getting-started.txt --- a/doc/en/getting-started.txt +++ b/doc/en/getting-started.txt @@ -22,7 +22,7 @@ To check your installation has installed the correct version:: $ py.test --version - This is py.test version 2.3.0.dev19, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc + This is py.test version 2.3.0.dev20, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc If you get an error checkout :ref:`installation issues`. @@ -44,7 +44,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 1 items test_sample.py F @@ -121,7 +121,7 @@ ================================= FAILURES ================================= ____________________________ TestClass.test_two ____________________________ - self = + self = def test_two(self): x = "hello" @@ -156,7 +156,7 @@ ================================= FAILURES ================================= _____________________________ test_needsfiles ______________________________ - tmpdir = local('/tmp/pytest-168/test_needsfiles0') + tmpdir = local('/tmp/pytest-46/test_needsfiles0') def test_needsfiles(tmpdir): print tmpdir @@ -165,7 +165,7 @@ test_tmpdir.py:3: AssertionError ----------------------------- Captured stdout ------------------------------ - /tmp/pytest-168/test_needsfiles0 + /tmp/pytest-46/test_needsfiles0 Before the test runs, a unique-per-test-invocation temporary directory was created. More info at :ref:`tmpdir handling`. diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/index.txt --- a/doc/en/index.txt +++ b/doc/en/index.txt @@ -1,48 +1,48 @@ -pytest: makes you a better programmer +pytest: makes you write better programs ============================================= -- **a mature full-featured Python testing tool** +**a mature full-featured Python testing tool** - runs on Posix/Windows, Python 2.4-3.3, PyPy and Jython-2.5.1 - :ref:`comprehensive online ` and `PDF documentation `_ - used in :ref:`many projects and organisations `, in test suites ranging from 10 to 10s of thousands of tests - comes with many :ref:`tested examples ` - - supports :ref:`good integration practises ` -- **provides no-boilerplate testing** +**provides easy no-boilerplate testing** - makes it :ref:`easy to get started `, - - refined :ref:`usage options ` - :ref:`assert with the assert statement` - helpful :ref:`traceback and failing assertion reporting ` - allows :ref:`print debugging ` and :ref:`the capturing of standard output during test execution ` - supports :pep:`8` compliant coding styles in tests + - refined :ref:`usage options ` -- **supports functional testing and complex test setups** +**scales from simple unit to complex functional testing** - (new in 2.3) :ref:`modular parametrizeable fixtures ` - - :ref:`marking and test selection ` + - :ref:`mark` - :ref:`parametrized test functions ` - - advanced :ref:`skip and xfail` + - :ref:`skipping` - can :ref:`distribute tests to multiple CPUs ` through :ref:`xdist plugin ` - can :ref:`continuously re-run failing tests ` - many :ref:`builtin helpers ` - flexible :ref:`Python test discovery` -- **integrates many common testing methods** +**integrates many common testing methods**: - can run many ``nose``, ``unittest.py`` and ``doctest.py`` style tests, including running testcases made for Django and trial + - supports :ref:`good integration practises ` - supports extended :ref:`xUnit style setup ` - supports domain-specific :ref:`non-python tests` - supports the generation of testing coverage reports - `Javascript unit- and functional testing`_ -- **extensive plugin and customization system** +**extensive plugin and customization system**: - all collection, reporting, running aspects are delegated to hook functions - customizations can be per-directory, per-project or per PyPI released plugins diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/parametrize.txt --- a/doc/en/parametrize.txt +++ b/doc/en/parametrize.txt @@ -9,7 +9,7 @@ pytest supports test parametrization in several well-integrated ways: -- :ref:`@pytest.fixture` allows to define :ref:`parametrization +- :py:func:`pytest.fixture` allows to define :ref:`parametrization at the level of fixture functions `. * `@pytest.mark.parametrize`_ allows to define parametrization at the @@ -21,6 +21,7 @@ .. _`@pytest.mark.parametrize`: + ``@pytest.mark.parametrize``: parametrizing test functions --------------------------------------------------------------------- @@ -43,12 +44,17 @@ def test_eval(input, expected): assert eval(input) == expected -The ``@parametrize`` decorator defines three different argument sets for the -two ``(input, output)`` arguments of ``test_eval`` function so the latter -will be run three times:: +Here, the ``@parametrize`` decorator defines three different argument +sets for the two ``(input, output)`` arguments of the ``test_eval`` function +which will thus run three times:: - $ py.test -q - ..F + $ py.test + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + collected 3 items + + test_expectation.py ..F + ================================= FAILURES ================================= ____________________________ test_eval[6*9-42] _____________________________ @@ -65,11 +71,12 @@ E + where 54 = eval('6*9') test_expectation.py:8: AssertionError + ==================== 1 failed, 2 passed in 0.01 seconds ==================== As expected only one pair of input/output values fails the simple test function. -As usual you can see the ``input`` and ``output`` values in the traceback. +And as usual with test function arguments, you can see the ``input`` and ``output`` values in the traceback. -Note that there are various ways how you can mark groups of functions, +Note that there ways how you can mark a class or a module, see :ref:`mark`. @@ -81,13 +88,14 @@ 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 for every test function. Through the special `metafunc` -object you can inspect the requesting test context and, most importantly, -you can call ``metafunc.parametrize()`` to pass in parametrizatin. -For example, let's say we want to execute a test that takes some string -input and we want to pass that in with a command line option -``--stringinput=value``. Let's first write a simple test accepting -a ``stringinput`` fixture function argument:: +which is called when collecting a test function. Through the passed in +`metafunc` object you can inspect the requesting test context and, most +importantly, you can call ``metafunc.parametrize()`` to cause +parametrization. + +For example, let's say we want to run a test taking string inputs which +we want to set via a new py.test command line option. Let's first write +a simple test accepting a ``stringinput`` fixture function argument:: # content of test_strings.py @@ -100,7 +108,7 @@ # content of conftest.py def pytest_addoption(parser): - parser.addoption("--stringinput", action="append", + parser.addoption("--stringinput", action="append", default=[], help="list of stringinputs to pass to test functions") def pytest_generate_tests(metafunc): @@ -113,7 +121,7 @@ $ py.test -q --stringinput="hello" --stringinput="world" test_strings.py .. -Let's run with a stringinput that will lead to an error:: +Let's also run with a stringinput that will lead to a failing test:: $ py.test -q --stringinput="!" test_strings.py F @@ -124,12 +132,21 @@ def test_valid_string(stringinput): > assert stringinput.isalpha() - E assert () - E + where = '!'.isalpha + E assert () + E + where = '!'.isalpha test_strings.py:3: AssertionError -As expected our test function will error out. +As expected our test function fails. + +If you don't specify a stringinput it will be skipped because +``metafunc.parametrize()`` will be called with an empty parameter +listlist:: + + $ py.test -q -rs test_strings.py + s + ========================= short test summary info ========================== + SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:949: got empty parameter set, function test_valid_string at /tmp/doc-exec-161/test_strings.py:1 For further examples, you might want to look at :ref:`more parametrization examples `. diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/plugins.txt --- a/doc/en/plugins.txt +++ b/doc/en/plugins.txt @@ -3,7 +3,7 @@ Working with plugins and conftest files ============================================= -py.test implements all aspects of configuration, collection, running and reporting by calling `well specified hooks`_. Virtually any Python module can be registered as a plugin. It can implement any number of hook functions (usually two or three) which all have a ``pytest_`` prefix, making hook functions easy to distinguish and find. There are three basic locations types: +py.test implements all aspects of configuration, collection, running and reporting by calling `well specified hooks`_. Virtually any Python module can be registered as a plugin. It can implement any number of hook functions (usually two or three) which all have a ``pytest_`` prefix, making hook functions easy to distinguish and find. There are three basic location types: * `builtin plugins`_: loaded from py.test's own ``pytest/plugin`` directory. * `external plugins`_: modules discovered through `setuptools entry points`_ @@ -64,14 +64,28 @@ pip uninstall pytest-NAME If a plugin is installed, py.test automatically finds and integrates it, -there is no need to activate it. Here is a list of known plugins: +there is no need to activate it. Here is a initial list of known plugins: + +.. _`django`: https://www.djangoproject.com/ + +* `pytest-django `: write tests + for `django`_ apps, using pytest integration. * `pytest-capturelog `_: to capture and assert about messages from the logging module * `pytest-xdist `_: - to distribute tests to CPUs and remote hosts, looponfailing mode, - see also :ref:`xdist` + to distribute tests to CPUs and remote hosts, to run in boxed + mode which allows to survive segmentation faults, to run in + looponfailing mode, automatically re-running failing tests + on file changes, see also :ref:`xdist` + +* `pytest-timeout `_: + to timeout tests based on function marks or global definitions. + +* `pytest-cache `_: + to interactively re-run failing tests and help other plugins to + store test run information across invocations. * `pytest-cov `_: coverage reporting, compatible with distributed testing @@ -81,7 +95,6 @@ * `oejskit `_: a plugin to run javascript unittests in life browsers - (**version 0.8.9 not compatible with pytest-2.0**) You may discover more plugins through a `pytest- pypi.python.org search`_. @@ -98,8 +111,8 @@ you can copy from: * a custom collection example plugin: :ref:`yaml plugin` -* around 20 `builtin plugins`_ which comprise py.test's own functionality -* around 10 `external plugins`_ providing additional features +* around 20 `builtin plugins`_ which provide py.test's own functionality +* many `external plugins`_ providing additional features All of these plugins implement the documented `well specified hooks`_ to extend and add functionality. diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/skipping.txt --- a/doc/en/skipping.txt +++ b/doc/en/skipping.txt @@ -1,5 +1,7 @@ .. _`skip and xfail`: +.. _skipping: + Skip and xfail: dealing with tests that can not succeed ===================================================================== @@ -130,7 +132,7 @@ example $ py.test -rx xfail_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 6 items xfail_demo.py xxxxxx diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/tmpdir.txt --- a/doc/en/tmpdir.txt +++ b/doc/en/tmpdir.txt @@ -28,7 +28,7 @@ $ py.test test_tmpdir.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 1 items test_tmpdir.py F @@ -36,7 +36,7 @@ ================================= FAILURES ================================= _____________________________ test_create_file _____________________________ - tmpdir = local('/tmp/pytest-169/test_create_file0') + tmpdir = local('/tmp/pytest-47/test_create_file0') def test_create_file(tmpdir): p = tmpdir.mkdir("sub").join("hello.txt") @@ -47,7 +47,7 @@ E assert 0 test_tmpdir.py:7: AssertionError - ========================= 1 failed in 0.02 seconds ========================= + ========================= 1 failed in 0.01 seconds ========================= .. _`base temporary directory`: diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/unittest.txt --- a/doc/en/unittest.txt +++ b/doc/en/unittest.txt @@ -4,7 +4,7 @@ Support for unittest.TestCase / Integration of fixtures ===================================================================== -py.test has limited support for running Python `unittest.py style`_ tests. +py.test has support for running Python `unittest.py style`_ tests. It will automatically collect ``unittest.TestCase`` subclasses and their ``test`` methods in test files. It will invoke ``setUp/tearDown`` methods but also perform py.test's standard ways @@ -24,7 +24,7 @@ $ py.test test_unittest.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev19 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 collected 1 items test_unittest.py F diff -r 6fda63e086f55c3f60e617b89670cdd0c6fa1bcd -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 doc/en/usage.txt --- a/doc/en/usage.txt +++ b/doc/en/usage.txt @@ -150,6 +150,8 @@ Currently only pasting to the http://bpaste.net service is implemented. +.. _`pytest.main-usage`: + Calling pytest from Python code ---------------------------------------------------- Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Tue Oct 9 16:49:35 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 09 Oct 2012 14:49:35 -0000 Subject: [py-svn] commit/pytest: hpk42: fix output of --fixtures for @pytest.fixture defined functions. Message-ID: <20121009144935.16118.19071@bitbucket25.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/ad850d485937/ changeset: ad850d485937 user: hpk42 date: 2012-10-09 16:49:04 summary: fix output of --fixtures for @pytest.fixture defined functions. affected #: 5 files diff -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 -r ad850d4859371966d2ce35a1854b9eb392c796b9 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev20' +__version__ = '2.3.0.dev21' diff -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 -r ad850d4859371966d2ce35a1854b9eb392c796b9 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -742,37 +742,45 @@ def _showfixtures_main(config, session): session.perform_collect() + curdir = py.path.local() if session.items: - plugins = session.items[0].getplugins() + nodeid = session.items[0].nodeid else: - plugins = session.getplugins() - curdir = py.path.local() + part = session._initialparts[0] + nodeid = "::".join(map(str, [curdir.bestrelpath(part[0])] + part[1:])) + tw = py.io.TerminalWriter() verbose = config.getvalue("verbose") - argprefix = session._fixturemanager._argprefix - for plugin in plugins: - available = [] - for name, factory in vars(plugin).items(): - if name.startswith(argprefix): - name = name[len(argprefix):] - if name not in available: - available.append([name, factory]) - if available: - pluginname = plugin.__name__ - for name, factory in available: - loc = getlocation(factory, curdir) - if verbose > 0: - funcargspec = "%s -- %s" %(name, loc,) - else: - funcargspec = name - tw.line(funcargspec, green=True) - doc = factory.__doc__ or "" - if doc: - for line in doc.split("\n"): - tw.line(" " + line.strip()) - else: - tw.line(" %s: no docstring available" %(loc,), - red=True) + + fm = session._fixturemanager + + available = [] + for argname in fm.arg2fixturedeflist: + fixturedeflist = fm.getfixturedeflist(argname, nodeid) + assert fixturedeflist is not None + if not fixturedeflist: + continue + fixturedef = fixturedeflist[-1] + loc = getlocation(fixturedef.func, curdir) + available.append((len(fixturedef.baseid), + curdir.bestrelpath(loc), + fixturedef.argname, fixturedef)) + + available.sort() + for baseid, bestrel, argname, fixturedef in available: + if verbose > 0: + funcargspec = "%s -- %s" %(name, loc,) + else: + funcargspec = argname # "%s %s" %(baseid, argname) + tw.line(funcargspec, green=True) + loc = getlocation(fixturedef.func, curdir) + doc = fixturedef.func.__doc__ or "" + if doc: + for line in doc.split("\n"): + tw.line(" " + line.strip()) + else: + tw.line(" %s: no docstring available" %(loc,), + red=True) def getlocation(function, curdir): import inspect diff -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 -r ad850d4859371966d2ce35a1854b9eb392c796b9 doc/en/Makefile --- a/doc/en/Makefile +++ b/doc/en/Makefile @@ -43,7 +43,7 @@ rsync -avz _build/html/ pytest.org:/www/pytest.org/dev installpdf: latexpdf - @scp $(BUILDDIR)/latex/pytest.pdf pytest.org:/www/pytest.org/latest + @scp $(BUILDDIR)/latex/pytest.pdf pytest.org:/www/pytest.org/dev installall: clean install installpdf @echo "done" diff -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 -r ad850d4859371966d2ce35a1854b9eb392c796b9 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev20', + version='2.3.0.dev21', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 3bf8975d2addd78ba3db63a17da0c5bcc4ed3093 -r ad850d4859371966d2ce35a1854b9eb392c796b9 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1515,13 +1515,50 @@ pass """ -def test_show_funcarg(testdir): - result = testdir.runpytest("--fixtures") - result.stdout.fnmatch_lines([ - "*tmpdir*", - "*temporary directory*", - ] - ) +class TestShowFixtures: + def test_show_fixtures(self, testdir): + result = testdir.runpytest("--fixtures") + result.stdout.fnmatch_lines([ + "*tmpdir*", + "*temporary directory*", + ] + ) + + def test_show_fixtures_testmodule(self, testdir): + p = testdir.makepyfile(''' + import pytest + @pytest.fixture + def arg1(): + """ hello world """ + ''') + result = testdir.runpytest("--fixtures", p) + result.stdout.fnmatch_lines(""" + *tmpdir* + *arg1* + *hello world* + """) + + @pytest.mark.parametrize("testmod", [True, False]) + def test_show_fixtures_conftest(self, testdir, testmod): + testdir.makeconftest(''' + import pytest + @pytest.fixture + def arg1(): + """ hello world """ + ''') + if testmod: + testdir.makepyfile(""" + def test_hello(): + pass + """) + result = testdir.runpytest("--fixtures") + result.stdout.fnmatch_lines(""" + *tmpdir* + *arg1* + *hello world* + """) + + class TestRaises: def test_raises(self): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 11 13:05:24 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 11 Oct 2012 11:05:24 -0000 Subject: [py-svn] commit/pytest: hpk42: make tmpdir fixture always return a realpath()ed tmpdir and make a note Message-ID: <20121011110524.16863.57049@bitbucket22.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/4ff9c2bc50bd/ changeset: 4ff9c2bc50bd user: hpk42 date: 2012-10-11 13:05:16 summary: make tmpdir fixture always return a realpath()ed tmpdir and make a note about it in the changed test. Currently, i don't see a reason why this is a bad idea (tm) affected #: 4 files diff -r ad850d4859371966d2ce35a1854b9eb392c796b9 -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev21' +__version__ = '2.3.0.dev22' diff -r ad850d4859371966d2ce35a1854b9eb392c796b9 -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c _pytest/tmpdir.py --- a/_pytest/tmpdir.py +++ b/_pytest/tmpdir.py @@ -40,7 +40,7 @@ basetemp.mkdir() else: basetemp = py.path.local.make_numbered_dir(prefix='pytest-') - self._basetemp = t = basetemp + self._basetemp = t = basetemp.realpath() self.trace("new basetemp", t) return t diff -r ad850d4859371966d2ce35a1854b9eb392c796b9 -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev21', + version='2.3.0.dev22', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r ad850d4859371966d2ce35a1854b9eb392c796b9 -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c testing/test_tmpdir.py --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -72,14 +72,21 @@ assert mytemp.join('hello').check() @pytest.mark.skipif("not hasattr(py.path.local, 'mksymlinkto')") -def test_tmpdir_keeps_symlinks(testdir): +def test_tmpdir_always_is_realpath(testdir): + # the reason why tmpdir should be a realpath is that + # when you cd to it and do "os.getcwd()" you will anyway + # get the realpath. Using the symlinked path can thus + # easily result in path-inequality + # XXX if that proves to be a problem, consider using + # os.environ["PWD"] realtemp = testdir.tmpdir.mkdir("myrealtemp") linktemp = testdir.tmpdir.join("symlinktemp") linktemp.mksymlinkto(realtemp) p = testdir.makepyfile(""" def test_1(tmpdir): import os - assert os.path.realpath(str(tmpdir)) != str(tmpdir) + assert os.path.realpath(str(tmpdir)) == str(tmpdir) """) result = testdir.runpytest("-s", p, '--basetemp=%s/bt' % linktemp) assert not result.ret + Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Oct 12 14:53:14 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 12 Oct 2012 12:53:14 -0000 Subject: [py-svn] commit/pytest: hpk42: improve docs further, refine unittest docs, rename ``autoactive`` to ``autouse`` Message-ID: <20121012125314.4077.91152@bitbucket05.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/0cf1ef1efbea/ changeset: 0cf1ef1efbea user: hpk42 date: 2012-10-12 14:52:36 summary: improve docs further, refine unittest docs, rename ``autoactive`` to ``autouse`` to better match ``@pytest.mark.usefixtures`` naming. affected #: 17 files diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev22' +__version__ = '2.3.0.dev23' diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -11,10 +11,10 @@ cutdir = py.path.local(_pytest.__file__).dirpath() class FixtureFunctionMarker: - def __init__(self, scope, params, autoactive=False): + def __init__(self, scope, params, autouse=False): self.scope = scope self.params = params - self.autoactive = autoactive + self.autouse = autouse def __call__(self, function): if inspect.isclass(function): @@ -23,7 +23,7 @@ return function -def fixture(scope="function", params=None, autoactive=False): +def fixture(scope="function", params=None, autouse=False): """ (return a) decorator to mark a fixture factory function. This decorator can be used (with or or without parameters) to define @@ -41,15 +41,15 @@ invocations of the fixture function and all of the tests using it. - :arg autoactive: if True, the fixture func is activated for all tests that + :arg autouse: if True, the fixture func is activated for all tests that can see it. If False (the default) then an explicit reference is needed to activate the fixture. """ - if py.builtin.callable(scope) and params is None and autoactive == False: + if py.builtin.callable(scope) and params is None and autouse == False: # direct decoration - return FixtureFunctionMarker("function", params, autoactive)(scope) + return FixtureFunctionMarker("function", params, autouse)(scope) else: - return FixtureFunctionMarker(scope, params, autoactive=autoactive) + return FixtureFunctionMarker(scope, params, autouse=autouse) defaultfuncargprefixmarker = fixture() @@ -1490,7 +1490,7 @@ unittest=unittest) faclist = self.arg2fixturedeflist.setdefault(name, []) faclist.append(fixturedef) - if marker.autoactive: + if marker.autouse: self._autofixtures.append(name) try: del self._defaultfixtures diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d doc/en/assert.txt --- a/doc/en/assert.txt +++ b/doc/en/assert.txt @@ -4,6 +4,8 @@ .. _`assertfeedback`: .. _`assert with the assert statement`: +.. _`assert`: + Asserting with the ``assert`` statement --------------------------------------------------------- diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d doc/en/builtin.txt --- a/doc/en/builtin.txt +++ b/doc/en/builtin.txt @@ -42,18 +42,18 @@ functions to force a certain test outcome. Note that most often you can rather use declarative marks, see :ref:`skipping`. -.. autofunction:: fail -.. autofunction:: skip -.. autofunction:: importorskip -.. autofunction:: xfail -.. autofunction:: exit +.. autofunction:: _pytest.runner.fail +.. autofunction:: _pytest.runner.skip +.. autofunction:: _pytest.runner.importorskip +.. autofunction:: _pytest.skipping.xfail +.. autofunction:: _pytest.runner.exit fixtures and requests ----------------------------------------------------- To mark a fixture function: -.. autofunction:: fixture +.. autofunction:: _pytest.python.fixture Tutorial at :ref:`fixtures`. diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.3.0.dev20" +version = release = "2.3.0.dev22" import sys, os diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -2,7 +2,7 @@ .. _fixtures: .. _`fixture functions`: -pytest fixtures: modular, explicit, scalable +pytest fixtures: explicit, modular, scalable ======================================================== .. currentmodule:: _pytest.python @@ -13,20 +13,28 @@ .. _`general purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software .. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection#Definition -pytest allows to create and use test fixtures in a modular and flexible -manner, offering dramatic improvements over the classic xUnit style of -setup/teardown functions. The `general purpose of test fixtures`_ -is to provide a fixed baseline upon which tests can reliably -and repeatedly execute. With pytest, fixtures have names and can be -activated by referencing them from test functions, modules, classes or -whole projects. Fixtures are implemented by *fixture functions* which -have full access to the requesting test context and can use other -fixtures, allowing a modular and flexible approach to organising -and parametrizing fixtures for an application. Complemented by -pytest's generic :ref:`parametrize features `, pytest -fixtures help to write test suites that scale from simple to complex -with minimal effort. +The `general purpose of test fixtures`_ is to provide a fixed baseline +upon which tests can reliably and repeatedly execute. pytest-2.3 fixtures +offer dramatic improvements over the classic xUnit style of setup/teardown +functions: +* fixtures have explicit names and are activated by declaring their use + from test functions, modules, classes or whole projects. + +* fixtures are implemented in a modular manner, as each fixture name + triggers a *fixture function* which can itself easily use other + fixtures. + +* fixture management scales from simple unit to complex + functional testing, allowing to parametrize fixtures or tests according + to configuration and component options. + +In addition to this next-generation (tm) style of organising test fixtures +in Python, pytest continues to support :ref:`xunitsetup` which it +originally introduced in 2005. You can mix both styles, moving +incrementally from classic to new style, if you prefer. You can also +start out from existing :ref:`unittest.TestCase style ` +or :ref:`nose based ` projects. .. _`funcargs`: .. _`funcarg mechanism`: @@ -39,9 +47,10 @@ Test functions can receive fixture objects by naming them as an input argument. For each argument name, a fixture function with that name provides -a fixture object. Fixture functions are registered by marking them with -:py:func:`pytest.fixture`. Let's look at a simple self-contained test -module containing a fixture and a test function using it:: +the fixture object. Fixture functions are registered by marking them with +:py:func:`@pytest.fixture `. Let's look at a simple +self-contained test module containing a fixture and a test function +using it:: # content of ./test_smtpsimple.py import pytest @@ -111,10 +120,10 @@ to see available fixtures. - In versions prior to 2.3 there was no @pytest.fixture marker + In versions prior to 2.3 there was no ``@pytest.fixture`` marker and you had to use a magic ``pytest_funcarg__NAME`` prefix for the fixture factory. This remains and will remain supported - but is not advertised as the primary means of declaring fixture + but is not anymore advertised as the primary means of declaring fixture functions. Funcargs a prime example of dependency injection @@ -134,6 +143,8 @@ functions take the role of the *injector* and test functions are the *consumers* of fixture objects. +.. _smtpshared: + Working with a session-shared fixture ----------------------------------------------------------------- @@ -141,12 +152,12 @@ Fixtures requiring network access depend on connectivity and are usually time-expensive to create. Extending the previous example, we -can add a ``scope='session'`` parameter to the ``smtp`` fixture function -to cause it to only be invoked once per session. Multiple test -functions will thus only involve a once-per-test session creation of the -fixture instance. Also, we now split the creation of the fixture into a -``conftest.py`` file which will automatically loaded when running a test -module:: +can add a ``scope='session'`` parameter to ``@pytest.fixture`` decorator. +to cause the decorated ``smtp`` fixture function to only be invoked once +per test session. Multiple test functions will thus each receive the +same ``smtp`` fixture instance. The next example also extracts +the fixture function into a separate ``conftest.py`` file so that +all tests in the directory can access the fixture function:: # content of conftest.py import pytest @@ -215,6 +226,7 @@ traceback. As a result, the two test functions using ``smtp`` run as quick as a single one because they reuse the same instance. +.. _`request-context`: Fixtures can interact with the requesting test context ------------------------------------------------------------- @@ -275,7 +287,7 @@ > assert 0, smtp.helo() E AssertionError: (250, 'hq.merlinux.eu') -.. _`request`: :ref:pyclass:`_pytest.python.FixtureRequest` +.. _`request`: :py:class:`_pytest.python.FixtureRequest` .. _`fixture-parametrize`: @@ -289,10 +301,10 @@ write exhaustive functional tests for components which themselves can be configured in multiple ways. -Extending the previous example, we can flag the fixture to create -two ``smtp`` fixture instances which will cause all tests using the -fixture to run twice. The fixture function gets -access to each parameter through the special `request`_ object:: +Extending the previous example, we can flag the fixture to create two +``smtp`` fixture instances which will cause all tests using the fixture +to run twice. The fixture function gets access to each parameter +through the special `request`_ object:: # content of conftest.py import pytest @@ -565,9 +577,9 @@ usefixtures = cleandir -.. _`autoactive fixtures`: +.. _`autouse fixtures`: -autoactive fixtures (xUnit setup on steroids) +autouse fixtures (xUnit setup on steroids) ---------------------------------------------------------------------- .. regendoc:wipe @@ -596,7 +608,7 @@ return DB() class TestClass: - @pytest.fixture(autoactive=True) + @pytest.fixture(autouse=True) def transact(self, request, db): db.begin(request.function.__name__) request.addfinalizer(db.rollback) @@ -607,7 +619,7 @@ def test_method2(self, db): assert db.intransaction == ["test_method2"] -The class-level ``transact`` fixture is marked with *autoactive=true* +The class-level ``transact`` fixture is marked with *autouse=true* which implies that all test methods in the class will use this fixture without a need to state it in the test function signature or with a class-level ``usefixtures`` decorator. @@ -617,15 +629,15 @@ $ py.test -q .. -Here is how autoactive fixtures work in other scopes: +Here is how autouse fixtures work in other scopes: -- if an autoactive fixture is defined in a test module, all its test +- if an autouse fixture is defined in a test module, all its test functions automatically use it. -- if an autoactive fixture is defined in a conftest.py file then all tests in +- if an autouse fixture is defined in a conftest.py file then all tests in all test modules belows its directory will invoke the fixture. -- lastly, and **please use that with care**: if you define an autoactive +- lastly, and **please use that with care**: if you define an autouse fixture in a plugin, it will be invoked for all tests in all projects where the plugin is installed. This can be useful if a fixture only anyway works in the presence of certain settings e. g. in the ini-file. Such @@ -635,7 +647,7 @@ Note that the above ``transact`` fixture may very well be a fixture that you want to make available in your project without having it generally active. The canonical way to do that is to put the transact definition -into a conftest.py file **without** using ``autoactive``:: +into a conftest.py file **without** using ``autouse``:: # content of conftest.py @pytest.fixture() diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d doc/en/funcarg_compare.txt --- a/doc/en/funcarg_compare.txt +++ b/doc/en/funcarg_compare.txt @@ -146,7 +146,7 @@ It is thus recommended to use the factory decorator. -solving per-session setup / autoactive fixtures +solving per-session setup / autouse fixtures -------------------------------------------------------------- pytest for a long time offered a pytest_configure and a pytest_sessionstart @@ -173,7 +173,7 @@ It follows that pytest_configure/session/runtest_setup are often not appropriate for implementing common fixture needs. Therefore, -pytest-2.3 introduces :ref:`autoactive fixtures` which fully +pytest-2.3 introduces :ref:`autouse fixtures` which fully integrate with the generic :ref:`fixture mechanism ` and obsolete many prior uses of pytest hooks. diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d doc/en/getting-started.txt --- a/doc/en/getting-started.txt +++ b/doc/en/getting-started.txt @@ -10,6 +10,7 @@ **documentation as PDF**: `download latest `_ .. _`getstarted`: +.. _installation: Installation ---------------------------------------- diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d doc/en/index.txt --- a/doc/en/index.txt +++ b/doc/en/index.txt @@ -1,4 +1,5 @@ +.. _features: pytest: makes you write better programs ============================================= diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d doc/en/nose.txt --- a/doc/en/nose.txt +++ b/doc/en/nose.txt @@ -5,10 +5,12 @@ py.test has basic support for running tests written for nose_. +.. _nosestyle: + Usage ------------- -type:: +After :ref:`installation` type:: py.test # instead of 'nosetests' diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d doc/en/setup.txt --- a/doc/en/setup.txt +++ b/doc/en/setup.txt @@ -1,10 +1,10 @@ -setup: is now an "autoactive fixture" +setup: is now an "autouse fixture" ======================================================== During development prior to the pytest-2.3 release the name ``pytest.setup`` was used but before the release it was renamed and moved to become part of the general fixture mechanism, -namely :ref:`autoactive fixtures` +namely :ref:`autouse fixtures` diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d doc/en/tmpdir.txt --- a/doc/en/tmpdir.txt +++ b/doc/en/tmpdir.txt @@ -1,5 +1,6 @@ .. _`tmpdir handling`: +.. _tmpdir: Temporary directories and files ================================================ diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d doc/en/unittest.txt --- a/doc/en/unittest.txt +++ b/doc/en/unittest.txt @@ -4,59 +4,141 @@ Support for unittest.TestCase / Integration of fixtures ===================================================================== +.. _`unittest.py style`: http://docs.python.org/library/unittest.html + py.test has support for running Python `unittest.py style`_ tests. -It will automatically collect ``unittest.TestCase`` subclasses -and their ``test`` methods in test files. It will invoke -``setUp/tearDown`` methods but also perform py.test's standard ways -of treating tests such as IO capturing:: +It's meant for leveraging existing unittest-style projects +to use pytest features. Concretely, pytest will automatically +collect ``unittest.TestCase`` subclasses and their ``test`` methods in +test files. It will invoke typlical ``setUp/tearDown`` methods and +generally try to make test suites written to run on unittest, to also +run using pytest. We assume here that you are familiar with writing +``unittest.TestCase`` style tests and rather focus on +integration aspects. - # content of test_unittest.py +Usage +------------------------------------------------------------------- + +After :ref:`installation` type:: + + py.test + +and you should be able to run your unittest-style tests if they +are contained in ``test_*`` modules. This way you can make +use of most :ref:`pytest features `, for example +``--pdb`` debugging in failures, using :ref:`plain assert-statements `, +:ref:`more informative tracebacks `, stdout-capturing or +distributing tests to multiple CPUs via the ``-nNUM`` option if you +installed the ``pytest-xdist`` plugin. Please refer to +the general pytest documentation for many more examples. + +Mixing pytest fixtures into unittest.TestCase style tests +----------------------------------------------------------- + +pytest supports using its :ref:`fixture mechanism ` with +``unittest.TestCase`` style tests. Assuming you have at least skimmed +the pytest fixture features, let's jump-start into an example that +integrates a pytest ``db_class`` fixture, setting up a +class-cached database object, and then reference it from +a unittest-style test:: + + # content of conftest.py + + # hooks and fixtures in this file are available throughout all test + # modules living below the directory of this conftest.py file + + import pytest + + @pytest.fixture(scope="class") + def db_class(request): + class DummyDB: + pass + request.cls.db = DummyDB() + +This defines a fixture function ``db_class`` which - if used - is +called once for each test class and which sets the class-level +``db`` attribute to a ``DummyDB`` instance. The fixture function +achieves this by receiving a special ``request`` object which gives +access to :ref:`the requesting test context ` such +as the ``cls`` attribute, denoting the class from which the fixture +is used. This architecture de-couples fixture writing from actual test +code and allows re-use of the fixture by a minimal reference, the fixture +name. So let's write an actual ``unittest.TestCase`` class using our +fixture definition:: + + # content of test_unittest_db.py import unittest + import pytest + + @pytest.mark.usefixtures("db_class") class MyTest(unittest.TestCase): - def setUp(self): - print ("hello") # output is captured - def test_method(self): - x = 1 - self.assertEquals(x, 3) - -Running it yields:: + def test_method1(self): + assert hasattr(self, "db") + assert 0, self.db # fail for demo purposes - $ py.test test_unittest.py + def test_method2(self): + assert 0, self.db # fail for demo purposes + +The ``@pytest.mark.usefixtures("db_class")`` class-decorator makes sure that +the pytest fixture function ``db_class`` is called. Due to the deliberately +failing assert statements, we can take a look at the ``self.db`` values +in the traceback:: + + $ py.test test_unittest_db.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 - collected 1 items + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev22 + plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout + collected 2 items - test_unittest.py F + test_unittest_db.py FF ================================= FAILURES ================================= - ____________________________ MyTest.test_method ____________________________ + ___________________________ MyTest.test_method1 ____________________________ - self = + self = - def test_method(self): - x = 1 - > self.assertEquals(x, 3) - E AssertionError: 1 != 3 + def test_method1(self): + assert hasattr(self, "db") + > assert 0, self.db # fail for demo purposes + E AssertionError: - test_unittest.py:8: AssertionError - ----------------------------- Captured stdout ------------------------------ - hello - ========================= 1 failed in 0.01 seconds ========================= + test_unittest_db.py:9: AssertionError + ___________________________ MyTest.test_method2 ____________________________ + + self = + + def test_method2(self): + > assert 0, self.db # fail for demo purposes + E AssertionError: + + test_unittest_db.py:12: AssertionError + ========================= 2 failed in 0.04 seconds ========================= -.. _`unittest.py style`: http://docs.python.org/library/unittest.html +This default pytest traceback shows that, indeed, the two test methods +see the same ``self.db`` attribute instance which was our intention +when writing the class-scoped fixture function. -Moreover, you can use pytest's new :ref:`autoactive fixtures` -functions, thereby connecting pytest's :ref:`fixture mechanism ` -with a setup/teardown style:: - # content of test_unittest_funcargs.py +autouse fixtures and accessing other fixtures +------------------------------------------------------------------- + +Although it's usually better to explicitely declare use of fixtures you need +for a given test, you may sometimes want to have fixtures that are +automatically used in a given context. For this, you can flag +fixture functions with ``@pytest.fixture(autouse=True)`` and define +the fixture function in the context where you want it used. Let's look +at an example which makes all test methods of a ``TestCase`` class +execute in a clean temporary directory, using a ``initdir`` fixture +which itself uses the pytest builtin ``tmpdir`` fixture:: + + # content of test_unittest_cleandir.py import pytest import unittest class MyTest(unittest.TestCase): - @pytest.fixture(autoactive=True) - def chdir(self, tmpdir): + @pytest.fixture(autouse=True) + def initdir(self, tmpdir): tmpdir.chdir() # change to pytest-provided temporary directory tmpdir.join("samplefile.ini").write("# testdata") @@ -64,71 +146,28 @@ s = open("samplefile.ini").read() assert "testdata" in s -Running this file should give us one passed test because the setup -function took care to prepare a directory with some test data -which the unittest-testcase method can now use:: +The ``initdir`` fixture function will be used for all methods of the +class where it is defined. This is basically just a shortcut for +using a ``@pytest.mark.usefixtures("initdir")`` on the class like in +the previous example. Note, that the ``initdir`` fixture function +accepts a :ref:`tmpdir ` argument, referencing a pytest +builtin fixture. - $ py.test -q test_unittest_funcargs.py +Running this test module ...:: + + $ py.test -q test_unittest_cleandir.py . -If you want to make a database attribute available on unittest.TestCases -instances, you can do it using :ref:`usefixtures` and a simple -:ref:`fixture function`:: +... gives us one passed test because the ``initdir`` fixture function +was executed ahead of the ``test_method``. - # content of test_unittest_marked_db.py - import pytest - import unittest +.. note:: - @pytest.fixture - def db(request): - class DummyDB: - entries = [] - db = DummyDB() - if request.instance is not None: - request.instance.db = db - return db - - @pytest.mark.usefixtures("db") - class MyTest(unittest.TestCase): - def test_append(self): - self.db.entries.append(1) - - def test_method2(self): - # check we have a fresh instance - assert len(self.db.entries) == 0 - -Running it passes both tests:: - - $ py.test -q test_unittest_marked_db.py - .. - -If you rather want to provide a class-cached "db" attribute, you -can write a slightly different fixture using a ``scope`` parameter -for the fixture decorator :: - - # content of test_unittest_class_db.py - import pytest - import unittest - - @pytest.fixture(scope="class") - def db_class(request): - class DummyDB: - entries = [] - db = DummyDB() - if request.cls is not None: - request.cls.db = db - return db - - @pytest.mark.usefixtures("db_class") - class MyTest(unittest.TestCase): - def test_append(self): - self.db.entries.append(1) - - def test_method2(self): - # check we DONT have a fresh instance - assert len(self.db.entries) == 1 - -Running it again passes both tests:: - - $ py.test -q test_unittest_class_db.py - .. + ``unittest.TestCase`` methods cannot directly receive fixture or + function arguments as implementing that is likely to inflict + on the ability to run general unittest.TestCase test suites. + Given enough demand, attempts might be made, though. If + unittest finally grows a reasonable plugin system that should + help as well. In the meanwhile, the above ``usefixtures`` and + ``autouse`` examples should help to mix in pytest fixtures into + unittest suites. diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev22', + version='2.3.0.dev23', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d testing/test_nose.py --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -28,7 +28,7 @@ from _pytest.nose import call_optional l = [] class A: - @pytest.fixture( autoactive=True) + @pytest.fixture( autouse=True) def f(self): l.append(1) call_optional(A(), "f") diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -767,7 +767,7 @@ def markers(request): return request.node.markers - @pytest.fixture(scope="class", autoactive=True) + @pytest.fixture(scope="class", autouse=True) def marking(request): request.applymarker(pytest.mark.XYZ("hello")) """) @@ -1975,14 +1975,14 @@ def pytest_funcarg__testdir(self, testdir): testdir.makeconftest(""" import pytest - @pytest.fixture( autoactive=True) + @pytest.fixture( autouse=True) def perfunction(request, tmpdir): pass @pytest.fixture() def arg1(tmpdir): pass - @pytest.fixture( autoactive=True) + @pytest.fixture( autouse=True) def perfunction2(arg1): pass @@ -2008,7 +2008,7 @@ testdir.makepyfile(""" import pytest class TestClass: - @pytest.fixture(autoactive=True) + @pytest.fixture(autouse=True) def permethod(self, request): request.instance.funcname = request.function.__name__ def test_method1(self): @@ -2031,7 +2031,7 @@ def db(request): return request.param - @pytest.fixture(enabled=enabled, autoactive=True) + @pytest.fixture(enabled=enabled, autouse=True) def createdb(db): pass @@ -2072,7 +2072,7 @@ def arg(): l.append(1) return 0 - @pytest.fixture(scope="class", autoactive=True) + @pytest.fixture(scope="class", autouse=True) def something(arg): l.append(2) @@ -2097,7 +2097,7 @@ def arg(request): return request.param - @pytest.fixture( autoactive=True) + @pytest.fixture( autouse=True) def something(arg): l.append(arg) @@ -2123,7 +2123,7 @@ def arg(request): return request.param - @pytest.fixture(scope="function", autoactive=True) + @pytest.fixture(scope="function", autouse=True) def append(request, arg): if request.function.__name__ == "test_some": l.append(arg) @@ -2153,7 +2153,7 @@ def carg(request): return request.param - @pytest.fixture(scope="function", autoactive=True) + @pytest.fixture(scope="function", autouse=True) def append(request, farg, carg): def fin(): l.append("fin_%s%s" % (carg, farg)) @@ -2179,13 +2179,13 @@ testdir.makepyfile(""" import pytest l = [] - @pytest.fixture(scope="function", autoactive=True) + @pytest.fixture(scope="function", autouse=True) def fappend2(): l.append(2) - @pytest.fixture(scope="class", autoactive=True) + @pytest.fixture(scope="class", autouse=True) def classappend3(): l.append(3) - @pytest.fixture(scope="module", autoactive=True) + @pytest.fixture(scope="module", autouse=True) def mappend(): l.append(1) @@ -2205,7 +2205,7 @@ if metafunc.cls is not None: metafunc.parametrize("item", [1,2], scope="class") class TestClass: - @pytest.fixture(scope="class", autoactive=True) + @pytest.fixture(scope="class", autouse=True) def addteardown(self, item, request): l.append("setup-%d" % item) request.addfinalizer(lambda: l.append("teardown-%d" % item)) @@ -2482,7 +2482,7 @@ def carg(request): return request.param - @pytest.fixture(scope="function", autoactive=True) + @pytest.fixture(scope="function", autouse=True) def append(request, farg, carg): def fin(): l.append("fin_%s%s" % (carg, farg)) @@ -2620,7 +2620,7 @@ def arg(request): return request.param - @pytest.fixture(scope="module", autoactive=True) + @pytest.fixture(scope="module", autouse=True) def mysetup(request, arg): request.addfinalizer(lambda: l.append("fin%s" % arg)) l.append("setup%s" % arg) @@ -2654,7 +2654,7 @@ def test_setup(self, testdir, scope, ok, error): testdir.makepyfile(""" import pytest - @pytest.fixture(scope=%r, autoactive=True) + @pytest.fixture(scope=%r, autouse=True) def myscoped(request): for x in %r: assert hasattr(request, x) @@ -2709,7 +2709,7 @@ def test_setupfunc_missing_funcarg(self, testdir): testdir.makepyfile(""" import pytest - @pytest.fixture( autoactive=True) + @pytest.fixture( autouse=True) def gen(qwe123): return 1 def test_something(): @@ -2744,7 +2744,7 @@ def arg(request): return request.param - @pytest.fixture( autoactive=True) + @pytest.fixture( autouse=True) def mysetup(request, arg): assert not hasattr(request, "param") def test_1(arg): @@ -2757,10 +2757,10 @@ testdir.makepyfile(""" import pytest l = [] - @pytest.fixture(scope='module', autoactive=True) + @pytest.fixture(scope='module', autouse=True) def setup_module(): l.append("module") - @pytest.fixture( autoactive=True) + @pytest.fixture( autouse=True) def setup_function(): l.append("function") @@ -2768,10 +2768,10 @@ pass class TestClass: - @pytest.fixture(scope="class", autoactive=True) + @pytest.fixture(scope="class", autouse=True) def setup_class(self): l.append("class") - @pytest.fixture( autoactive=True) + @pytest.fixture( autouse=True) def setup_method(self): l.append("method") def test_method(self): @@ -2790,7 +2790,7 @@ import pytest l = [] - @pytest.fixture( autoactive=True) + @pytest.fixture( autouse=True) def fix1(): l.append(1) @pytest.fixture() @@ -2812,7 +2812,7 @@ @pytest.fixture() def farg(arg1): pass - @pytest.fixture( autoactive=True) + @pytest.fixture( autouse=True) def sarg(tmpdir): pass def test_function(request, farg): diff -r 4ff9c2bc50bdf82809f9ded29f55c50c8103e92c -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d testing/test_unittest.py --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -497,10 +497,10 @@ import unittest import pytest class MyTestCase(unittest.TestCase): - @pytest.fixture(scope="class", autoactive=True) + @pytest.fixture(scope="class", autouse=True) def perclass(self, request): request.cls.hello = "world" - @pytest.fixture(scope="function", autoactive=True) + @pytest.fixture(scope="function", autouse=True) def perfunction(self, request): request.instance.funcname = request.function.__name__ Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Tue Oct 16 16:13:36 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 16 Oct 2012 14:13:36 -0000 Subject: [py-svn] commit/pytest: 7 new changesets Message-ID: <20121016141336.20893.81050@bitbucket13.managed.contegix.com> 7 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/baed16626500/ changeset: baed16626500 user: hpk42 date: 2012-10-16 13:47:59 summary: add plan for better fixture implementation, an xfailing test and a slight refactoring of Metafunc tests/creation affected #: 3 files diff -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d -r baed16626500839e76c81901e9112cc898b61536 IMPL.txt --- /dev/null +++ b/IMPL.txt @@ -0,0 +1,40 @@ + +internal fixture mechanics +---------------------------- + +fixture mechanics are contained in the python.py plugin. + +pytest fixtures are stored and managed from the FixtureManager (fm) class. +Upon collection fm.parsefactories() is called multiple times to parse +fixture function definitions into FixtureDef objects. + +During collection of test functions, metafunc needs to grow a "fixturenames" +list so that pytest_generate_tests() hooks can check it. These fixturenames +are a closure of all known fixtures to be used for this function: + +- ini-defined usefixtures +- autouse-marked fixtures along the collection chain up from the function +- usefixtures markers at module/class/function level +- test function funcargs + +The latter list (without the closure) is also called "_initialfixtures" +and will be used during the test setup phase. + +Upon the test-setup phases initialfixtures are instantiated which +will subsequently create the full fixture closure (as was computed in +metafunc.fixturenames during collection). As fixture functions +can invoke request.getfuncargvalue() the actual closure may be even +bigger. + +object model +--------------------- + +As part of the metafunc-protocol parents of Function nodes get a +FuncFixtureInfos() object, containing helping/caching for +for a function. .getfixtureinfo(func) returns a FixtureInfo with these +attributes: + +- names_initial: list of initial fixture names (see above) +- names_closure: closure of all fixture names +- name2fixturedeflist: for creating the value + diff -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d -r baed16626500839e76c81901e9112cc898b61536 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -309,8 +309,7 @@ clscol = self.getparent(Class) cls = clscol and clscol.obj or None transfer_markers(funcobj, cls, module) - metafunc = Metafunc(funcobj, parentnode=self, config=self.config, - cls=cls, module=module) + metafunc = Metafunc(funcobj, parentnode=self, cls=cls, module=module) gentesthook = self.config.hook.pytest_generate_tests extra = [module] if cls is not None: @@ -612,25 +611,23 @@ return self.fixturenames class Metafunc(FuncargnamesCompatAttr): - def __init__(self, function, config=None, cls=None, module=None, - parentnode=None): - self.config = config + def __init__(self, function, parentnode, cls=None, module=None): + self.config = parentnode.config self.module = module self.function = function self.parentnode = parentnode - self._parentid = getattr(parentnode, "nodeid", "") argnames = getfuncargnames(function, startindex=int(cls is not None)) - if parentnode is not None: + try: fm = parentnode.session._fixturemanager + except AttributeError: + self.fixturenames = argnames + else: self.fixturenames, self._arg2fixturedeflist = fm.getfixtureclosure( argnames, parentnode) - else: - self.fixturenames = argnames self.cls = cls self.module = module self._calls = [] self._ids = py.builtin.set() - self._arg2scopenum = {} def parametrize(self, argnames, argvalues, indirect=False, ids=None, scope=None): diff -r 0cf1ef1efbea1aae3d892ff97dec2b8d4ac4231d -r baed16626500839e76c81901e9112cc898b61536 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -284,22 +284,22 @@ pass def func2(): pass - f1 = pytest.Function(name="name", config=config, - args=(1,), callobj=func1, session=session) + f1 = pytest.Function(name="name", parent=session, config=config, + args=(1,), callobj=func1) f2 = pytest.Function(name="name",config=config, - args=(1,), callobj=func2, session=session) + args=(1,), callobj=func2, parent=session) assert not f1 == f2 assert f1 != f2 - f3 = pytest.Function(name="name", config=config, - args=(1,2), callobj=func2, session=session) + f3 = pytest.Function(name="name", parent=session, config=config, + args=(1,2), callobj=func2) assert not f3 == f2 assert f3 != f2 assert not f3 == f1 assert f3 != f1 - f1_b = pytest.Function(name="name", config=config, - args=(1,), callobj=func1, session=session) + f1_b = pytest.Function(name="name", parent=session, config=config, + args=(1,), callobj=func1) assert f1 == f1_b assert not f1 != f1_b @@ -909,15 +909,20 @@ ]) class TestMetafunc: + def Metafunc(self, func): + class parent: + config = None + return funcargs.Metafunc(func, parentnode=parent) + def test_no_funcargs(self, testdir): def function(): pass - metafunc = funcargs.Metafunc(function) + metafunc = self.Metafunc(function) assert not metafunc.fixturenames repr(metafunc._calls) def test_function_basic(self): def func(arg1, arg2="qwe"): pass - metafunc = funcargs.Metafunc(func) + metafunc = self.Metafunc(func) assert len(metafunc.fixturenames) == 1 assert 'arg1' in metafunc.fixturenames assert metafunc.function is func @@ -925,7 +930,7 @@ def test_addcall_no_args(self): def func(arg1): pass - metafunc = funcargs.Metafunc(func) + metafunc = self.Metafunc(func) metafunc.addcall() assert len(metafunc._calls) == 1 call = metafunc._calls[0] @@ -934,7 +939,7 @@ def test_addcall_id(self): def func(arg1): pass - metafunc = funcargs.Metafunc(func) + metafunc = self.Metafunc(func) pytest.raises(ValueError, "metafunc.addcall(id=None)") metafunc.addcall(id=1) @@ -947,7 +952,7 @@ def test_addcall_param(self): def func(arg1): pass - metafunc = funcargs.Metafunc(func) + metafunc = self.Metafunc(func) class obj: pass metafunc.addcall(param=obj) metafunc.addcall(param=obj) @@ -959,7 +964,7 @@ def test_addcall_funcargs(self): def func(x): pass - metafunc = funcargs.Metafunc(func) + metafunc = self.Metafunc(func) class obj: pass metafunc.addcall(funcargs={"x": 2}) metafunc.addcall(funcargs={"x": 3}) @@ -971,7 +976,7 @@ def test_parametrize_error(self): def func(x, y): pass - metafunc = funcargs.Metafunc(func) + metafunc = self.Metafunc(func) metafunc.parametrize("x", [1,2]) pytest.raises(ValueError, lambda: metafunc.parametrize("x", [5,6])) pytest.raises(ValueError, lambda: metafunc.parametrize("x", [5,6])) @@ -981,7 +986,7 @@ def test_parametrize_and_id(self): def func(x, y): pass - metafunc = funcargs.Metafunc(func) + metafunc = self.Metafunc(func) metafunc.parametrize("x", [1,2], ids=['basic', 'advanced']) metafunc.parametrize("y", ["abc", "def"]) @@ -990,7 +995,7 @@ def test_parametrize_with_userobjects(self): def func(x, y): pass - metafunc = funcargs.Metafunc(func) + metafunc = self.Metafunc(func) class A: pass metafunc.parametrize("x", [A(), A()]) @@ -1002,7 +1007,7 @@ def test_addcall_and_parametrize(self): def func(x, y): pass - metafunc = funcargs.Metafunc(func) + metafunc = self.Metafunc(func) metafunc.addcall({'x': 1}) metafunc.parametrize('y', [2,3]) assert len(metafunc._calls) == 2 @@ -1013,7 +1018,7 @@ def test_parametrize_indirect(self): def func(x, y): pass - metafunc = funcargs.Metafunc(func) + metafunc = self.Metafunc(func) metafunc.parametrize('x', [1], indirect=True) metafunc.parametrize('y', [2,3], indirect=True) metafunc.parametrize('unnamed', [1], indirect=True) @@ -1025,7 +1030,7 @@ def test_addcalls_and_parametrize_indirect(self): def func(x, y): pass - metafunc = funcargs.Metafunc(func) + metafunc = self.Metafunc(func) metafunc.addcall(param="123") metafunc.parametrize('x', [1], indirect=True) metafunc.parametrize('y', [2,3], indirect=True) @@ -1038,7 +1043,6 @@ def test_parametrize_functional(self, testdir): testdir.makepyfile(""" def pytest_generate_tests(metafunc): - assert "test_parametrize_functional" in metafunc._parentid metafunc.parametrize('x', [1,2], indirect=True) metafunc.parametrize('y', [2]) def pytest_funcarg__x(request): @@ -1058,7 +1062,7 @@ ]) def test_parametrize_onearg(self): - metafunc = funcargs.Metafunc(lambda x: None) + metafunc = self.Metafunc(lambda x: None) metafunc.parametrize("x", [1,2]) assert len(metafunc._calls) == 2 assert metafunc._calls[0].funcargs == dict(x=1) @@ -1067,7 +1071,7 @@ assert metafunc._calls[1].id == "2" def test_parametrize_onearg_indirect(self): - metafunc = funcargs.Metafunc(lambda x: None) + metafunc = self.Metafunc(lambda x: None) metafunc.parametrize("x", [1,2], indirect=True) assert metafunc._calls[0].params == dict(x=1) assert metafunc._calls[0].id == "1" @@ -1075,7 +1079,7 @@ assert metafunc._calls[1].id == "2" def test_parametrize_twoargs(self): - metafunc = funcargs.Metafunc(lambda x,y: None) + metafunc = self.Metafunc(lambda x,y: None) metafunc.parametrize(("x", "y"), [(1,2), (3,4)]) assert len(metafunc._calls) == 2 assert metafunc._calls[0].funcargs == dict(x=1, y=2) @@ -2004,6 +2008,28 @@ reprec = testdir.inline_run("-s") reprec.assertoutcome(passed=1) + @pytest.mark.xfail + def test_two_classes_separated_autouse(self, testdir): + testdir.makepyfile(""" + import pytest + class TestA: + l = [] + @pytest.fixture(autouse=True) + def setup1(self): + self.l.append(1) + def test_setup1(self): + assert self.l == [1] + class TestB: + l = [] + @pytest.fixture(autouse=True) + def setup2(self): + self.l.append(1) + def test_setup2(self): + assert self.l == [1] + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2) + def test_setup_at_classlevel(self, testdir): testdir.makepyfile(""" import pytest https://bitbucket.org/hpk42/pytest/changeset/20d77f40c3ab/ changeset: 20d77f40c3ab user: hpk42 date: 2012-10-16 13:47:59 summary: implement fixture information stored on the parentnode of functions to be reused by metafunc mechanics and Function setup affected #: 4 files diff -r baed16626500839e76c81901e9112cc898b61536 -r 20d77f40c3abab579f2be1b310db8529b5234e8c IMPL.txt --- a/IMPL.txt +++ b/IMPL.txt @@ -30,7 +30,7 @@ --------------------- As part of the metafunc-protocol parents of Function nodes get a -FuncFixtureInfos() object, containing helping/caching for +ParentFixtures() object, containing helping/caching for for a function. .getfixtureinfo(func) returns a FixtureInfo with these attributes: diff -r baed16626500839e76c81901e9112cc898b61536 -r 20d77f40c3abab579f2be1b310db8529b5234e8c _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -309,7 +309,11 @@ clscol = self.getparent(Class) cls = clscol and clscol.obj or None transfer_markers(funcobj, cls, module) - metafunc = Metafunc(funcobj, parentnode=self, cls=cls, module=module) + if not hasattr(self, "_fixturemapper"): + self._fixturemapper = FixtureMapper(self) + fixtureinfo = self._fixturemapper.getfixtureinfo(funcobj, cls) + metafunc = Metafunc(funcobj, fixtureinfo, self.config, + cls=cls, module=module) gentesthook = self.config.hook.pytest_generate_tests extra = [module] if cls is not None: @@ -326,6 +330,32 @@ callspec=callspec, callobj=funcobj, keywords={callspec.id:True}) +class FixtureMapper: + def __init__(self, node): + self.node = node + self.fm = node.session._fixturemanager + self._name2fixtureinfo = {} + + def getfixtureinfo(self, func, cls): + try: + return self._name2fixtureinfo[func] + except KeyError: + pass + argnames = getfuncargnames(func, int(cls is not None)) + usefixtures = getattr(func, "usefixtures", None) + if usefixtures is not None: + argnames = usefixtures.args + argnames + names_closure, arg2fixturedeflist = self.fm.getfixtureclosure( + argnames, self.node) + fixtureinfo = FuncFixtureInfo(names_closure, arg2fixturedeflist) + self._name2fixtureinfo[func] = fixtureinfo + return fixtureinfo + +class FuncFixtureInfo: + def __init__(self, names_closure, name2fixturedeflist): + self.names_closure = names_closure + self.name2fixturedeflist = name2fixturedeflist + def transfer_markers(funcobj, cls, mod): # XXX this should rather be code in the mark plugin or the mark # plugin should merge with the python plugin. @@ -611,19 +641,12 @@ return self.fixturenames class Metafunc(FuncargnamesCompatAttr): - def __init__(self, function, parentnode, cls=None, module=None): - self.config = parentnode.config + def __init__(self, function, fixtureinfo, config, cls=None, module=None): + self.config = config self.module = module self.function = function - self.parentnode = parentnode - argnames = getfuncargnames(function, startindex=int(cls is not None)) - try: - fm = parentnode.session._fixturemanager - except AttributeError: - self.fixturenames = argnames - else: - self.fixturenames, self._arg2fixturedeflist = fm.getfixtureclosure( - argnames, parentnode) + self.fixturenames = fixtureinfo.names_closure + self._arg2fixturedeflist = fixtureinfo.name2fixturedeflist self.cls = cls self.module = module self._calls = [] @@ -878,7 +901,7 @@ Python test function. """ _genid = None - def __init__(self, name, parent=None, args=None, config=None, + def __init__(self, name, parent, args=None, config=None, callspec=None, callobj=_dummy, keywords=None, session=None): super(Function, self).__init__(name, parent, config=config, session=session) @@ -908,9 +931,9 @@ # contstruct a list of all neccessary fixtures for this test function try: - usefixtures = list(self.markers.usefixtures.args) + usefixtures = self.markers.usefixtures.args except AttributeError: - usefixtures = [] + usefixtures = () self.fixturenames = (self.session._fixturemanager.getdefaultfixtures() + usefixtures + self._getfuncargnames()) @@ -1377,16 +1400,16 @@ self.pytest_plugin_registered(plugin) def getdefaultfixtures(self): - """ return a list of default fixture names (XXX for the given file path). """ + """ return a tuple of default fixture names (XXX for the given file path). """ try: return self._defaultfixtures except AttributeError: - defaultfixtures = list(self.config.getini("usefixtures")) + defaultfixtures = tuple(self.config.getini("usefixtures")) # make sure the self._autofixtures list is sorted # by scope, scopenum 0 is session self._autofixtures.sort( key=lambda x: self.arg2fixturedeflist[x][-1].scopenum) - defaultfixtures.extend(self._autofixtures) + defaultfixtures = defaultfixtures + tuple(self._autofixtures) self._defaultfixtures = defaultfixtures return defaultfixtures @@ -1573,8 +1596,8 @@ getattr(function, '__defaults__', None)) or () numdefaults = len(defaults) if numdefaults: - return argnames[startindex:-numdefaults] - return argnames[startindex:] + return tuple(argnames[startindex:-numdefaults]) + return tuple(argnames[startindex:]) # algorithm for sorting on a per-parametrized resource setup basis diff -r baed16626500839e76c81901e9112cc898b61536 -r 20d77f40c3abab579f2be1b310db8529b5234e8c _pytest/unittest.py --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -53,7 +53,7 @@ _excinfo = None def _getfuncargnames(self): - return [] + return () def setup(self): self._testcase = self.parent.obj(self.name) diff -r baed16626500839e76c81901e9112cc898b61536 -r 20d77f40c3abab579f2be1b310db8529b5234e8c testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -535,17 +535,17 @@ def f(): pass assert not funcargs.getfuncargnames(f) def g(arg): pass - assert funcargs.getfuncargnames(g) == ['arg'] + assert funcargs.getfuncargnames(g) == ('arg',) def h(arg1, arg2="hello"): pass - assert funcargs.getfuncargnames(h) == ['arg1'] + assert funcargs.getfuncargnames(h) == ('arg1',) def h(arg1, arg2, arg3="hello"): pass - assert funcargs.getfuncargnames(h) == ['arg1', 'arg2'] + assert funcargs.getfuncargnames(h) == ('arg1', 'arg2') class A: def f(self, arg1, arg2="hello"): pass - assert funcargs.getfuncargnames(A().f) == ['arg1'] + assert funcargs.getfuncargnames(A().f) == ('arg1',) if sys.version_info < (3,0): - assert funcargs.getfuncargnames(A.f) == ['arg1'] + assert funcargs.getfuncargnames(A.f) == ('arg1',) class TestFillFixtures: @@ -910,9 +910,16 @@ class TestMetafunc: def Metafunc(self, func): - class parent: - config = None - return funcargs.Metafunc(func, parentnode=parent) + # the unit tests of this class check if things work correctly + # on the funcarg level, so we don't need a full blown + # initiliazation + class FixtureInfo: + name2fixturedeflist = None + def __init__(self, names): + self.names_closure = names + names = funcargs.getfuncargnames(func) + fixtureinfo = FixtureInfo(names) + return funcargs.Metafunc(func, fixtureinfo, None) def test_no_funcargs(self, testdir): def function(): pass @@ -1393,6 +1400,20 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=5) + def test_usemarkers_seen_in_generate_tests(self, testdir): + testdir.makepyfile(""" + import pytest + def pytest_generate_tests(metafunc): + assert "abc" in metafunc.fixturenames + metafunc.parametrize("abc", [1]) + + @pytest.mark.usefixtures("abc") + def test_function(): + pass + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) + def test_conftest_funcargs_only_available_in_subdir(testdir): sub1 = testdir.mkpydir("sub1") sub2 = testdir.mkpydir("sub2") https://bitbucket.org/hpk42/pytest/changeset/08b363d15353/ changeset: 08b363d15353 user: hpk42 date: 2012-10-16 13:48:00 summary: use fixturemapper/fixtureinfo from Function objects affected #: 3 files diff -r 20d77f40c3abab579f2be1b310db8529b5234e8c -r 08b363d153530c7821817f10a4b2dce355ee2724 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -266,6 +266,18 @@ return fspath, lineno, modpath class PyCollector(PyobjMixin, pytest.Collector): + def _fixturemapper(): + def get(self): + try: + return self._fixturemapper_memo + except AttributeError: + self._fixturemapper_memo = FixtureMapper(self, funcargs=False) + return self._fixturemapper_memo + def set(self, val): + assert not hasattr(self, "_fixturemapper_memo") + self._fixturemapper_memo = val + return property(get, set) + _fixturemapper = _fixturemapper() def funcnamefilter(self, name): for prefix in self.config.getini("python_functions"): @@ -309,7 +321,7 @@ clscol = self.getparent(Class) cls = clscol and clscol.obj or None transfer_markers(funcobj, cls, module) - if not hasattr(self, "_fixturemapper"): + if not hasattr(self, "_fixturemapper_memo"): self._fixturemapper = FixtureMapper(self) fixtureinfo = self._fixturemapper.getfixtureinfo(funcobj, cls) metafunc = Metafunc(funcobj, fixtureinfo, self.config, @@ -331,17 +343,21 @@ keywords={callspec.id:True}) class FixtureMapper: - def __init__(self, node): + def __init__(self, node, funcargs=True): self.node = node self.fm = node.session._fixturemanager self._name2fixtureinfo = {} + self.hasfuncargs = funcargs def getfixtureinfo(self, func, cls): try: return self._name2fixtureinfo[func] except KeyError: pass - argnames = getfuncargnames(func, int(cls is not None)) + if self.hasfuncargs: + argnames = getfuncargnames(func, int(cls is not None)) + else: + argnames = () usefixtures = getattr(func, "usefixtures", None) if usefixtures is not None: argnames = usefixtures.args + argnames @@ -929,17 +945,9 @@ for name, val in keywords.items(): setattr(self.markers, name, val) - # contstruct a list of all neccessary fixtures for this test function - try: - usefixtures = self.markers.usefixtures.args - except AttributeError: - usefixtures = () - self.fixturenames = (self.session._fixturemanager.getdefaultfixtures() + - usefixtures + self._getfuncargnames()) - - def _getfuncargnames(self): - startindex = int(self.cls is not None) - return getfuncargnames(self.obj, startindex=startindex) + fixtureinfo = self.parent._fixturemapper.getfixtureinfo(self.obj, + self.cls) + self.fixturenames = fixtureinfo.names_closure @property def function(self): diff -r 20d77f40c3abab579f2be1b310db8529b5234e8c -r 08b363d153530c7821817f10a4b2dce355ee2724 _pytest/unittest.py --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -20,6 +20,7 @@ return UnitTestCase(name, parent=collector) class UnitTestCase(pytest.Class): + def collect(self): self.session._fixturemanager._parsefactories(self.obj, self.nodeid, unittest=True) @@ -52,9 +53,6 @@ class TestCaseFunction(pytest.Function): _excinfo = None - def _getfuncargnames(self): - return () - def setup(self): self._testcase = self.parent.obj(self.name) self._obj = getattr(self._testcase, self.name) diff -r 20d77f40c3abab579f2be1b310db8529b5234e8c -r 08b363d153530c7821817f10a4b2dce355ee2724 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -280,6 +280,7 @@ config = testdir.parseconfigure() session = testdir.Session(config) session._fixturemanager = FixtureManager(session) + session._fixturemapper = funcargs.FixtureMapper(session, funcargs=False) def func1(): pass def func2(): @@ -579,6 +580,7 @@ pass """) funcargs.fillfixtures(item) + del item.funcargs["request"] assert len(item.funcargs) == 2 assert item.funcargs['some'] == "test_func" assert item.funcargs['other'] == 42 @@ -685,7 +687,9 @@ val2 = req.getfuncargvalue("other") # see about caching assert val2 == 2 pytest._fillfuncargs(item) - assert item.funcargs == {'something': 1} + assert item.funcargs["something"] == 1 + assert len(item.funcargs) == 2 + assert "request" in item.funcargs #assert item.funcargs == {'something': 1, "other": 2} def test_request_addfinalizer(self, testdir): https://bitbucket.org/hpk42/pytest/changeset/4bdfe68104d5/ changeset: 4bdfe68104d5 user: hpk42 date: 2012-10-16 13:48:00 summary: use FixtureInfo from FixtureRequest affected #: 1 file diff -r 08b363d153530c7821817f10a4b2dce355ee2724 -r 4bdfe68104d5ec3a93670bf9f1e6b0702cd3a8c8 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -372,6 +372,16 @@ self.names_closure = names_closure self.name2fixturedeflist = name2fixturedeflist + def getname2fixturedeflist_copy(self): + d = {} + for name, val in self.name2fixturedeflist.items(): + try: + val = list(val) + except TypeError: + pass + d[name] = val + return d + def transfer_markers(funcobj, cls, mod): # XXX this should rather be code in the mark plugin or the mark # plugin should merge with the python plugin. @@ -580,6 +590,9 @@ try: request = function._request except AttributeError: + # the special jstests class with a custom .obj + fi = FixtureMapper(function).getfixtureinfo(function.obj, None) + function._fixtureinfo = fi request = function._request = FixtureRequest(function) request._fillfixtures() @@ -922,6 +935,18 @@ super(Function, self).__init__(name, parent, config=config, session=session) self._args = args + if callobj is not _dummy: + self.obj = callobj + + for name, val in (py.builtin._getfuncdict(self.obj) or {}).items(): + setattr(self.markers, name, val) + if keywords: + for name, val in keywords.items(): + setattr(self.markers, name, val) + + fi = self.parent._fixturemapper.getfixtureinfo(self.obj, self.cls) + self._fixtureinfo = fi + self.fixturenames = fi.names_closure if self._isyieldedfunction(): assert not callspec, ( "yielded functions (deprecated) cannot have funcargs") @@ -936,18 +961,6 @@ self.funcargs = {} self._request = req = FixtureRequest(self) #req._discoverfactories() - if callobj is not _dummy: - self.obj = callobj - - for name, val in (py.builtin._getfuncdict(self.obj) or {}).items(): - setattr(self.markers, name, val) - if keywords: - for name, val in keywords.items(): - setattr(self.markers, name, val) - - fixtureinfo = self.parent._fixturemapper.getfixtureinfo(self.obj, - self.cls) - self.fixturenames = fixtureinfo.names_closure @property def function(self): @@ -1041,13 +1054,11 @@ self.scope = "function" self.getparent = pyfuncitem.getparent self._funcargs = self._pyfuncitem.funcargs.copy() - self._arg2fixturedeflist = {} + self._fixtureinfo = fi = pyfuncitem._fixtureinfo + self._arg2fixturedeflist = fi.getname2fixturedeflist_copy() + self.fixturenames = self._fixtureinfo.names_closure self._fixturemanager = pyfuncitem.session._fixturemanager self._parentid = pyfuncitem.parent.nodeid - self.fixturenames, self._arg2fixturedeflist_ = \ - self._fixturemanager.getfixtureclosure( - getfuncargnames(self.function), # XXX _pyfuncitem... - pyfuncitem.parent) self._fixturestack = [] @property https://bitbucket.org/hpk42/pytest/changeset/648c93461248/ changeset: 648c93461248 user: hpk42 date: 2012-10-16 13:59:12 summary: make factorydeflist immutable by using an index affected #: 3 files diff -r 4bdfe68104d5ec3a93670bf9f1e6b0702cd3a8c8 -r 648c9346124894406687104a05891d6b68eb844b IMPL.txt --- a/IMPL.txt +++ b/IMPL.txt @@ -36,5 +36,5 @@ - names_initial: list of initial fixture names (see above) - names_closure: closure of all fixture names -- name2fixturedeflist: for creating the value +- name2fixturedefs: for creating the value diff -r 4bdfe68104d5ec3a93670bf9f1e6b0702cd3a8c8 -r 648c9346124894406687104a05891d6b68eb844b _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -361,26 +361,16 @@ usefixtures = getattr(func, "usefixtures", None) if usefixtures is not None: argnames = usefixtures.args + argnames - names_closure, arg2fixturedeflist = self.fm.getfixtureclosure( + names_closure, arg2fixturedefs = self.fm.getfixtureclosure( argnames, self.node) - fixtureinfo = FuncFixtureInfo(names_closure, arg2fixturedeflist) + fixtureinfo = FuncFixtureInfo(names_closure, arg2fixturedefs) self._name2fixtureinfo[func] = fixtureinfo return fixtureinfo class FuncFixtureInfo: - def __init__(self, names_closure, name2fixturedeflist): + def __init__(self, names_closure, name2fixturedefs): self.names_closure = names_closure - self.name2fixturedeflist = name2fixturedeflist - - def getname2fixturedeflist_copy(self): - d = {} - for name, val in self.name2fixturedeflist.items(): - try: - val = list(val) - except TypeError: - pass - d[name] = val - return d + self.name2fixturedefs = name2fixturedefs def transfer_markers(funcobj, cls, mod): # XXX this should rather be code in the mark plugin or the mark @@ -675,7 +665,7 @@ self.module = module self.function = function self.fixturenames = fixtureinfo.names_closure - self._arg2fixturedeflist = fixtureinfo.name2fixturedeflist + self._arg2fixturedefs = fixtureinfo.name2fixturedefs self.cls = cls self.module = module self._calls = [] @@ -804,12 +794,12 @@ fm = session._fixturemanager available = [] - for argname in fm.arg2fixturedeflist: - fixturedeflist = fm.getfixturedeflist(argname, nodeid) - assert fixturedeflist is not None - if not fixturedeflist: + for argname in fm.arg2fixturedefs: + fixturedefs = fm.getfixturedefs(argname, nodeid) + assert fixturedefs is not None + if not fixturedefs: continue - fixturedef = fixturedeflist[-1] + fixturedef = fixturedefs[-1] loc = getlocation(fixturedef.func, curdir) available.append((len(fixturedef.baseid), curdir.bestrelpath(loc), @@ -1055,7 +1045,8 @@ self.getparent = pyfuncitem.getparent self._funcargs = self._pyfuncitem.funcargs.copy() self._fixtureinfo = fi = pyfuncitem._fixtureinfo - self._arg2fixturedeflist = fi.getname2fixturedeflist_copy() + self._arg2fixturedefs = fi.name2fixturedefs + self._arg2index = {} self.fixturenames = self._fixtureinfo.names_closure self._fixturemanager = pyfuncitem.session._fixturemanager self._parentid = pyfuncitem.parent.nodeid @@ -1066,15 +1057,20 @@ """ underlying collection node (depends on current request scope)""" return self._getscopeitem(self.scope) - def _getfixturedeflist(self, argname): - fixturedeflist = self._arg2fixturedeflist.get(argname, None) - if fixturedeflist is None: - fixturedeflist = self._fixturemanager.getfixturedeflist( + def _getnextfixturedef(self, argname): + fixturedefs = self._arg2fixturedefs.get(argname, None) + if fixturedefs is None: + # we arrive here because of a getfuncargvalue(argname) usage which + # was naturally not knowable at parsing/collection time + fixturedefs = self._fixturemanager.getfixturedefs( argname, self._parentid) - self._arg2fixturedeflist[argname] = fixturedeflist - if not fixturedeflist: + self._arg2fixturedefs[argname] = fixturedefs + # fixturedefs is immutable so we maintain a decreasing index + index = self._arg2index.get(argname, 0) - 1 + if fixturedefs is None or (-index > len(fixturedefs)): raise FixtureLookupError(argname, self) - return fixturedeflist + self._arg2index[argname] = index + return fixturedefs[index] @property def config(self): @@ -1212,12 +1208,11 @@ except KeyError: pass try: - fixturedeflist = self._getfixturedeflist(argname) + fixturedef = self._getnextfixturedef(argname) except FixtureLookupError: if argname == "request": return self raise - fixturedef = fixturedeflist.pop() self._fixturestack.append(fixturedef) try: result = self._getfuncargvalue(fixturedef) @@ -1353,7 +1348,7 @@ fm = self.request._fixturemanager nodeid = self.request._parentid available = [] - for name, fixturedef in fm.arg2fixturedeflist.items(): + for name, fixturedef in fm.arg2fixturedefs.items(): faclist = list(fm._matchfactories(fixturedef, self.request._parentid)) if faclist: available.append(name) @@ -1388,7 +1383,7 @@ def __init__(self, session): self.session = session self.config = session.config - self.arg2fixturedeflist = {} + self.arg2fixturedefs = {} self._seenplugins = set() self._holderobjseen = set() self._arg2finish = {} @@ -1427,7 +1422,7 @@ # make sure the self._autofixtures list is sorted # by scope, scopenum 0 is session self._autofixtures.sort( - key=lambda x: self.arg2fixturedeflist[x][-1].scopenum) + key=lambda x: self.arg2fixturedefs[x][-1].scopenum) defaultfixtures = defaultfixtures + tuple(self._autofixtures) self._defaultfixtures = defaultfixtures return defaultfixtures @@ -1435,7 +1430,7 @@ def getfixtureclosure(self, fixturenames, parentnode): # collect the closure of all fixtures , starting with the given # fixturenames as the initial set. As we have to visit all - # factory definitions anyway, we also return a arg2fixturedeflist + # factory definitions anyway, we also return a arg2fixturedefs # mapping so that the caller can reuse it and does not have # to re-discover fixturedefs again for each fixturename # (discovering matching fixtures for a given name/node is expensive) @@ -1447,23 +1442,23 @@ if arg not in fixturenames_closure: fixturenames_closure.append(arg) merge(fixturenames) - arg2fixturedeflist = {} + arg2fixturedefs = {} lastlen = -1 while lastlen != len(fixturenames_closure): lastlen = len(fixturenames_closure) for argname in fixturenames_closure: - if argname in arg2fixturedeflist: + if argname in arg2fixturedefs: continue - fixturedeflist = self.getfixturedeflist(argname, parentid) - arg2fixturedeflist[argname] = fixturedeflist - if fixturedeflist is not None: - for fixturedef in fixturedeflist: + fixturedefs = self.getfixturedefs(argname, parentid) + arg2fixturedefs[argname] = fixturedefs + if fixturedefs is not None: + for fixturedef in fixturedefs: merge(fixturedef.fixturenames) - return fixturenames_closure, arg2fixturedeflist + return fixturenames_closure, arg2fixturedefs def pytest_generate_tests(self, metafunc): for argname in metafunc.fixturenames: - faclist = metafunc._arg2fixturedeflist[argname] + faclist = metafunc._arg2fixturedefs[argname] if faclist is None: continue # will raise FixtureLookupError at setup time for fixturedef in faclist: @@ -1527,7 +1522,7 @@ fixturedef = FixtureDef(self, nodeid, name, obj, marker.scope, marker.params, unittest=unittest) - faclist = self.arg2fixturedeflist.setdefault(name, []) + faclist = self.arg2fixturedefs.setdefault(name, []) faclist.append(fixturedef) if marker.autouse: self._autofixtures.append(name) @@ -1536,16 +1531,16 @@ except AttributeError: pass - def getfixturedeflist(self, argname, nodeid): + def getfixturedefs(self, argname, nodeid): try: - fixturedeflist = self.arg2fixturedeflist[argname] + fixturedefs = self.arg2fixturedefs[argname] except KeyError: return None else: - return list(self._matchfactories(fixturedeflist, nodeid)) + return tuple(self._matchfactories(fixturedefs, nodeid)) - def _matchfactories(self, fixturedeflist, nodeid): - for fixturedef in fixturedeflist: + def _matchfactories(self, fixturedefs, nodeid): + for fixturedef in fixturedefs: if nodeid.startswith(fixturedef.baseid): yield fixturedef diff -r 4bdfe68104d5ec3a93670bf9f1e6b0702cd3a8c8 -r 648c9346124894406687104a05891d6b68eb844b testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -640,7 +640,7 @@ assert req.cls.__name__ == "TestB" assert req.instance.__class__ == req.cls - def XXXtest_request_contains_funcarg_arg2fixturedeflist(self, testdir): + def XXXtest_request_contains_funcarg_arg2fixturedefs(self, testdir): modcol = testdir.getmodulecol(""" def pytest_funcarg__something(request): pass @@ -650,9 +650,9 @@ """) item1, = testdir.genitems([modcol]) assert item1.name == "test_method" - arg2fixturedeflist = funcargs.FixtureRequest(item1)._arg2fixturedeflist - assert len(arg2fixturedeflist) == 1 - assert arg2fixturedeflist[0].__name__ == "pytest_funcarg__something" + arg2fixturedefs = funcargs.FixtureRequest(item1)._arg2fixturedefs + assert len(arg2fixturedefs) == 1 + assert arg2fixturedefs[0].__name__ == "pytest_funcarg__something" def test_getfuncargvalue_recursive(self, testdir): testdir.makeconftest(""" @@ -918,7 +918,7 @@ # on the funcarg level, so we don't need a full blown # initiliazation class FixtureInfo: - name2fixturedeflist = None + name2fixturedefs = None def __init__(self, names): self.names_closure = names names = funcargs.getfuncargnames(func) @@ -1974,7 +1974,7 @@ testdir.makepyfile(""" def test_hello(item, fm): for name in ("fm", "hello", "item"): - faclist = fm.getfixturedeflist(name, item.nodeid) + faclist = fm.getfixturedefs(name, item.nodeid) assert len(faclist) == 1 fac = faclist[0] assert fac.func.__name__ == "pytest_funcarg__" + name @@ -1990,7 +1990,7 @@ def pytest_funcarg__hello(self, request): return "class" def test_hello(self, item, fm): - faclist = fm.getfixturedeflist("hello", item.nodeid) + faclist = fm.getfixturedefs("hello", item.nodeid) print (faclist) assert len(faclist) == 3 assert faclist[0].func(item._request) == "conftest" https://bitbucket.org/hpk42/pytest/changeset/eb495bd8132e/ changeset: eb495bd8132e user: hpk42 date: 2012-10-16 14:19:38 summary: strike another use of getfuncargnames() and rename FixtureDef.fixturenames to "argnames" because it's really just the fixture function arguments affected #: 1 file diff -r 648c9346124894406687104a05891d6b68eb844b -r eb495bd8132e038f69f45ca775d60cdd1a7af818 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -142,25 +142,9 @@ if pyfuncitem._isyieldedfunction(): testfunction(*pyfuncitem._args) else: - try: - fixturenames = pyfuncitem.fixturenames - except AttributeError: - funcargs = pyfuncitem.funcargs - else: - funcargs = {} - for name in fixturenames: - funcargs[name] = pyfuncitem.funcargs[name] - testfunction(**funcargs) - -def pytest_pyfunc_call(__multicall__, pyfuncitem): - if not __multicall__.execute(): - testfunction = pyfuncitem.obj - if pyfuncitem._isyieldedfunction(): - testfunction(*pyfuncitem._args) - else: funcargs = pyfuncitem.funcargs testargs = {} - for arg in getfuncargnames(testfunction): + for arg in pyfuncitem._fixtureinfo.argnames: testargs[arg] = funcargs[arg] testfunction(**testargs) @@ -359,16 +343,19 @@ else: argnames = () usefixtures = getattr(func, "usefixtures", None) + initialnames = argnames if usefixtures is not None: - argnames = usefixtures.args + argnames + initialnames = usefixtures.args + initialnames names_closure, arg2fixturedefs = self.fm.getfixtureclosure( - argnames, self.node) - fixtureinfo = FuncFixtureInfo(names_closure, arg2fixturedefs) + initialnames, self.node) + fixtureinfo = FuncFixtureInfo(argnames, names_closure, + arg2fixturedefs) self._name2fixtureinfo[func] = fixtureinfo return fixtureinfo class FuncFixtureInfo: - def __init__(self, names_closure, name2fixturedefs): + def __init__(self, argnames, names_closure, name2fixturedefs): + self.argnames = argnames self.names_closure = names_closure self.name2fixturedefs = name2fixturedefs @@ -1265,7 +1252,7 @@ # (XXX analyse exact finalizing mechanics / cleanup) self.session._setupstate.addfinalizer(fixturedef.finish, self.node) self._fixturemanager.addargfinalizer(fixturedef.finish, argname) - for subargname in fixturedef.fixturenames: # XXX all deps? + for subargname in fixturedef.argnames: # XXX all deps? self._fixturemanager.addargfinalizer(fixturedef.finish, subargname) mp.setattr(self, "addfinalizer", fixturedef.addfinalizer) # finally perform the fixture call @@ -1453,7 +1440,7 @@ arg2fixturedefs[argname] = fixturedefs if fixturedefs is not None: for fixturedef in fixturedefs: - merge(fixturedef.fixturenames) + merge(fixturedef.argnames) return fixturenames_closure, arg2fixturedefs def pytest_generate_tests(self, metafunc): @@ -1567,7 +1554,7 @@ self.scopenum = scopes.index(scope or "function") self.params = params startindex = unittest and 1 or None - self.fixturenames = getfuncargnames(func, startindex=startindex) + self.argnames = getfuncargnames(func, startindex=startindex) self.unittest = unittest self.active = False self._finalizer = [] @@ -1587,7 +1574,7 @@ def execute(self, request): kwargs = {} - for newname in self.fixturenames: + for newname in self.argnames: kwargs[newname] = request.getfuncargvalue(newname) if self.unittest: result = self.func(request.instance, **kwargs) https://bitbucket.org/hpk42/pytest/changeset/5f7bfa93ff2e/ changeset: 5f7bfa93ff2e user: hpk42 date: 2012-10-16 16:13:12 summary: refine parsefactories interface, fix two_classes test originally reported by Alex Okrushko, also add a few more tests to make sure autouse-fixtures are properly distinguished affected #: 5 files diff -r eb495bd8132e038f69f45ca775d60cdd1a7af818 -r 5f7bfa93ff2eb7a7dded4c2096704264e692311d _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev23' +__version__ = '2.3.0.dev24' diff -r eb495bd8132e038f69f45ca775d60cdd1a7af818 -r 5f7bfa93ff2eb7a7dded4c2096704264e692311d _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -83,7 +83,7 @@ group.addoption('--fixtures', '--fixtures', action="store_true", dest="showfixtures", default=False, help="show available fixtures, sorted by plugin appearance") - parser.addini("usefixtures", type="args", default=(), + parser.addini("usefixtures", type="args", default=[], help="list of default fixtures to be used with this project") parser.addini("python_files", type="args", default=('test_*.py', '*_test.py'), @@ -379,7 +379,7 @@ return self._memoizedcall('_obj', self._importtestmodule) def collect(self): - self.session._fixturemanager._parsefactories(self.obj, self.nodeid) + self.session._fixturemanager.parsefactories(self) return super(Module, self).collect() def _importtestmodule(self): @@ -452,7 +452,7 @@ return obj def collect(self): - self.session._fixturemanager._parsefactories(self.obj, self.nodeid) + self.session._fixturemanager.parsefactories(self) return super(Instance, self).collect() def newinstance(self): @@ -781,7 +781,7 @@ fm = session._fixturemanager available = [] - for argname in fm.arg2fixturedefs: + for argname in fm._arg2fixturedefs: fixturedefs = fm.getfixturedefs(argname, nodeid) assert fixturedefs is not None if not fixturedefs: @@ -1335,7 +1335,7 @@ fm = self.request._fixturemanager nodeid = self.request._parentid available = [] - for name, fixturedef in fm.arg2fixturedefs.items(): + for name, fixturedef in fm._arg2fixturedefs.items(): faclist = list(fm._matchfactories(fixturedef, self.request._parentid)) if faclist: available.append(name) @@ -1370,11 +1370,11 @@ def __init__(self, session): self.session = session self.config = session.config - self.arg2fixturedefs = {} + self._arg2fixturedefs = {} self._seenplugins = set() self._holderobjseen = set() self._arg2finish = {} - self._autofixtures = [] + self._nodeid_and_autousenames = [("", self.config.getini("usefixtures"))] session.config.pluginmanager.register(self, "funcmanage") ### XXX this hook should be called for historic events like pytest_configure @@ -1391,7 +1391,7 @@ else: if p.basename.startswith("conftest.py"): nodeid = p.dirpath().relto(self.session.fspath) - self._parsefactories(plugin, nodeid) + self.parsefactories(plugin, nodeid) self._seenplugins.add(plugin) @pytest.mark.tryfirst @@ -1400,19 +1400,21 @@ for plugin in plugins: self.pytest_plugin_registered(plugin) - def getdefaultfixtures(self): - """ return a tuple of default fixture names (XXX for the given file path). """ - try: - return self._defaultfixtures - except AttributeError: - defaultfixtures = tuple(self.config.getini("usefixtures")) - # make sure the self._autofixtures list is sorted - # by scope, scopenum 0 is session - self._autofixtures.sort( - key=lambda x: self.arg2fixturedefs[x][-1].scopenum) - defaultfixtures = defaultfixtures + tuple(self._autofixtures) - self._defaultfixtures = defaultfixtures - return defaultfixtures + def _getautousenames(self, nodeid): + """ return a tuple of fixture names to be used. """ + autousenames = [] + for baseid, basenames in self._nodeid_and_autousenames: + if nodeid.startswith(baseid): + if baseid: + i = len(baseid) + 1 + nextchar = nodeid[i:i+1] + if nextchar and nextchar not in ":/": + continue + autousenames.extend(basenames) + # make sure autousenames are sorted by scope, scopenum 0 is session + autousenames.sort( + key=lambda x: self._arg2fixturedefs[x][-1].scopenum) + return autousenames def getfixtureclosure(self, fixturenames, parentnode): # collect the closure of all fixtures , starting with the given @@ -1423,7 +1425,7 @@ # (discovering matching fixtures for a given name/node is expensive) parentid = parentnode.nodeid - fixturenames_closure = list(self.getdefaultfixtures()) + fixturenames_closure = self._getautousenames(parentid) def merge(otherlist): for arg in otherlist: if arg not in fixturenames_closure: @@ -1484,10 +1486,16 @@ for fin in l: fin() - def _parsefactories(self, holderobj, nodeid, unittest=False): + def parsefactories(self, node_or_obj, nodeid=None, unittest=False): + if nodeid is not None: + holderobj = node_or_obj + else: + holderobj = node_or_obj.obj + nodeid = node_or_obj.nodeid if holderobj in self._holderobjseen: return self._holderobjseen.add(holderobj) + autousenames = [] for name in dir(holderobj): obj = getattr(holderobj, name) if not callable(obj): @@ -1509,18 +1517,16 @@ fixturedef = FixtureDef(self, nodeid, name, obj, marker.scope, marker.params, unittest=unittest) - faclist = self.arg2fixturedefs.setdefault(name, []) + faclist = self._arg2fixturedefs.setdefault(name, []) faclist.append(fixturedef) if marker.autouse: - self._autofixtures.append(name) - try: - del self._defaultfixtures - except AttributeError: - pass + autousenames.append(name) + if autousenames: + self._nodeid_and_autousenames.append((nodeid, autousenames)) def getfixturedefs(self, argname, nodeid): try: - fixturedefs = self.arg2fixturedefs[argname] + fixturedefs = self._arg2fixturedefs[argname] except KeyError: return None else: diff -r eb495bd8132e038f69f45ca775d60cdd1a7af818 -r 5f7bfa93ff2eb7a7dded4c2096704264e692311d _pytest/unittest.py --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -22,8 +22,7 @@ class UnitTestCase(pytest.Class): def collect(self): - self.session._fixturemanager._parsefactories(self.obj, self.nodeid, - unittest=True) + self.session._fixturemanager.parsefactories(self, unittest=True) loader = py.std.unittest.TestLoader() module = self.getparent(pytest.Module).obj cls = self.obj diff -r eb495bd8132e038f69f45ca775d60cdd1a7af818 -r 5f7bfa93ff2eb7a7dded4c2096704264e692311d setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev23', + version='2.3.0.dev24', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r eb495bd8132e038f69f45ca775d60cdd1a7af818 -r 5f7bfa93ff2eb7a7dded4c2096704264e692311d testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -2026,14 +2026,14 @@ def test_parsefactories_conftest(self, testdir): testdir.makepyfile(""" def test_check_setup(item, fm): - assert len(fm._autofixtures) == 2 - assert "perfunction2" in fm._autofixtures - assert "perfunction" in fm._autofixtures + autousenames = fm._getautousenames(item.nodeid) + assert len(autousenames) == 2 + assert "perfunction2" in autousenames + assert "perfunction" in autousenames """) reprec = testdir.inline_run("-s") reprec.assertoutcome(passed=1) - @pytest.mark.xfail def test_two_classes_separated_autouse(self, testdir): testdir.makepyfile(""" import pytest @@ -2113,6 +2113,45 @@ reprec = testdir.inline_run("-s") reprec.assertoutcome(failed=0, passed=0) + def test_autouse_in_conftests(self, testdir): + a = testdir.mkdir("a") + b = testdir.mkdir("a1") + conftest = testdir.makeconftest(""" + import pytest + @pytest.fixture(autouse=True) + def hello(): + xxx + """) + conftest.move(a.join(conftest.basename)) + a.join("test_something.py").write("def test_func(): pass") + b.join("test_otherthing.py").write("def test_func(): pass") + result = testdir.runpytest() + result.stdout.fnmatch_lines(""" + *1 passed*1 error* + """) + + def test_autouse_in_module_and_two_classes(self, testdir): + testdir.makepyfile(""" + import pytest + l = [] + @pytest.fixture(autouse=True) + def append1(): + l.append("module") + def test_x(): + assert l == ["module"] + + class TestA: + @pytest.fixture(autouse=True) + def append2(self): + l.append("A") + def test_hello(self): + assert l == ["module", "module", "A"], l + class TestA2: + def test_world(self): + assert l == ["module", "module", "A", "module"], l + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=3) class TestSetupManagement: def test_funcarg_and_setup(self, testdir): @@ -2220,7 +2259,7 @@ def test_2(self): pass """) - reprec = testdir.inline_run("-v",) + reprec = testdir.inline_run("-v","-s") reprec.assertoutcome(passed=8) config = reprec.getcalls("pytest_unconfigure")[0].config l = config._conftest.getconftestmodules(p)[0].l Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Oct 17 11:20:57 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 17 Oct 2012 09:20:57 -0000 Subject: [py-svn] commit/pytest: 3 new changesets Message-ID: <20121017092057.1569.1769@bitbucket16.managed.contegix.com> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/7a247b9d8f29/ changeset: 7a247b9d8f29 user: hpk42 date: 2012-10-16 16:27:51 summary: remove outdated IMPL.txt and move up-to-date doc bits to FixtureMapper class. affected #: 2 files diff -r 5f7bfa93ff2eb7a7dded4c2096704264e692311d -r 7a247b9d8f29ca21c0a545d0b00545bd3507f57d IMPL.txt --- a/IMPL.txt +++ /dev/null @@ -1,40 +0,0 @@ - -internal fixture mechanics ----------------------------- - -fixture mechanics are contained in the python.py plugin. - -pytest fixtures are stored and managed from the FixtureManager (fm) class. -Upon collection fm.parsefactories() is called multiple times to parse -fixture function definitions into FixtureDef objects. - -During collection of test functions, metafunc needs to grow a "fixturenames" -list so that pytest_generate_tests() hooks can check it. These fixturenames -are a closure of all known fixtures to be used for this function: - -- ini-defined usefixtures -- autouse-marked fixtures along the collection chain up from the function -- usefixtures markers at module/class/function level -- test function funcargs - -The latter list (without the closure) is also called "_initialfixtures" -and will be used during the test setup phase. - -Upon the test-setup phases initialfixtures are instantiated which -will subsequently create the full fixture closure (as was computed in -metafunc.fixturenames during collection). As fixture functions -can invoke request.getfuncargvalue() the actual closure may be even -bigger. - -object model ---------------------- - -As part of the metafunc-protocol parents of Function nodes get a -ParentFixtures() object, containing helping/caching for -for a function. .getfixtureinfo(func) returns a FixtureInfo with these -attributes: - -- names_initial: list of initial fixture names (see above) -- names_closure: closure of all fixture names -- name2fixturedefs: for creating the value - diff -r 5f7bfa93ff2eb7a7dded4c2096704264e692311d -r 7a247b9d8f29ca21c0a545d0b00545bd3507f57d _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -327,6 +327,38 @@ keywords={callspec.id:True}) class FixtureMapper: + """ + pytest fixtures definitions and information is stored and managed + from this class. + + During collection fm.parsefactories() is called multiple times to parse + fixture function definitions into FixtureDef objects and internal + data structures. + + During collection of test functions, metafunc-mechanics instantiate + a FuncFixtureInfo object which is cached in a FixtureMapper instance + which itself lives on the parent collector. This FuncFixtureInfo object + is later retrieved by Function nodes which themselves offer a fixturenames + attribute. + + The FuncFixtureInfo object holds information about fixtures and FixtureDefs + relevant for a particular function. An initial list of fixtures is + assembled like this: + + - ini-defined usefixtures + - autouse-marked fixtures along the collection chain up from the function + - usefixtures markers at module/class/function level + - test function funcargs + + Subsequently the funcfixtureinfo.fixturenames attribute is computed + as the closure of the fixtures needed to setup the initial fixtures, + i. e. fixtures needed by fixture functions themselves are appended + to the fixturenames list. + + Upon the test-setup phases all fixturenames are instantiated, retrieved + by a lookup on a FixtureMapper(). + """ + def __init__(self, node, funcargs=True): self.node = node self.fm = node.session._fixturemanager https://bitbucket.org/hpk42/pytest/changeset/c5d791065a53/ changeset: c5d791065a53 user: hpk42 date: 2012-10-17 09:21:04 summary: typographic fixes, little simplification affected #: 3 files diff -r 7a247b9d8f29ca21c0a545d0b00545bd3507f57d -r c5d791065a5339c6e9a5fb90aa66692af465f257 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -361,7 +361,6 @@ def __init__(self, node, funcargs=True): self.node = node - self.fm = node.session._fixturemanager self._name2fixtureinfo = {} self.hasfuncargs = funcargs @@ -378,8 +377,9 @@ initialnames = argnames if usefixtures is not None: initialnames = usefixtures.args + initialnames - names_closure, arg2fixturedefs = self.fm.getfixtureclosure( - initialnames, self.node) + fm = self.node.session._fixturemanager + names_closure, arg2fixturedefs = fm.getfixtureclosure(initialnames, + self.node) fixtureinfo = FuncFixtureInfo(argnames, names_closure, arg2fixturedefs) self._name2fixtureinfo[func] = fixtureinfo diff -r 7a247b9d8f29ca21c0a545d0b00545bd3507f57d -r c5d791065a5339c6e9a5fb90aa66692af465f257 testing/test_nose.py --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -28,7 +28,7 @@ from _pytest.nose import call_optional l = [] class A: - @pytest.fixture( autouse=True) + @pytest.fixture(autouse=True) def f(self): l.append(1) call_optional(A(), "f") diff -r 7a247b9d8f29ca21c0a545d0b00545bd3507f57d -r c5d791065a5339c6e9a5fb90aa66692af465f257 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -456,24 +456,6 @@ "*2 passed*" ]) -def test_generate_tests_only_done_in_subdir(testdir): - sub1 = testdir.mkpydir("sub1") - sub2 = testdir.mkpydir("sub2") - sub1.join("conftest.py").write(py.code.Source(""" - def pytest_generate_tests(metafunc): - assert metafunc.function.__name__ == "test_1" - """)) - sub2.join("conftest.py").write(py.code.Source(""" - def pytest_generate_tests(metafunc): - assert metafunc.function.__name__ == "test_2" - """)) - sub1.join("test_in_sub1.py").write("def test_1(): pass") - sub2.join("test_in_sub2.py").write("def test_2(): pass") - result = testdir.runpytest("-v", "-s", sub1, sub2, sub1) - result.stdout.fnmatch_lines([ - "*3 passed*" - ]) - def test_modulecol_roundtrip(testdir): modcol = testdir.getmodulecol("pass", withinit=True) trail = modcol.nodeid @@ -1404,7 +1386,7 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=5) - def test_usemarkers_seen_in_generate_tests(self, testdir): + def test_usefixtures_seen_in_generate_tests(self, testdir): testdir.makepyfile(""" import pytest def pytest_generate_tests(metafunc): @@ -1418,6 +1400,25 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=1) + def test_generate_tests_only_done_in_subdir(self, testdir): + sub1 = testdir.mkpydir("sub1") + sub2 = testdir.mkpydir("sub2") + sub1.join("conftest.py").write(py.code.Source(""" + def pytest_generate_tests(metafunc): + assert metafunc.function.__name__ == "test_1" + """)) + sub2.join("conftest.py").write(py.code.Source(""" + def pytest_generate_tests(metafunc): + assert metafunc.function.__name__ == "test_2" + """)) + sub1.join("test_in_sub1.py").write("def test_1(): pass") + sub2.join("test_in_sub2.py").write("def test_2(): pass") + result = testdir.runpytest("-v", "-s", sub1, sub2, sub1) + result.stdout.fnmatch_lines([ + "*3 passed*" + ]) + + def test_conftest_funcargs_only_available_in_subdir(testdir): sub1 = testdir.mkpydir("sub1") sub2 = testdir.mkpydir("sub2") @@ -1464,6 +1465,7 @@ assert clscol.funcargs['arg1'] == 42 + def test_funcarg_lookup_error(testdir): p = testdir.makepyfile(""" def test_lookup_error(unknown): @@ -2000,18 +2002,18 @@ reprec = testdir.inline_run("-s") reprec.assertoutcome(passed=1) -class TestSetupDiscovery: +class TestAutouseDiscovery: def pytest_funcarg__testdir(self, testdir): testdir.makeconftest(""" import pytest - @pytest.fixture( autouse=True) + @pytest.fixture(autouse=True) def perfunction(request, tmpdir): pass @pytest.fixture() def arg1(tmpdir): pass - @pytest.fixture( autouse=True) + @pytest.fixture(autouse=True) def perfunction2(arg1): pass @@ -2153,7 +2155,7 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=3) -class TestSetupManagement: +class TestAutouseManagement: def test_funcarg_and_setup(self, testdir): testdir.makepyfile(""" import pytest @@ -2179,7 +2181,7 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=2) - def test_setup_uses_parametrized_resource(self, testdir): + def test_uses_parametrized_resource(self, testdir): testdir.makepyfile(""" import pytest l = [] @@ -2187,7 +2189,7 @@ def arg(request): return request.param - @pytest.fixture( autouse=True) + @pytest.fixture(autouse=True) def something(arg): l.append(arg) @@ -2203,7 +2205,7 @@ reprec = testdir.inline_run("-s") reprec.assertoutcome(passed=2) - def test_session_parametrized_function_setup(self, testdir): + def test_session_parametrized_function(self, testdir): testdir.makepyfile(""" import pytest @@ -2265,7 +2267,7 @@ l = config._conftest.getconftestmodules(p)[0].l assert l == ["fin_a1", "fin_a2", "fin_b1", "fin_b2"] * 2 - def test_setup_scope_ordering(self, testdir): + def test_scope_ordering(self, testdir): testdir.makepyfile(""" import pytest l = [] @@ -2799,7 +2801,7 @@ def test_setupfunc_missing_funcarg(self, testdir): testdir.makepyfile(""" import pytest - @pytest.fixture( autouse=True) + @pytest.fixture(autouse=True) def gen(qwe123): return 1 def test_something(): @@ -2834,7 +2836,7 @@ def arg(request): return request.param - @pytest.fixture( autouse=True) + @pytest.fixture(autouse=True) def mysetup(request, arg): assert not hasattr(request, "param") def test_1(arg): @@ -2850,7 +2852,7 @@ @pytest.fixture(scope='module', autouse=True) def setup_module(): l.append("module") - @pytest.fixture( autouse=True) + @pytest.fixture(autouse=True) def setup_function(): l.append("function") @@ -2861,7 +2863,7 @@ @pytest.fixture(scope="class", autouse=True) def setup_class(self): l.append("class") - @pytest.fixture( autouse=True) + @pytest.fixture(autouse=True) def setup_method(self): l.append("method") def test_method(self): @@ -2880,7 +2882,7 @@ import pytest l = [] - @pytest.fixture( autouse=True) + @pytest.fixture(autouse=True) def fix1(): l.append(1) @pytest.fixture() @@ -2902,7 +2904,7 @@ @pytest.fixture() def farg(arg1): pass - @pytest.fixture( autouse=True) + @pytest.fixture(autouse=True) def sarg(tmpdir): pass def test_function(request, farg): https://bitbucket.org/hpk42/pytest/changeset/b2aa891c2fcf/ changeset: b2aa891c2fcf user: hpk42 date: 2012-10-17 11:20:45 summary: simplify/integrate fixturemapper into FixtureManager also fix jstests test failures affected #: 5 files diff -r c5d791065a5339c6e9a5fb90aa66692af465f257 -r b2aa891c2fcf1dcfe5dcf833df00d76e2b645765 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev24' +__version__ = '2.3.0.dev25' diff -r c5d791065a5339c6e9a5fb90aa66692af465f257 -r b2aa891c2fcf1dcfe5dcf833df00d76e2b645765 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -250,18 +250,6 @@ return fspath, lineno, modpath class PyCollector(PyobjMixin, pytest.Collector): - def _fixturemapper(): - def get(self): - try: - return self._fixturemapper_memo - except AttributeError: - self._fixturemapper_memo = FixtureMapper(self, funcargs=False) - return self._fixturemapper_memo - def set(self, val): - assert not hasattr(self, "_fixturemapper_memo") - self._fixturemapper_memo = val - return property(get, set) - _fixturemapper = _fixturemapper() def funcnamefilter(self, name): for prefix in self.config.getini("python_functions"): @@ -305,9 +293,8 @@ clscol = self.getparent(Class) cls = clscol and clscol.obj or None transfer_markers(funcobj, cls, module) - if not hasattr(self, "_fixturemapper_memo"): - self._fixturemapper = FixtureMapper(self) - fixtureinfo = self._fixturemapper.getfixtureinfo(funcobj, cls) + fm = self.session._fixturemanager + fixtureinfo = fm.getfixtureinfo(self, funcobj, cls) metafunc = Metafunc(funcobj, fixtureinfo, self.config, cls=cls, module=module) gentesthook = self.config.hook.pytest_generate_tests @@ -326,64 +313,6 @@ callspec=callspec, callobj=funcobj, keywords={callspec.id:True}) -class FixtureMapper: - """ - pytest fixtures definitions and information is stored and managed - from this class. - - During collection fm.parsefactories() is called multiple times to parse - fixture function definitions into FixtureDef objects and internal - data structures. - - During collection of test functions, metafunc-mechanics instantiate - a FuncFixtureInfo object which is cached in a FixtureMapper instance - which itself lives on the parent collector. This FuncFixtureInfo object - is later retrieved by Function nodes which themselves offer a fixturenames - attribute. - - The FuncFixtureInfo object holds information about fixtures and FixtureDefs - relevant for a particular function. An initial list of fixtures is - assembled like this: - - - ini-defined usefixtures - - autouse-marked fixtures along the collection chain up from the function - - usefixtures markers at module/class/function level - - test function funcargs - - Subsequently the funcfixtureinfo.fixturenames attribute is computed - as the closure of the fixtures needed to setup the initial fixtures, - i. e. fixtures needed by fixture functions themselves are appended - to the fixturenames list. - - Upon the test-setup phases all fixturenames are instantiated, retrieved - by a lookup on a FixtureMapper(). - """ - - def __init__(self, node, funcargs=True): - self.node = node - self._name2fixtureinfo = {} - self.hasfuncargs = funcargs - - def getfixtureinfo(self, func, cls): - try: - return self._name2fixtureinfo[func] - except KeyError: - pass - if self.hasfuncargs: - argnames = getfuncargnames(func, int(cls is not None)) - else: - argnames = () - usefixtures = getattr(func, "usefixtures", None) - initialnames = argnames - if usefixtures is not None: - initialnames = usefixtures.args + initialnames - fm = self.node.session._fixturemanager - names_closure, arg2fixturedefs = fm.getfixtureclosure(initialnames, - self.node) - fixtureinfo = FuncFixtureInfo(argnames, names_closure, - arg2fixturedefs) - self._name2fixtureinfo[func] = fixtureinfo - return fixtureinfo class FuncFixtureInfo: def __init__(self, argnames, names_closure, name2fixturedefs): @@ -599,11 +528,22 @@ try: request = function._request except AttributeError: - # the special jstests class with a custom .obj - fi = FixtureMapper(function).getfixtureinfo(function.obj, None) + # XXX this special code path is only expected to execute + # with the oejskit plugin. It uses classes with funcargs + # and we thus have to work a bit to allow this. + fm = function.session._fixturemanager + fi = fm.getfixtureinfo(function.parent, function.obj, None) function._fixtureinfo = fi request = function._request = FixtureRequest(function) - request._fillfixtures() + request._fillfixtures() + # prune out funcargs for jstests + newfuncargs = {} + for name in fi.argnames: + newfuncargs[name] = function.funcargs[name] + function.funcargs = newfuncargs + else: + request._fillfixtures() + _notexists = object() @@ -953,8 +893,9 @@ for name, val in keywords.items(): setattr(self.markers, name, val) - fi = self.parent._fixturemapper.getfixtureinfo(self.obj, self.cls) - self._fixtureinfo = fi + fm = self.session._fixturemanager + self._fixtureinfo = fi = fm.getfixtureinfo(self.parent, + self.obj, self.cls) self.fixturenames = fi.names_closure if self._isyieldedfunction(): assert not callspec, ( @@ -1395,6 +1336,37 @@ tw.line("%s:%d" % (self.filename, self.firstlineno+1)) class FixtureManager: + """ + pytest fixtures definitions and information is stored and managed + from this class. + + During collection fm.parsefactories() is called multiple times to parse + fixture function definitions into FixtureDef objects and internal + data structures. + + During collection of test functions, metafunc-mechanics instantiate + a FuncFixtureInfo object which is cached per node/func-name. + This FuncFixtureInfo object is later retrieved by Function nodes + which themselves offer a fixturenames attribute. + + The FuncFixtureInfo object holds information about fixtures and FixtureDefs + relevant for a particular function. An initial list of fixtures is + assembled like this: + + - ini-defined usefixtures + - autouse-marked fixtures along the collection chain up from the function + - usefixtures markers at module/class/function level + - test function funcargs + + Subsequently the funcfixtureinfo.fixturenames attribute is computed + as the closure of the fixtures needed to setup the initial fixtures, + i. e. fixtures needed by fixture functions themselves are appended + to the fixturenames list. + + Upon the test-setup phases all fixturenames are instantiated, retrieved + by a lookup of their FuncFixtureInfo. + """ + _argprefix = "pytest_funcarg__" FixtureLookupError = FixtureLookupError FixtureLookupErrorRepr = FixtureLookupErrorRepr @@ -1409,6 +1381,34 @@ self._nodeid_and_autousenames = [("", self.config.getini("usefixtures"))] session.config.pluginmanager.register(self, "funcmanage") + self._nodename2fixtureinfo = {} + + def getfixtureinfo(self, node, func, cls): + key = (node, func.__name__) + try: + return self._nodename2fixtureinfo[key] + except KeyError: + pass + if not hasattr(node, "nofuncargs"): + if cls is not None: + startindex = 1 + else: + startindex = None + argnames = getfuncargnames(func, startindex) + else: + argnames = () + usefixtures = getattr(func, "usefixtures", None) + initialnames = argnames + if usefixtures is not None: + initialnames = usefixtures.args + initialnames + fm = node.session._fixturemanager + names_closure, arg2fixturedefs = fm.getfixtureclosure(initialnames, + node) + fixtureinfo = FuncFixtureInfo(argnames, names_closure, + arg2fixturedefs) + self._nodename2fixtureinfo[key] = fixtureinfo + return fixtureinfo + ### XXX this hook should be called for historic events like pytest_configure ### so that we don't have to do the below pytest_configure hook def pytest_plugin_registered(self, plugin): diff -r c5d791065a5339c6e9a5fb90aa66692af465f257 -r b2aa891c2fcf1dcfe5dcf833df00d76e2b645765 _pytest/unittest.py --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -20,6 +20,8 @@ return UnitTestCase(name, parent=collector) class UnitTestCase(pytest.Class): + nofuncargs = True # marker for fixturemanger.getfixtureinfo() + # to declare that our children do not support funcargs def collect(self): self.session._fixturemanager.parsefactories(self, unittest=True) diff -r c5d791065a5339c6e9a5fb90aa66692af465f257 -r b2aa891c2fcf1dcfe5dcf833df00d76e2b645765 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev24', + version='2.3.0.dev25', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r c5d791065a5339c6e9a5fb90aa66692af465f257 -r b2aa891c2fcf1dcfe5dcf833df00d76e2b645765 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -280,7 +280,6 @@ config = testdir.parseconfigure() session = testdir.Session(config) session._fixturemanager = FixtureManager(session) - session._fixturemapper = funcargs.FixtureMapper(session, funcargs=False) def func1(): pass def func2(): @@ -1440,29 +1439,58 @@ "*2 passed*" ]) -def test_funcarg_non_pycollectobj(testdir): # rough jstests usage - testdir.makeconftest(""" - import pytest - def pytest_pycollect_makeitem(collector, name, obj): - if name == "MyClass": - return MyCollector(name, parent=collector) - class MyCollector(pytest.Collector): - def reportinfo(self): - return self.fspath, 3, "xyz" - """) - modcol = testdir.getmodulecol(""" - def pytest_funcarg__arg1(request): - return 42 - class MyClass: - pass - """) - # this hook finds funcarg factories - rep = modcol.ihook.pytest_make_collect_report(collector=modcol) - clscol = rep.result[0] - clscol.obj = lambda arg1: None - clscol.funcargs = {} - funcargs.fillfixtures(clscol) - assert clscol.funcargs['arg1'] == 42 +class TestOEJSKITSpecials: + def test_funcarg_non_pycollectobj(self, testdir): # rough jstests usage + testdir.makeconftest(""" + import pytest + def pytest_pycollect_makeitem(collector, name, obj): + if name == "MyClass": + return MyCollector(name, parent=collector) + class MyCollector(pytest.Collector): + def reportinfo(self): + return self.fspath, 3, "xyz" + """) + modcol = testdir.getmodulecol(""" + def pytest_funcarg__arg1(request): + return 42 + class MyClass: + pass + """) + # this hook finds funcarg factories + rep = modcol.ihook.pytest_make_collect_report(collector=modcol) + clscol = rep.result[0] + clscol.obj = lambda arg1: None + clscol.funcargs = {} + funcargs.fillfixtures(clscol) + assert clscol.funcargs['arg1'] == 42 + + def test_autouse_fixture(self, testdir): # rough jstests usage + testdir.makeconftest(""" + import pytest + def pytest_pycollect_makeitem(collector, name, obj): + if name == "MyClass": + return MyCollector(name, parent=collector) + class MyCollector(pytest.Collector): + def reportinfo(self): + return self.fspath, 3, "xyz" + """) + modcol = testdir.getmodulecol(""" + import pytest + @pytest.fixture(autouse=True) + def hello(): + pass + def pytest_funcarg__arg1(request): + return 42 + class MyClass: + pass + """) + # this hook finds funcarg factories + rep = modcol.ihook.pytest_make_collect_report(collector=modcol) + clscol = rep.result[0] + clscol.obj = lambda: None + clscol.funcargs = {} + funcargs.fillfixtures(clscol) + assert not clscol.funcargs Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Oct 17 13:05:22 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 17 Oct 2012 11:05:22 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20121017110522.23251.18089@bitbucket21.managed.contegix.com> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/b39424a4326a/ changeset: b39424a4326a user: hpk42 date: 2012-10-17 11:50:32 summary: remove unused code affected #: 1 file diff -r b2aa891c2fcf1dcfe5dcf833df00d76e2b645765 -r b39424a4326a04c6c20ae2bf25e578e703599fd6 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -53,21 +53,6 @@ defaultfuncargprefixmarker = fixture() -def cached_property(f): - """returns a cached property that is calculated by function f. - taken from http://code.activestate.com/recipes/576563-cached-property/""" - def get(self): - try: - return self._property_cache[f] - except AttributeError: - self._property_cache = {} - x = self._property_cache[f] = f(self) - return x - except KeyError: - x = self._property_cache[f] = f(self) - return x - return property(get) - def pyobj_property(name): def get(self): node = self.getparent(getattr(pytest, name)) https://bitbucket.org/hpk42/pytest/changeset/29f960b2322f/ changeset: 29f960b2322f user: hpk42 date: 2012-10-17 12:57:05 summary: improve --fixtures output with per-plugin grouping and hiding underscore names in non-verbose mode, re-introduce --funcargs for compatibiliy affected #: 4 files diff -r b39424a4326a04c6c20ae2bf25e578e703599fd6 -r 29f960b2322f533da940d72265adc8b0de5dc27c _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev25' +__version__ = '2.3.0.dev26' diff -r b39424a4326a04c6c20ae2bf25e578e703599fd6 -r 29f960b2322f533da940d72265adc8b0de5dc27c _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -65,7 +65,7 @@ def pytest_addoption(parser): group = parser.getgroup("general") - group.addoption('--fixtures', '--fixtures', + group.addoption('--fixtures', '--funcargs', action="store_true", dest="showfixtures", default=False, help="show available fixtures, sorted by plugin appearance") parser.addini("usefixtures", type="args", default=[], @@ -746,15 +746,24 @@ fixturedef = fixturedefs[-1] loc = getlocation(fixturedef.func, curdir) available.append((len(fixturedef.baseid), + fixturedef.func.__module__, curdir.bestrelpath(loc), fixturedef.argname, fixturedef)) available.sort() - for baseid, bestrel, argname, fixturedef in available: + currentmodule = None + for baseid, module, bestrel, argname, fixturedef in available: + if currentmodule != module: + if not module.startswith("_pytest."): + tw.line() + tw.sep("-", "fixtures defined from %s" %(module,)) + currentmodule = module + if verbose <= 0 and argname[0] == "_": + continue if verbose > 0: - funcargspec = "%s -- %s" %(name, loc,) + funcargspec = "%s -- %s" %(argname, loc,) else: - funcargspec = argname # "%s %s" %(baseid, argname) + funcargspec = argname tw.line(funcargspec, green=True) loc = getlocation(fixturedef.func, curdir) doc = fixturedef.func.__doc__ or "" diff -r b39424a4326a04c6c20ae2bf25e578e703599fd6 -r 29f960b2322f533da940d72265adc8b0de5dc27c setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev25', + version='2.3.0.dev26', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r b39424a4326a04c6c20ae2bf25e578e703599fd6 -r 29f960b2322f533da940d72265adc8b0de5dc27c testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1575,6 +1575,10 @@ """ class TestShowFixtures: + def test_funcarg_compat(self, testdir): + config = testdir.parseconfigure("--funcargs") + assert config.option.showfixtures + def test_show_fixtures(self, testdir): result = testdir.runpytest("--fixtures") result.stdout.fnmatch_lines([ @@ -1583,19 +1587,32 @@ ] ) + def test_show_fixtures_verbose(self, testdir): + result = testdir.runpytest("--fixtures", "-v") + result.stdout.fnmatch_lines([ + "*tmpdir*", + "*temporary directory*", + ] + ) + def test_show_fixtures_testmodule(self, testdir): p = testdir.makepyfile(''' import pytest @pytest.fixture + def _arg0(): + """ hidden """ + @pytest.fixture def arg1(): """ hello world """ ''') result = testdir.runpytest("--fixtures", p) result.stdout.fnmatch_lines(""" - *tmpdir* + *tmpdir + *fixtures defined from* *arg1* *hello world* """) + assert "arg0" not in result.stdout.str() @pytest.mark.parametrize("testmod", [True, False]) def test_show_fixtures_conftest(self, testdir, testmod): @@ -1613,6 +1630,7 @@ result = testdir.runpytest("--fixtures") result.stdout.fnmatch_lines(""" *tmpdir* + *fixtures defined from*conftest* *arg1* *hello world* """) Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Oct 17 13:13:54 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 17 Oct 2012 11:13:54 -0000 Subject: [py-svn] commit/pytest: hpk42: fix and test --fixtures location information Message-ID: <20121017111354.13775.61309@bitbucket24.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/fbd9f8995972/ changeset: fbd9f8995972 user: hpk42 date: 2012-10-17 13:12:33 summary: fix and test --fixtures location information affected #: 2 files diff -r 29f960b2322f533da940d72265adc8b0de5dc27c -r fbd9f89959726658746f4f17b0ba67aca8ec1939 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -761,7 +761,7 @@ if verbose <= 0 and argname[0] == "_": continue if verbose > 0: - funcargspec = "%s -- %s" %(argname, loc,) + funcargspec = "%s -- %s" %(argname, bestrel,) else: funcargspec = argname tw.line(funcargspec, green=True) diff -r 29f960b2322f533da940d72265adc8b0de5dc27c -r fbd9f89959726658746f4f17b0ba67aca8ec1939 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1590,7 +1590,7 @@ def test_show_fixtures_verbose(self, testdir): result = testdir.runpytest("--fixtures", "-v") result.stdout.fnmatch_lines([ - "*tmpdir*", + "*tmpdir*--*tmpdir.py*", "*temporary directory*", ] ) Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Oct 17 13:46:52 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 17 Oct 2012 11:46:52 -0000 Subject: [py-svn] commit/pytest: hpk42: fix issue198 - detection of fixtures from conftest.py files in deeper nested dir structures with certain invocations Message-ID: <20121017114652.26391.43432@bitbucket16.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/8f712ab42644/ changeset: 8f712ab42644 user: hpk42 date: 2012-10-17 13:42:40 summary: fix issue198 - detection of fixtures from conftest.py files in deeper nested dir structures with certain invocations affected #: 5 files diff -r fbd9f89959726658746f4f17b0ba67aca8ec1939 -r 8f712ab4264478a8c6cddc4e5b60b24d546d7bda CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ Changes between 2.2.4 and 2.3.0.dev ----------------------------------- +- fix issue198 - conftest fixtures were not found on windows32 in some + circumstances with nested directory structures due to path manipulation issues - fix issue193 skip test functions with were parametrized with empty parameter sets - fix python3.3 compat, mostly reporting bits that previously depended diff -r fbd9f89959726658746f4f17b0ba67aca8ec1939 -r 8f712ab4264478a8c6cddc4e5b60b24d546d7bda _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev26' +__version__ = '2.3.0.dev27' diff -r fbd9f89959726658746f4f17b0ba67aca8ec1939 -r 8f712ab4264478a8c6cddc4e5b60b24d546d7bda _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -731,6 +731,7 @@ else: part = session._initialparts[0] nodeid = "::".join(map(str, [curdir.bestrelpath(part[0])] + part[1:])) + nodeid.replace(session.fspath.sep, "/") tw = py.io.TerminalWriter() verbose = config.getvalue("verbose") @@ -1417,6 +1418,8 @@ else: if p.basename.startswith("conftest.py"): nodeid = p.dirpath().relto(self.session.fspath) + if p.sep != "/": + nodeid.replace(p.sep, "/") self.parsefactories(plugin, nodeid) self._seenplugins.add(plugin) diff -r fbd9f89959726658746f4f17b0ba67aca8ec1939 -r 8f712ab4264478a8c6cddc4e5b60b24d546d7bda setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev26', + version='2.3.0.dev27', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r fbd9f89959726658746f4f17b0ba67aca8ec1939 -r 8f712ab4264478a8c6cddc4e5b60b24d546d7bda testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -2974,3 +2974,19 @@ """) reprec = testdir.inline_run() reprec.assertoutcome(passed=1) + +def test_fixtures_sub_subdir_normalize_sep(testdir): + # this makes sure that normlization of nodeids takes place + b = testdir.mkdir("tests").mkdir("unit") + b.join("conftest.py").write(py.code.Source(""" + def pytest_funcarg__arg1(): + pass + """)) + p = b.join("test_module.py") + p.write("def test_func(arg1): pass") + result = testdir.runpytest(p, "--fixtures") + assert result.ret == 0 + result.stdout.fnmatch_lines(""" + *fixtures defined*conftest* + *arg1* + """) Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Oct 17 13:55:16 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 17 Oct 2012 11:55:16 -0000 Subject: [py-svn] commit/pytest: hol...@merlinux.eu: fixing the fix of the last commit Message-ID: <20121017115516.25889.18651@bitbucket21.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/7e08d8a2ece0/ changeset: 7e08d8a2ece0 user: hol... at merlinux.eu date: 2012-10-17 13:45:03 summary: fixing the fix of the last commit affected #: 1 file diff -r 8f712ab4264478a8c6cddc4e5b60b24d546d7bda -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1419,7 +1419,7 @@ if p.basename.startswith("conftest.py"): nodeid = p.dirpath().relto(self.session.fspath) if p.sep != "/": - nodeid.replace(p.sep, "/") + nodeid = nodeid.replace(p.sep, "/") self.parsefactories(plugin, nodeid) self._seenplugins.add(plugin) Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 18 12:25:05 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 18 Oct 2012 10:25:05 -0000 Subject: [py-svn] commit/pytest: hpk42: many doc improvements and fixes Message-ID: <20121018102505.9586.60941@bitbucket22.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/ea37533878f0/ changeset: ea37533878f0 user: hpk42 date: 2012-10-18 12:24:50 summary: many doc improvements and fixes affected #: 25 files diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 .hgignore --- a/.hgignore +++ b/.hgignore @@ -24,3 +24,4 @@ 3rdparty/ .tox .cache +.coverage diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/announce/release-2.3.0.txt --- /dev/null +++ b/doc/en/announce/release-2.3.0.txt @@ -0,0 +1,45 @@ +pytest-2.3: generalized fixtures/funcarg mechanism +============================================================================= + +pytest is a popular tool for writing automated tests in Python. Version +2.3 comes with several innovations for writing test fixtures -- +needed to provide a fixed base state or preinitialized objects +for your tests. For info and tutorial-style examples, see + + http://pytest.org/dev/fixture.html + +All changes are backward compatible and you should be able to continue +to run your existing test suites. In particular, dependency-injected +"funcargs" still form the base of the improved fixture system but it is +now easy to create parametrized funcargs/fixtures or cache them across +modules or test sessions. Moreover, there is now support for using +pytest fixtures with unittest-style suites, see here for example: + + http://pytest.org/dev/unittest.html + +If you are interested in the precise reasoning of the pytest-2.3 fixture +evolution, please consult http://pytest.org/dev/funcarg_compare.html + +For general info on installation and getting started: + + http://pytest.org/dev/getting-started.html + +Docs and PDF access as usual at: + + http://pytest.org + +and more details for those already in the knowing of pytest can be found +in the CHANGELOG below. + +Particular thanks for this release go to Floris Bruynooghe, Alex Okrushko +Carl Meyer, Ronny Pfannschmidt, Benjamin Peterson and Alex Gaynor for helping +to get the new features right and well integrated. Ronny and Floris +also helped to fix a number of bugs and yet more people helped by +providing bug reports. + +have fun, +holger krekel + + + + diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/assert.txt --- a/doc/en/assert.txt +++ b/doc/en/assert.txt @@ -26,7 +26,7 @@ $ py.test test_assert1.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 1 items test_assert1.py F @@ -110,7 +110,7 @@ $ py.test test_assert2.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 1 items test_assert2.py F diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/attic_fixtures.txt --- /dev/null +++ b/doc/en/attic_fixtures.txt @@ -0,0 +1,186 @@ + +**Test classes, modules or whole projects can make use of +one or more fixtures**. All required fixture functions will execute +before a test from the specifying context executes. As You can use this +to make tests operate from a pre-initialized directory or with +certain environment variables or with pre-configured global application +settings. + +For example, the Django_ project requires database +initialization to be able to import from and use its model objects. +For that, the `pytest-django`_ plugin provides fixtures which your +project can then easily depend or extend on, simply by referencing the +name of the particular fixture. + +Fixture functions have limited visilibity which depends on where they +are defined. If they are defined on a test class, only its test methods +may use it. A fixture defined in a module can only be used +from that test module. A fixture defined in a conftest.py file +can only be used by the tests below the directory of that file. +Lastly, plugins can define fixtures which are available across all +projects. + + + + + +Python, Java and many other languages support a so called xUnit_ style +for providing a fixed state, `test fixtures`_, for running tests. It +typically involves calling a autouse function ahead and a teardown +function after test execute. In 2005 pytest introduced a scope-specific +model of automatically detecting and calling autouse and teardown +functions on a per-module, class or function basis. The Python unittest +package and nose have subsequently incorporated them. This model +remains supported by pytest as :ref:`classic xunit`. + +One property of xunit fixture functions is that they work implicitely +by preparing global state or setting attributes on TestCase objects. +By contrast, pytest provides :ref:`funcargs` which allow to +dependency-inject application test state into test functions or +methods as function arguments. If your application is sufficiently modular +or if you are creating a new project, we recommend you now rather head over to +:ref:`funcargs` instead because many pytest users agree that using this +paradigm leads to better application and test organisation. + +However, not all programs and frameworks work and can be tested in +a fully modular way. They rather require preparation of global state +like database autouse on which further fixtures like preparing application +specific tables or wrapping tests in transactions can take place. For those +needs, pytest-2.3 now supports new **fixture functions** which come with +a ton of improvements over classic xunit fixture writing. Fixture functions: + +- allow to separate different autouse concerns into multiple modular functions + +- can receive and fully interoperate with :ref:`funcargs `, + +- are called multiple times if its funcargs are parametrized, + +- don't need to be defined directly in your test classes or modules, + they can also be defined in a plugin or :ref:`conftest.py ` files and get called + +- are called on a per-session, per-module, per-class or per-function basis + by means of a simple "scope" declaration. + +- can access the :ref:`request ` object which allows to + introspect and interact with the (scoped) testcontext. + +- can add cleanup functions which will be invoked when the last test + of the fixture test context has finished executing. + +All of these features are now demonstrated by little examples. + + + + + +test modules accessing a global resource +------------------------------------------------------- + +.. note:: + + Relying on `global state is considered bad programming practise `_ but when you work with an application + that relies on it you often have no choice. + +If you want test modules to access a global resource, +you can stick the resource to the module globals in +a per-module autouse function. We use a :ref:`resource factory +<@pytest.fixture>` to create our global resource:: + + # content of conftest.py + import pytest + + class GlobalResource: + def __init__(self): + pass + + @pytest.fixture(scope="session") + def globresource(): + return GlobalResource() + + @pytest.fixture(scope="module") + def setresource(request, globresource): + request.module.globresource = globresource + +Now any test module can access ``globresource`` as a module global:: + + # content of test_glob.py + + def test_1(): + print ("test_1 %s" % globresource) + def test_2(): + print ("test_2 %s" % globresource) + +Let's run this module without output-capturing:: + + $ py.test -qs test_glob.py + FF + ================================= FAILURES ================================= + __________________________________ test_1 __________________________________ + + def test_1(): + > print ("test_1 %s" % globresource) + E NameError: global name 'globresource' is not defined + + test_glob.py:3: NameError + __________________________________ test_2 __________________________________ + + def test_2(): + > print ("test_2 %s" % globresource) + E NameError: global name 'globresource' is not defined + + test_glob.py:5: NameError + +The two tests see the same global ``globresource`` object. + +Parametrizing the global resource ++++++++++++++++++++++++++++++++++++++++++++++++++ + +We extend the previous example and add parametrization to the globresource +factory and also add a finalizer:: + + # content of conftest.py + + import pytest + + class GlobalResource: + def __init__(self, param): + self.param = param + + @pytest.fixture(scope="session", params=[1,2]) + def globresource(request): + g = GlobalResource(request.param) + def fin(): + print "finalizing", g + request.addfinalizer(fin) + return g + + @pytest.fixture(scope="module") + def setresource(request, globresource): + request.module.globresource = globresource + +And then re-run our test module:: + + $ py.test -qs test_glob.py + FF + ================================= FAILURES ================================= + __________________________________ test_1 __________________________________ + + def test_1(): + > print ("test_1 %s" % globresource) + E NameError: global name 'globresource' is not defined + + test_glob.py:3: NameError + __________________________________ test_2 __________________________________ + + def test_2(): + > print ("test_2 %s" % globresource) + E NameError: global name 'globresource' is not defined + + test_glob.py:5: NameError + +We are now running the two tests twice with two different global resource +instances. Note that the tests are ordered such that only +one instance is active at any given time: the finalizer of +the first globresource instance is called before the second +instance is created and sent to the autouse functions. + diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/builtin.txt --- a/doc/en/builtin.txt +++ b/doc/en/builtin.txt @@ -83,13 +83,6 @@ captured output available via ``capsys.readouterr()`` method calls which return a ``(out, err)`` tuple. - tmpdir - 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`_ - path object. - monkeypatch The returned ``monkeypatch`` funcarg provides these helper methods to modify objects, dictionaries or os.environ:: @@ -108,6 +101,8 @@ 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: @@ -117,4 +112,11 @@ See http://docs.python.org/library/warnings.html for information on warning categories. + tmpdir + 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`_ + path object. + diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/capture.txt --- a/doc/en/capture.txt +++ b/doc/en/capture.txt @@ -64,7 +64,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 2 items test_module.py .F @@ -78,7 +78,7 @@ test_module.py:9: AssertionError ----------------------------- Captured stdout ------------------------------ - setting up + setting up ==================== 1 failed, 1 passed in 0.01 seconds ==================== Accessing captured output from a test function diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.3.0.dev22" +version = release = "2.3.0.dev26" import sys, os diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/doctest.txt --- a/doc/en/doctest.txt +++ b/doc/en/doctest.txt @@ -44,7 +44,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 1 items mymodule.py . diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -26,19 +26,19 @@ $ py.test -v -m webtest =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:3: test_send_http PASSED =================== 1 tests deselected by "-m 'webtest'" =================== - ================== 1 passed, 1 deselected in 0.00 seconds ================== + ================== 1 passed, 1 deselected in 0.01 seconds ================== Or the inverse, running all tests except the webtest ones:: $ py.test -v -m "not webtest" =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:6: test_something_quick PASSED @@ -143,7 +143,7 @@ $ py.test -k send_http # running with the above defined examples =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 4 items test_server.py . @@ -155,7 +155,7 @@ $ py.test -k-send_http =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 4 items test_mark_classlevel.py .. @@ -168,7 +168,7 @@ $ py.test -kTestClass =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 4 items test_mark_classlevel.py .. @@ -221,18 +221,18 @@ $ py.test -E stage2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 1 items test_someenv.py s - ======================== 1 skipped in 0.01 seconds ========================= + ======================== 1 skipped in 0.00 seconds ========================= and here is one that specifies exactly the environment needed:: $ py.test -E stage1 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 1 items test_someenv.py . @@ -347,12 +347,12 @@ $ py.test -rs # this option reports skip reasons =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 4 items test_plat.py s.s. ========================= short test summary info ========================== - SKIP [2] /tmp/doc-exec-189/conftest.py:12: cannot run on platform linux2 + SKIP [2] /tmp/doc-exec-54/conftest.py:12: cannot run on platform linux2 =================== 2 passed, 2 skipped in 0.01 seconds ==================== @@ -360,7 +360,7 @@ $ py.test -m linux2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 4 items test_plat.py . diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/example/nonpython.txt --- a/doc/en/example/nonpython.txt +++ b/doc/en/example/nonpython.txt @@ -27,15 +27,17 @@ nonpython $ py.test test_simple.yml =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 - collected 0 items / 1 errors + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + collected 2 items - ================================== ERRORS ================================== - _____________________ ERROR collecting test_simple.yml _____________________ - conftest.py:11: in collect - > import yaml # we need a yaml parser, e.g. PyYAML - E ImportError: No module named yaml - ========================= 1 error in 0.00 seconds ========================== + test_simple.yml .F + + ================================= FAILURES ================================= + ______________________________ usecase: hello ______________________________ + usecase execution failed + spec failed: 'some': 'other' + no further details known at this point. + ==================== 1 failed, 1 passed in 0.03 seconds ==================== You get one dot for the passing ``sub1: sub1`` check and one failure. Obviously in the above ``conftest.py`` you'll want to implement a more @@ -54,27 +56,28 @@ nonpython $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 -- /home/hpk/p/pytest/.tox/regen/bin/python - collecting ... collected 0 items / 1 errors + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/p/pytest/.tox/regen/bin/python + collecting ... collected 2 items - ================================== ERRORS ================================== - _____________________ ERROR collecting test_simple.yml _____________________ - conftest.py:11: in collect - > import yaml # we need a yaml parser, e.g. PyYAML - E ImportError: No module named yaml - ========================= 1 error in 0.01 seconds ========================== + test_simple.yml:1: usecase: ok PASSED + test_simple.yml:1: usecase: hello FAILED + + ================================= FAILURES ================================= + ______________________________ usecase: hello ______________________________ + usecase execution failed + spec failed: 'some': 'other' + no further details known at this point. + ==================== 1 failed, 1 passed in 0.03 seconds ==================== While developing your custom test collection and execution it's also interesting to just look at the collection tree:: nonpython $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 - collected 0 items / 1 errors + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + collected 2 items + + + - ================================== ERRORS ================================== - _____________________ ERROR collecting test_simple.yml _____________________ - conftest.py:11: in collect - > import yaml # we need a yaml parser, e.g. PyYAML - E ImportError: No module named yaml - ========================= 1 error in 0.01 seconds ========================== + ============================= in 0.02 seconds ============================= diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/example/parametrize.txt --- a/doc/en/example/parametrize.txt +++ b/doc/en/example/parametrize.txt @@ -7,58 +7,11 @@ .. currentmodule:: _pytest.python py.test allows to easily parametrize test functions. +For basic docs, see :ref:`parametrize-basics`. + In the following we provide some examples using the builtin mechanisms. -.. _parametrizemark: - -Simple "decorator" parametrization of a test function ----------------------------------------------------------------------------- - -.. versionadded:: 2.2 - -The builtin ``pytest.mark.parametrize`` decorator directly enables -parametrization of arguments for a test function. Here is an example -of a test function that wants to compare that processing some input -results in expected output:: - - # content of test_expectation.py - import pytest - @pytest.mark.parametrize(("input", "expected"), [ - ("3+5", 8), - ("2+4", 6), - ("6*9", 42), - ]) - def test_eval(input, expected): - assert eval(input) == expected - -we parametrize two arguments of the test function so that the test -function is called three times. Let's run it:: - - $ py.test -q - ..F - ================================= FAILURES ================================= - ____________________________ test_eval[6*9-42] _____________________________ - - input = '6*9', expected = 42 - - @pytest.mark.parametrize(("input", "expected"), [ - ("3+5", 8), - ("2+4", 6), - ("6*9", 42), - ]) - def test_eval(input, expected): - > assert eval(input) == expected - E assert 54 == 42 - E + where 54 = eval('6*9') - - test_expectation.py:8: AssertionError - -As expected only one pair of input/output values fails the simple test function. - -Note that there are various ways how you can mark groups of functions, -see :ref:`mark`. - Generating parameters combinations, depending on command line ---------------------------------------------------------------------------- @@ -151,7 +104,7 @@ $ py.test test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 4 items test_scenarios.py .... @@ -163,7 +116,7 @@ $ py.test --collectonly test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 4 items @@ -203,6 +156,7 @@ creates a database object for the actual test invocations:: # content of conftest.py + import pytest def pytest_generate_tests(metafunc): if 'db' in metafunc.fixturenames: @@ -213,7 +167,8 @@ class DB2: "alternative database object" - def pytest_funcarg__db(request): + @pytest.fixture + def db(request): if request.param == "d1": return DB1() elif request.param == "d2": @@ -225,7 +180,7 @@ $ py.test test_backends.py --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 2 items @@ -240,7 +195,7 @@ ================================= FAILURES ================================= _________________________ test_db_initialized[d2] __________________________ - db = + db = def test_db_initialized(db): # a dummy test @@ -250,7 +205,7 @@ test_backends.py:6: Failed -The first invocation with ``db == "DB1"`` passed while the second with ``db == "DB2"`` failed. Our ``pytest_funcarg__db`` factory has instantiated each of the DB values during the setup phase while the ``pytest_generate_tests`` generated two according calls to the ``test_db_initialized`` during the collection phase. +The first invocation with ``db == "DB1"`` passed while the second with ``db == "DB2"`` failed. Our ``db`` fixture function has instantiated each of the DB values during the setup phase while the ``pytest_generate_tests`` generated two according calls to the ``test_db_initialized`` during the collection phase. .. regendoc:wipe @@ -295,7 +250,7 @@ ================================= FAILURES ================================= ________________________ TestClass.test_equals[1-2] ________________________ - self = , a = 1, b = 2 + self = , a = 1, b = 2 def test_equals(self, a, b): > assert a == b @@ -303,13 +258,13 @@ test_parametrize.py:18: AssertionError -Indirect parametrization with multiple resources +Indirect parametrization with multiple fixtures -------------------------------------------------------------- Here is a stripped down real-life example of using parametrized -testing for testing serialization, invoking different python interpreters. -We define a ``test_basic_objects`` function which is to be run -with different sets of arguments for its three arguments: +testing for testing serialization of objects between different python +interpreters. We define a ``test_basic_objects`` function which +is to be run with different sets of arguments for its three arguments: * ``python1``: first python interpreter, run to pickle-dump an object to a file * ``python2``: second interpreter, run to pickle-load an object from a file diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/example/pythoncollection.txt --- a/doc/en/example/pythoncollection.txt +++ b/doc/en/example/pythoncollection.txt @@ -43,7 +43,7 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 2 items @@ -82,7 +82,7 @@ . $ py.test --collectonly pythoncollection.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 3 items @@ -91,7 +91,7 @@ - ============================= in 0.00 seconds ============================= + ============================= in 0.01 seconds ============================= customizing test collection to find all .py files --------------------------------------------------------- @@ -135,7 +135,7 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 1 items diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/example/remoteinterp.txt --- a/doc/en/example/remoteinterp.txt +++ b/doc/en/example/remoteinterp.txt @@ -63,7 +63,7 @@ $ py.test test_remoteinterpreter.py Traceback (most recent call last): File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in - load_entry_point('pytest==2.3.0.dev20', 'console_scripts', 'py.test')() + load_entry_point('pytest==2.3.0.dev27', 'console_scripts', 'py.test')() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 473, in main config = _prepareconfig(args, plugins) File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig @@ -98,7 +98,7 @@ self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport __import__(modname) - File "/tmp/doc-exec-193/conftest.py", line 2, in + File "/tmp/doc-exec-58/conftest.py", line 2, in from remoteinterpreter import RemoteInterpreter ImportError: No module named remoteinterpreter @@ -150,7 +150,7 @@ $ py.test -q test_ssh.py -rs Traceback (most recent call last): File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in - load_entry_point('pytest==2.3.0.dev20', 'console_scripts', 'py.test')() + load_entry_point('pytest==2.3.0.dev27', 'console_scripts', 'py.test')() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 473, in main config = _prepareconfig(args, plugins) File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig @@ -185,7 +185,7 @@ self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport __import__(modname) - File "/tmp/doc-exec-193/conftest.py", line 2, in + File "/tmp/doc-exec-58/conftest.py", line 2, in from myapp import MyApp ImportError: No module named myapp diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/example/reportingdemo.txt --- a/doc/en/example/reportingdemo.txt +++ b/doc/en/example/reportingdemo.txt @@ -13,7 +13,7 @@ assertion $ py.test failure_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 39 items failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF @@ -30,7 +30,7 @@ failure_demo.py:15: AssertionError _________________________ TestFailing.test_simple __________________________ - self = + self = def test_simple(self): def f(): @@ -40,13 +40,13 @@ > assert f() == g() E assert 42 == 43 - E + where 42 = () - E + and 43 = () + E + where 42 = () + E + and 43 = () failure_demo.py:28: AssertionError ____________________ TestFailing.test_simple_multiline _____________________ - self = + self = def test_simple_multiline(self): otherfunc_multi( @@ -66,19 +66,19 @@ failure_demo.py:11: AssertionError ___________________________ TestFailing.test_not ___________________________ - self = + self = def test_not(self): def f(): return 42 > assert not f() E assert not 42 - E + where 42 = () + E + where 42 = () failure_demo.py:38: AssertionError _________________ TestSpecialisedExplanations.test_eq_text _________________ - self = + self = def test_eq_text(self): > assert 'spam' == 'eggs' @@ -89,7 +89,7 @@ failure_demo.py:42: AssertionError _____________ TestSpecialisedExplanations.test_eq_similar_text _____________ - self = + self = def test_eq_similar_text(self): > assert 'foo 1 bar' == 'foo 2 bar' @@ -102,7 +102,7 @@ failure_demo.py:45: AssertionError ____________ TestSpecialisedExplanations.test_eq_multiline_text ____________ - self = + self = def test_eq_multiline_text(self): > assert 'foo\nspam\nbar' == 'foo\neggs\nbar' @@ -115,7 +115,7 @@ failure_demo.py:48: AssertionError ______________ TestSpecialisedExplanations.test_eq_long_text _______________ - self = + self = def test_eq_long_text(self): a = '1'*100 + 'a' + '2'*100 @@ -132,7 +132,7 @@ failure_demo.py:53: AssertionError _________ TestSpecialisedExplanations.test_eq_long_text_multiline __________ - self = + self = def test_eq_long_text_multiline(self): a = '1\n'*100 + 'a' + '2\n'*100 @@ -156,7 +156,7 @@ failure_demo.py:58: AssertionError _________________ TestSpecialisedExplanations.test_eq_list _________________ - self = + self = def test_eq_list(self): > assert [0, 1, 2] == [0, 1, 3] @@ -166,7 +166,7 @@ failure_demo.py:61: AssertionError ______________ TestSpecialisedExplanations.test_eq_list_long _______________ - self = + self = def test_eq_list_long(self): a = [0]*100 + [1] + [3]*100 @@ -178,7 +178,7 @@ failure_demo.py:66: AssertionError _________________ TestSpecialisedExplanations.test_eq_dict _________________ - self = + self = def test_eq_dict(self): > assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2} @@ -191,7 +191,7 @@ failure_demo.py:69: AssertionError _________________ TestSpecialisedExplanations.test_eq_set __________________ - self = + self = def test_eq_set(self): > assert set([0, 10, 11, 12]) == set([0, 20, 21]) @@ -207,7 +207,7 @@ failure_demo.py:72: AssertionError _____________ TestSpecialisedExplanations.test_eq_longer_list ______________ - self = + self = def test_eq_longer_list(self): > assert [1,2] == [1,2,3] @@ -217,7 +217,7 @@ failure_demo.py:75: AssertionError _________________ TestSpecialisedExplanations.test_in_list _________________ - self = + self = def test_in_list(self): > assert 1 in [0, 2, 3, 4, 5] @@ -226,7 +226,7 @@ failure_demo.py:78: AssertionError __________ TestSpecialisedExplanations.test_not_in_text_multiline __________ - self = + self = def test_not_in_text_multiline(self): text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail' @@ -244,7 +244,7 @@ failure_demo.py:82: AssertionError ___________ TestSpecialisedExplanations.test_not_in_text_single ____________ - self = + self = def test_not_in_text_single(self): text = 'single foo line' @@ -257,7 +257,7 @@ failure_demo.py:86: AssertionError _________ TestSpecialisedExplanations.test_not_in_text_single_long _________ - self = + self = def test_not_in_text_single_long(self): text = 'head ' * 50 + 'foo ' + 'tail ' * 20 @@ -270,7 +270,7 @@ failure_demo.py:90: AssertionError ______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______ - self = + self = def test_not_in_text_single_long_term(self): text = 'head ' * 50 + 'f'*70 + 'tail ' * 20 @@ -289,7 +289,7 @@ i = Foo() > assert i.b == 2 E assert 1 == 2 - E + where 1 = .b + E + where 1 = .b failure_demo.py:101: AssertionError _________________________ test_attribute_instance __________________________ @@ -299,8 +299,8 @@ b = 1 > assert Foo().b == 2 E assert 1 == 2 - E + where 1 = .b - E + where = () + E + where 1 = .b + E + where = () failure_demo.py:107: AssertionError __________________________ test_attribute_failure __________________________ @@ -316,7 +316,7 @@ failure_demo.py:116: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - self = + self = def _get_b(self): > raise Exception('Failed to get attrib') @@ -332,15 +332,15 @@ b = 2 > assert Foo().b == Bar().b E assert 1 == 2 - E + where 1 = .b - E + where = () - E + and 2 = .b - E + where = () + E + where 1 = .b + E + where = () + E + and 2 = .b + E + where = () failure_demo.py:124: AssertionError __________________________ TestRaises.test_raises __________________________ - self = + self = def test_raises(self): s = 'qwe' @@ -352,10 +352,10 @@ > int(s) E ValueError: invalid literal for int() with base 10: 'qwe' - <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:833>:1: ValueError + <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:834>:1: ValueError ______________________ TestRaises.test_raises_doesnt _______________________ - self = + self = def test_raises_doesnt(self): > raises(IOError, "int('3')") @@ -364,7 +364,7 @@ failure_demo.py:136: Failed __________________________ TestRaises.test_raise ___________________________ - self = + self = def test_raise(self): > raise ValueError("demo error") @@ -373,7 +373,7 @@ failure_demo.py:139: ValueError ________________________ TestRaises.test_tupleerror ________________________ - self = + self = def test_tupleerror(self): > a,b = [1] @@ -382,7 +382,7 @@ failure_demo.py:142: ValueError ______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______ - self = + self = def test_reinterpret_fails_with_print_for_the_fun_of_it(self): l = [1,2,3] @@ -395,7 +395,7 @@ l is [1, 2, 3] ________________________ TestRaises.test_some_error ________________________ - self = + self = def test_some_error(self): > if namenotexi: @@ -423,7 +423,7 @@ <2-codegen 'abc-123' /home/hpk/p/pytest/doc/en/example/assertion/failure_demo.py:162>:2: AssertionError ____________________ TestMoreErrors.test_complex_error _____________________ - self = + self = def test_complex_error(self): def f(): @@ -452,7 +452,7 @@ failure_demo.py:5: AssertionError ___________________ TestMoreErrors.test_z1_unpack_error ____________________ - self = + self = def test_z1_unpack_error(self): l = [] @@ -462,7 +462,7 @@ failure_demo.py:179: ValueError ____________________ TestMoreErrors.test_z2_type_error _____________________ - self = + self = def test_z2_type_error(self): l = 3 @@ -472,19 +472,19 @@ failure_demo.py:183: TypeError ______________________ TestMoreErrors.test_startswith ______________________ - self = + self = def test_startswith(self): s = "123" g = "456" > assert s.startswith(g) - E assert ('456') - E + where = '123'.startswith + E assert ('456') + E + where = '123'.startswith failure_demo.py:188: AssertionError __________________ TestMoreErrors.test_startswith_nested ___________________ - self = + self = def test_startswith_nested(self): def f(): @@ -492,15 +492,15 @@ def g(): return "456" > assert f().startswith(g()) - E assert ('456') - E + where = '123'.startswith - E + where '123' = () - E + and '456' = () + E assert ('456') + E + where = '123'.startswith + E + where '123' = () + E + and '456' = () failure_demo.py:195: AssertionError _____________________ TestMoreErrors.test_global_func ______________________ - self = + self = def test_global_func(self): > assert isinstance(globf(42), float) @@ -510,18 +510,18 @@ failure_demo.py:198: AssertionError _______________________ TestMoreErrors.test_instance _______________________ - self = + self = def test_instance(self): self.x = 6*7 > assert self.x != 42 E assert 42 != 42 - E + where 42 = .x + E + where 42 = .x failure_demo.py:202: AssertionError _______________________ TestMoreErrors.test_compare ________________________ - self = + self = def test_compare(self): > assert globf(10) < 5 @@ -531,7 +531,7 @@ failure_demo.py:205: AssertionError _____________________ TestMoreErrors.test_try_finally ______________________ - self = + self = def test_try_finally(self): x = 1 diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/example/simple.txt --- a/doc/en/example/simple.txt +++ b/doc/en/example/simple.txt @@ -106,7 +106,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 0 items ============================= in 0.00 seconds ============================= @@ -150,20 +150,20 @@ $ py.test -rs # "-rs" means report details on the little 's' =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 2 items test_module.py .s ========================= short test summary info ========================== - SKIP [1] /tmp/doc-exec-195/conftest.py:9: need --runslow option to run + SKIP [1] /tmp/doc-exec-60/conftest.py:9: need --runslow option to run - =================== 1 passed, 1 skipped in 0.01 seconds ==================== + =================== 1 passed, 1 skipped in 0.03 seconds ==================== Or run it including the ``slow`` marked test:: $ py.test --runslow =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 2 items test_module.py .. @@ -253,7 +253,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 project deps: mylib-1.1 collected 0 items @@ -276,7 +276,7 @@ $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/p/pytest/.tox/regen/bin/python info1: did you know that ... did you? collecting ... collected 0 items @@ -287,7 +287,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 0 items ============================= in 0.00 seconds ============================= @@ -319,7 +319,7 @@ $ py.test --durations=3 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 3 items test_some_are_slow.py ... @@ -327,5 +327,79 @@ ========================= 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 call test_some_are_slow.py::test_funcfast + 0.00s setup test_some_are_slow.py::test_funcfast ========================= 3 passed in 0.31 seconds ========================= + +incremental testing - test steps +--------------------------------------------------- + +.. regendoc:wipe + +Sometimes you may have a testing situation which consists of a series +of test steps. If one step fails it makes no sense to execute further +steps as they are all expected to fail anyway and their tracebacks +add no insight. Here is a simple ``conftest.py`` file which introduces +an ``incremental`` marker which is to be used on classes:: + + # content of conftest.py + + import pytest + + def pytest_runtest_makereport(item, call): + if hasattr(item.markers, "incremental"): + if call.excinfo is not None: + parent = item.parent + parent._previousfailed = item + + def pytest_runtest_setup(item): + if hasattr(item.markers, "incremental"): + previousfailed = getattr(item.parent, "_previousfailed", None) + if previousfailed is not None: + pytest.xfail("previous test failed (%s)" %previousfailed.name) + +These two hook implementations work together to abort incremental-marked +tests in a class. Here is a test module example:: + + # content of test_step.py + + import pytest + + @pytest.mark.incremental + class TestUserHandling: + def test_login(self): + pass + def test_modification(self): + assert 0 + def test_deletion(self): + pass + + def test_normal(): + pass + +If we run this:: + + $ py.test -rx + =========================== test session starts ============================ + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + collected 4 items + + test_step.py .Fx. + + ================================= FAILURES ================================= + ____________________ TestUserHandling.test_modification ____________________ + + self = + + def test_modification(self): + > assert 0 + 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.01 seconds =============== + +We'll see that ``test_deletion`` was not executed because ``test_modification`` +failed. It is reported as an "expected failure". + diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -29,8 +29,7 @@ functional testing, allowing to parametrize fixtures or tests according to configuration and component options. -In addition to this next-generation (tm) style of organising test fixtures -in Python, pytest continues to support :ref:`xunitsetup` which it +In addition, pytest continues to support :ref:`xunitsetup` which it originally introduced in 2005. You can mix both styles, moving incrementally from classic to new style, if you prefer. You can also start out from existing :ref:`unittest.TestCase style ` @@ -72,8 +71,7 @@ $ py.test test_smtpsimple.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 - plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 1 items test_smtpsimple.py F @@ -81,7 +79,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response, msg = smtp.ehlo() @@ -91,7 +89,7 @@ E assert 0 test_smtpsimple.py:12: AssertionError - ========================= 1 failed in 0.11 seconds ========================= + ========================= 1 failed in 0.25 seconds ========================= In the failure traceback we see that the test function was called with a ``smtp`` argument, the ``smtplib.SMTP()`` instance created by the fixture @@ -189,8 +187,7 @@ $ py.test test_module.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 - plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 2 items test_module.py FF @@ -198,18 +195,19 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() assert response[0] == 250 - > assert "python" in response[1] - E assert 'python' in 'hq.merlinux.eu\nPIPELINING\nSIZE 25000000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN' + assert "merlinux" in response[1] + > assert 0 # for demo purposes + E assert 0 - test_module.py:5: AssertionError + test_module.py:6: AssertionError ________________________________ test_noop _________________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -218,7 +216,7 @@ E assert 0 test_module.py:11: AssertionError - ========================= 2 failed in 0.12 seconds ========================= + ========================= 2 failed in 0.24 seconds ========================= You see the two ``assert 0`` failing and more importantly you can also see that the same (session-scoped) ``smtp`` object was passed into the two @@ -260,7 +258,7 @@ $ py.test -s -q --tb=no FF - finalizing + finalizing We see that the ``smtp`` instance is finalized after the two tests using it tests executed. If we had specified ``scope='function'`` @@ -330,18 +328,19 @@ ================================= FAILURES ================================= __________________________ test_ehlo[merlinux.eu] __________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() assert response[0] == 250 - > assert "python" in response[1] - E assert 'python' in 'hq.merlinux.eu\nPIPELINING\nSIZE 25000000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN' + assert "merlinux" in response[1] + > assert 0 # for demo purposes + E assert 0 - test_module.py:5: AssertionError + test_module.py:6: AssertionError __________________________ test_noop[merlinux.eu] __________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -352,19 +351,18 @@ test_module.py:11: AssertionError ________________________ test_ehlo[mail.python.org] ________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() assert response[0] == 250 - assert "python" in response[1] - > assert 0 # for demo purposes - E assert 0 + > assert "merlinux" in response[1] + E assert 'merlinux' in 'mail.python.org\nSIZE 10240000\nETRN\nSTARTTLS\nENHANCEDSTATUSCODES\n8BITMIME\nDSN' - test_module.py:6: AssertionError + test_module.py:5: AssertionError ________________________ test_noop[mail.python.org] ________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -412,15 +410,13 @@ $ py.test -v test_appsetup.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 -- /home/hpk/venv/1/bin/python - cachedir: /tmp/doc-exec-135/.cache - plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED - ========================= 2 passed in 0.08 seconds ========================= + ========================= 2 passed in 0.09 seconds ========================= Due to the parametrization of ``smtp`` the test will run twice with two different ``App`` instances and respective smtp servers. There is no @@ -476,9 +472,7 @@ $ py.test -v -s test_module.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 -- /home/hpk/venv/1/bin/python - cachedir: /tmp/doc-exec-135/.cache - plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 8 items test_module.py:16: test_0[1] PASSED @@ -490,7 +484,7 @@ test_module.py:20: test_2[1-mod2] PASSED test_module.py:20: test_2[2-mod2] PASSED - ========================= 8 passed in 0.02 seconds ========================= + ========================= 8 passed in 0.01 seconds ========================= test0 1 test0 2 create mod1 diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/genapi.py --- /dev/null +++ b/doc/en/genapi.py @@ -0,0 +1,41 @@ +import textwrap +import inspect + +class Writer: + def __init__(self, clsname): + self.clsname = clsname + + def __enter__(self): + self.file = open("%s.api" % self.clsname, "w") + return self + + def __exit__(self, *args): + self.file.close() + print "wrote", self.file.name + + def line(self, line): + self.file.write(line+"\n") + + def docmethod(self, method): + doc = " ".join(method.__doc__.split()) + indent = " " + w = textwrap.TextWrapper(initial_indent=indent, + subsequent_indent=indent) + + spec = inspect.getargspec(method) + del spec.args[0] + self.line(".. py:method:: " + method.__name__ + + inspect.formatargspec(*spec)) + self.line("") + self.line(w.fill(doc)) + self.line("") + +def pytest_funcarg__a(request): + with Writer("request") as writer: + writer.docmethod(request.getfuncargvalue) + writer.docmethod(request.cached_setup) + writer.docmethod(request.addfinalizer) + writer.docmethod(request.applymarker) + +def test_hello(a): + pass diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/getting-started.txt --- a/doc/en/getting-started.txt +++ b/doc/en/getting-started.txt @@ -23,7 +23,7 @@ To check your installation has installed the correct version:: $ py.test --version - This is py.test version 2.3.0.dev20, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc + This is py.test version 2.3.0.dev27, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc If you get an error checkout :ref:`installation issues`. @@ -45,7 +45,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 1 items test_sample.py F @@ -122,7 +122,7 @@ ================================= FAILURES ================================= ____________________________ TestClass.test_two ____________________________ - self = + self = def test_two(self): x = "hello" @@ -157,7 +157,7 @@ ================================= FAILURES ================================= _____________________________ test_needsfiles ______________________________ - tmpdir = local('/tmp/pytest-46/test_needsfiles0') + tmpdir = local('/tmp/pytest-914/test_needsfiles0') def test_needsfiles(tmpdir): print tmpdir @@ -166,7 +166,7 @@ test_tmpdir.py:3: AssertionError ----------------------------- Captured stdout ------------------------------ - /tmp/pytest-46/test_needsfiles0 + /tmp/pytest-914/test_needsfiles0 Before the test runs, a unique-per-test-invocation temporary directory was created. More info at :ref:`tmpdir handling`. diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/index.txt --- a/doc/en/index.txt +++ b/doc/en/index.txt @@ -14,23 +14,22 @@ **provides easy no-boilerplate testing** - - makes it :ref:`easy to get started `, + - makes it :ref:`easy to get started `, + many :ref:`usage options ` - :ref:`assert with the assert statement` - helpful :ref:`traceback and failing assertion reporting ` - allows :ref:`print debugging ` and :ref:`the capturing of standard output during test execution ` - - supports :pep:`8` compliant coding styles in tests - - refined :ref:`usage options ` **scales from simple unit to complex functional testing** - (new in 2.3) :ref:`modular parametrizeable fixtures ` + - :ref:`parametrized test functions ` - :ref:`mark` - - :ref:`parametrized test functions ` - :ref:`skipping` - can :ref:`distribute tests to multiple CPUs ` through :ref:`xdist plugin ` - can :ref:`continuously re-run failing tests ` - - many :ref:`builtin helpers ` + - many :ref:`builtin helpers ` and :ref:`plugins ` - flexible :ref:`Python test discovery` **integrates many common testing methods**: @@ -42,6 +41,7 @@ - supports domain-specific :ref:`non-python tests` - supports the generation of testing coverage reports - `Javascript unit- and functional testing`_ + - supports :pep:`8` compliant coding styles in tests **extensive plugin and customization system**: diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/parametrize.txt --- a/doc/en/parametrize.txt +++ b/doc/en/parametrize.txt @@ -4,6 +4,8 @@ .. _`parametrized test functions`: .. _`parametrize`: +.. _`parametrize-basics`: + Parametrizing fixtures and test functions ========================================================================== @@ -19,6 +21,7 @@ * `pytest_generate_tests`_ enables implementing your own custom dynamic parametrization scheme or extensions. +.. _parametrizemark: .. _`@pytest.mark.parametrize`: @@ -50,7 +53,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 3 items test_expectation.py ..F @@ -132,8 +135,8 @@ def test_valid_string(stringinput): > assert stringinput.isalpha() - E assert () - E + where = '!'.isalpha + E assert () + E + where = '!'.isalpha test_strings.py:3: AssertionError @@ -146,7 +149,7 @@ $ py.test -q -rs test_strings.py s ========================= short test summary info ========================== - SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:949: got empty parameter set, function test_valid_string at /tmp/doc-exec-161/test_strings.py:1 + SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:943: got empty parameter set, function test_valid_string at /tmp/doc-exec-26/test_strings.py:1 For further examples, you might want to look at :ref:`more parametrization examples `. diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/plugins.txt --- a/doc/en/plugins.txt +++ b/doc/en/plugins.txt @@ -68,7 +68,7 @@ .. _`django`: https://www.djangoproject.com/ -* `pytest-django `: write tests +* `pytest-django `_: write tests for `django`_ apps, using pytest integration. * `pytest-capturelog `_: diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/skipping.txt --- a/doc/en/skipping.txt +++ b/doc/en/skipping.txt @@ -132,7 +132,7 @@ example $ py.test -rx xfail_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 6 items xfail_demo.py xxxxxx diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/tmpdir.txt --- a/doc/en/tmpdir.txt +++ b/doc/en/tmpdir.txt @@ -29,7 +29,7 @@ $ py.test test_tmpdir.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev20 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 1 items test_tmpdir.py F @@ -37,7 +37,7 @@ ================================= FAILURES ================================= _____________________________ test_create_file _____________________________ - tmpdir = local('/tmp/pytest-47/test_create_file0') + tmpdir = local('/tmp/pytest-915/test_create_file0') def test_create_file(tmpdir): p = tmpdir.mkdir("sub").join("hello.txt") @@ -48,7 +48,7 @@ E assert 0 test_tmpdir.py:7: AssertionError - ========================= 1 failed in 0.01 seconds ========================= + ========================= 1 failed in 0.02 seconds ========================= .. _`base temporary directory`: diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 doc/en/unittest.txt --- a/doc/en/unittest.txt +++ b/doc/en/unittest.txt @@ -81,14 +81,13 @@ assert 0, self.db # fail for demo purposes The ``@pytest.mark.usefixtures("db_class")`` class-decorator makes sure that -the pytest fixture function ``db_class`` is called. Due to the deliberately -failing assert statements, we can take a look at the ``self.db`` values -in the traceback:: +the pytest fixture function ``db_class`` is called for each test method. +Due to the deliberately failing assert statements, we can take a look at +the ``self.db`` values in the traceback:: $ py.test test_unittest_db.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev22 - plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov, timeout + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 collected 2 items test_unittest_db.py FF @@ -101,7 +100,7 @@ def test_method1(self): assert hasattr(self, "db") > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:9: AssertionError ___________________________ MyTest.test_method2 ____________________________ @@ -110,14 +109,14 @@ def test_method2(self): > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:12: AssertionError - ========================= 2 failed in 0.04 seconds ========================= + ========================= 2 failed in 0.02 seconds ========================= This default pytest traceback shows that, indeed, the two test methods -see the same ``self.db`` attribute instance which was our intention -when writing the class-scoped fixture function. +share the same ``self.db`` instance which was our intention +when writing the class-scoped fixture function above. autouse fixtures and accessing other fixtures @@ -128,9 +127,10 @@ automatically used in a given context. For this, you can flag fixture functions with ``@pytest.fixture(autouse=True)`` and define the fixture function in the context where you want it used. Let's look -at an example which makes all test methods of a ``TestCase`` class -execute in a clean temporary directory, using a ``initdir`` fixture -which itself uses the pytest builtin ``tmpdir`` fixture:: +at an ``initdir`` fixrure which makes all test methods of a ``TestCase`` class +execute in a temporary directory with a pre-initialized ``samplefile.ini``. +Our ``initdir`` fixture itself uses the pytest builtin :ref:`tmpdir ` +fixture to help with creating a temporary dir:: # content of test_unittest_cleandir.py import pytest @@ -146,12 +146,10 @@ s = open("samplefile.ini").read() assert "testdata" in s -The ``initdir`` fixture function will be used for all methods of the -class where it is defined. This is basically just a shortcut for -using a ``@pytest.mark.usefixtures("initdir")`` on the class like in -the previous example. Note, that the ``initdir`` fixture function -accepts a :ref:`tmpdir ` argument, referencing a pytest -builtin fixture. +Due to the ``autouse`` flag the ``initdir`` fixture function will be +used for all methods of the class where it is defined. This is a +shortcut for using a ``@pytest.mark.usefixtures("initdir")`` on the +class like in the previous example. Running this test module ...:: @@ -163,7 +161,7 @@ .. note:: - ``unittest.TestCase`` methods cannot directly receive fixture or + ``unittest.TestCase`` methods cannot directly receive fixture function arguments as implementing that is likely to inflict on the ability to run general unittest.TestCase test suites. Given enough demand, attempts might be made, though. If diff -r 7e08d8a2ece0dc6b1e42b623b08aafa24399fa9f -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 tox.ini --- a/tox.ini +++ b/tox.ini @@ -45,6 +45,7 @@ basepython=python changedir=doc/en deps=:pypi:sphinx + :pypi:PyYAML pytest commands= @@ -55,6 +56,7 @@ basepython=python changedir=doc/en deps=:pypi:sphinx + :pypi:PyYAML pytest commands= make regen @@ -90,3 +92,4 @@ python_classes=Test Acceptance python_functions=test pep8ignore = E401 E225 E261 E128 E124 E302 +norecursedirs = .tox doc/ja Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 18 13:52:47 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 18 Oct 2012 11:52:47 -0000 Subject: [py-svn] commit/pytest: hpk42: remove .markers attribute which was added in development and after 2.2.4 Message-ID: <20121018115247.16761.64969@bitbucket25.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/df67c0e7b365/ changeset: df67c0e7b365 user: hpk42 date: 2012-10-18 13:52:32 summary: remove .markers attribute which was added in development and after 2.2.4 so never released. Rather extend keywords to also exist on nodes. Assigning to node.keywords will make the value appear on all subchildren's keywords. affected #: 9 files diff -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 -r df67c0e7b3654198d1fd45bc3c723307401c84fe _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev27' +__version__ = '2.3.0.dev28' diff -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 -r df67c0e7b3654198d1fd45bc3c723307401c84fe _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -4,6 +4,10 @@ import pytest, _pytest import inspect import os, sys, imp +try: + from collections import MutableMapping as MappingMixin +except ImportError: + from UserDict import DictMixin as MappingMixin from _pytest.mark import MarkInfo @@ -160,6 +164,32 @@ return property(fget) +class NodeKeywords(MappingMixin): + def __init__(self, node): + parent = node.parent + bases = parent and (parent.keywords._markers,) or () + self._markers = type("dynmarker", bases, {node.name: True}) + + def __getitem__(self, key): + try: + return getattr(self._markers, key) + except AttributeError: + raise KeyError(key) + + def __setitem__(self, key, value): + setattr(self._markers, key, value) + + def __delitem__(self, key): + delattr(self._markers, key) + + def __iter__(self): + return iter(self.keys()) + + def __len__(self): + return len(self.keys()) + + def keys(self): + return dir(self._markers) class Node(object): """ base class for Collector and Item the test collection tree. @@ -184,30 +214,11 @@ #: fspath sensitive hook proxy used to call pytest hooks self.ihook = self.session.gethookproxy(self.fspath) - bases = parent and (parent.markers,) or () - - #: marker class with markers from all scopes accessible as attributes - self.markers = type("dynmarker", bases, {self.name: True}) + #: keywords/markers collected from all scopes + self.keywords = NodeKeywords(self) #self.extrainit() - @property - def keywords(self): - """ dictionary of Keywords / markers on this node. """ - return vars(self.markers) - - def applymarker(self, marker): - """ Apply a marker to this item. This method is - useful if you have several parametrized function - and want to mark a single one of them. - - :arg marker: a :py:class:`_pytest.mark.MarkDecorator` object - created by a call to ``py.test.mark.NAME(...)``. - """ - if not isinstance(marker, pytest.mark.XYZ.__class__): - raise ValueError("%r is not a py.test.mark.* object") - setattr(self.markers, marker.markname, marker) - #def extrainit(self): # """"extra initialization after Node is initialized. Implemented # by some subclasses. """ diff -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 -r df67c0e7b3654198d1fd45bc3c723307401c84fe _pytest/mark.py --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -86,7 +86,7 @@ if not keywordexpr: return - itemkeywords = getkeywords(colitem) + itemkeywords = colitem.keywords for key in filter(None, keywordexpr.split()): eor = key[:1] == '-' if eor: @@ -94,14 +94,6 @@ if not (eor ^ matchonekeyword(key, itemkeywords)): return True -def getkeywords(node): - keywords = {} - while node is not None: - keywords.update(node.keywords) - node = node.parent - return keywords - - def matchonekeyword(key, itemkeywords): for elem in key.split("."): for kw in itemkeywords: diff -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 -r df67c0e7b3654198d1fd45bc3c723307401c84fe _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -883,10 +883,10 @@ self.obj = callobj for name, val in (py.builtin._getfuncdict(self.obj) or {}).items(): - setattr(self.markers, name, val) + self.keywords[name] = val if keywords: for name, val in keywords.items(): - setattr(self.markers, name, val) + self.keywords[name] = val fm = self.session._fixturemanager self._fixtureinfo = fi = fm.getfixtureinfo(self.parent, @@ -1066,7 +1066,7 @@ @property def keywords(self): - """ (deprecated, use node.markers class) dictionary of markers. """ + """ keywords/markers dictionary for the underlying node. """ return self._pyfuncitem.keywords @property @@ -1099,7 +1099,10 @@ :arg marker: a :py:class:`_pytest.mark.MarkDecorator` object created by a call to ``py.test.mark.NAME(...)``. """ - self.node.applymarker(marker) + try: + self.node.keywords[marker.markname] = marker + except AttributeError: + raise ValueError(marker) def raiseerror(self, msg): """ raise a FixtureLookupError with the given message. """ diff -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 -r df67c0e7b3654198d1fd45bc3c723307401c84fe doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.3.0.dev26" +version = release = "2.3.0.dev28" import sys, os diff -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 -r df67c0e7b3654198d1fd45bc3c723307401c84fe doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -26,7 +26,9 @@ $ py.test -v -m webtest =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/venv/1/bin/python + cachedir: /tmp/doc-exec-66/.cache + plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov collecting ... collected 2 items test_server.py:3: test_send_http PASSED @@ -38,13 +40,15 @@ $ py.test -v -m "not webtest" =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/venv/1/bin/python + cachedir: /tmp/doc-exec-66/.cache + plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov collecting ... collected 2 items test_server.py:6: test_something_quick PASSED ================= 1 tests deselected by "-m 'not webtest'" ================= - ================== 1 passed, 1 deselected in 0.00 seconds ================== + ================== 1 passed, 1 deselected in 0.01 seconds ================== Registering markers ------------------------------------- @@ -65,6 +69,8 @@ $ py.test --markers @pytest.mark.webtest: mark a test as a webtest. + @pytest.mark.timeout(timeout, method=None): Set a timeout and timeout method on just one test item. The first argument, *timeout*, is the timeout in seconds while the keyword, *method*, takes the same values as the --timeout_method option. + @pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has 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. @pytest.mark.xfail(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied. @@ -144,37 +150,40 @@ $ py.test -k send_http # running with the above defined examples =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov collected 4 items test_server.py . =================== 3 tests deselected by '-ksend_http' ==================== - ================== 1 passed, 3 deselected in 0.01 seconds ================== + ================== 1 passed, 3 deselected in 0.02 seconds ================== And you can also run all tests except the ones that match the keyword:: $ py.test -k-send_http =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov collected 4 items test_mark_classlevel.py .. test_server.py . =================== 1 tests deselected by '-k-send_http' =================== - ================== 3 passed, 1 deselected in 0.01 seconds ================== + ================== 3 passed, 1 deselected in 0.02 seconds ================== Or to only select the class:: $ py.test -kTestClass =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov collected 4 items test_mark_classlevel.py .. =================== 2 tests deselected by '-kTestClass' ==================== - ================== 2 passed, 2 deselected in 0.01 seconds ================== + ================== 2 passed, 2 deselected in 0.02 seconds ================== .. _`adding a custom marker from a plugin`: @@ -201,7 +210,7 @@ "env(name): mark test to run only on named environment") def pytest_runtest_setup(item): - envmarker = getattr(item.markers, 'env', None) + envmarker = item.keywords.get("env", None) if envmarker is not None: envname = envmarker.args[0] if envname != item.config.option.env: @@ -222,28 +231,32 @@ $ py.test -E stage2 =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov collected 1 items test_someenv.py s - ======================== 1 skipped in 0.00 seconds ========================= + ======================== 1 skipped in 0.01 seconds ========================= and here is one that specifies exactly the environment needed:: $ py.test -E stage1 =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov collected 1 items test_someenv.py . - ========================= 1 passed in 0.00 seconds ========================= + ========================= 1 passed in 0.01 seconds ========================= The ``--markers`` option always gives you a list of available markers:: $ py.test --markers @pytest.mark.env(name): mark test to run only on named environment + @pytest.mark.timeout(timeout, method=None): Set a timeout and timeout method on just one test item. The first argument, *timeout*, is the timeout in seconds while the keyword, *method*, takes the same values as the --timeout_method option. + @pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has 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. @pytest.mark.xfail(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied. @@ -282,7 +295,7 @@ import sys def pytest_runtest_setup(item): - g = getattr(item.markers, "glob", None) + g = item.keywords.get("glob", None) if g is not None: for info in g: print ("glob args=%s kwargs=%s" %(info.args, info.kwargs)) @@ -317,8 +330,8 @@ def pytest_runtest_setup(item): if isinstance(item, item.Function): plat = sys.platform - if not hasattr(item.markers, plat): - if ALL.intersection(set(item.obj.__dict__)): + if plat not in item.keywords: + if ALL.intersection(item.keywords): pytest.skip("cannot run on platform %s" %(plat)) then tests will be skipped if they were specified for a different platform. @@ -348,19 +361,21 @@ $ py.test -rs # this option reports skip reasons =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov collected 4 items test_plat.py s.s. ========================= short test summary info ========================== - SKIP [2] /tmp/doc-exec-54/conftest.py:12: cannot run on platform linux2 + SKIP [2] /tmp/doc-exec-66/conftest.py:12: cannot run on platform linux2 - =================== 2 passed, 2 skipped in 0.01 seconds ==================== + =================== 2 passed, 2 skipped in 0.02 seconds ==================== Note that if you specify a platform via the marker-command line option like this:: $ py.test -m linux2 =========================== test session starts ============================ platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov collected 4 items test_plat.py . diff -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 -r df67c0e7b3654198d1fd45bc3c723307401c84fe setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev27', + version='2.3.0.dev28', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 -r df67c0e7b3654198d1fd45bc3c723307401c84fe testing/test_mark.py --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -306,7 +306,10 @@ check(keyword, 'test_one') check('TestClass.test', 'test_method_one') - def test_select_extra_keywords(self, testdir): + @pytest.mark.parametrize("keyword", [ + 'xxx', 'xxx test_2', 'TestClass', 'xxx -test_1', + 'TestClass test_2', 'xxx TestClass test_2']) + def test_select_extra_keywords(self, testdir, keyword): p = testdir.makepyfile(test_select=""" def test_1(): pass @@ -318,19 +321,17 @@ def pytest_pycollect_makeitem(__multicall__, name): if name == "TestClass": item = __multicall__.execute() - item.markers.xxx = True + item.keywords["xxx"] = True return item """) - for keyword in ('xxx', 'xxx test_2', 'TestClass', 'xxx -test_1', - 'TestClass test_2', 'xxx TestClass test_2',): - reprec = testdir.inline_run(p.dirpath(), '-s', '-k', keyword) - py.builtin.print_("keyword", repr(keyword)) - passed, skipped, failed = reprec.listoutcomes() - assert len(passed) == 1 - assert passed[0].nodeid.endswith("test_2") - dlist = reprec.getcalls("pytest_deselected") - assert len(dlist) == 1 - assert dlist[0].items[0].name == 'test_1' + reprec = testdir.inline_run(p.dirpath(), '-s', '-k', keyword) + py.builtin.print_("keyword", repr(keyword)) + passed, skipped, failed = reprec.listoutcomes() + assert len(passed) == 1 + assert passed[0].nodeid.endswith("test_2") + dlist = reprec.getcalls("pytest_deselected") + assert len(dlist) == 1 + assert dlist[0].items[0].name == 'test_1' def test_select_starton(self, testdir): threepass = testdir.makepyfile(test_threepass=""" diff -r ea37533878f0aea376f3b126f7c90253ec2fe6d9 -r df67c0e7b3654198d1fd45bc3c723307401c84fe testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -731,16 +731,16 @@ assert 'skipif' in item1.keywords pytest.raises(ValueError, "req1.applymarker(42)") - def test_accessmarker_function(self, testdir): + def test_accesskeywords(self, testdir): testdir.makepyfile(""" import pytest @pytest.fixture() - def markers(request): - return request.node.markers + def keywords(request): + return request.keywords @pytest.mark.XYZ - def test_function(markers): - assert markers.XYZ is not None - assert not hasattr(markers, "abc") + def test_function(keywords): + assert keywords["XYZ"] + assert "abc" not in keywords """) reprec = testdir.inline_run() reprec.assertoutcome(passed=1) @@ -749,8 +749,8 @@ testdir.makeconftest(""" import pytest @pytest.fixture() - def markers(request): - return request.node.markers + def keywords(request): + return request.keywords @pytest.fixture(scope="class", autouse=True) def marking(request): @@ -758,12 +758,12 @@ """) testdir.makepyfile(""" import pytest - def test_fun1(markers): - assert markers.XYZ is not None - assert not hasattr(markers, "abc") - def test_fun2(markers): - assert markers.XYZ is not None - assert not hasattr(markers, "abc") + def test_fun1(keywords): + assert keywords["XYZ"] is not None + assert "abc" not in keywords + def test_fun2(keywords): + assert keywords["XYZ"] is not None + assert "abc" not in keywords """) reprec = testdir.inline_run() reprec.assertoutcome(passed=2) Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 18 15:09:47 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 18 Oct 2012 13:09:47 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20121018130947.31593.78132@bitbucket03.managed.contegix.com> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/78edfa2f3a76/ changeset: 78edfa2f3a76 user: hpk42 date: 2012-10-18 15:06:55 summary: refine docs, fix a marker/keywords bit, and add a test that request.keywords points to node.keywords. affected #: 23 files diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev28' +__version__ = '2.3.0.dev29' diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1067,7 +1067,7 @@ @property def keywords(self): """ keywords/markers dictionary for the underlying node. """ - return self._pyfuncitem.keywords + return self.node.keywords @property def session(self): @@ -1246,7 +1246,7 @@ if scope == "function": return self._pyfuncitem elif scope == "session": - return None + return self.session elif scope == "class": x = self._pyfuncitem.getparent(pytest.Class) if x is not None: diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/assert.txt --- a/doc/en/assert.txt +++ b/doc/en/assert.txt @@ -26,7 +26,7 @@ $ py.test test_assert1.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 1 items test_assert1.py F @@ -110,7 +110,7 @@ $ py.test test_assert2.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 1 items test_assert2.py F diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/capture.txt --- a/doc/en/capture.txt +++ b/doc/en/capture.txt @@ -64,7 +64,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 2 items test_module.py .F @@ -78,7 +78,7 @@ test_module.py:9: AssertionError ----------------------------- Captured stdout ------------------------------ - setting up + setting up ==================== 1 failed, 1 passed in 0.01 seconds ==================== Accessing captured output from a test function diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/doctest.txt --- a/doc/en/doctest.txt +++ b/doc/en/doctest.txt @@ -44,7 +44,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 1 items mymodule.py . diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/example/attic_remoteinterpreter.txt --- /dev/null +++ b/doc/en/example/attic_remoteinterpreter.txt @@ -0,0 +1,198 @@ + +.. highlightlang:: python + +.. _myapp: + +Building an SSH connecting Application fixture +========================================================== + +The goal of this tutorial-example is to show how you can put efficient +test support and fixture code in one place, allowing test modules and +test functions to stay ignorant of importing, configuration or +setup/teardown details. + +The tutorial implements a simple ``RemoteInterpreter`` object that +allows evaluation of python expressions. We are going to use +the `execnet `_ package for the +underlying cross-python bridge functionality. + + +Step 1: Implementing a first test +-------------------------------------------------------------- + +Let's write a simple test function using a not yet defined ``interp`` fixture:: + + # content of test_remoteinterpreter.py + + def test_eval_simple(interp): + assert interp.eval("6*9") == 42 + +The test function needs an argument named `interp` and therefore pytest will +look for a :ref:`fixture function` that matches this name. We'll define it +in a :ref:`local plugin ` to make it available also to other +test modules:: + + # content of conftest.py + + from .remoteinterpreter import RemoteInterpreter + + @pytest.fixture + def interp(request): + import execnet + gw = execnet.makegateway() + return RemoteInterpreter(gw) + +To run the example we furthermore need to implement a RemoteInterpreter +object which working with the injected execnet-gateway connection:: + + # content of remoteintepreter.py + + class RemoteInterpreter: + def __init__(self, gateway): + self.gateway = gateway + + def eval(self, expression): + # execnet open a "gateway" to the remote process + # which enables to remotely execute code and communicate + # to and fro via channels + ch = self.gateway.remote_exec("channel.send(%s)" % expression) + return ch.receive() + +That's it, we can now run the test:: + + $ py.test test_remoteinterpreter.py + Traceback (most recent call last): + File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in + load_entry_point('pytest==2.3.0.dev28', 'console_scripts', 'py.test')() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 473, in main + config = _prepareconfig(args, plugins) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig + pluginmanager=_pluginmanager, args=args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 422, in __call__ + return self._docall(methods, kwargs) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 433, in _docall + res = mc.execute() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute + res = method(**kwargs) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/helpconfig.py", line 25, in pytest_cmdline_parse + config = __multicall__.execute() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute + res = method(**kwargs) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 10, in pytest_cmdline_parse + config.parse(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 344, in parse + self._preparse(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 322, in _preparse + self._setinitialconftest(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 301, in _setinitialconftest + self._conftest.setinitial(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 160, in setinitial + self._try_load_conftest(anchor) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 166, in _try_load_conftest + self._path2confmods[None] = self.getconftestmodules(anchor) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 190, in getconftestmodules + clist[:0] = self.getconftestmodules(dp) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 189, in getconftestmodules + clist.append(self.importconftest(conftestpath)) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 218, in importconftest + self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport + __import__(modname) + File "/tmp/doc-exec-111/conftest.py", line 2, in + from .remoteinterpreter import RemoteInterpreter + ValueError: Attempted relative import in non-package + +.. _`tut-cmdlineoption`: + +Step 2: Adding command line configuration +----------------------------------------------------------- + +To add a command line option we update the ``conftest.py`` of +the previous example and add a command line option which +is passed on to the MyApp object:: + + # content of ./conftest.py + import pytest + from myapp import MyApp + + def pytest_addoption(parser): # pytest hook called during initialisation + parser.addoption("--ssh", action="store", default=None, + help="specify ssh host to run tests with") + + @pytest.fixture + def mysetup(request): # "mysetup" factory function + return MySetup(request.config) + + class MySetup: + def __init__(self, config): + self.config = config + self.app = MyApp() + + def getsshconnection(self): + import execnet + host = self.config.option.ssh + if host is None: + pytest.skip("specify ssh host with --ssh") + return execnet.SshGateway(host) + + +Now any test function can use the ``mysetup.getsshconnection()`` method +like this:: + + # content of test_ssh.py + class TestClass: + def test_function(self, mysetup): + conn = mysetup.getsshconnection() + # work with conn + +Running it yields:: + + $ py.test -q test_ssh.py -rs + Traceback (most recent call last): + File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in + load_entry_point('pytest==2.3.0.dev28', 'console_scripts', 'py.test')() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 473, in main + config = _prepareconfig(args, plugins) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig + pluginmanager=_pluginmanager, args=args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 422, in __call__ + return self._docall(methods, kwargs) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 433, in _docall + res = mc.execute() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute + res = method(**kwargs) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/helpconfig.py", line 25, in pytest_cmdline_parse + config = __multicall__.execute() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute + res = method(**kwargs) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 10, in pytest_cmdline_parse + config.parse(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 344, in parse + self._preparse(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 322, in _preparse + self._setinitialconftest(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 301, in _setinitialconftest + self._conftest.setinitial(args) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 160, in setinitial + self._try_load_conftest(anchor) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 166, in _try_load_conftest + self._path2confmods[None] = self.getconftestmodules(anchor) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 190, in getconftestmodules + clist[:0] = self.getconftestmodules(dp) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 189, in getconftestmodules + clist.append(self.importconftest(conftestpath)) + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 218, in importconftest + self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() + File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport + __import__(modname) + File "/tmp/doc-exec-111/conftest.py", line 2, in + from myapp import MyApp + ImportError: No module named myapp + +If you specify a command line option like ``py.test --ssh=python.org`` the test will execute as expected. + +Note that neither the ``TestClass`` nor the ``test_function`` need to +know anything about how to setup the test state. It is handled separately +in the ``conftest.py`` file. It is easy +to extend the ``mysetup`` object for further needs in the test code - and for use by any other test functions in the files and directories below the ``conftest.py`` file. + diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -26,29 +26,25 @@ $ py.test -v -m webtest =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/venv/1/bin/python - cachedir: /tmp/doc-exec-66/.cache - plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:3: test_send_http PASSED =================== 1 tests deselected by "-m 'webtest'" =================== - ================== 1 passed, 1 deselected in 0.01 seconds ================== + ================== 1 passed, 1 deselected in 0.00 seconds ================== Or the inverse, running all tests except the webtest ones:: $ py.test -v -m "not webtest" =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/venv/1/bin/python - cachedir: /tmp/doc-exec-66/.cache - plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:6: test_something_quick PASSED ================= 1 tests deselected by "-m 'not webtest'" ================= - ================== 1 passed, 1 deselected in 0.01 seconds ================== + ================== 1 passed, 1 deselected in 0.00 seconds ================== Registering markers ------------------------------------- @@ -69,8 +65,6 @@ $ py.test --markers @pytest.mark.webtest: mark a test as a webtest. - @pytest.mark.timeout(timeout, method=None): Set a timeout and timeout method on just one test item. The first argument, *timeout*, is the timeout in seconds while the keyword, *method*, takes the same values as the --timeout_method option. - @pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has 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. @pytest.mark.xfail(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied. @@ -149,41 +143,38 @@ $ py.test -k send_http # running with the above defined examples =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 - plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 4 items test_server.py . =================== 3 tests deselected by '-ksend_http' ==================== - ================== 1 passed, 3 deselected in 0.02 seconds ================== + ================== 1 passed, 3 deselected in 0.01 seconds ================== And you can also run all tests except the ones that match the keyword:: $ py.test -k-send_http =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 - plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 4 items test_mark_classlevel.py .. test_server.py . =================== 1 tests deselected by '-k-send_http' =================== - ================== 3 passed, 1 deselected in 0.02 seconds ================== + ================== 3 passed, 1 deselected in 0.01 seconds ================== Or to only select the class:: $ py.test -kTestClass =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 - plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 4 items test_mark_classlevel.py .. =================== 2 tests deselected by '-kTestClass' ==================== - ================== 2 passed, 2 deselected in 0.02 seconds ================== + ================== 2 passed, 2 deselected in 0.01 seconds ================== .. _`adding a custom marker from a plugin`: @@ -230,8 +221,7 @@ $ py.test -E stage2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 - plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 1 items test_someenv.py s @@ -242,8 +232,7 @@ $ py.test -E stage1 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 - plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 1 items test_someenv.py . @@ -255,8 +244,6 @@ $ py.test --markers @pytest.mark.env(name): mark test to run only on named environment - @pytest.mark.timeout(timeout, method=None): Set a timeout and timeout method on just one test item. The first argument, *timeout*, is the timeout in seconds while the keyword, *method*, takes the same values as the --timeout_method option. - @pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has 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. @pytest.mark.xfail(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied. @@ -360,22 +347,20 @@ $ py.test -rs # this option reports skip reasons =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 - plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 4 items test_plat.py s.s. ========================= short test summary info ========================== - SKIP [2] /tmp/doc-exec-66/conftest.py:12: cannot run on platform linux2 + SKIP [2] /tmp/doc-exec-113/conftest.py:12: cannot run on platform linux2 - =================== 2 passed, 2 skipped in 0.02 seconds ==================== + =================== 2 passed, 2 skipped in 0.01 seconds ==================== Note that if you specify a platform via the marker-command line option like this:: $ py.test -m linux2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 - plugins: xdist, bugzilla, cache, cli, pep8, oejskit, timeout, cov + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 4 items test_plat.py . diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/example/nonpython.txt --- a/doc/en/example/nonpython.txt +++ b/doc/en/example/nonpython.txt @@ -27,7 +27,7 @@ nonpython $ py.test test_simple.yml =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 2 items test_simple.yml .F @@ -56,7 +56,7 @@ nonpython $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_simple.yml:1: usecase: ok PASSED @@ -74,7 +74,7 @@ nonpython $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 2 items diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/example/parametrize.txt --- a/doc/en/example/parametrize.txt +++ b/doc/en/example/parametrize.txt @@ -104,7 +104,7 @@ $ py.test test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 4 items test_scenarios.py .... @@ -116,7 +116,7 @@ $ py.test --collectonly test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 4 items @@ -180,7 +180,7 @@ $ py.test test_backends.py --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 2 items @@ -195,7 +195,7 @@ ================================= FAILURES ================================= _________________________ test_db_initialized[d2] __________________________ - db = + db = def test_db_initialized(db): # a dummy test @@ -250,7 +250,7 @@ ================================= FAILURES ================================= ________________________ TestClass.test_equals[1-2] ________________________ - self = , a = 1, b = 2 + self = , a = 1, b = 2 def test_equals(self, a, b): > assert a == b diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/example/pythoncollection.txt --- a/doc/en/example/pythoncollection.txt +++ b/doc/en/example/pythoncollection.txt @@ -43,7 +43,7 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 2 items @@ -82,7 +82,7 @@ . $ py.test --collectonly pythoncollection.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 3 items @@ -91,7 +91,7 @@ - ============================= in 0.01 seconds ============================= + ============================= in 0.00 seconds ============================= customizing test collection to find all .py files --------------------------------------------------------- @@ -135,7 +135,7 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 1 items diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/example/remoteinterp.txt --- a/doc/en/example/remoteinterp.txt +++ /dev/null @@ -1,198 +0,0 @@ - -.. highlightlang:: python - -.. _myapp: - -Building an SSH connecting Application fixture -========================================================== - -The goal of this tutorial-example is to show how you can put efficient -test support and fixture code in one place, allowing test modules and -test functions to stay ignorant of importing, configuration or -setup/teardown details. - -The tutorial implements a simple ``RemoteInterpreter`` object that -allows evaluation of python expressions. We are going to use -the `execnet `_ package for the -underlying cross-python bridge functionality. - - -Step 1: Implementing a first test --------------------------------------------------------------- - -Let's write a simple test function using a not yet defined ``interp`` fixture:: - - # content of test_remoteinterpreter.py - - def test_eval_simple(interp): - assert interp.eval("6*9") == 42 - -The test function needs an argument named `interp` and therefore pytest will -look for a :ref:`fixture function` that matches this name. We'll define it -in a :ref:`local plugin ` to make it available also to other -test modules:: - - # content of conftest.py - - from remoteinterpreter import RemoteInterpreter - - @pytest.fixture - def interp(request): - import execnet - gw = execnet.makegateway() - return RemoteInterpreter(gw) - -To run the example we furthermore need to implement a RemoteInterpreter -object which working with the injected execnet-gateway connection:: - - # content of remoteintepreter.py - - class RemoteInterpreter: - def __init__(self, gateway): - self.gateway = gateway - - def eval(self, expression): - # execnet open a "gateway" to the remote process - # which enables to remotely execute code and communicate - # to and fro via channels - ch = self.gateway.remote_exec("channel.send(%s)" % expression) - return ch.receive() - -That's it, we can now run the test:: - - $ py.test test_remoteinterpreter.py - Traceback (most recent call last): - File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in - load_entry_point('pytest==2.3.0.dev27', 'console_scripts', 'py.test')() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 473, in main - config = _prepareconfig(args, plugins) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig - pluginmanager=_pluginmanager, args=args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 422, in __call__ - return self._docall(methods, kwargs) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 433, in _docall - res = mc.execute() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute - res = method(**kwargs) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/helpconfig.py", line 25, in pytest_cmdline_parse - config = __multicall__.execute() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute - res = method(**kwargs) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 10, in pytest_cmdline_parse - config.parse(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 344, in parse - self._preparse(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 322, in _preparse - self._setinitialconftest(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 301, in _setinitialconftest - self._conftest.setinitial(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 160, in setinitial - self._try_load_conftest(anchor) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 166, in _try_load_conftest - self._path2confmods[None] = self.getconftestmodules(anchor) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 190, in getconftestmodules - clist[:0] = self.getconftestmodules(dp) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 189, in getconftestmodules - clist.append(self.importconftest(conftestpath)) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 218, in importconftest - self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport - __import__(modname) - File "/tmp/doc-exec-58/conftest.py", line 2, in - from remoteinterpreter import RemoteInterpreter - ImportError: No module named remoteinterpreter - -.. _`tut-cmdlineoption`: - -Step 2: Adding command line configuration ------------------------------------------------------------ - -To add a command line option we update the ``conftest.py`` of -the previous example and add a command line option which -is passed on to the MyApp object:: - - # content of ./conftest.py - import pytest - from myapp import MyApp - - def pytest_addoption(parser): # pytest hook called during initialisation - parser.addoption("--ssh", action="store", default=None, - help="specify ssh host to run tests with") - - @pytest.fixture - def mysetup(request): # "mysetup" factory function - return MySetup(request.config) - - class MySetup: - def __init__(self, config): - self.config = config - self.app = MyApp() - - def getsshconnection(self): - import execnet - host = self.config.option.ssh - if host is None: - pytest.skip("specify ssh host with --ssh") - return execnet.SshGateway(host) - - -Now any test function can use the ``mysetup.getsshconnection()`` method -like this:: - - # content of test_ssh.py - class TestClass: - def test_function(self, mysetup): - conn = mysetup.getsshconnection() - # work with conn - -Running it yields:: - - $ py.test -q test_ssh.py -rs - Traceback (most recent call last): - File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in - load_entry_point('pytest==2.3.0.dev27', 'console_scripts', 'py.test')() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 473, in main - config = _prepareconfig(args, plugins) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig - pluginmanager=_pluginmanager, args=args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 422, in __call__ - return self._docall(methods, kwargs) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 433, in _docall - res = mc.execute() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute - res = method(**kwargs) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/helpconfig.py", line 25, in pytest_cmdline_parse - config = __multicall__.execute() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute - res = method(**kwargs) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 10, in pytest_cmdline_parse - config.parse(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 344, in parse - self._preparse(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 322, in _preparse - self._setinitialconftest(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 301, in _setinitialconftest - self._conftest.setinitial(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 160, in setinitial - self._try_load_conftest(anchor) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 166, in _try_load_conftest - self._path2confmods[None] = self.getconftestmodules(anchor) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 190, in getconftestmodules - clist[:0] = self.getconftestmodules(dp) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 189, in getconftestmodules - clist.append(self.importconftest(conftestpath)) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 218, in importconftest - self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport - __import__(modname) - File "/tmp/doc-exec-58/conftest.py", line 2, in - from myapp import MyApp - ImportError: No module named myapp - -If you specify a command line option like ``py.test --ssh=python.org`` the test will execute as expected. - -Note that neither the ``TestClass`` nor the ``test_function`` need to -know anything about how to setup the test state. It is handled separately -in the ``conftest.py`` file. It is easy -to extend the ``mysetup`` object for further needs in the test code - and for use by any other test functions in the files and directories below the ``conftest.py`` file. - diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/example/reportingdemo.txt --- a/doc/en/example/reportingdemo.txt +++ b/doc/en/example/reportingdemo.txt @@ -13,7 +13,7 @@ assertion $ py.test failure_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 39 items failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF @@ -30,7 +30,7 @@ failure_demo.py:15: AssertionError _________________________ TestFailing.test_simple __________________________ - self = + self = def test_simple(self): def f(): @@ -40,13 +40,13 @@ > assert f() == g() E assert 42 == 43 - E + where 42 = () - E + and 43 = () + E + where 42 = () + E + and 43 = () failure_demo.py:28: AssertionError ____________________ TestFailing.test_simple_multiline _____________________ - self = + self = def test_simple_multiline(self): otherfunc_multi( @@ -66,19 +66,19 @@ failure_demo.py:11: AssertionError ___________________________ TestFailing.test_not ___________________________ - self = + self = def test_not(self): def f(): return 42 > assert not f() E assert not 42 - E + where 42 = () + E + where 42 = () failure_demo.py:38: AssertionError _________________ TestSpecialisedExplanations.test_eq_text _________________ - self = + self = def test_eq_text(self): > assert 'spam' == 'eggs' @@ -89,7 +89,7 @@ failure_demo.py:42: AssertionError _____________ TestSpecialisedExplanations.test_eq_similar_text _____________ - self = + self = def test_eq_similar_text(self): > assert 'foo 1 bar' == 'foo 2 bar' @@ -102,7 +102,7 @@ failure_demo.py:45: AssertionError ____________ TestSpecialisedExplanations.test_eq_multiline_text ____________ - self = + self = def test_eq_multiline_text(self): > assert 'foo\nspam\nbar' == 'foo\neggs\nbar' @@ -115,7 +115,7 @@ failure_demo.py:48: AssertionError ______________ TestSpecialisedExplanations.test_eq_long_text _______________ - self = + self = def test_eq_long_text(self): a = '1'*100 + 'a' + '2'*100 @@ -132,7 +132,7 @@ failure_demo.py:53: AssertionError _________ TestSpecialisedExplanations.test_eq_long_text_multiline __________ - self = + self = def test_eq_long_text_multiline(self): a = '1\n'*100 + 'a' + '2\n'*100 @@ -156,7 +156,7 @@ failure_demo.py:58: AssertionError _________________ TestSpecialisedExplanations.test_eq_list _________________ - self = + self = def test_eq_list(self): > assert [0, 1, 2] == [0, 1, 3] @@ -166,7 +166,7 @@ failure_demo.py:61: AssertionError ______________ TestSpecialisedExplanations.test_eq_list_long _______________ - self = + self = def test_eq_list_long(self): a = [0]*100 + [1] + [3]*100 @@ -178,7 +178,7 @@ failure_demo.py:66: AssertionError _________________ TestSpecialisedExplanations.test_eq_dict _________________ - self = + self = def test_eq_dict(self): > assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2} @@ -191,7 +191,7 @@ failure_demo.py:69: AssertionError _________________ TestSpecialisedExplanations.test_eq_set __________________ - self = + self = def test_eq_set(self): > assert set([0, 10, 11, 12]) == set([0, 20, 21]) @@ -207,7 +207,7 @@ failure_demo.py:72: AssertionError _____________ TestSpecialisedExplanations.test_eq_longer_list ______________ - self = + self = def test_eq_longer_list(self): > assert [1,2] == [1,2,3] @@ -217,7 +217,7 @@ failure_demo.py:75: AssertionError _________________ TestSpecialisedExplanations.test_in_list _________________ - self = + self = def test_in_list(self): > assert 1 in [0, 2, 3, 4, 5] @@ -226,7 +226,7 @@ failure_demo.py:78: AssertionError __________ TestSpecialisedExplanations.test_not_in_text_multiline __________ - self = + self = def test_not_in_text_multiline(self): text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail' @@ -244,7 +244,7 @@ failure_demo.py:82: AssertionError ___________ TestSpecialisedExplanations.test_not_in_text_single ____________ - self = + self = def test_not_in_text_single(self): text = 'single foo line' @@ -257,7 +257,7 @@ failure_demo.py:86: AssertionError _________ TestSpecialisedExplanations.test_not_in_text_single_long _________ - self = + self = def test_not_in_text_single_long(self): text = 'head ' * 50 + 'foo ' + 'tail ' * 20 @@ -270,7 +270,7 @@ failure_demo.py:90: AssertionError ______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______ - self = + self = def test_not_in_text_single_long_term(self): text = 'head ' * 50 + 'f'*70 + 'tail ' * 20 @@ -289,7 +289,7 @@ i = Foo() > assert i.b == 2 E assert 1 == 2 - E + where 1 = .b + E + where 1 = .b failure_demo.py:101: AssertionError _________________________ test_attribute_instance __________________________ @@ -299,8 +299,8 @@ b = 1 > assert Foo().b == 2 E assert 1 == 2 - E + where 1 = .b - E + where = () + E + where 1 = .b + E + where = () failure_demo.py:107: AssertionError __________________________ test_attribute_failure __________________________ @@ -316,7 +316,7 @@ failure_demo.py:116: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - self = + self = def _get_b(self): > raise Exception('Failed to get attrib') @@ -332,15 +332,15 @@ b = 2 > assert Foo().b == Bar().b E assert 1 == 2 - E + where 1 = .b - E + where = () - E + and 2 = .b - E + where = () + E + where 1 = .b + E + where = () + E + and 2 = .b + E + where = () failure_demo.py:124: AssertionError __________________________ TestRaises.test_raises __________________________ - self = + self = def test_raises(self): s = 'qwe' @@ -355,7 +355,7 @@ <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:834>:1: ValueError ______________________ TestRaises.test_raises_doesnt _______________________ - self = + self = def test_raises_doesnt(self): > raises(IOError, "int('3')") @@ -364,7 +364,7 @@ failure_demo.py:136: Failed __________________________ TestRaises.test_raise ___________________________ - self = + self = def test_raise(self): > raise ValueError("demo error") @@ -373,7 +373,7 @@ failure_demo.py:139: ValueError ________________________ TestRaises.test_tupleerror ________________________ - self = + self = def test_tupleerror(self): > a,b = [1] @@ -382,7 +382,7 @@ failure_demo.py:142: ValueError ______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______ - self = + self = def test_reinterpret_fails_with_print_for_the_fun_of_it(self): l = [1,2,3] @@ -395,7 +395,7 @@ l is [1, 2, 3] ________________________ TestRaises.test_some_error ________________________ - self = + self = def test_some_error(self): > if namenotexi: @@ -423,7 +423,7 @@ <2-codegen 'abc-123' /home/hpk/p/pytest/doc/en/example/assertion/failure_demo.py:162>:2: AssertionError ____________________ TestMoreErrors.test_complex_error _____________________ - self = + self = def test_complex_error(self): def f(): @@ -452,7 +452,7 @@ failure_demo.py:5: AssertionError ___________________ TestMoreErrors.test_z1_unpack_error ____________________ - self = + self = def test_z1_unpack_error(self): l = [] @@ -462,7 +462,7 @@ failure_demo.py:179: ValueError ____________________ TestMoreErrors.test_z2_type_error _____________________ - self = + self = def test_z2_type_error(self): l = 3 @@ -472,19 +472,19 @@ failure_demo.py:183: TypeError ______________________ TestMoreErrors.test_startswith ______________________ - self = + self = def test_startswith(self): s = "123" g = "456" > assert s.startswith(g) - E assert ('456') - E + where = '123'.startswith + E assert ('456') + E + where = '123'.startswith failure_demo.py:188: AssertionError __________________ TestMoreErrors.test_startswith_nested ___________________ - self = + self = def test_startswith_nested(self): def f(): @@ -492,15 +492,15 @@ def g(): return "456" > assert f().startswith(g()) - E assert ('456') - E + where = '123'.startswith - E + where '123' = () - E + and '456' = () + E assert ('456') + E + where = '123'.startswith + E + where '123' = () + E + and '456' = () failure_demo.py:195: AssertionError _____________________ TestMoreErrors.test_global_func ______________________ - self = + self = def test_global_func(self): > assert isinstance(globf(42), float) @@ -510,18 +510,18 @@ failure_demo.py:198: AssertionError _______________________ TestMoreErrors.test_instance _______________________ - self = + self = def test_instance(self): self.x = 6*7 > assert self.x != 42 E assert 42 != 42 - E + where 42 = .x + E + where 42 = .x failure_demo.py:202: AssertionError _______________________ TestMoreErrors.test_compare ________________________ - self = + self = def test_compare(self): > assert globf(10) < 5 @@ -531,7 +531,7 @@ failure_demo.py:205: AssertionError _____________________ TestMoreErrors.test_try_finally ______________________ - self = + self = def test_try_finally(self): x = 1 diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/example/simple.txt --- a/doc/en/example/simple.txt +++ b/doc/en/example/simple.txt @@ -106,7 +106,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 0 items ============================= in 0.00 seconds ============================= @@ -150,20 +150,20 @@ $ py.test -rs # "-rs" means report details on the little 's' =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 2 items test_module.py .s ========================= short test summary info ========================== - SKIP [1] /tmp/doc-exec-60/conftest.py:9: need --runslow option to run + SKIP [1] /tmp/doc-exec-118/conftest.py:9: need --runslow option to run - =================== 1 passed, 1 skipped in 0.03 seconds ==================== + =================== 1 passed, 1 skipped in 0.01 seconds ==================== Or run it including the ``slow`` marked test:: $ py.test --runslow =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 2 items test_module.py .. @@ -253,7 +253,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 project deps: mylib-1.1 collected 0 items @@ -276,7 +276,7 @@ $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 -- /home/hpk/p/pytest/.tox/regen/bin/python info1: did you know that ... did you? collecting ... collected 0 items @@ -287,7 +287,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 0 items ============================= in 0.00 seconds ============================= @@ -319,7 +319,7 @@ $ py.test --durations=3 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 3 items test_some_are_slow.py ... @@ -346,13 +346,13 @@ import pytest def pytest_runtest_makereport(item, call): - if hasattr(item.markers, "incremental"): + if "incremental" in item.keywords: if call.excinfo is not None: parent = item.parent parent._previousfailed = item def pytest_runtest_setup(item): - if hasattr(item.markers, "incremental"): + if "incremental" in item.keywords: previousfailed = getattr(item.parent, "_previousfailed", None) if previousfailed is not None: pytest.xfail("previous test failed (%s)" %previousfailed.name) @@ -380,7 +380,7 @@ $ py.test -rx =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 4 items test_step.py .Fx. @@ -388,7 +388,7 @@ ================================= FAILURES ================================= ____________________ TestUserHandling.test_modification ____________________ - self = + self = def test_modification(self): > assert 0 diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -71,7 +71,7 @@ $ py.test test_smtpsimple.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 1 items test_smtpsimple.py F @@ -79,7 +79,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response, msg = smtp.ehlo() @@ -130,7 +130,7 @@ When injecting fixtures to test functions, pytest-2.0 introduced the term "funcargs" or "funcarg mechanism" which continues to be present also in pytest-2.3 docs. It now refers to the specific case of injecting -fixture values to test functions by arguments. With pytest-2.3 there are +fixture values as arguments to test functions. With pytest-2.3 there are more possibilities to use fixtures but "funcargs" probably will remain as the main way of dealing with fixtures. @@ -187,7 +187,7 @@ $ py.test test_module.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 2 items test_module.py FF @@ -195,7 +195,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -207,7 +207,7 @@ test_module.py:6: AssertionError ________________________________ test_noop _________________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -216,7 +216,7 @@ E assert 0 test_module.py:11: AssertionError - ========================= 2 failed in 0.24 seconds ========================= + ========================= 2 failed in 0.22 seconds ========================= You see the two ``assert 0`` failing and more importantly you can also see that the same (session-scoped) ``smtp`` object was passed into the two @@ -229,9 +229,9 @@ Fixtures can interact with the requesting test context ------------------------------------------------------------- -By accepting the special :py:class:`request ` argument, +Using the special :py:class:`request ` argument, fixture functions can introspect the function, class or module for which -they are invoked and can optionally register finalizing cleanup +they are invoked or can register finalizing (cleanup) functions which are called when the last test finished execution. Further extending the previous ``smtp`` fixture example, let's try to @@ -258,7 +258,7 @@ $ py.test -s -q --tb=no FF - finalizing + finalizing We see that the ``smtp`` instance is finalized after the two tests using it tests executed. If we had specified ``scope='function'`` @@ -270,7 +270,7 @@ # content of test_anothersmtp.py - smtpserver = "merlinux.eu" # will be read by smtp fixture + smtpserver = "mail.python.org" # will be read by smtp fixture def test_showhelo(smtp): assert 0, smtp.helo() @@ -328,7 +328,7 @@ ================================= FAILURES ================================= __________________________ test_ehlo[merlinux.eu] __________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -340,7 +340,7 @@ test_module.py:6: AssertionError __________________________ test_noop[merlinux.eu] __________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -351,7 +351,7 @@ test_module.py:11: AssertionError ________________________ test_ehlo[mail.python.org] ________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -362,7 +362,7 @@ test_module.py:5: AssertionError ________________________ test_noop[mail.python.org] ________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -410,13 +410,13 @@ $ py.test -v test_appsetup.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED - ========================= 2 passed in 0.09 seconds ========================= + ========================= 2 passed in 0.11 seconds ========================= Due to the parametrization of ``smtp`` the test will run twice with two different ``App`` instances and respective smtp servers. There is no @@ -472,7 +472,7 @@ $ py.test -v -s test_module.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 8 items test_module.py:16: test_0[1] PASSED diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/getting-started.txt --- a/doc/en/getting-started.txt +++ b/doc/en/getting-started.txt @@ -23,7 +23,7 @@ To check your installation has installed the correct version:: $ py.test --version - This is py.test version 2.3.0.dev27, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc + This is py.test version 2.3.0.dev28, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc If you get an error checkout :ref:`installation issues`. @@ -45,7 +45,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 1 items test_sample.py F @@ -122,7 +122,7 @@ ================================= FAILURES ================================= ____________________________ TestClass.test_two ____________________________ - self = + self = def test_two(self): x = "hello" @@ -157,7 +157,7 @@ ================================= FAILURES ================================= _____________________________ test_needsfiles ______________________________ - tmpdir = local('/tmp/pytest-914/test_needsfiles0') + tmpdir = local('/tmp/pytest-990/test_needsfiles0') def test_needsfiles(tmpdir): print tmpdir @@ -166,7 +166,7 @@ test_tmpdir.py:3: AssertionError ----------------------------- Captured stdout ------------------------------ - /tmp/pytest-914/test_needsfiles0 + /tmp/pytest-990/test_needsfiles0 Before the test runs, a unique-per-test-invocation temporary directory was created. More info at :ref:`tmpdir handling`. diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/parametrize.txt --- a/doc/en/parametrize.txt +++ b/doc/en/parametrize.txt @@ -53,7 +53,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 3 items test_expectation.py ..F @@ -135,8 +135,8 @@ def test_valid_string(stringinput): > assert stringinput.isalpha() - E assert () - E + where = '!'.isalpha + E assert () + E + where = '!'.isalpha test_strings.py:3: AssertionError @@ -149,7 +149,7 @@ $ py.test -q -rs test_strings.py s ========================= short test summary info ========================== - SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:943: got empty parameter set, function test_valid_string at /tmp/doc-exec-26/test_strings.py:1 + SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:943: got empty parameter set, function test_valid_string at /tmp/doc-exec-84/test_strings.py:1 For further examples, you might want to look at :ref:`more parametrization examples `. diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/skipping.txt --- a/doc/en/skipping.txt +++ b/doc/en/skipping.txt @@ -132,7 +132,7 @@ example $ py.test -rx xfail_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 6 items xfail_demo.py xxxxxx diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/tmpdir.txt --- a/doc/en/tmpdir.txt +++ b/doc/en/tmpdir.txt @@ -29,7 +29,7 @@ $ py.test test_tmpdir.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 1 items test_tmpdir.py F @@ -37,7 +37,7 @@ ================================= FAILURES ================================= _____________________________ test_create_file _____________________________ - tmpdir = local('/tmp/pytest-915/test_create_file0') + tmpdir = local('/tmp/pytest-991/test_create_file0') def test_create_file(tmpdir): p = tmpdir.mkdir("sub").join("hello.txt") @@ -48,7 +48,7 @@ E assert 0 test_tmpdir.py:7: AssertionError - ========================= 1 failed in 0.02 seconds ========================= + ========================= 1 failed in 0.07 seconds ========================= .. _`base temporary directory`: diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 doc/en/unittest.txt --- a/doc/en/unittest.txt +++ b/doc/en/unittest.txt @@ -87,7 +87,7 @@ $ py.test test_unittest_db.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev27 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 collected 2 items test_unittest_db.py FF @@ -100,7 +100,7 @@ def test_method1(self): assert hasattr(self, "db") > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:9: AssertionError ___________________________ MyTest.test_method2 ____________________________ @@ -109,7 +109,7 @@ def test_method2(self): > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:12: AssertionError ========================= 2 failed in 0.02 seconds ========================= diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev28', + version='2.3.0.dev29', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 testing/test_mark.py --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -284,6 +284,25 @@ assert l[0].args == ("pos0",) assert l[1].args == ("pos1",) + def test_keywords_at_node_level(self, testdir): + p = testdir.makepyfile(""" + import pytest + @pytest.fixture(scope="session", autouse=True) + def some(request): + request.keywords["hello"] = 42 + assert "world" not in request.keywords + + @pytest.fixture(scope="function", autouse=True) + def funcsetup(request): + assert "world" in request.keywords + assert "hello" in request.keywords + + @pytest.mark.world + def test_function(): + pass + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) class TestKeywordSelection: def test_select_simple(self, testdir): diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1805,7 +1805,7 @@ """) result = testdir.runpytest() assert result.ret != 0 - result.stderr.fnmatch_lines([ + result.stdout.fnmatch_lines([ "*3/x*", "*ZeroDivisionError*", ]) diff -r df67c0e7b3654198d1fd45bc3c723307401c84fe -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 tox.ini --- a/tox.ini +++ b/tox.ini @@ -59,6 +59,7 @@ :pypi:PyYAML pytest commands= + rm -rf /tmp/doc-exec* make regen [testenv:py31] https://bitbucket.org/hpk42/pytest/changeset/e2b773c2d77a/ changeset: e2b773c2d77a user: hpk42 date: 2012-10-18 15:09:20 summary: fix trial tests affected #: 4 files diff -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 -r e2b773c2d77a7242c173a49d1a72be9674d6f443 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev29' +__version__ = '2.3.0.dev30' diff -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 -r e2b773c2d77a7242c173a49d1a72be9674d6f443 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1529,7 +1529,7 @@ self._holderobjseen.add(holderobj) autousenames = [] for name in dir(holderobj): - obj = getattr(holderobj, name) + obj = getattr(holderobj, name, None) if not callable(obj): continue # fixture functions have a pytest_funcarg__ prefix (pre-2.3 style) diff -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 -r e2b773c2d77a7242c173a49d1a72be9674d6f443 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev29', + version='2.3.0.dev30', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 78edfa2f3a76b28a3b20874a38e8431aacb21978 -r e2b773c2d77a7242c173a49d1a72be9674d6f443 testing/test_unittest.py --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -313,7 +313,7 @@ "*i2wanto*", "*sys.version_info*", "*skip_in_method*", - "*4 skipped*3 xfail*1 xpass*", + "*5 skipped*3 xfail*1 xpass*", ]) def test_trial_error(self, testdir): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 18 15:36:26 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 18 Oct 2012 13:36:26 -0000 Subject: [py-svn] commit/pytest: hpk42: avoid recursing into "ja" japanese examples Message-ID: <20121018133626.13766.49411@bitbucket02.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/1e20f378b5ec/ changeset: 1e20f378b5ec user: hpk42 date: 2012-10-18 15:32:30 summary: avoid recursing into "ja" japanese examples affected #: 1 file diff -r e2b773c2d77a7242c173a49d1a72be9674d6f443 -r 1e20f378b5ec9105394df56dc6368bb999ce2359 tox.ini --- a/tox.ini +++ b/tox.ini @@ -93,4 +93,4 @@ python_classes=Test Acceptance python_functions=test pep8ignore = E401 E225 E261 E128 E124 E302 -norecursedirs = .tox doc/ja +norecursedirs = .tox ja Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Oct 19 10:17:20 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 19 Oct 2012 08:17:20 -0000 Subject: [py-svn] commit/pytest: 3 new changesets Message-ID: <20121019081720.8652.89963@bitbucket25.managed.contegix.com> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/81c80d8dfdff/ changeset: 81c80d8dfdff user: hpk42 date: 2012-10-19 10:07:11 summary: some more finalization of docs affected #: 9 files diff -r 1e20f378b5ec9105394df56dc6368bb999ce2359 -r 81c80d8dfdff2509b1e223962dfdf287471b70bc CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,17 +1,15 @@ Changes between 2.2.4 and 2.3.0.dev ----------------------------------- +- fix issue139 - introduce @pytest.fixture which allows direct scoping + and parametrization of funcarg factories. Introduce new @pytest.setup + marker to allow the writing of setup functions which accept funcargs. - fix issue198 - conftest fixtures were not found on windows32 in some circumstances with nested directory structures due to path manipulation issues - fix issue193 skip test functions with were parametrized with empty parameter sets - fix python3.3 compat, mostly reporting bits that previously depended on dict ordering -- introduce a generic "markers" object on Nodes and a request.node - attribute pointing to the scope-specific collection node of a request. - node.markers allows reading and manipulating of MarkInfo objects - previously attached with @pytest.mark.* or request.applymarker or - setattr(node.markers, name, pytest.mark.*) calls. - introduce re-ordering of tests by resource and parametrization setup which takes precedence to the usual file-ordering - fix issue185 monkeypatching time.time does not cause pytest to fail @@ -22,9 +20,6 @@ it is constructed correctly from the original current working dir. - fix "python setup.py test" example to cause a proper "errno" return - fix issue165 - fix broken doc links and mention stackoverflow for FAQ -- fix issue139 - introduce @pytest.factory which allows direct scoping - and parametrization of funcarg factories. Introduce new @pytest.setup - marker to allow the writing of setup functions which accept funcargs. - catch unicode-issues when writing failure representations to terminal to prevent the whole session from crashing - fix xfail/skip confusion: a skip-mark or an imperative pytest.skip @@ -48,6 +43,10 @@ especially with respect to the "magic" history, also mention pytest-django, trial and unittest integration. +- make request.keywords and node.keywords writable. All descendant + collection nodes will see keyword values. Keywords are dictionaries + containing markers and other info. + - fix issue 178: xml binary escapes are now wrapped in py.xml.raw - fix issue 176: correctly catch the builtin AssertionError diff -r 1e20f378b5ec9105394df56dc6368bb999ce2359 -r 81c80d8dfdff2509b1e223962dfdf287471b70bc _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev30' +__version__ = '2.3.0.dev31' diff -r 1e20f378b5ec9105394df56dc6368bb999ce2359 -r 81c80d8dfdff2509b1e223962dfdf287471b70bc _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -10,6 +10,8 @@ import _pytest cutdir = py.path.local(_pytest.__file__).dirpath() +callable = py.builtin.callable + class FixtureFunctionMarker: def __init__(self, scope, params, autouse=False): self.scope = scope @@ -45,7 +47,7 @@ can see it. If False (the default) then an explicit reference is needed to activate the fixture. """ - if py.builtin.callable(scope) and params is None and autouse == False: + if callable(scope) and params is None and autouse == False: # direct decoration return FixtureFunctionMarker("function", params, autouse)(scope) else: @@ -474,7 +476,7 @@ seen = {} for i, x in enumerate(self.obj()): name, call, args = self.getcallargs(x) - if not py.builtin.callable(call): + if not callable(call): raise TypeError("%r yielded non callable test %r" %(self.obj, call,)) if name is None: name = "[%d]" % i diff -r 1e20f378b5ec9105394df56dc6368bb999ce2359 -r 81c80d8dfdff2509b1e223962dfdf287471b70bc doc/en/announce/release-2.3.0.txt --- a/doc/en/announce/release-2.3.0.txt +++ b/doc/en/announce/release-2.3.0.txt @@ -1,22 +1,32 @@ -pytest-2.3: generalized fixtures/funcarg mechanism +pytest-2.3: improved fixtures / better unittest integration ============================================================================= -pytest is a popular tool for writing automated tests in Python. Version -2.3 comes with several innovations for writing test fixtures -- -needed to provide a fixed base state or preinitialized objects -for your tests. For info and tutorial-style examples, see +pytest-2.3 comes with many major improvements for fixture/funcarg management +and parametrized testing in Python. It is now easier, more efficient and +more predicatable to re-run the same tests with different fixture +instances. Also, you can directly declare the caching "scope" of +fixtures so that dependent tests throughout your whole test suite can +re-use database or other expensive fixture objects with ease. Lastly, +it's possible for fixture functions (formerly known as funcarg +factories) to use other fixtures, allowing for a completely modular and +re-useable fixture design. + +For detailed info and tutorial-style examples, see: http://pytest.org/dev/fixture.html -All changes are backward compatible and you should be able to continue -to run your existing test suites. In particular, dependency-injected -"funcargs" still form the base of the improved fixture system but it is -now easy to create parametrized funcargs/fixtures or cache them across -modules or test sessions. Moreover, there is now support for using -pytest fixtures with unittest-style suites, see here for example: +Moreover, there is now support for using pytest fixtures/funcargs with +unittest-style suites, see here for examples: http://pytest.org/dev/unittest.html +Besides, more unittest-test suites are now expected to "simply work" +with pytest. + +All changes are backward compatible and you should be able to continue +to run your test suites and 3rd party plugins that worked with +pytest-2.2.4. + If you are interested in the precise reasoning of the pytest-2.3 fixture evolution, please consult http://pytest.org/dev/funcarg_compare.html @@ -41,5 +51,1120 @@ holger krekel +Changes between 2.2.4 and 2.3.0.dev +----------------------------------- +- fix issue139 - introduce @pytest.fixture which allows direct scoping + and parametrization of funcarg factories. Introduce new @pytest.setup + marker to allow the writing of setup functions which accept funcargs. +- fix issue198 - conftest fixtures were not found on windows32 in some + circumstances with nested directory structures due to path manipulation issues +- fix issue193 skip test functions with were parametrized with empty + parameter sets +- fix python3.3 compat, mostly reporting bits that previously depended + on dict ordering +- introduce re-ordering of tests by resource and parametrization setup + which takes precedence to the usual file-ordering +- fix issue185 monkeypatching time.time does not cause pytest to fail +- fix issue172 duplicate call of pytest.setup-decoratored setup_module + functions +- fix junitxml=path construction so that if tests change the + current working directory and the path is a relative path + it is constructed correctly from the original current working dir. +- fix "python setup.py test" example to cause a proper "errno" return +- fix issue165 - fix broken doc links and mention stackoverflow for FAQ +- catch unicode-issues when writing failure representations + to terminal to prevent the whole session from crashing +- fix xfail/skip confusion: a skip-mark or an imperative pytest.skip + will now take precedence before xfail-markers because we + can't determine xfail/xpass status in case of a skip. see also: + http://stackoverflow.com/questions/11105828/in-py-test-when-i-explicitly-skip-a-test-that-is-marked-as-xfail-how-can-i-get +- always report installed 3rd party plugins in the header of a test run + +- fix issue160: a failing setup of an xfail-marked tests should + be reported as xfail (not xpass) + +- fix issue128: show captured output when capsys/capfd are used + +- fix issue179: propperly show the dependency chain of factories + +- pluginmanager.register(...) now raises ValueError if the + plugin has been already registered or the name is taken + +- fix issue159: improve http://pytest.org/latest/faq.html + especially with respect to the "magic" history, also mention + pytest-django, trial and unittest integration. + +- make request.keywords and node.keywords writable. All descendant + collection nodes will see keyword values. Keywords are dictionaries + containing markers and other info. + +- fix issue 178: xml binary escapes are now wrapped in py.xml.raw + +- fix issue 176: correctly catch the builtin AssertionError + even when we replaced AssertionError with a subclass on the + python level + +- factory discovery no longer fails with magic global callables + that provide no sane __code__ object (mock.call for example) + +- fix issue 182: testdir.inprocess_run now considers passed plugins + +- fix issue 188: ensure sys.exc_info is clear on python2 + before calling into a test + +- fix issue 191: add unittest TestCase runTest method support +- fix issue 156: monkeypatch correctly handles class level descriptors + +- reporting refinements: + + - pytest_report_header now receives a "startdir" so that + you can use startdir.bestrelpath(yourpath) to show + nice relative path + + - allow plugins to implement both pytest_report_header and + pytest_sessionstart (sessionstart is invoked first). + + - don't show deselected reason line if there is none + + - py.test -vv will show all of assert comparisations instead of truncating + +Changes between 2.2.3 and 2.2.4 +----------------------------------- + +- fix error message for rewritten assertions involving the % operator +- fix issue 126: correctly match all invalid xml characters for junitxml + binary escape +- fix issue with unittest: now @unittest.expectedFailure markers should + be processed correctly (you can also use @pytest.mark markers) +- document integration with the extended distribute/setuptools test commands +- fix issue 140: propperly get the real functions + of bound classmethods for setup/teardown_class +- fix issue #141: switch from the deceased paste.pocoo.org to bpaste.net +- fix issue #143: call unconfigure/sessionfinish always when + configure/sessionstart where called +- fix issue #144: better mangle test ids to junitxml classnames +- upgrade distribute_setup.py to 0.6.27 + +Changes between 2.2.2 and 2.2.3 +---------------------------------------- + +- fix uploaded package to only include neccesary files + +Changes between 2.2.1 and 2.2.2 +---------------------------------------- + +- fix issue101: wrong args to unittest.TestCase test function now + produce better output +- fix issue102: report more useful errors and hints for when a + test directory was renamed and some pyc/__pycache__ remain +- fix issue106: allow parametrize to be applied multiple times + e.g. from module, class and at function level. +- fix issue107: actually perform session scope finalization +- don't check in parametrize if indirect parameters are funcarg names +- add chdir method to monkeypatch funcarg +- fix crash resulting from calling monkeypatch undo a second time +- fix issue115: make --collectonly robust against early failure + (missing files/directories) +- "-qq --collectonly" now shows only files and the number of tests in them +- "-q --collectonly" now shows test ids +- allow adding of attributes to test reports such that it also works + with distributed testing (no upgrade of pytest-xdist needed) + +Changes between 2.2.0 and 2.2.1 +---------------------------------------- + +- fix issue99 (in pytest and py) internallerrors with resultlog now + produce better output - fixed by normalizing pytest_internalerror + input arguments. +- fix issue97 / traceback issues (in pytest and py) improve traceback output + in conjunction with jinja2 and cython which hack tracebacks +- fix issue93 (in pytest and pytest-xdist) avoid "delayed teardowns": + the final test in a test node will now run its teardown directly + instead of waiting for the end of the session. Thanks Dave Hunt for + the good reporting and feedback. The pytest_runtest_protocol as well + as the pytest_runtest_teardown hooks now have "nextitem" available + which will be None indicating the end of the test run. +- fix collection crash due to unknown-source collected items, thanks + to Ralf Schmitt (fixed by depending on a more recent pylib) + +Changes between 2.1.3 and 2.2.0 +---------------------------------------- + +- fix issue90: introduce eager tearing down of test items so that + teardown function are called earlier. +- add an all-powerful metafunc.parametrize function which allows to + parametrize test function arguments in multiple steps and therefore + from indepdenent plugins and palces. +- add a @pytest.mark.parametrize helper which allows to easily + call a test function with different argument values +- Add examples to the "parametrize" example page, including a quick port + of Test scenarios and the new parametrize function and decorator. +- introduce registration for "pytest.mark.*" helpers via ini-files + or through plugin hooks. Also introduce a "--strict" option which + will treat unregistered markers as errors + allowing to avoid typos and maintain a well described set of markers + for your test suite. See exaples at http://pytest.org/latest/mark.html + and its links. +- issue50: introduce "-m marker" option to select tests based on markers + (this is a stricter and more predictable version of '-k' in that "-m" + only matches complete markers and has more obvious rules for and/or + semantics. +- new feature to help optimizing the speed of your tests: + --durations=N option for displaying N slowest test calls + and setup/teardown methods. +- fix issue87: --pastebin now works with python3 +- fix issue89: --pdb with unexpected exceptions in doctest work more sensibly +- fix and cleanup pytest's own test suite to not leak FDs +- fix issue83: link to generated funcarg list +- fix issue74: pyarg module names are now checked against imp.find_module false positives +- fix compatibility with twisted/trial-11.1.0 use cases +- simplify Node.listchain +- simplify junitxml output code by relying on py.xml +- add support for skip properties on unittest classes and functions + +Changes between 2.1.2 and 2.1.3 +---------------------------------------- + +- fix issue79: assertion rewriting failed on some comparisons in boolops +- correctly handle zero length arguments (a la pytest '') +- fix issue67 / junitxml now contains correct test durations, thanks ronny +- fix issue75 / skipping test failure on jython +- fix issue77 / Allow assertrepr_compare hook to apply to a subset of tests + +Changes between 2.1.1 and 2.1.2 +---------------------------------------- + +- fix assertion rewriting on files with windows newlines on some Python versions +- refine test discovery by package/module name (--pyargs), thanks Florian Mayer +- fix issue69 / assertion rewriting fixed on some boolean operations +- fix issue68 / packages now work with assertion rewriting +- fix issue66: use different assertion rewriting caches when the -O option is passed +- don't try assertion rewriting on Jython, use reinterp + +Changes between 2.1.0 and 2.1.1 +---------------------------------------------- + +- fix issue64 / pytest.set_trace now works within pytest_generate_tests hooks +- fix issue60 / fix error conditions involving the creation of __pycache__ +- fix issue63 / assertion rewriting on inserts involving strings containing '%' +- fix assertion rewriting on calls with a ** arg +- don't cache rewritten modules if bytecode generation is disabled +- fix assertion rewriting in read-only directories +- fix issue59: provide system-out/err tags for junitxml output +- fix issue61: assertion rewriting on boolean operations with 3 or more operands +- you can now build a man page with "cd doc ; make man" + +Changes between 2.0.3 and 2.1.0.DEV +---------------------------------------------- + +- fix issue53 call nosestyle setup functions with correct ordering +- fix issue58 and issue59: new assertion code fixes +- merge Benjamin's assertionrewrite branch: now assertions + for test modules on python 2.6 and above are done by rewriting + the AST and saving the pyc file before the test module is imported. + see doc/assert.txt for more info. +- fix issue43: improve doctests with better traceback reporting on + unexpected exceptions +- fix issue47: timing output in junitxml for test cases is now correct +- fix issue48: typo in MarkInfo repr leading to exception +- fix issue49: avoid confusing error when initizaliation partially fails +- fix issue44: env/username expansion for junitxml file path +- show releaselevel information in test runs for pypy +- reworked doc pages for better navigation and PDF generation +- report KeyboardInterrupt even if interrupted during session startup +- fix issue 35 - provide PDF doc version and download link from index page + +Changes between 2.0.2 and 2.0.3 +---------------------------------------------- + +- fix issue38: nicer tracebacks on calls to hooks, particularly early + configure/sessionstart ones + +- fix missing skip reason/meta information in junitxml files, reported + via http://lists.idyll.org/pipermail/testing-in-python/2011-March/003928.html + +- fix issue34: avoid collection failure with "test" prefixed classes + deriving from object. + +- don't require zlib (and other libs) for genscript plugin without + --genscript actually being used. + +- speed up skips (by not doing a full traceback represenation + internally) + +- fix issue37: avoid invalid characters in junitxml's output + +Changes between 2.0.1 and 2.0.2 +---------------------------------------------- + +- tackle issue32 - speed up test runs of very quick test functions + by reducing the relative overhead + +- fix issue30 - extended xfail/skipif handling and improved reporting. + If you have a syntax error in your skip/xfail + expressions you now get nice error reports. + + Also you can now access module globals from xfail/skipif + expressions so that this for example works now:: + + import pytest + import mymodule + @pytest.mark.skipif("mymodule.__version__[0] == "1") + def test_function(): + pass + + This will not run the test function if the module's version string + does not start with a "1". Note that specifying a string instead + of a boolean expressions allows py.test to report meaningful information + when summarizing a test run as to what conditions lead to skipping + (or xfail-ing) tests. + +- fix issue28 - setup_method and pytest_generate_tests work together + The setup_method fixture method now gets called also for + test function invocations generated from the pytest_generate_tests + hook. + +- fix issue27 - collectonly and keyword-selection (-k) now work together + Also, if you do "py.test --collectonly -q" you now get a flat list + of test ids that you can use to paste to the py.test commandline + in order to execute a particular test. + +- fix issue25 avoid reported problems with --pdb and python3.2/encodings output + +- fix issue23 - tmpdir argument now works on Python3.2 and WindowsXP + Starting with Python3.2 os.symlink may be supported. By requiring + a newer py lib version the py.path.local() implementation acknowledges + this. + +- fixed typos in the docs (thanks Victor Garcia, Brianna Laugher) and particular + thanks to Laura Creighton who also revieved parts of the documentation. + +- fix slighly wrong output of verbose progress reporting for classes + (thanks Amaury) + +- more precise (avoiding of) deprecation warnings for node.Class|Function accesses + +- avoid std unittest assertion helper code in tracebacks (thanks Ronny) + +Changes between 2.0.0 and 2.0.1 +---------------------------------------------- + +- refine and unify initial capturing so that it works nicely + even if the logging module is used on an early-loaded conftest.py + file or plugin. +- allow to omit "()" in test ids to allow for uniform test ids + as produced by Alfredo's nice pytest.vim plugin. +- fix issue12 - show plugin versions with "--version" and + "--traceconfig" and also document how to add extra information + to reporting test header +- fix issue17 (import-* reporting issue on python3) by + requiring py>1.4.0 (1.4.1 is going to include it) +- fix issue10 (numpy arrays truth checking) by refining + assertion interpretation in py lib +- fix issue15: make nose compatibility tests compatible + with python3 (now that nose-1.0 supports python3) +- remove somewhat surprising "same-conftest" detection because + it ignores conftest.py when they appear in several subdirs. +- improve assertions ("not in"), thanks Floris Bruynooghe +- improve behaviour/warnings when running on top of "python -OO" + (assertions and docstrings are turned off, leading to potential + false positives) +- introduce a pytest_cmdline_processargs(args) hook + to allow dynamic computation of command line arguments. + This fixes a regression because py.test prior to 2.0 + allowed to set command line options from conftest.py + files which so far pytest-2.0 only allowed from ini-files now. +- fix issue7: assert failures in doctest modules. + unexpected failures in doctests will not generally + show nicer, i.e. within the doctest failing context. +- fix issue9: setup/teardown functions for an xfail-marked + test will report as xfail if they fail but report as normally + passing (not xpassing) if they succeed. This only is true + for "direct" setup/teardown invocations because teardown_class/ + teardown_module cannot closely relate to a single test. +- fix issue14: no logging errors at process exit +- refinements to "collecting" output on non-ttys +- refine internal plugin registration and --traceconfig output +- introduce a mechanism to prevent/unregister plugins from the + command line, see http://pytest.org/plugins.html#cmdunregister +- activate resultlog plugin by default +- fix regression wrt yielded tests which due to the + collection-before-running semantics were not + setup as with pytest 1.3.4. Note, however, that + the recommended and much cleaner way to do test + parametraization remains the "pytest_generate_tests" + mechanism, see the docs. + +Changes between 1.3.4 and 2.0.0 +---------------------------------------------- + +- pytest-2.0 is now its own package and depends on pylib-2.0 +- new ability: python -m pytest / python -m pytest.main ability +- new python invcation: pytest.main(args, plugins) to load + some custom plugins early. +- try harder to run unittest test suites in a more compatible manner + by deferring setup/teardown semantics to the unittest package. + also work harder to run twisted/trial and Django tests which + should now basically work by default. +- introduce a new way to set config options via ini-style files, + by default setup.cfg and tox.ini files are searched. The old + ways (certain environment variables, dynamic conftest.py reading + is removed). +- add a new "-q" option which decreases verbosity and prints a more + nose/unittest-style "dot" output. +- fix issue135 - marks now work with unittest test cases as well +- fix issue126 - introduce py.test.set_trace() to trace execution via + PDB during the running of tests even if capturing is ongoing. +- fix issue123 - new "python -m py.test" invocation for py.test + (requires Python 2.5 or above) +- fix issue124 - make reporting more resilient against tests opening + files on filedescriptor 1 (stdout). +- fix issue109 - sibling conftest.py files will not be loaded. + (and Directory collectors cannot be customized anymore from a Directory's + conftest.py - this needs to happen at least one level up). +- introduce (customizable) assertion failure representations and enhance + output on assertion failures for comparisons and other cases (Floris Bruynooghe) +- nose-plugin: pass through type-signature failures in setup/teardown + functions instead of not calling them (Ed Singleton) +- remove py.test.collect.Directory (follows from a major refactoring + and simplification of the collection process) +- majorly reduce py.test core code, shift function/python testing to own plugin +- fix issue88 (finding custom test nodes from command line arg) +- refine 'tmpdir' creation, will now create basenames better associated + with test names (thanks Ronny) +- "xpass" (unexpected pass) tests don't cause exitcode!=0 +- fix issue131 / issue60 - importing doctests in __init__ files used as namespace packages +- fix issue93 stdout/stderr is captured while importing conftest.py +- fix bug: unittest collected functions now also can have "pytestmark" + applied at class/module level +- add ability to use "class" level for cached_setup helper +- fix strangeness: mark.* objects are now immutable, create new instances + +Changes between 1.3.3 and 1.3.4 +---------------------------------------------- + +- fix issue111: improve install documentation for windows +- fix issue119: fix custom collectability of __init__.py as a module +- fix issue116: --doctestmodules work with __init__.py files as well +- fix issue115: unify internal exception passthrough/catching/GeneratorExit +- fix issue118: new --tb=native for presenting cpython-standard exceptions + +Changes between 1.3.2 and 1.3.3 +---------------------------------------------- + +- fix issue113: assertion representation problem with triple-quoted strings + (and possibly other cases) +- make conftest loading detect that a conftest file with the same + content was already loaded, avoids surprises in nested directory structures + which can be produced e.g. by Hudson. It probably removes the need to use + --confcutdir in most cases. +- fix terminal coloring for win32 + (thanks Michael Foord for reporting) +- fix weirdness: make terminal width detection work on stdout instead of stdin + (thanks Armin Ronacher for reporting) +- remove trailing whitespace in all py/text distribution files + +Changes between 1.3.1 and 1.3.2 +---------------------------------------------- + +New features +++++++++++++++++++ + +- fix issue103: introduce py.test.raises as context manager, examples:: + + with py.test.raises(ZeroDivisionError): + x = 0 + 1 / x + + with py.test.raises(RuntimeError) as excinfo: + call_something() + + # you may do extra checks on excinfo.value|type|traceback here + + (thanks Ronny Pfannschmidt) + +- Funcarg factories can now dynamically apply a marker to a + test invocation. This is for example useful if a factory + provides parameters to a test which are expected-to-fail:: + + def pytest_funcarg__arg(request): + request.applymarker(py.test.mark.xfail(reason="flaky config")) + ... + + def test_function(arg): + ... + +- improved error reporting on collection and import errors. This makes + use of a more general mechanism, namely that for custom test item/collect + nodes ``node.repr_failure(excinfo)`` is now uniformly called so that you can + override it to return a string error representation of your choice + which is going to be reported as a (red) string. + +- introduce '--junitprefix=STR' option to prepend a prefix + to all reports in the junitxml file. + +Bug fixes / Maintenance +++++++++++++++++++++++++++ + +- make tests and the ``pytest_recwarn`` plugin in particular fully compatible + to Python2.7 (if you use the ``recwarn`` funcarg warnings will be enabled so that + you can properly check for their existence in a cross-python manner). +- refine --pdb: ignore xfailed tests, unify its TB-reporting and + don't display failures again at the end. +- fix assertion interpretation with the ** operator (thanks Benjamin Peterson) +- fix issue105 assignment on the same line as a failing assertion (thanks Benjamin Peterson) +- fix issue104 proper escaping for test names in junitxml plugin (thanks anonymous) +- fix issue57 -f|--looponfail to work with xpassing tests (thanks Ronny) +- fix issue92 collectonly reporter and --pastebin (thanks Benjamin Peterson) +- fix py.code.compile(source) to generate unique filenames +- fix assertion re-interp problems on PyPy, by defering code + compilation to the (overridable) Frame.eval class. (thanks Amaury Forgeot) +- fix py.path.local.pyimport() to work with directories +- streamline py.path.local.mkdtemp implementation and usage +- don't print empty lines when showing junitxml-filename +- add optional boolean ignore_errors parameter to py.path.local.remove +- fix terminal writing on win32/python2.4 +- py.process.cmdexec() now tries harder to return properly encoded unicode objects + on all python versions +- install plain py.test/py.which scripts also for Jython, this helps to + get canonical script paths in virtualenv situations +- make path.bestrelpath(path) return ".", note that when calling + X.bestrelpath the assumption is that X is a directory. +- make initial conftest discovery ignore "--" prefixed arguments +- fix resultlog plugin when used in an multicpu/multihost xdist situation + (thanks Jakub Gustak) +- perform distributed testing related reporting in the xdist-plugin + rather than having dist-related code in the generic py.test + distribution +- fix homedir detection on Windows +- ship distribute_setup.py version 0.6.13 + +Changes between 1.3.0 and 1.3.1 +--------------------------------------------- + +New features +++++++++++++++++++ + +- issue91: introduce new py.test.xfail(reason) helper + to imperatively mark a test as expected to fail. Can + be used from within setup and test functions. This is + useful especially for parametrized tests when certain + configurations are expected-to-fail. In this case the + declarative approach with the @py.test.mark.xfail cannot + be used as it would mark all configurations as xfail. + +- issue102: introduce new --maxfail=NUM option to stop + test runs after NUM failures. This is a generalization + of the '-x' or '--exitfirst' option which is now equivalent + to '--maxfail=1'. Both '-x' and '--maxfail' will + now also print a line near the end indicating the Interruption. + +- issue89: allow py.test.mark decorators to be used on classes + (class decorators were introduced with python2.6) and + also allow to have multiple markers applied at class/module level + by specifying a list. + +- improve and refine letter reporting in the progress bar: + . pass + f failed test + s skipped tests (reminder: use for dependency/platform mismatch only) + x xfailed test (test that was expected to fail) + X xpassed test (test that was expected to fail but passed) + + You can use any combination of 'fsxX' with the '-r' extended + reporting option. The xfail/xpass results will show up as + skipped tests in the junitxml output - which also fixes + issue99. + +- make py.test.cmdline.main() return the exitstatus instead of raising + SystemExit and also allow it to be called multiple times. This of + course requires that your application and tests are properly teared + down and don't have global state. + +Fixes / Maintenance +++++++++++++++++++++++ + +- improved traceback presentation: + - improved and unified reporting for "--tb=short" option + - Errors during test module imports are much shorter, (using --tb=short style) + - raises shows shorter more relevant tracebacks + - --fulltrace now more systematically makes traces longer / inhibits cutting + +- improve support for raises and other dynamically compiled code by + manipulating python's linecache.cache instead of the previous + rather hacky way of creating custom code objects. This makes + it seemlessly work on Jython and PyPy where it previously didn't. + +- fix issue96: make capturing more resilient against Control-C + interruptions (involved somewhat substantial refactoring + to the underlying capturing functionality to avoid race + conditions). + +- fix chaining of conditional skipif/xfail decorators - so it works now + as expected to use multiple @py.test.mark.skipif(condition) decorators, + including specific reporting which of the conditions lead to skipping. + +- fix issue95: late-import zlib so that it's not required + for general py.test startup. + +- fix issue94: make reporting more robust against bogus source code + (and internally be more careful when presenting unexpected byte sequences) + + +Changes between 1.2.1 and 1.3.0 +--------------------------------------------- + +- deprecate --report option in favour of a new shorter and easier to + remember -r option: it takes a string argument consisting of any + combination of 'xfsX' characters. They relate to the single chars + you see during the dotted progress printing and will print an extra line + per test at the end of the test run. This extra line indicates the exact + position or test ID that you directly paste to the py.test cmdline in order + to re-run a particular test. + +- allow external plugins to register new hooks via the new + pytest_addhooks(pluginmanager) hook. The new release of + the pytest-xdist plugin for distributed and looponfailing + testing requires this feature. + +- add a new pytest_ignore_collect(path, config) hook to allow projects and + plugins to define exclusion behaviour for their directory structure - + for example you may define in a conftest.py this method:: + + def pytest_ignore_collect(path): + return path.check(link=1) + + to prevent even a collection try of any tests in symlinked dirs. + +- new pytest_pycollect_makemodule(path, parent) hook for + allowing customization of the Module collection object for a + matching test module. + +- extend and refine xfail mechanism: + ``@py.test.mark.xfail(run=False)`` do not run the decorated test + ``@py.test.mark.xfail(reason="...")`` prints the reason string in xfail summaries + specifiying ``--runxfail`` on command line virtually ignores xfail markers + +- expose (previously internal) commonly useful methods: + py.io.get_terminal_with() -> return terminal width + py.io.ansi_print(...) -> print colored/bold text on linux/win32 + py.io.saferepr(obj) -> return limited representation string + +- expose test outcome related exceptions as py.test.skip.Exception, + py.test.raises.Exception etc., useful mostly for plugins + doing special outcome interpretation/tweaking + +- (issue85) fix junitxml plugin to handle tests with non-ascii output + +- fix/refine python3 compatibility (thanks Benjamin Peterson) + +- fixes for making the jython/win32 combination work, note however: + jython2.5.1/win32 does not provide a command line launcher, see + http://bugs.jython.org/issue1491 . See pylib install documentation + for how to work around. + +- fixes for handling of unicode exception values and unprintable objects + +- (issue87) fix unboundlocal error in assertionold code + +- (issue86) improve documentation for looponfailing + +- refine IO capturing: stdin-redirect pseudo-file now has a NOP close() method + +- ship distribute_setup.py version 0.6.10 + +- added links to the new capturelog and coverage plugins + + +Changes between 1.2.1 and 1.2.0 +--------------------------------------------- + +- refined usage and options for "py.cleanup":: + + py.cleanup # remove "*.pyc" and "*$py.class" (jython) files + py.cleanup -e .swp -e .cache # also remove files with these extensions + py.cleanup -s # remove "build" and "dist" directory next to setup.py files + py.cleanup -d # also remove empty directories + py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'" + py.cleanup -n # dry run, only show what would be removed + +- add a new option "py.test --funcargs" which shows available funcargs + and their help strings (docstrings on their respective factory function) + for a given test path + +- display a short and concise traceback if a funcarg lookup fails + +- early-load "conftest.py" files in non-dot first-level sub directories. + allows to conveniently keep and access test-related options in a ``test`` + subdir and still add command line options. + +- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value + +- fix issue78: always call python-level teardown functions even if the + according setup failed. This includes refinements for calling setup_module/class functions + which will now only be called once instead of the previous behaviour where they'd be called + multiple times if they raise an exception (including a Skipped exception). Any exception + will be re-corded and associated with all tests in the according module/class scope. + +- fix issue63: assume <40 columns to be a bogus terminal width, default to 80 + +- fix pdb debugging to be in the correct frame on raises-related errors + +- update apipkg.py to fix an issue where recursive imports might + unnecessarily break importing + +- fix plugin links + +Changes between 1.2 and 1.1.1 +--------------------------------------------- + +- moved dist/looponfailing from py.test core into a new + separately released pytest-xdist plugin. + +- new junitxml plugin: --junitxml=path will generate a junit style xml file + which is processable e.g. by the Hudson CI system. + +- new option: --genscript=path will generate a standalone py.test script + which will not need any libraries installed. thanks to Ralf Schmitt. + +- new option: --ignore will prevent specified path from collection. + Can be specified multiple times. + +- new option: --confcutdir=dir will make py.test only consider conftest + files that are relative to the specified dir. + +- new funcarg: "pytestconfig" is the pytest config object for access + to command line args and can now be easily used in a test. + +- install 'py.test' and `py.which` with a ``-$VERSION`` suffix to + disambiguate between Python3, python2.X, Jython and PyPy installed versions. + +- new "pytestconfig" funcarg allows access to test config object + +- new "pytest_report_header" hook can return additional lines + to be displayed at the header of a test run. + +- (experimental) allow "py.test path::name1::name2::..." for pointing + to a test within a test collection directly. This might eventually + evolve as a full substitute to "-k" specifications. + +- streamlined plugin loading: order is now as documented in + customize.html: setuptools, ENV, commandline, conftest. + also setuptools entry point names are turned to canonical namees ("pytest_*") + +- automatically skip tests that need 'capfd' but have no os.dup + +- allow pytest_generate_tests to be defined in classes as well + +- deprecate usage of 'disabled' attribute in favour of pytestmark +- deprecate definition of Directory, Module, Class and Function nodes + in conftest.py files. Use pytest collect hooks instead. + +- collection/item node specific runtest/collect hooks are only called exactly + on matching conftest.py files, i.e. ones which are exactly below + the filesystem path of an item + +- change: the first pytest_collect_directory hook to return something + will now prevent further hooks to be called. + +- change: figleaf plugin now requires --figleaf to run. Also + change its long command line options to be a bit shorter (see py.test -h). + +- change: pytest doctest plugin is now enabled by default and has a + new option --doctest-glob to set a pattern for file matches. + +- change: remove internal py._* helper vars, only keep py._pydir + +- robustify capturing to survive if custom pytest_runtest_setup + code failed and prevented the capturing setup code from running. + +- make py.test.* helpers provided by default plugins visible early - + works transparently both for pydoc and for interactive sessions + which will regularly see e.g. py.test.mark and py.test.importorskip. + +- simplify internal plugin manager machinery +- simplify internal collection tree by introducing a RootCollector node + +- fix assert reinterpreation that sees a call containing "keyword=..." + +- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish + hooks on slaves during dist-testing, report module/session teardown + hooks correctly. + +- fix issue65: properly handle dist-testing if no + execnet/py lib installed remotely. + +- skip some install-tests if no execnet is available + +- fix docs, fix internal bin/ script generation + + +Changes between 1.1.1 and 1.1.0 +--------------------------------------------- + +- introduce automatic plugin registration via 'pytest11' + entrypoints via setuptools' pkg_resources.iter_entry_points + +- fix py.test dist-testing to work with execnet >= 1.0.0b4 + +- re-introduce py.test.cmdline.main() for better backward compatibility + +- svn paths: fix a bug with path.check(versioned=True) for svn paths, + allow '%' in svn paths, make svnwc.update() default to interactive mode + like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction. + +- refine distributed tarball to contain test and no pyc files + +- try harder to have deprecation warnings for py.compat.* accesses + report a correct location + +Changes between 1.1.0 and 1.0.2 +--------------------------------------------- + +* adjust and improve docs + +* remove py.rest tool and internal namespace - it was + never really advertised and can still be used with + the old release if needed. If there is interest + it could be revived into its own tool i guess. + +* fix issue48 and issue59: raise an Error if the module + from an imported test file does not seem to come from + the filepath - avoids "same-name" confusion that has + been reported repeatedly + +* merged Ronny's nose-compatibility hacks: now + nose-style setup_module() and setup() functions are + supported + +* introduce generalized py.test.mark function marking + +* reshuffle / refine command line grouping + +* deprecate parser.addgroup in favour of getgroup which creates option group + +* add --report command line option that allows to control showing of skipped/xfailed sections + +* generalized skipping: a new way to mark python functions with skipif or xfail + at function, class and modules level based on platform or sys-module attributes. + +* extend py.test.mark decorator to allow for positional args + +* introduce and test "py.cleanup -d" to remove empty directories + +* fix issue #59 - robustify unittest test collection + +* make bpython/help interaction work by adding an __all__ attribute + to ApiModule, cleanup initpkg + +* use MIT license for pylib, add some contributors + +* remove py.execnet code and substitute all usages with 'execnet' proper + +* fix issue50 - cached_setup now caches more to expectations + for test functions with multiple arguments. + +* merge Jarko's fixes, issue #45 and #46 + +* add the ability to specify a path for py.lookup to search in + +* fix a funcarg cached_setup bug probably only occuring + in distributed testing and "module" scope with teardown. + +* many fixes and changes for making the code base python3 compatible, + many thanks to Benjamin Peterson for helping with this. + +* consolidate builtins implementation to be compatible with >=2.3, + add helpers to ease keeping 2 and 3k compatible code + +* deprecate py.compat.doctest|subprocess|textwrap|optparse + +* deprecate py.magic.autopath, remove py/magic directory + +* move pytest assertion handling to py/code and a pytest_assertion + plugin, add "--no-assert" option, deprecate py.magic namespaces + in favour of (less) py.code ones. + +* consolidate and cleanup py/code classes and files + +* cleanup py/misc, move tests to bin-for-dist + +* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg + +* consolidate py.log implementation, remove old approach. + +* introduce py.io.TextIO and py.io.BytesIO for distinguishing between + text/unicode and byte-streams (uses underlying standard lib io.* + if available) + +* make py.unittest_convert helper script available which converts "unittest.py" + style files into the simpler assert/direct-test-classes py.test/nosetests + style. The script was written by Laura Creighton. + +* simplified internal localpath implementation + +Changes between 1.0.1 and 1.0.2 +------------------------------------------- + +* fixing packaging issues, triggered by fedora redhat packaging, + also added doc, examples and contrib dirs to the tarball. + +* added a documentation link to the new django plugin. + +Changes between 1.0.0 and 1.0.1 +------------------------------------------- + +* added a 'pytest_nose' plugin which handles nose.SkipTest, + nose-style function/method/generator setup/teardown and + tries to report functions correctly. + +* capturing of unicode writes or encoded strings to sys.stdout/err + work better, also terminalwriting was adapted and somewhat + unified between windows and linux. + +* improved documentation layout and content a lot + +* added a "--help-config" option to show conftest.py / ENV-var names for + all longopt cmdline options, and some special conftest.py variables. + renamed 'conf_capture' conftest setting to 'option_capture' accordingly. + +* fix issue #27: better reporting on non-collectable items given on commandline + (e.g. pyc files) + +* fix issue #33: added --version flag (thanks Benjamin Peterson) + +* fix issue #32: adding support for "incomplete" paths to wcpath.status() + +* "Test" prefixed classes are *not* collected by default anymore if they + have an __init__ method + +* monkeypatch setenv() now accepts a "prepend" parameter + +* improved reporting of collection error tracebacks + +* simplified multicall mechanism and plugin architecture, + renamed some internal methods and argnames + +Changes between 1.0.0b9 and 1.0.0 +------------------------------------------- + +* more terse reporting try to show filesystem path relatively to current dir +* improve xfail output a bit + +Changes between 1.0.0b8 and 1.0.0b9 +------------------------------------------- + +* cleanly handle and report final teardown of test setup + +* fix svn-1.6 compat issue with py.path.svnwc().versioned() + (thanks Wouter Vanden Hove) + +* setup/teardown or collection problems now show as ERRORs + or with big "E"'s in the progress lines. they are reported + and counted separately. + +* dist-testing: properly handle test items that get locally + collected but cannot be collected on the remote side - often + due to platform/dependency reasons + +* simplified py.test.mark API - see keyword plugin documentation + +* integrate better with logging: capturing now by default captures + test functions and their immediate setup/teardown in a single stream + +* capsys and capfd funcargs now have a readouterr() and a close() method + (underlyingly py.io.StdCapture/FD objects are used which grew a + readouterr() method as well to return snapshots of captured out/err) + +* make assert-reinterpretation work better with comparisons not + returning bools (reported with numpy from thanks maciej fijalkowski) + +* reworked per-test output capturing into the pytest_iocapture.py plugin + and thus removed capturing code from config object + +* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr) + + +Changes between 1.0.0b7 and 1.0.0b8 +------------------------------------------- + +* pytest_unittest-plugin is now enabled by default + +* introduced pytest_keyboardinterrupt hook and + refined pytest_sessionfinish hooked, added tests. + +* workaround a buggy logging module interaction ("closing already closed + files"). Thanks to Sridhar Ratnakumar for triggering. + +* if plugins use "py.test.importorskip" for importing + a dependency only a warning will be issued instead + of exiting the testing process. + +* many improvements to docs: + - refined funcargs doc , use the term "factory" instead of "provider" + - added a new talk/tutorial doc page + - better download page + - better plugin docstrings + - added new plugins page and automatic doc generation script + +* fixed teardown problem related to partially failing funcarg setups + (thanks MrTopf for reporting), "pytest_runtest_teardown" is now + always invoked even if the "pytest_runtest_setup" failed. + +* tweaked doctest output for docstrings in py modules, + thanks Radomir. + +Changes between 1.0.0b3 and 1.0.0b7 +------------------------------------------- + +* renamed py.test.xfail back to py.test.mark.xfail to avoid + two ways to decorate for xfail + +* re-added py.test.mark decorator for setting keywords on functions + (it was actually documented so removing it was not nice) + +* remove scope-argument from request.addfinalizer() because + request.cached_setup has the scope arg. TOOWTDI. + +* perform setup finalization before reporting failures + +* apply modified patches from Andreas Kloeckner to allow + test functions to have no func_code (#22) and to make + "-k" and function keywords work (#20) + +* apply patch from Daniel Peolzleithner (issue #23) + +* resolve issue #18, multiprocessing.Manager() and + redirection clash + +* make __name__ == "__channelexec__" for remote_exec code + +Changes between 1.0.0b1 and 1.0.0b3 +------------------------------------------- + +* plugin classes are removed: one now defines + hooks directly in conftest.py or global pytest_*.py + files. + +* added new pytest_namespace(config) hook that allows + to inject helpers directly to the py.test.* namespace. + +* documented and refined many hooks + +* added new style of generative tests via + pytest_generate_tests hook that integrates + well with function arguments. + + +Changes between 0.9.2 and 1.0.0b1 +------------------------------------------- + +* introduced new "funcarg" setup method, + see doc/test/funcarg.txt + +* introduced plugin architecuture and many + new py.test plugins, see + doc/test/plugins.txt + +* teardown_method is now guaranteed to get + called after a test method has run. + +* new method: py.test.importorskip(mod,minversion) + will either import or call py.test.skip() + +* completely revised internal py.test architecture + +* new py.process.ForkedFunc object allowing to + fork execution of a function to a sub process + and getting a result back. + +XXX lots of things missing here XXX + +Changes between 0.9.1 and 0.9.2 +------------------------------------------- + +* refined installation and metadata, created new setup.py, + now based on setuptools/ez_setup (thanks to Ralf Schmitt + for his support). + +* improved the way of making py.* scripts available in + windows environments, they are now added to the + Scripts directory as ".cmd" files. + +* py.path.svnwc.status() now is more complete and + uses xml output from the 'svn' command if available + (Guido Wesdorp) + +* fix for py.path.svn* to work with svn 1.5 + (Chris Lamb) + +* fix path.relto(otherpath) method on windows to + use normcase for checking if a path is relative. + +* py.test's traceback is better parseable from editors + (follows the filenames:LINENO: MSG convention) + (thanks to Osmo Salomaa) + +* fix to javascript-generation, "py.test --runbrowser" + should work more reliably now + +* removed previously accidentally added + py.test.broken and py.test.notimplemented helpers. + +* there now is a py.__version__ attribute + +Changes between 0.9.0 and 0.9.1 +------------------------------------------- + +This is a fairly complete list of changes between 0.9 and 0.9.1, which can +serve as a reference for developers. + +* allowing + signs in py.path.svn urls [39106] +* fixed support for Failed exceptions without excinfo in py.test [39340] +* added support for killing processes for Windows (as well as platforms that + support os.kill) in py.misc.killproc [39655] +* added setup/teardown for generative tests to py.test [40702] +* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739] +* fixed problem with calling .remove() on wcpaths of non-versioned files in + py.path [44248] +* fixed some import and inheritance issues in py.test [41480, 44648, 44655] +* fail to run greenlet tests when pypy is available, but without stackless + [45294] +* small fixes in rsession tests [45295] +* fixed issue with 2.5 type representations in py.test [45483, 45484] +* made that internal reporting issues displaying is done atomically in py.test + [45518] +* made that non-existing files are igored by the py.lookup script [45519] +* improved exception name creation in py.test [45535] +* made that less threads are used in execnet [merge in 45539] +* removed lock required for atomical reporting issue displaying in py.test + [45545] +* removed globals from execnet [45541, 45547] +* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit + get called in 2.5 (py.execnet) [45548] +* fixed bug in joining threads in py.execnet's servemain [45549] +* refactored py.test.rsession tests to not rely on exact output format anymore + [45646] +* using repr() on test outcome [45647] +* added 'Reason' classes for py.test.skip() [45648, 45649] +* killed some unnecessary sanity check in py.test.collect [45655] +* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only + usable by Administrators [45901] +* added support for locking and non-recursive commits to py.path.svnwc [45994] +* locking files in py.execnet to prevent CPython from segfaulting [46010] +* added export() method to py.path.svnurl +* fixed -d -x in py.test [47277] +* fixed argument concatenation problem in py.path.svnwc [49423] +* restore py.test behaviour that it exits with code 1 when there are failures + [49974] +* don't fail on html files that don't have an accompanying .txt file [50606] +* fixed 'utestconvert.py < input' [50645] +* small fix for code indentation in py.code.source [50755] +* fix _docgen.py documentation building [51285] +* improved checks for source representation of code blocks in py.test [51292] +* added support for passing authentication to py.path.svn* objects [52000, + 52001] +* removed sorted() call for py.apigen tests in favour of [].sort() to support + Python 2.3 [52481] diff -r 1e20f378b5ec9105394df56dc6368bb999ce2359 -r 81c80d8dfdff2509b1e223962dfdf287471b70bc doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.3.0.dev28" +version = release = "2.3.0" import sys, os diff -r 1e20f378b5ec9105394df56dc6368bb999ce2359 -r 81c80d8dfdff2509b1e223962dfdf287471b70bc doc/en/contents.txt --- a/doc/en/contents.txt +++ b/doc/en/contents.txt @@ -12,9 +12,9 @@ :maxdepth: 2 overview - example/index apiref plugins + example/index talks develop funcarg_compare.txt diff -r 1e20f378b5ec9105394df56dc6368bb999ce2359 -r 81c80d8dfdff2509b1e223962dfdf287471b70bc doc/en/unittest.txt --- a/doc/en/unittest.txt +++ b/doc/en/unittest.txt @@ -10,9 +10,9 @@ It's meant for leveraging existing unittest-style projects to use pytest features. Concretely, pytest will automatically collect ``unittest.TestCase`` subclasses and their ``test`` methods in -test files. It will invoke typlical ``setUp/tearDown`` methods and +test files. It will invoke typical setup/teardown methods and generally try to make test suites written to run on unittest, to also -run using pytest. We assume here that you are familiar with writing +run using ``py.test``. We assume here that you are familiar with writing ``unittest.TestCase`` style tests and rather focus on integration aspects. @@ -24,8 +24,8 @@ py.test and you should be able to run your unittest-style tests if they -are contained in ``test_*`` modules. This way you can make -use of most :ref:`pytest features `, for example +are contained in ``test_*`` modules. If that works for you then +you can make use of most :ref:`pytest features `, for example ``--pdb`` debugging in failures, using :ref:`plain assert-statements `, :ref:`more informative tracebacks `, stdout-capturing or distributing tests to multiple CPUs via the ``-nNUM`` option if you @@ -35,17 +35,17 @@ Mixing pytest fixtures into unittest.TestCase style tests ----------------------------------------------------------- -pytest supports using its :ref:`fixture mechanism ` with -``unittest.TestCase`` style tests. Assuming you have at least skimmed -the pytest fixture features, let's jump-start into an example that -integrates a pytest ``db_class`` fixture, setting up a -class-cached database object, and then reference it from -a unittest-style test:: +Running your unittest with ``py.test`` allows you to use its +:ref:`fixture mechanism ` with ``unittest.TestCase`` style +tests. Assuming you have at least skimmed the pytest fixture features, +let's jump-start into an example that integrates a pytest ``db_class`` +fixture, setting up a class-cached database object, and then reference +it from a unittest-style test:: # content of conftest.py - # hooks and fixtures in this file are available throughout all test - # modules living below the directory of this conftest.py file + # we define a fixture function below and it will be "used" by + # referencing its name from tests import pytest @@ -53,6 +53,7 @@ def db_class(request): class DummyDB: pass + # set a class attribute on the invoking test context request.cls.db = DummyDB() This defines a fixture function ``db_class`` which - if used - is @@ -81,7 +82,7 @@ assert 0, self.db # fail for demo purposes The ``@pytest.mark.usefixtures("db_class")`` class-decorator makes sure that -the pytest fixture function ``db_class`` is called for each test method. +the pytest fixture function ``db_class`` is called once per class. Due to the deliberately failing assert statements, we can take a look at the ``self.db`` values in the traceback:: @@ -114,7 +115,7 @@ test_unittest_db.py:12: AssertionError ========================= 2 failed in 0.02 seconds ========================= -This default pytest traceback shows that, indeed, the two test methods +This default pytest traceback shows that the two test methods share the same ``self.db`` instance which was our intention when writing the class-scoped fixture function above. @@ -124,13 +125,17 @@ Although it's usually better to explicitely declare use of fixtures you need for a given test, you may sometimes want to have fixtures that are -automatically used in a given context. For this, you can flag -fixture functions with ``@pytest.fixture(autouse=True)`` and define -the fixture function in the context where you want it used. Let's look -at an ``initdir`` fixrure which makes all test methods of a ``TestCase`` class -execute in a temporary directory with a pre-initialized ``samplefile.ini``. -Our ``initdir`` fixture itself uses the pytest builtin :ref:`tmpdir ` -fixture to help with creating a temporary dir:: +automatically used in a given context. After all, the traditional +style of unittest-setup mandates the use of this implicit fixture writing +and chances are, you are used to it or like it. + +You can flag fixture functions with ``@pytest.fixture(autouse=True)`` +and define the fixture function in the context where you want it used. +Let's look at an ``initdir`` fixture which makes all test methods of a +``TestCase`` class execute in a temporary directory with a +pre-initialized ``samplefile.ini``. Our ``initdir`` fixture itself uses +the pytest builtin :ref:`tmpdir ` fixture to delegate the +creation of a per-test temporary directory:: # content of test_unittest_cleandir.py import pytest @@ -148,8 +153,8 @@ Due to the ``autouse`` flag the ``initdir`` fixture function will be used for all methods of the class where it is defined. This is a -shortcut for using a ``@pytest.mark.usefixtures("initdir")`` on the -class like in the previous example. +shortcut for using a ``@pytest.mark.usefixtures("initdir")`` marker +on the class like in the previous example. Running this test module ...:: @@ -161,11 +166,13 @@ .. note:: - ``unittest.TestCase`` methods cannot directly receive fixture + While pytest supports receiving fixtures via :ref:`test function arguments ` for non-unittest test methods, ``unittest.TestCase`` methods cannot directly receive fixture function arguments as implementing that is likely to inflict on the ability to run general unittest.TestCase test suites. - Given enough demand, attempts might be made, though. If - unittest finally grows a reasonable plugin system that should - help as well. In the meanwhile, the above ``usefixtures`` and - ``autouse`` examples should help to mix in pytest fixtures into - unittest suites. + Maybe optional support would be possible, though. If unittest finally + grows a plugin system that should help as well. In the meanwhile, the + above ``usefixtures`` and ``autouse`` examples should help to mix in + pytest fixtures into unittest suites. And of course you can also start + to selectively leave away the ``unittest.TestCase`` subclassing, use + plain asserts and get the unlimited pytest feature set. + diff -r 1e20f378b5ec9105394df56dc6368bb999ce2359 -r 81c80d8dfdff2509b1e223962dfdf287471b70bc setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev30', + version='2.3.0.dev31', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 1e20f378b5ec9105394df56dc6368bb999ce2359 -r 81c80d8dfdff2509b1e223962dfdf287471b70bc tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] distshare={homedir}/.tox/distshare -envlist=py26,py27,py31,py32,py33,py27-xdist,py25,py24 +envlist=py26,py27,py31,py32,py33,py27-xdist,py25,trial indexserver= pypi = http://pypi.python.org/simple testrun = http://pypi.testrun.org https://bitbucket.org/hpk42/pytest/changeset/9658138fc210/ changeset: 9658138fc210 user: hpk42 date: 2012-10-19 10:07:13 summary: improve automatic id generation for parametrized tests affected #: 5 files diff -r 81c80d8dfdff2509b1e223962dfdf287471b70bc -r 9658138fc210bcc72a5fcc215eaa4e01f991e837 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ Changes between 2.2.4 and 2.3.0.dev ----------------------------------- +- fix issue202 - better automatic names for parametrized test functions - fix issue139 - introduce @pytest.fixture which allows direct scoping and parametrization of funcarg factories. Introduce new @pytest.setup marker to allow the writing of setup functions which accept funcargs. diff -r 81c80d8dfdff2509b1e223962dfdf287471b70bc -r 9658138fc210bcc72a5fcc215eaa4e01f991e837 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev31' +__version__ = '2.3.0.dev32' diff -r 81c80d8dfdff2509b1e223962dfdf287471b70bc -r 9658138fc210bcc72a5fcc215eaa4e01f991e837 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -660,8 +660,7 @@ self.function, arg)) valtype = indirect and "params" or "funcargs" if not ids: - idmaker = IDMaker() - ids = list(map(idmaker, argvalues)) + ids = idmaker(argnames, argvalues) newcalls = [] for callspec in self._calls or [CallSpec2(self)]: for i, valset in enumerate(argvalues): @@ -708,18 +707,17 @@ cs.setall(funcargs, id, param) self._calls.append(cs) -class IDMaker: - def __init__(self): - self.counter = 0 - def __call__(self, valset): - l = [] - for val in valset: - if not isinstance(val, (int, str)): - val = "."+str(self.counter) - self.counter += 1 - l.append(str(val)) - return "-".join(l) - +def idmaker(argnames, argvalues): + idlist = [] + for valindex, valset in enumerate(argvalues): + this_id = [] + for nameindex, val in enumerate(valset): + if not isinstance(val, (float, int, str)): + this_id.append(argnames[nameindex]+str(valindex)) + else: + this_id.append(str(val)) + idlist.append("-".join(this_id)) + return idlist def showfixtures(config): from _pytest.main import wrap_session diff -r 81c80d8dfdff2509b1e223962dfdf287471b70bc -r 9658138fc210bcc72a5fcc215eaa4e01f991e837 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev31', + version='2.3.0.dev32', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 81c80d8dfdff2509b1e223962dfdf287471b70bc -r 9658138fc210bcc72a5fcc215eaa4e01f991e837 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -992,10 +992,21 @@ pass metafunc.parametrize("x", [A(), A()]) metafunc.parametrize("y", list("ab")) - assert metafunc._calls[0].id == ".0-a" - assert metafunc._calls[1].id == ".0-b" - assert metafunc._calls[2].id == ".1-a" - assert metafunc._calls[3].id == ".1-b" + assert metafunc._calls[0].id == "x0-a" + assert metafunc._calls[1].id == "x0-b" + assert metafunc._calls[2].id == "x1-a" + assert metafunc._calls[3].id == "x1-b" + + def test_idmaker_autoname(self): + from _pytest.python import idmaker + result = idmaker(("a", "b"), [("string", 1.0), + ("st-ring", 2.0)]) + assert result == ["string-1.0", "st-ring-2.0"] + + result = idmaker(("a", "b"), [(object(), 1.0), + (object(), object())]) + assert result == ["a0-1.0", "a1-b1"] + def test_addcall_and_parametrize(self): def func(x, y): pass @@ -1362,6 +1373,41 @@ "*1 passed*" ]) + def test_parametrize_with_ids(self, testdir): + testdir.makepyfile(""" + import pytest + def pytest_generate_tests(metafunc): + metafunc.parametrize(("a", "b"), [(1,1), (1,2)], + ids=["basic", "advanced"]) + + def test_function(a, b): + assert a == b + """) + result = testdir.runpytest("-v") + assert result.ret == 1 + result.stdout.fnmatch_lines_random([ + "*test_function*basic*PASSED", + "*test_function*advanced*FAILED", + ]) + + def test_parametrize_without_ids(self, testdir): + testdir.makepyfile(""" + import pytest + def pytest_generate_tests(metafunc): + metafunc.parametrize(("a", "b"), + [(1,object()), (1.3,object())]) + + def test_function(a, b): + assert 1 + """) + result = testdir.runpytest("-v") + result.stdout.fnmatch_lines(""" + *test_function*1-b0* + *test_function*1.3-b1* + """) + + + @pytest.mark.parametrize(("scope", "length"), [("module", 2), ("function", 4)]) def test_parametrize_scope_overrides(self, testdir, scope, length): @@ -1985,24 +2031,6 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=2) -class TestResourceIntegrationFunctional: - def test_parametrize_with_ids(self, testdir): - testdir.makepyfile(""" - import pytest - def pytest_generate_tests(metafunc): - metafunc.parametrize(("a", "b"), [(1,1), (1,2)], - ids=["basic", "advanced"]) - - def test_function(a, b): - assert a == b - """) - result = testdir.runpytest("-v") - assert result.ret == 1 - result.stdout.fnmatch_lines_random([ - "*test_function*basic*PASSED", - "*test_function*advanced*FAILED", - ]) - class TestFixtureManager: def pytest_funcarg__testdir(self, request): testdir = request.getfuncargvalue("testdir") https://bitbucket.org/hpk42/pytest/changeset/72ae4efeefa5/ changeset: 72ae4efeefa5 user: hpk42 date: 2012-10-19 10:17:13 summary: make usefixtures appear in py.test --markers output affected #: 4 files diff -r 9658138fc210bcc72a5fcc215eaa4e01f991e837 -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev32' +__version__ = '2.3.0.dev33' diff -r 9658138fc210bcc72a5fcc215eaa4e01f991e837 -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -101,6 +101,10 @@ "@parametrize('arg1', [1,2]) would lead to two calls of the decorated " "test function, one with arg1=1 and another with arg1=2." ) + config.addinivalue_line("markers", + "usefixtures(fixturename1, fixturename2, ...): mark tests as needing " + "all of the specified fixtures." + ) def pytest_sessionstart(session): session._fixturemanager = FixtureManager(session) diff -r 9658138fc210bcc72a5fcc215eaa4e01f991e837 -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev32', + version='2.3.0.dev33', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 9658138fc210bcc72a5fcc215eaa4e01f991e837 -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1445,6 +1445,12 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=1) + def test_usefixtures_seen_in_showmarkers(self, testdir): + result = testdir.runpytest("--markers") + result.stdout.fnmatch_lines(""" + *usefixtures(fixturename1*mark tests*fixtures* + """) + def test_generate_tests_only_done_in_subdir(self, testdir): sub1 = testdir.mkpydir("sub1") sub2 = testdir.mkpydir("sub2") Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Oct 19 10:53:46 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 19 Oct 2012 08:53:46 -0000 Subject: [py-svn] commit/pytest: hpk42: fixing links for 2.3 release, and fixing a windows32 failure on py3 Message-ID: <20121019085346.21034.12215@bitbucket15.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/c83169fe80e8/ changeset: c83169fe80e8 user: hpk42 date: 2012-10-19 10:53:28 summary: fixing links for 2.3 release, and fixing a windows32 failure on py3 affected #: 21 files diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -Changes between 2.2.4 and 2.3.0.dev +Changes between 2.2.4 and 2.3.0 ----------------------------------- - fix issue202 - better automatic names for parametrized test functions diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev33' +__version__ = '2.3.0' diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/announce/release-2.3.0.txt --- a/doc/en/announce/release-2.3.0.txt +++ b/doc/en/announce/release-2.3.0.txt @@ -13,12 +13,12 @@ For detailed info and tutorial-style examples, see: - http://pytest.org/dev/fixture.html + http://pytest.org/latest/fixture.html Moreover, there is now support for using pytest fixtures/funcargs with unittest-style suites, see here for examples: - http://pytest.org/dev/unittest.html + http://pytest.org/latest/unittest.html Besides, more unittest-test suites are now expected to "simply work" with pytest. @@ -28,11 +28,11 @@ pytest-2.2.4. If you are interested in the precise reasoning of the pytest-2.3 fixture -evolution, please consult http://pytest.org/dev/funcarg_compare.html +evolution, please consult http://pytest.org/latest/funcarg_compare.html For general info on installation and getting started: - http://pytest.org/dev/getting-started.html + http://pytest.org/latest/getting-started.html Docs and PDF access as usual at: @@ -51,9 +51,10 @@ holger krekel -Changes between 2.2.4 and 2.3.0.dev +Changes between 2.2.4 and 2.3.0 ----------------------------------- +- fix issue202 - better automatic names for parametrized test functions - fix issue139 - introduce @pytest.fixture which allows direct scoping and parametrization of funcarg factories. Introduce new @pytest.setup marker to allow the writing of setup functions which accept funcargs. @@ -130,1041 +131,3 @@ - py.test -vv will show all of assert comparisations instead of truncating -Changes between 2.2.3 and 2.2.4 ------------------------------------ - -- fix error message for rewritten assertions involving the % operator -- fix issue 126: correctly match all invalid xml characters for junitxml - binary escape -- fix issue with unittest: now @unittest.expectedFailure markers should - be processed correctly (you can also use @pytest.mark markers) -- document integration with the extended distribute/setuptools test commands -- fix issue 140: propperly get the real functions - of bound classmethods for setup/teardown_class -- fix issue #141: switch from the deceased paste.pocoo.org to bpaste.net -- fix issue #143: call unconfigure/sessionfinish always when - configure/sessionstart where called -- fix issue #144: better mangle test ids to junitxml classnames -- upgrade distribute_setup.py to 0.6.27 - -Changes between 2.2.2 and 2.2.3 ----------------------------------------- - -- fix uploaded package to only include neccesary files - -Changes between 2.2.1 and 2.2.2 ----------------------------------------- - -- fix issue101: wrong args to unittest.TestCase test function now - produce better output -- fix issue102: report more useful errors and hints for when a - test directory was renamed and some pyc/__pycache__ remain -- fix issue106: allow parametrize to be applied multiple times - e.g. from module, class and at function level. -- fix issue107: actually perform session scope finalization -- don't check in parametrize if indirect parameters are funcarg names -- add chdir method to monkeypatch funcarg -- fix crash resulting from calling monkeypatch undo a second time -- fix issue115: make --collectonly robust against early failure - (missing files/directories) -- "-qq --collectonly" now shows only files and the number of tests in them -- "-q --collectonly" now shows test ids -- allow adding of attributes to test reports such that it also works - with distributed testing (no upgrade of pytest-xdist needed) - -Changes between 2.2.0 and 2.2.1 ----------------------------------------- - -- fix issue99 (in pytest and py) internallerrors with resultlog now - produce better output - fixed by normalizing pytest_internalerror - input arguments. -- fix issue97 / traceback issues (in pytest and py) improve traceback output - in conjunction with jinja2 and cython which hack tracebacks -- fix issue93 (in pytest and pytest-xdist) avoid "delayed teardowns": - the final test in a test node will now run its teardown directly - instead of waiting for the end of the session. Thanks Dave Hunt for - the good reporting and feedback. The pytest_runtest_protocol as well - as the pytest_runtest_teardown hooks now have "nextitem" available - which will be None indicating the end of the test run. -- fix collection crash due to unknown-source collected items, thanks - to Ralf Schmitt (fixed by depending on a more recent pylib) - -Changes between 2.1.3 and 2.2.0 ----------------------------------------- - -- fix issue90: introduce eager tearing down of test items so that - teardown function are called earlier. -- add an all-powerful metafunc.parametrize function which allows to - parametrize test function arguments in multiple steps and therefore - from indepdenent plugins and palces. -- add a @pytest.mark.parametrize helper which allows to easily - call a test function with different argument values -- Add examples to the "parametrize" example page, including a quick port - of Test scenarios and the new parametrize function and decorator. -- introduce registration for "pytest.mark.*" helpers via ini-files - or through plugin hooks. Also introduce a "--strict" option which - will treat unregistered markers as errors - allowing to avoid typos and maintain a well described set of markers - for your test suite. See exaples at http://pytest.org/latest/mark.html - and its links. -- issue50: introduce "-m marker" option to select tests based on markers - (this is a stricter and more predictable version of '-k' in that "-m" - only matches complete markers and has more obvious rules for and/or - semantics. -- new feature to help optimizing the speed of your tests: - --durations=N option for displaying N slowest test calls - and setup/teardown methods. -- fix issue87: --pastebin now works with python3 -- fix issue89: --pdb with unexpected exceptions in doctest work more sensibly -- fix and cleanup pytest's own test suite to not leak FDs -- fix issue83: link to generated funcarg list -- fix issue74: pyarg module names are now checked against imp.find_module false positives -- fix compatibility with twisted/trial-11.1.0 use cases -- simplify Node.listchain -- simplify junitxml output code by relying on py.xml -- add support for skip properties on unittest classes and functions - -Changes between 2.1.2 and 2.1.3 ----------------------------------------- - -- fix issue79: assertion rewriting failed on some comparisons in boolops -- correctly handle zero length arguments (a la pytest '') -- fix issue67 / junitxml now contains correct test durations, thanks ronny -- fix issue75 / skipping test failure on jython -- fix issue77 / Allow assertrepr_compare hook to apply to a subset of tests - -Changes between 2.1.1 and 2.1.2 ----------------------------------------- - -- fix assertion rewriting on files with windows newlines on some Python versions -- refine test discovery by package/module name (--pyargs), thanks Florian Mayer -- fix issue69 / assertion rewriting fixed on some boolean operations -- fix issue68 / packages now work with assertion rewriting -- fix issue66: use different assertion rewriting caches when the -O option is passed -- don't try assertion rewriting on Jython, use reinterp - -Changes between 2.1.0 and 2.1.1 ----------------------------------------------- - -- fix issue64 / pytest.set_trace now works within pytest_generate_tests hooks -- fix issue60 / fix error conditions involving the creation of __pycache__ -- fix issue63 / assertion rewriting on inserts involving strings containing '%' -- fix assertion rewriting on calls with a ** arg -- don't cache rewritten modules if bytecode generation is disabled -- fix assertion rewriting in read-only directories -- fix issue59: provide system-out/err tags for junitxml output -- fix issue61: assertion rewriting on boolean operations with 3 or more operands -- you can now build a man page with "cd doc ; make man" - -Changes between 2.0.3 and 2.1.0.DEV ----------------------------------------------- - -- fix issue53 call nosestyle setup functions with correct ordering -- fix issue58 and issue59: new assertion code fixes -- merge Benjamin's assertionrewrite branch: now assertions - for test modules on python 2.6 and above are done by rewriting - the AST and saving the pyc file before the test module is imported. - see doc/assert.txt for more info. -- fix issue43: improve doctests with better traceback reporting on - unexpected exceptions -- fix issue47: timing output in junitxml for test cases is now correct -- fix issue48: typo in MarkInfo repr leading to exception -- fix issue49: avoid confusing error when initizaliation partially fails -- fix issue44: env/username expansion for junitxml file path -- show releaselevel information in test runs for pypy -- reworked doc pages for better navigation and PDF generation -- report KeyboardInterrupt even if interrupted during session startup -- fix issue 35 - provide PDF doc version and download link from index page - -Changes between 2.0.2 and 2.0.3 ----------------------------------------------- - -- fix issue38: nicer tracebacks on calls to hooks, particularly early - configure/sessionstart ones - -- fix missing skip reason/meta information in junitxml files, reported - via http://lists.idyll.org/pipermail/testing-in-python/2011-March/003928.html - -- fix issue34: avoid collection failure with "test" prefixed classes - deriving from object. - -- don't require zlib (and other libs) for genscript plugin without - --genscript actually being used. - -- speed up skips (by not doing a full traceback represenation - internally) - -- fix issue37: avoid invalid characters in junitxml's output - -Changes between 2.0.1 and 2.0.2 ----------------------------------------------- - -- tackle issue32 - speed up test runs of very quick test functions - by reducing the relative overhead - -- fix issue30 - extended xfail/skipif handling and improved reporting. - If you have a syntax error in your skip/xfail - expressions you now get nice error reports. - - Also you can now access module globals from xfail/skipif - expressions so that this for example works now:: - - import pytest - import mymodule - @pytest.mark.skipif("mymodule.__version__[0] == "1") - def test_function(): - pass - - This will not run the test function if the module's version string - does not start with a "1". Note that specifying a string instead - of a boolean expressions allows py.test to report meaningful information - when summarizing a test run as to what conditions lead to skipping - (or xfail-ing) tests. - -- fix issue28 - setup_method and pytest_generate_tests work together - The setup_method fixture method now gets called also for - test function invocations generated from the pytest_generate_tests - hook. - -- fix issue27 - collectonly and keyword-selection (-k) now work together - Also, if you do "py.test --collectonly -q" you now get a flat list - of test ids that you can use to paste to the py.test commandline - in order to execute a particular test. - -- fix issue25 avoid reported problems with --pdb and python3.2/encodings output - -- fix issue23 - tmpdir argument now works on Python3.2 and WindowsXP - Starting with Python3.2 os.symlink may be supported. By requiring - a newer py lib version the py.path.local() implementation acknowledges - this. - -- fixed typos in the docs (thanks Victor Garcia, Brianna Laugher) and particular - thanks to Laura Creighton who also revieved parts of the documentation. - -- fix slighly wrong output of verbose progress reporting for classes - (thanks Amaury) - -- more precise (avoiding of) deprecation warnings for node.Class|Function accesses - -- avoid std unittest assertion helper code in tracebacks (thanks Ronny) - -Changes between 2.0.0 and 2.0.1 ----------------------------------------------- - -- refine and unify initial capturing so that it works nicely - even if the logging module is used on an early-loaded conftest.py - file or plugin. -- allow to omit "()" in test ids to allow for uniform test ids - as produced by Alfredo's nice pytest.vim plugin. -- fix issue12 - show plugin versions with "--version" and - "--traceconfig" and also document how to add extra information - to reporting test header -- fix issue17 (import-* reporting issue on python3) by - requiring py>1.4.0 (1.4.1 is going to include it) -- fix issue10 (numpy arrays truth checking) by refining - assertion interpretation in py lib -- fix issue15: make nose compatibility tests compatible - with python3 (now that nose-1.0 supports python3) -- remove somewhat surprising "same-conftest" detection because - it ignores conftest.py when they appear in several subdirs. -- improve assertions ("not in"), thanks Floris Bruynooghe -- improve behaviour/warnings when running on top of "python -OO" - (assertions and docstrings are turned off, leading to potential - false positives) -- introduce a pytest_cmdline_processargs(args) hook - to allow dynamic computation of command line arguments. - This fixes a regression because py.test prior to 2.0 - allowed to set command line options from conftest.py - files which so far pytest-2.0 only allowed from ini-files now. -- fix issue7: assert failures in doctest modules. - unexpected failures in doctests will not generally - show nicer, i.e. within the doctest failing context. -- fix issue9: setup/teardown functions for an xfail-marked - test will report as xfail if they fail but report as normally - passing (not xpassing) if they succeed. This only is true - for "direct" setup/teardown invocations because teardown_class/ - teardown_module cannot closely relate to a single test. -- fix issue14: no logging errors at process exit -- refinements to "collecting" output on non-ttys -- refine internal plugin registration and --traceconfig output -- introduce a mechanism to prevent/unregister plugins from the - command line, see http://pytest.org/plugins.html#cmdunregister -- activate resultlog plugin by default -- fix regression wrt yielded tests which due to the - collection-before-running semantics were not - setup as with pytest 1.3.4. Note, however, that - the recommended and much cleaner way to do test - parametraization remains the "pytest_generate_tests" - mechanism, see the docs. - -Changes between 1.3.4 and 2.0.0 ----------------------------------------------- - -- pytest-2.0 is now its own package and depends on pylib-2.0 -- new ability: python -m pytest / python -m pytest.main ability -- new python invcation: pytest.main(args, plugins) to load - some custom plugins early. -- try harder to run unittest test suites in a more compatible manner - by deferring setup/teardown semantics to the unittest package. - also work harder to run twisted/trial and Django tests which - should now basically work by default. -- introduce a new way to set config options via ini-style files, - by default setup.cfg and tox.ini files are searched. The old - ways (certain environment variables, dynamic conftest.py reading - is removed). -- add a new "-q" option which decreases verbosity and prints a more - nose/unittest-style "dot" output. -- fix issue135 - marks now work with unittest test cases as well -- fix issue126 - introduce py.test.set_trace() to trace execution via - PDB during the running of tests even if capturing is ongoing. -- fix issue123 - new "python -m py.test" invocation for py.test - (requires Python 2.5 or above) -- fix issue124 - make reporting more resilient against tests opening - files on filedescriptor 1 (stdout). -- fix issue109 - sibling conftest.py files will not be loaded. - (and Directory collectors cannot be customized anymore from a Directory's - conftest.py - this needs to happen at least one level up). -- introduce (customizable) assertion failure representations and enhance - output on assertion failures for comparisons and other cases (Floris Bruynooghe) -- nose-plugin: pass through type-signature failures in setup/teardown - functions instead of not calling them (Ed Singleton) -- remove py.test.collect.Directory (follows from a major refactoring - and simplification of the collection process) -- majorly reduce py.test core code, shift function/python testing to own plugin -- fix issue88 (finding custom test nodes from command line arg) -- refine 'tmpdir' creation, will now create basenames better associated - with test names (thanks Ronny) -- "xpass" (unexpected pass) tests don't cause exitcode!=0 -- fix issue131 / issue60 - importing doctests in __init__ files used as namespace packages -- fix issue93 stdout/stderr is captured while importing conftest.py -- fix bug: unittest collected functions now also can have "pytestmark" - applied at class/module level -- add ability to use "class" level for cached_setup helper -- fix strangeness: mark.* objects are now immutable, create new instances - -Changes between 1.3.3 and 1.3.4 ----------------------------------------------- - -- fix issue111: improve install documentation for windows -- fix issue119: fix custom collectability of __init__.py as a module -- fix issue116: --doctestmodules work with __init__.py files as well -- fix issue115: unify internal exception passthrough/catching/GeneratorExit -- fix issue118: new --tb=native for presenting cpython-standard exceptions - -Changes between 1.3.2 and 1.3.3 ----------------------------------------------- - -- fix issue113: assertion representation problem with triple-quoted strings - (and possibly other cases) -- make conftest loading detect that a conftest file with the same - content was already loaded, avoids surprises in nested directory structures - which can be produced e.g. by Hudson. It probably removes the need to use - --confcutdir in most cases. -- fix terminal coloring for win32 - (thanks Michael Foord for reporting) -- fix weirdness: make terminal width detection work on stdout instead of stdin - (thanks Armin Ronacher for reporting) -- remove trailing whitespace in all py/text distribution files - -Changes between 1.3.1 and 1.3.2 ----------------------------------------------- - -New features -++++++++++++++++++ - -- fix issue103: introduce py.test.raises as context manager, examples:: - - with py.test.raises(ZeroDivisionError): - x = 0 - 1 / x - - with py.test.raises(RuntimeError) as excinfo: - call_something() - - # you may do extra checks on excinfo.value|type|traceback here - - (thanks Ronny Pfannschmidt) - -- Funcarg factories can now dynamically apply a marker to a - test invocation. This is for example useful if a factory - provides parameters to a test which are expected-to-fail:: - - def pytest_funcarg__arg(request): - request.applymarker(py.test.mark.xfail(reason="flaky config")) - ... - - def test_function(arg): - ... - -- improved error reporting on collection and import errors. This makes - use of a more general mechanism, namely that for custom test item/collect - nodes ``node.repr_failure(excinfo)`` is now uniformly called so that you can - override it to return a string error representation of your choice - which is going to be reported as a (red) string. - -- introduce '--junitprefix=STR' option to prepend a prefix - to all reports in the junitxml file. - -Bug fixes / Maintenance -++++++++++++++++++++++++++ - -- make tests and the ``pytest_recwarn`` plugin in particular fully compatible - to Python2.7 (if you use the ``recwarn`` funcarg warnings will be enabled so that - you can properly check for their existence in a cross-python manner). -- refine --pdb: ignore xfailed tests, unify its TB-reporting and - don't display failures again at the end. -- fix assertion interpretation with the ** operator (thanks Benjamin Peterson) -- fix issue105 assignment on the same line as a failing assertion (thanks Benjamin Peterson) -- fix issue104 proper escaping for test names in junitxml plugin (thanks anonymous) -- fix issue57 -f|--looponfail to work with xpassing tests (thanks Ronny) -- fix issue92 collectonly reporter and --pastebin (thanks Benjamin Peterson) -- fix py.code.compile(source) to generate unique filenames -- fix assertion re-interp problems on PyPy, by defering code - compilation to the (overridable) Frame.eval class. (thanks Amaury Forgeot) -- fix py.path.local.pyimport() to work with directories -- streamline py.path.local.mkdtemp implementation and usage -- don't print empty lines when showing junitxml-filename -- add optional boolean ignore_errors parameter to py.path.local.remove -- fix terminal writing on win32/python2.4 -- py.process.cmdexec() now tries harder to return properly encoded unicode objects - on all python versions -- install plain py.test/py.which scripts also for Jython, this helps to - get canonical script paths in virtualenv situations -- make path.bestrelpath(path) return ".", note that when calling - X.bestrelpath the assumption is that X is a directory. -- make initial conftest discovery ignore "--" prefixed arguments -- fix resultlog plugin when used in an multicpu/multihost xdist situation - (thanks Jakub Gustak) -- perform distributed testing related reporting in the xdist-plugin - rather than having dist-related code in the generic py.test - distribution -- fix homedir detection on Windows -- ship distribute_setup.py version 0.6.13 - -Changes between 1.3.0 and 1.3.1 ---------------------------------------------- - -New features -++++++++++++++++++ - -- issue91: introduce new py.test.xfail(reason) helper - to imperatively mark a test as expected to fail. Can - be used from within setup and test functions. This is - useful especially for parametrized tests when certain - configurations are expected-to-fail. In this case the - declarative approach with the @py.test.mark.xfail cannot - be used as it would mark all configurations as xfail. - -- issue102: introduce new --maxfail=NUM option to stop - test runs after NUM failures. This is a generalization - of the '-x' or '--exitfirst' option which is now equivalent - to '--maxfail=1'. Both '-x' and '--maxfail' will - now also print a line near the end indicating the Interruption. - -- issue89: allow py.test.mark decorators to be used on classes - (class decorators were introduced with python2.6) and - also allow to have multiple markers applied at class/module level - by specifying a list. - -- improve and refine letter reporting in the progress bar: - . pass - f failed test - s skipped tests (reminder: use for dependency/platform mismatch only) - x xfailed test (test that was expected to fail) - X xpassed test (test that was expected to fail but passed) - - You can use any combination of 'fsxX' with the '-r' extended - reporting option. The xfail/xpass results will show up as - skipped tests in the junitxml output - which also fixes - issue99. - -- make py.test.cmdline.main() return the exitstatus instead of raising - SystemExit and also allow it to be called multiple times. This of - course requires that your application and tests are properly teared - down and don't have global state. - -Fixes / Maintenance -++++++++++++++++++++++ - -- improved traceback presentation: - - improved and unified reporting for "--tb=short" option - - Errors during test module imports are much shorter, (using --tb=short style) - - raises shows shorter more relevant tracebacks - - --fulltrace now more systematically makes traces longer / inhibits cutting - -- improve support for raises and other dynamically compiled code by - manipulating python's linecache.cache instead of the previous - rather hacky way of creating custom code objects. This makes - it seemlessly work on Jython and PyPy where it previously didn't. - -- fix issue96: make capturing more resilient against Control-C - interruptions (involved somewhat substantial refactoring - to the underlying capturing functionality to avoid race - conditions). - -- fix chaining of conditional skipif/xfail decorators - so it works now - as expected to use multiple @py.test.mark.skipif(condition) decorators, - including specific reporting which of the conditions lead to skipping. - -- fix issue95: late-import zlib so that it's not required - for general py.test startup. - -- fix issue94: make reporting more robust against bogus source code - (and internally be more careful when presenting unexpected byte sequences) - - -Changes between 1.2.1 and 1.3.0 ---------------------------------------------- - -- deprecate --report option in favour of a new shorter and easier to - remember -r option: it takes a string argument consisting of any - combination of 'xfsX' characters. They relate to the single chars - you see during the dotted progress printing and will print an extra line - per test at the end of the test run. This extra line indicates the exact - position or test ID that you directly paste to the py.test cmdline in order - to re-run a particular test. - -- allow external plugins to register new hooks via the new - pytest_addhooks(pluginmanager) hook. The new release of - the pytest-xdist plugin for distributed and looponfailing - testing requires this feature. - -- add a new pytest_ignore_collect(path, config) hook to allow projects and - plugins to define exclusion behaviour for their directory structure - - for example you may define in a conftest.py this method:: - - def pytest_ignore_collect(path): - return path.check(link=1) - - to prevent even a collection try of any tests in symlinked dirs. - -- new pytest_pycollect_makemodule(path, parent) hook for - allowing customization of the Module collection object for a - matching test module. - -- extend and refine xfail mechanism: - ``@py.test.mark.xfail(run=False)`` do not run the decorated test - ``@py.test.mark.xfail(reason="...")`` prints the reason string in xfail summaries - specifiying ``--runxfail`` on command line virtually ignores xfail markers - -- expose (previously internal) commonly useful methods: - py.io.get_terminal_with() -> return terminal width - py.io.ansi_print(...) -> print colored/bold text on linux/win32 - py.io.saferepr(obj) -> return limited representation string - -- expose test outcome related exceptions as py.test.skip.Exception, - py.test.raises.Exception etc., useful mostly for plugins - doing special outcome interpretation/tweaking - -- (issue85) fix junitxml plugin to handle tests with non-ascii output - -- fix/refine python3 compatibility (thanks Benjamin Peterson) - -- fixes for making the jython/win32 combination work, note however: - jython2.5.1/win32 does not provide a command line launcher, see - http://bugs.jython.org/issue1491 . See pylib install documentation - for how to work around. - -- fixes for handling of unicode exception values and unprintable objects - -- (issue87) fix unboundlocal error in assertionold code - -- (issue86) improve documentation for looponfailing - -- refine IO capturing: stdin-redirect pseudo-file now has a NOP close() method - -- ship distribute_setup.py version 0.6.10 - -- added links to the new capturelog and coverage plugins - - -Changes between 1.2.1 and 1.2.0 ---------------------------------------------- - -- refined usage and options for "py.cleanup":: - - py.cleanup # remove "*.pyc" and "*$py.class" (jython) files - py.cleanup -e .swp -e .cache # also remove files with these extensions - py.cleanup -s # remove "build" and "dist" directory next to setup.py files - py.cleanup -d # also remove empty directories - py.cleanup -a # synonym for "-s -d -e 'pip-log.txt'" - py.cleanup -n # dry run, only show what would be removed - -- add a new option "py.test --funcargs" which shows available funcargs - and their help strings (docstrings on their respective factory function) - for a given test path - -- display a short and concise traceback if a funcarg lookup fails - -- early-load "conftest.py" files in non-dot first-level sub directories. - allows to conveniently keep and access test-related options in a ``test`` - subdir and still add command line options. - -- fix issue67: new super-short traceback-printing option: "--tb=line" will print a single line for each failing (python) test indicating its filename, lineno and the failure value - -- fix issue78: always call python-level teardown functions even if the - according setup failed. This includes refinements for calling setup_module/class functions - which will now only be called once instead of the previous behaviour where they'd be called - multiple times if they raise an exception (including a Skipped exception). Any exception - will be re-corded and associated with all tests in the according module/class scope. - -- fix issue63: assume <40 columns to be a bogus terminal width, default to 80 - -- fix pdb debugging to be in the correct frame on raises-related errors - -- update apipkg.py to fix an issue where recursive imports might - unnecessarily break importing - -- fix plugin links - -Changes between 1.2 and 1.1.1 ---------------------------------------------- - -- moved dist/looponfailing from py.test core into a new - separately released pytest-xdist plugin. - -- new junitxml plugin: --junitxml=path will generate a junit style xml file - which is processable e.g. by the Hudson CI system. - -- new option: --genscript=path will generate a standalone py.test script - which will not need any libraries installed. thanks to Ralf Schmitt. - -- new option: --ignore will prevent specified path from collection. - Can be specified multiple times. - -- new option: --confcutdir=dir will make py.test only consider conftest - files that are relative to the specified dir. - -- new funcarg: "pytestconfig" is the pytest config object for access - to command line args and can now be easily used in a test. - -- install 'py.test' and `py.which` with a ``-$VERSION`` suffix to - disambiguate between Python3, python2.X, Jython and PyPy installed versions. - -- new "pytestconfig" funcarg allows access to test config object - -- new "pytest_report_header" hook can return additional lines - to be displayed at the header of a test run. - -- (experimental) allow "py.test path::name1::name2::..." for pointing - to a test within a test collection directly. This might eventually - evolve as a full substitute to "-k" specifications. - -- streamlined plugin loading: order is now as documented in - customize.html: setuptools, ENV, commandline, conftest. - also setuptools entry point names are turned to canonical namees ("pytest_*") - -- automatically skip tests that need 'capfd' but have no os.dup - -- allow pytest_generate_tests to be defined in classes as well - -- deprecate usage of 'disabled' attribute in favour of pytestmark -- deprecate definition of Directory, Module, Class and Function nodes - in conftest.py files. Use pytest collect hooks instead. - -- collection/item node specific runtest/collect hooks are only called exactly - on matching conftest.py files, i.e. ones which are exactly below - the filesystem path of an item - -- change: the first pytest_collect_directory hook to return something - will now prevent further hooks to be called. - -- change: figleaf plugin now requires --figleaf to run. Also - change its long command line options to be a bit shorter (see py.test -h). - -- change: pytest doctest plugin is now enabled by default and has a - new option --doctest-glob to set a pattern for file matches. - -- change: remove internal py._* helper vars, only keep py._pydir - -- robustify capturing to survive if custom pytest_runtest_setup - code failed and prevented the capturing setup code from running. - -- make py.test.* helpers provided by default plugins visible early - - works transparently both for pydoc and for interactive sessions - which will regularly see e.g. py.test.mark and py.test.importorskip. - -- simplify internal plugin manager machinery -- simplify internal collection tree by introducing a RootCollector node - -- fix assert reinterpreation that sees a call containing "keyword=..." - -- fix issue66: invoke pytest_sessionstart and pytest_sessionfinish - hooks on slaves during dist-testing, report module/session teardown - hooks correctly. - -- fix issue65: properly handle dist-testing if no - execnet/py lib installed remotely. - -- skip some install-tests if no execnet is available - -- fix docs, fix internal bin/ script generation - - -Changes between 1.1.1 and 1.1.0 ---------------------------------------------- - -- introduce automatic plugin registration via 'pytest11' - entrypoints via setuptools' pkg_resources.iter_entry_points - -- fix py.test dist-testing to work with execnet >= 1.0.0b4 - -- re-introduce py.test.cmdline.main() for better backward compatibility - -- svn paths: fix a bug with path.check(versioned=True) for svn paths, - allow '%' in svn paths, make svnwc.update() default to interactive mode - like in 1.0.x and add svnwc.update(interactive=False) to inhibit interaction. - -- refine distributed tarball to contain test and no pyc files - -- try harder to have deprecation warnings for py.compat.* accesses - report a correct location - -Changes between 1.1.0 and 1.0.2 ---------------------------------------------- - -* adjust and improve docs - -* remove py.rest tool and internal namespace - it was - never really advertised and can still be used with - the old release if needed. If there is interest - it could be revived into its own tool i guess. - -* fix issue48 and issue59: raise an Error if the module - from an imported test file does not seem to come from - the filepath - avoids "same-name" confusion that has - been reported repeatedly - -* merged Ronny's nose-compatibility hacks: now - nose-style setup_module() and setup() functions are - supported - -* introduce generalized py.test.mark function marking - -* reshuffle / refine command line grouping - -* deprecate parser.addgroup in favour of getgroup which creates option group - -* add --report command line option that allows to control showing of skipped/xfailed sections - -* generalized skipping: a new way to mark python functions with skipif or xfail - at function, class and modules level based on platform or sys-module attributes. - -* extend py.test.mark decorator to allow for positional args - -* introduce and test "py.cleanup -d" to remove empty directories - -* fix issue #59 - robustify unittest test collection - -* make bpython/help interaction work by adding an __all__ attribute - to ApiModule, cleanup initpkg - -* use MIT license for pylib, add some contributors - -* remove py.execnet code and substitute all usages with 'execnet' proper - -* fix issue50 - cached_setup now caches more to expectations - for test functions with multiple arguments. - -* merge Jarko's fixes, issue #45 and #46 - -* add the ability to specify a path for py.lookup to search in - -* fix a funcarg cached_setup bug probably only occuring - in distributed testing and "module" scope with teardown. - -* many fixes and changes for making the code base python3 compatible, - many thanks to Benjamin Peterson for helping with this. - -* consolidate builtins implementation to be compatible with >=2.3, - add helpers to ease keeping 2 and 3k compatible code - -* deprecate py.compat.doctest|subprocess|textwrap|optparse - -* deprecate py.magic.autopath, remove py/magic directory - -* move pytest assertion handling to py/code and a pytest_assertion - plugin, add "--no-assert" option, deprecate py.magic namespaces - in favour of (less) py.code ones. - -* consolidate and cleanup py/code classes and files - -* cleanup py/misc, move tests to bin-for-dist - -* introduce delattr/delitem/delenv methods to py.test's monkeypatch funcarg - -* consolidate py.log implementation, remove old approach. - -* introduce py.io.TextIO and py.io.BytesIO for distinguishing between - text/unicode and byte-streams (uses underlying standard lib io.* - if available) - -* make py.unittest_convert helper script available which converts "unittest.py" - style files into the simpler assert/direct-test-classes py.test/nosetests - style. The script was written by Laura Creighton. - -* simplified internal localpath implementation - -Changes between 1.0.1 and 1.0.2 -------------------------------------------- - -* fixing packaging issues, triggered by fedora redhat packaging, - also added doc, examples and contrib dirs to the tarball. - -* added a documentation link to the new django plugin. - -Changes between 1.0.0 and 1.0.1 -------------------------------------------- - -* added a 'pytest_nose' plugin which handles nose.SkipTest, - nose-style function/method/generator setup/teardown and - tries to report functions correctly. - -* capturing of unicode writes or encoded strings to sys.stdout/err - work better, also terminalwriting was adapted and somewhat - unified between windows and linux. - -* improved documentation layout and content a lot - -* added a "--help-config" option to show conftest.py / ENV-var names for - all longopt cmdline options, and some special conftest.py variables. - renamed 'conf_capture' conftest setting to 'option_capture' accordingly. - -* fix issue #27: better reporting on non-collectable items given on commandline - (e.g. pyc files) - -* fix issue #33: added --version flag (thanks Benjamin Peterson) - -* fix issue #32: adding support for "incomplete" paths to wcpath.status() - -* "Test" prefixed classes are *not* collected by default anymore if they - have an __init__ method - -* monkeypatch setenv() now accepts a "prepend" parameter - -* improved reporting of collection error tracebacks - -* simplified multicall mechanism and plugin architecture, - renamed some internal methods and argnames - -Changes between 1.0.0b9 and 1.0.0 -------------------------------------------- - -* more terse reporting try to show filesystem path relatively to current dir -* improve xfail output a bit - -Changes between 1.0.0b8 and 1.0.0b9 -------------------------------------------- - -* cleanly handle and report final teardown of test setup - -* fix svn-1.6 compat issue with py.path.svnwc().versioned() - (thanks Wouter Vanden Hove) - -* setup/teardown or collection problems now show as ERRORs - or with big "E"'s in the progress lines. they are reported - and counted separately. - -* dist-testing: properly handle test items that get locally - collected but cannot be collected on the remote side - often - due to platform/dependency reasons - -* simplified py.test.mark API - see keyword plugin documentation - -* integrate better with logging: capturing now by default captures - test functions and their immediate setup/teardown in a single stream - -* capsys and capfd funcargs now have a readouterr() and a close() method - (underlyingly py.io.StdCapture/FD objects are used which grew a - readouterr() method as well to return snapshots of captured out/err) - -* make assert-reinterpretation work better with comparisons not - returning bools (reported with numpy from thanks maciej fijalkowski) - -* reworked per-test output capturing into the pytest_iocapture.py plugin - and thus removed capturing code from config object - -* item.repr_failure(excinfo) instead of item.repr_failure(excinfo, outerr) - - -Changes between 1.0.0b7 and 1.0.0b8 -------------------------------------------- - -* pytest_unittest-plugin is now enabled by default - -* introduced pytest_keyboardinterrupt hook and - refined pytest_sessionfinish hooked, added tests. - -* workaround a buggy logging module interaction ("closing already closed - files"). Thanks to Sridhar Ratnakumar for triggering. - -* if plugins use "py.test.importorskip" for importing - a dependency only a warning will be issued instead - of exiting the testing process. - -* many improvements to docs: - - refined funcargs doc , use the term "factory" instead of "provider" - - added a new talk/tutorial doc page - - better download page - - better plugin docstrings - - added new plugins page and automatic doc generation script - -* fixed teardown problem related to partially failing funcarg setups - (thanks MrTopf for reporting), "pytest_runtest_teardown" is now - always invoked even if the "pytest_runtest_setup" failed. - -* tweaked doctest output for docstrings in py modules, - thanks Radomir. - -Changes between 1.0.0b3 and 1.0.0b7 -------------------------------------------- - -* renamed py.test.xfail back to py.test.mark.xfail to avoid - two ways to decorate for xfail - -* re-added py.test.mark decorator for setting keywords on functions - (it was actually documented so removing it was not nice) - -* remove scope-argument from request.addfinalizer() because - request.cached_setup has the scope arg. TOOWTDI. - -* perform setup finalization before reporting failures - -* apply modified patches from Andreas Kloeckner to allow - test functions to have no func_code (#22) and to make - "-k" and function keywords work (#20) - -* apply patch from Daniel Peolzleithner (issue #23) - -* resolve issue #18, multiprocessing.Manager() and - redirection clash - -* make __name__ == "__channelexec__" for remote_exec code - -Changes between 1.0.0b1 and 1.0.0b3 -------------------------------------------- - -* plugin classes are removed: one now defines - hooks directly in conftest.py or global pytest_*.py - files. - -* added new pytest_namespace(config) hook that allows - to inject helpers directly to the py.test.* namespace. - -* documented and refined many hooks - -* added new style of generative tests via - pytest_generate_tests hook that integrates - well with function arguments. - - -Changes between 0.9.2 and 1.0.0b1 -------------------------------------------- - -* introduced new "funcarg" setup method, - see doc/test/funcarg.txt - -* introduced plugin architecuture and many - new py.test plugins, see - doc/test/plugins.txt - -* teardown_method is now guaranteed to get - called after a test method has run. - -* new method: py.test.importorskip(mod,minversion) - will either import or call py.test.skip() - -* completely revised internal py.test architecture - -* new py.process.ForkedFunc object allowing to - fork execution of a function to a sub process - and getting a result back. - -XXX lots of things missing here XXX - -Changes between 0.9.1 and 0.9.2 -------------------------------------------- - -* refined installation and metadata, created new setup.py, - now based on setuptools/ez_setup (thanks to Ralf Schmitt - for his support). - -* improved the way of making py.* scripts available in - windows environments, they are now added to the - Scripts directory as ".cmd" files. - -* py.path.svnwc.status() now is more complete and - uses xml output from the 'svn' command if available - (Guido Wesdorp) - -* fix for py.path.svn* to work with svn 1.5 - (Chris Lamb) - -* fix path.relto(otherpath) method on windows to - use normcase for checking if a path is relative. - -* py.test's traceback is better parseable from editors - (follows the filenames:LINENO: MSG convention) - (thanks to Osmo Salomaa) - -* fix to javascript-generation, "py.test --runbrowser" - should work more reliably now - -* removed previously accidentally added - py.test.broken and py.test.notimplemented helpers. - -* there now is a py.__version__ attribute - -Changes between 0.9.0 and 0.9.1 -------------------------------------------- - -This is a fairly complete list of changes between 0.9 and 0.9.1, which can -serve as a reference for developers. - -* allowing + signs in py.path.svn urls [39106] -* fixed support for Failed exceptions without excinfo in py.test [39340] -* added support for killing processes for Windows (as well as platforms that - support os.kill) in py.misc.killproc [39655] -* added setup/teardown for generative tests to py.test [40702] -* added detection of FAILED TO LOAD MODULE to py.test [40703, 40738, 40739] -* fixed problem with calling .remove() on wcpaths of non-versioned files in - py.path [44248] -* fixed some import and inheritance issues in py.test [41480, 44648, 44655] -* fail to run greenlet tests when pypy is available, but without stackless - [45294] -* small fixes in rsession tests [45295] -* fixed issue with 2.5 type representations in py.test [45483, 45484] -* made that internal reporting issues displaying is done atomically in py.test - [45518] -* made that non-existing files are igored by the py.lookup script [45519] -* improved exception name creation in py.test [45535] -* made that less threads are used in execnet [merge in 45539] -* removed lock required for atomical reporting issue displaying in py.test - [45545] -* removed globals from execnet [45541, 45547] -* refactored cleanup mechanics, made that setDaemon is set to 1 to make atexit - get called in 2.5 (py.execnet) [45548] -* fixed bug in joining threads in py.execnet's servemain [45549] -* refactored py.test.rsession tests to not rely on exact output format anymore - [45646] -* using repr() on test outcome [45647] -* added 'Reason' classes for py.test.skip() [45648, 45649] -* killed some unnecessary sanity check in py.test.collect [45655] -* avoid using os.tmpfile() in py.io.fdcapture because on Windows it's only - usable by Administrators [45901] -* added support for locking and non-recursive commits to py.path.svnwc [45994] -* locking files in py.execnet to prevent CPython from segfaulting [46010] -* added export() method to py.path.svnurl -* fixed -d -x in py.test [47277] -* fixed argument concatenation problem in py.path.svnwc [49423] -* restore py.test behaviour that it exits with code 1 when there are failures - [49974] -* don't fail on html files that don't have an accompanying .txt file [50606] -* fixed 'utestconvert.py < input' [50645] -* small fix for code indentation in py.code.source [50755] -* fix _docgen.py documentation building [51285] -* improved checks for source representation of code blocks in py.test [51292] -* added support for passing authentication to py.path.svn* objects [52000, - 52001] -* removed sorted() call for py.apigen tests in favour of [].sort() to support - Python 2.3 [52481] diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/assert.txt --- a/doc/en/assert.txt +++ b/doc/en/assert.txt @@ -26,7 +26,7 @@ $ py.test test_assert1.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 1 items test_assert1.py F @@ -110,7 +110,7 @@ $ py.test test_assert2.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 1 items test_assert2.py F diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/capture.txt --- a/doc/en/capture.txt +++ b/doc/en/capture.txt @@ -64,7 +64,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 2 items test_module.py .F @@ -78,7 +78,7 @@ test_module.py:9: AssertionError ----------------------------- Captured stdout ------------------------------ - setting up + setting up ==================== 1 failed, 1 passed in 0.01 seconds ==================== Accessing captured output from a test function diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/doctest.txt --- a/doc/en/doctest.txt +++ b/doc/en/doctest.txt @@ -44,9 +44,9 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 1 items mymodule.py . - ========================= 1 passed in 0.02 seconds ========================= + ========================= 1 passed in 0.04 seconds ========================= diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/example/attic_remoteinterpreter.txt --- a/doc/en/example/attic_remoteinterpreter.txt +++ b/doc/en/example/attic_remoteinterpreter.txt @@ -63,7 +63,7 @@ $ py.test test_remoteinterpreter.py Traceback (most recent call last): File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in - load_entry_point('pytest==2.3.0.dev28', 'console_scripts', 'py.test')() + load_entry_point('pytest==2.3.0', 'console_scripts', 'py.test')() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 473, in main config = _prepareconfig(args, plugins) File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig @@ -98,7 +98,7 @@ self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport __import__(modname) - File "/tmp/doc-exec-111/conftest.py", line 2, in + File "/tmp/doc-exec-172/conftest.py", line 2, in from .remoteinterpreter import RemoteInterpreter ValueError: Attempted relative import in non-package @@ -150,7 +150,7 @@ $ py.test -q test_ssh.py -rs Traceback (most recent call last): File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in - load_entry_point('pytest==2.3.0.dev28', 'console_scripts', 'py.test')() + load_entry_point('pytest==2.3.0', 'console_scripts', 'py.test')() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 473, in main config = _prepareconfig(args, plugins) File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig @@ -185,7 +185,7 @@ self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport __import__(modname) - File "/tmp/doc-exec-111/conftest.py", line 2, in + File "/tmp/doc-exec-172/conftest.py", line 2, in from myapp import MyApp ImportError: No module named myapp diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -26,7 +26,7 @@ $ py.test -v -m webtest =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:3: test_send_http PASSED @@ -38,7 +38,7 @@ $ py.test -v -m "not webtest" =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:6: test_something_quick PASSED @@ -71,6 +71,8 @@ @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2. + @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. + @pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible. @pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible. @@ -143,7 +145,7 @@ $ py.test -k send_http # running with the above defined examples =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 4 items test_server.py . @@ -155,7 +157,7 @@ $ py.test -k-send_http =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 4 items test_mark_classlevel.py .. @@ -168,7 +170,7 @@ $ py.test -kTestClass =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 4 items test_mark_classlevel.py .. @@ -221,23 +223,23 @@ $ py.test -E stage2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 1 items test_someenv.py s - ======================== 1 skipped in 0.01 seconds ========================= + ======================== 1 skipped in 0.00 seconds ========================= and here is one that specifies exactly the environment needed:: $ py.test -E stage1 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 1 items test_someenv.py . - ========================= 1 passed in 0.01 seconds ========================= + ========================= 1 passed in 0.00 seconds ========================= The ``--markers`` option always gives you a list of available markers:: @@ -250,6 +252,8 @@ @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2. + @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. + @pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible. @pytest.mark.trylast: mark a hook implementation function such that the plugin machinery will try to call it last/as late as possible. @@ -347,12 +351,12 @@ $ py.test -rs # this option reports skip reasons =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 4 items test_plat.py s.s. ========================= short test summary info ========================== - SKIP [2] /tmp/doc-exec-113/conftest.py:12: cannot run on platform linux2 + SKIP [2] /tmp/doc-exec-174/conftest.py:12: cannot run on platform linux2 =================== 2 passed, 2 skipped in 0.01 seconds ==================== @@ -360,7 +364,7 @@ $ py.test -m linux2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 4 items test_plat.py . diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/example/nonpython.txt --- a/doc/en/example/nonpython.txt +++ b/doc/en/example/nonpython.txt @@ -27,7 +27,7 @@ nonpython $ py.test test_simple.yml =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 2 items test_simple.yml .F @@ -56,7 +56,7 @@ nonpython $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_simple.yml:1: usecase: ok PASSED @@ -74,7 +74,7 @@ nonpython $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 2 items diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/example/parametrize.txt --- a/doc/en/example/parametrize.txt +++ b/doc/en/example/parametrize.txt @@ -104,7 +104,7 @@ $ py.test test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 4 items test_scenarios.py .... @@ -116,7 +116,7 @@ $ py.test --collectonly test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 4 items @@ -180,7 +180,7 @@ $ py.test test_backends.py --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 2 items @@ -195,7 +195,7 @@ ================================= FAILURES ================================= _________________________ test_db_initialized[d2] __________________________ - db = + db = def test_db_initialized(db): # a dummy test @@ -250,7 +250,7 @@ ================================= FAILURES ================================= ________________________ TestClass.test_equals[1-2] ________________________ - self = , a = 1, b = 2 + self = , a = 1, b = 2 def test_equals(self, a, b): > assert a == b diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/example/pythoncollection.txt --- a/doc/en/example/pythoncollection.txt +++ b/doc/en/example/pythoncollection.txt @@ -43,7 +43,7 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 2 items @@ -51,7 +51,7 @@ - ============================= in 0.00 seconds ============================= + ============================= in 0.01 seconds ============================= Interpreting cmdline arguments as Python packages ----------------------------------------------------- @@ -82,7 +82,7 @@ . $ py.test --collectonly pythoncollection.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 3 items @@ -135,7 +135,7 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 1 items diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/example/reportingdemo.txt --- a/doc/en/example/reportingdemo.txt +++ b/doc/en/example/reportingdemo.txt @@ -13,7 +13,7 @@ assertion $ py.test failure_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 39 items failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF @@ -30,7 +30,7 @@ failure_demo.py:15: AssertionError _________________________ TestFailing.test_simple __________________________ - self = + self = def test_simple(self): def f(): @@ -40,13 +40,13 @@ > assert f() == g() E assert 42 == 43 - E + where 42 = () - E + and 43 = () + E + where 42 = () + E + and 43 = () failure_demo.py:28: AssertionError ____________________ TestFailing.test_simple_multiline _____________________ - self = + self = def test_simple_multiline(self): otherfunc_multi( @@ -66,19 +66,19 @@ failure_demo.py:11: AssertionError ___________________________ TestFailing.test_not ___________________________ - self = + self = def test_not(self): def f(): return 42 > assert not f() E assert not 42 - E + where 42 = () + E + where 42 = () failure_demo.py:38: AssertionError _________________ TestSpecialisedExplanations.test_eq_text _________________ - self = + self = def test_eq_text(self): > assert 'spam' == 'eggs' @@ -89,7 +89,7 @@ failure_demo.py:42: AssertionError _____________ TestSpecialisedExplanations.test_eq_similar_text _____________ - self = + self = def test_eq_similar_text(self): > assert 'foo 1 bar' == 'foo 2 bar' @@ -102,7 +102,7 @@ failure_demo.py:45: AssertionError ____________ TestSpecialisedExplanations.test_eq_multiline_text ____________ - self = + self = def test_eq_multiline_text(self): > assert 'foo\nspam\nbar' == 'foo\neggs\nbar' @@ -115,7 +115,7 @@ failure_demo.py:48: AssertionError ______________ TestSpecialisedExplanations.test_eq_long_text _______________ - self = + self = def test_eq_long_text(self): a = '1'*100 + 'a' + '2'*100 @@ -132,7 +132,7 @@ failure_demo.py:53: AssertionError _________ TestSpecialisedExplanations.test_eq_long_text_multiline __________ - self = + self = def test_eq_long_text_multiline(self): a = '1\n'*100 + 'a' + '2\n'*100 @@ -156,7 +156,7 @@ failure_demo.py:58: AssertionError _________________ TestSpecialisedExplanations.test_eq_list _________________ - self = + self = def test_eq_list(self): > assert [0, 1, 2] == [0, 1, 3] @@ -166,7 +166,7 @@ failure_demo.py:61: AssertionError ______________ TestSpecialisedExplanations.test_eq_list_long _______________ - self = + self = def test_eq_list_long(self): a = [0]*100 + [1] + [3]*100 @@ -178,7 +178,7 @@ failure_demo.py:66: AssertionError _________________ TestSpecialisedExplanations.test_eq_dict _________________ - self = + self = def test_eq_dict(self): > assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2} @@ -191,7 +191,7 @@ failure_demo.py:69: AssertionError _________________ TestSpecialisedExplanations.test_eq_set __________________ - self = + self = def test_eq_set(self): > assert set([0, 10, 11, 12]) == set([0, 20, 21]) @@ -207,7 +207,7 @@ failure_demo.py:72: AssertionError _____________ TestSpecialisedExplanations.test_eq_longer_list ______________ - self = + self = def test_eq_longer_list(self): > assert [1,2] == [1,2,3] @@ -217,7 +217,7 @@ failure_demo.py:75: AssertionError _________________ TestSpecialisedExplanations.test_in_list _________________ - self = + self = def test_in_list(self): > assert 1 in [0, 2, 3, 4, 5] @@ -226,7 +226,7 @@ failure_demo.py:78: AssertionError __________ TestSpecialisedExplanations.test_not_in_text_multiline __________ - self = + self = def test_not_in_text_multiline(self): text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail' @@ -244,7 +244,7 @@ failure_demo.py:82: AssertionError ___________ TestSpecialisedExplanations.test_not_in_text_single ____________ - self = + self = def test_not_in_text_single(self): text = 'single foo line' @@ -257,7 +257,7 @@ failure_demo.py:86: AssertionError _________ TestSpecialisedExplanations.test_not_in_text_single_long _________ - self = + self = def test_not_in_text_single_long(self): text = 'head ' * 50 + 'foo ' + 'tail ' * 20 @@ -270,7 +270,7 @@ failure_demo.py:90: AssertionError ______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______ - self = + self = def test_not_in_text_single_long_term(self): text = 'head ' * 50 + 'f'*70 + 'tail ' * 20 @@ -289,7 +289,7 @@ i = Foo() > assert i.b == 2 E assert 1 == 2 - E + where 1 = .b + E + where 1 = .b failure_demo.py:101: AssertionError _________________________ test_attribute_instance __________________________ @@ -299,8 +299,8 @@ b = 1 > assert Foo().b == 2 E assert 1 == 2 - E + where 1 = .b - E + where = () + E + where 1 = .b + E + where = () failure_demo.py:107: AssertionError __________________________ test_attribute_failure __________________________ @@ -316,7 +316,7 @@ failure_demo.py:116: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - self = + self = def _get_b(self): > raise Exception('Failed to get attrib') @@ -332,15 +332,15 @@ b = 2 > assert Foo().b == Bar().b E assert 1 == 2 - E + where 1 = .b - E + where = () - E + and 2 = .b - E + where = () + E + where 1 = .b + E + where = () + E + and 2 = .b + E + where = () failure_demo.py:124: AssertionError __________________________ TestRaises.test_raises __________________________ - self = + self = def test_raises(self): s = 'qwe' @@ -352,10 +352,10 @@ > int(s) E ValueError: invalid literal for int() with base 10: 'qwe' - <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:834>:1: ValueError + <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:838>:1: ValueError ______________________ TestRaises.test_raises_doesnt _______________________ - self = + self = def test_raises_doesnt(self): > raises(IOError, "int('3')") @@ -364,7 +364,7 @@ failure_demo.py:136: Failed __________________________ TestRaises.test_raise ___________________________ - self = + self = def test_raise(self): > raise ValueError("demo error") @@ -373,7 +373,7 @@ failure_demo.py:139: ValueError ________________________ TestRaises.test_tupleerror ________________________ - self = + self = def test_tupleerror(self): > a,b = [1] @@ -382,7 +382,7 @@ failure_demo.py:142: ValueError ______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______ - self = + self = def test_reinterpret_fails_with_print_for_the_fun_of_it(self): l = [1,2,3] @@ -395,7 +395,7 @@ l is [1, 2, 3] ________________________ TestRaises.test_some_error ________________________ - self = + self = def test_some_error(self): > if namenotexi: @@ -423,7 +423,7 @@ <2-codegen 'abc-123' /home/hpk/p/pytest/doc/en/example/assertion/failure_demo.py:162>:2: AssertionError ____________________ TestMoreErrors.test_complex_error _____________________ - self = + self = def test_complex_error(self): def f(): @@ -452,7 +452,7 @@ failure_demo.py:5: AssertionError ___________________ TestMoreErrors.test_z1_unpack_error ____________________ - self = + self = def test_z1_unpack_error(self): l = [] @@ -462,7 +462,7 @@ failure_demo.py:179: ValueError ____________________ TestMoreErrors.test_z2_type_error _____________________ - self = + self = def test_z2_type_error(self): l = 3 @@ -472,19 +472,19 @@ failure_demo.py:183: TypeError ______________________ TestMoreErrors.test_startswith ______________________ - self = + self = def test_startswith(self): s = "123" g = "456" > assert s.startswith(g) - E assert ('456') - E + where = '123'.startswith + E assert ('456') + E + where = '123'.startswith failure_demo.py:188: AssertionError __________________ TestMoreErrors.test_startswith_nested ___________________ - self = + self = def test_startswith_nested(self): def f(): @@ -492,15 +492,15 @@ def g(): return "456" > assert f().startswith(g()) - E assert ('456') - E + where = '123'.startswith - E + where '123' = () - E + and '456' = () + E assert ('456') + E + where = '123'.startswith + E + where '123' = () + E + and '456' = () failure_demo.py:195: AssertionError _____________________ TestMoreErrors.test_global_func ______________________ - self = + self = def test_global_func(self): > assert isinstance(globf(42), float) @@ -510,18 +510,18 @@ failure_demo.py:198: AssertionError _______________________ TestMoreErrors.test_instance _______________________ - self = + self = def test_instance(self): self.x = 6*7 > assert self.x != 42 E assert 42 != 42 - E + where 42 = .x + E + where 42 = .x failure_demo.py:202: AssertionError _______________________ TestMoreErrors.test_compare ________________________ - self = + self = def test_compare(self): > assert globf(10) < 5 @@ -531,7 +531,7 @@ failure_demo.py:205: AssertionError _____________________ TestMoreErrors.test_try_finally ______________________ - self = + self = def test_try_finally(self): x = 1 @@ -540,4 +540,4 @@ E assert 1 == 0 failure_demo.py:210: AssertionError - ======================== 39 failed in 0.16 seconds ========================= + ======================== 39 failed in 0.17 seconds ========================= diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/example/simple.txt --- a/doc/en/example/simple.txt +++ b/doc/en/example/simple.txt @@ -106,7 +106,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 0 items ============================= in 0.00 seconds ============================= @@ -150,12 +150,12 @@ $ py.test -rs # "-rs" means report details on the little 's' =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 2 items test_module.py .s ========================= short test summary info ========================== - SKIP [1] /tmp/doc-exec-118/conftest.py:9: need --runslow option to run + SKIP [1] /tmp/doc-exec-179/conftest.py:9: need --runslow option to run =================== 1 passed, 1 skipped in 0.01 seconds ==================== @@ -163,7 +163,7 @@ $ py.test --runslow =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 2 items test_module.py .. @@ -253,7 +253,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 project deps: mylib-1.1 collected 0 items @@ -276,7 +276,7 @@ $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 -- /home/hpk/p/pytest/.tox/regen/bin/python info1: did you know that ... did you? collecting ... collected 0 items @@ -287,7 +287,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 0 items ============================= in 0.00 seconds ============================= @@ -319,7 +319,7 @@ $ py.test --durations=3 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 3 items test_some_are_slow.py ... @@ -327,7 +327,7 @@ ========================= 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_funcfast + 0.00s call test_some_are_slow.py::test_funcfast ========================= 3 passed in 0.31 seconds ========================= incremental testing - test steps @@ -380,7 +380,7 @@ $ py.test -rx =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 4 items test_step.py .Fx. @@ -388,7 +388,7 @@ ================================= FAILURES ================================= ____________________ TestUserHandling.test_modification ____________________ - self = + self = def test_modification(self): > assert 0 diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -71,7 +71,7 @@ $ py.test test_smtpsimple.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 1 items test_smtpsimple.py F @@ -79,7 +79,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response, msg = smtp.ehlo() @@ -89,7 +89,7 @@ E assert 0 test_smtpsimple.py:12: AssertionError - ========================= 1 failed in 0.25 seconds ========================= + ========================= 1 failed in 0.54 seconds ========================= In the failure traceback we see that the test function was called with a ``smtp`` argument, the ``smtplib.SMTP()`` instance created by the fixture @@ -187,7 +187,7 @@ $ py.test test_module.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 2 items test_module.py FF @@ -195,7 +195,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -207,7 +207,7 @@ test_module.py:6: AssertionError ________________________________ test_noop _________________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -216,7 +216,7 @@ E assert 0 test_module.py:11: AssertionError - ========================= 2 failed in 0.22 seconds ========================= + ========================= 2 failed in 0.21 seconds ========================= You see the two ``assert 0`` failing and more importantly you can also see that the same (session-scoped) ``smtp`` object was passed into the two @@ -258,7 +258,7 @@ $ py.test -s -q --tb=no FF - finalizing + finalizing We see that the ``smtp`` instance is finalized after the two tests using it tests executed. If we had specified ``scope='function'`` @@ -283,7 +283,7 @@ ______________________________ test_showhelo _______________________________ test_anothersmtp.py:5: in test_showhelo > assert 0, smtp.helo() - E AssertionError: (250, 'hq.merlinux.eu') + E AssertionError: (250, 'mail.python.org') .. _`request`: :py:class:`_pytest.python.FixtureRequest` @@ -328,7 +328,7 @@ ================================= FAILURES ================================= __________________________ test_ehlo[merlinux.eu] __________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -340,7 +340,7 @@ test_module.py:6: AssertionError __________________________ test_noop[merlinux.eu] __________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -351,7 +351,7 @@ test_module.py:11: AssertionError ________________________ test_ehlo[mail.python.org] ________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -362,7 +362,7 @@ test_module.py:5: AssertionError ________________________ test_noop[mail.python.org] ________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -410,13 +410,13 @@ $ py.test -v test_appsetup.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED - ========================= 2 passed in 0.11 seconds ========================= + ========================= 2 passed in 0.25 seconds ========================= Due to the parametrization of ``smtp`` the test will run twice with two different ``App`` instances and respective smtp servers. There is no @@ -472,7 +472,7 @@ $ py.test -v -s test_module.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 8 items test_module.py:16: test_0[1] PASSED diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/getting-started.txt --- a/doc/en/getting-started.txt +++ b/doc/en/getting-started.txt @@ -23,7 +23,7 @@ To check your installation has installed the correct version:: $ py.test --version - This is py.test version 2.3.0.dev28, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc + This is py.test version 2.3.0, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc If you get an error checkout :ref:`installation issues`. @@ -45,7 +45,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 1 items test_sample.py F @@ -122,7 +122,7 @@ ================================= FAILURES ================================= ____________________________ TestClass.test_two ____________________________ - self = + self = def test_two(self): x = "hello" @@ -157,7 +157,7 @@ ================================= FAILURES ================================= _____________________________ test_needsfiles ______________________________ - tmpdir = local('/tmp/pytest-990/test_needsfiles0') + tmpdir = local('/tmp/pytest-1351/test_needsfiles0') def test_needsfiles(tmpdir): print tmpdir @@ -166,7 +166,7 @@ test_tmpdir.py:3: AssertionError ----------------------------- Captured stdout ------------------------------ - /tmp/pytest-990/test_needsfiles0 + /tmp/pytest-1351/test_needsfiles0 Before the test runs, a unique-per-test-invocation temporary directory was created. More info at :ref:`tmpdir handling`. diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/parametrize.txt --- a/doc/en/parametrize.txt +++ b/doc/en/parametrize.txt @@ -53,7 +53,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 3 items test_expectation.py ..F @@ -135,8 +135,8 @@ def test_valid_string(stringinput): > assert stringinput.isalpha() - E assert () - E + where = '!'.isalpha + E assert () + E + where = '!'.isalpha test_strings.py:3: AssertionError @@ -149,7 +149,7 @@ $ py.test -q -rs test_strings.py s ========================= short test summary info ========================== - SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:943: got empty parameter set, function test_valid_string at /tmp/doc-exec-84/test_strings.py:1 + SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:947: got empty parameter set, function test_valid_string at /tmp/doc-exec-145/test_strings.py:1 For further examples, you might want to look at :ref:`more parametrization examples `. diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/skipping.txt --- a/doc/en/skipping.txt +++ b/doc/en/skipping.txt @@ -132,7 +132,7 @@ example $ py.test -rx xfail_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 6 items xfail_demo.py xxxxxx diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/tmpdir.txt --- a/doc/en/tmpdir.txt +++ b/doc/en/tmpdir.txt @@ -29,7 +29,7 @@ $ py.test test_tmpdir.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 1 items test_tmpdir.py F @@ -37,7 +37,7 @@ ================================= FAILURES ================================= _____________________________ test_create_file _____________________________ - tmpdir = local('/tmp/pytest-991/test_create_file0') + tmpdir = local('/tmp/pytest-1352/test_create_file0') def test_create_file(tmpdir): p = tmpdir.mkdir("sub").join("hello.txt") @@ -48,7 +48,7 @@ E assert 0 test_tmpdir.py:7: AssertionError - ========================= 1 failed in 0.07 seconds ========================= + ========================= 1 failed in 0.03 seconds ========================= .. _`base temporary directory`: diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a doc/en/unittest.txt --- a/doc/en/unittest.txt +++ b/doc/en/unittest.txt @@ -88,7 +88,7 @@ $ py.test test_unittest_db.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev28 + platform linux2 -- Python 2.7.3 -- pytest-2.3.0 collected 2 items test_unittest_db.py FF @@ -101,7 +101,7 @@ def test_method1(self): assert hasattr(self, "db") > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:9: AssertionError ___________________________ MyTest.test_method2 ____________________________ @@ -110,7 +110,7 @@ def test_method2(self): > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:12: AssertionError ========================= 2 failed in 0.02 seconds ========================= diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev33', + version='2.3.0', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -32,7 +32,7 @@ author_email='holger at merlinux.eu', entry_points= make_entry_points(), # the following should be enabled for release - install_requires=['py>=1.4.8'], + install_requires=['py>=1.4.10'], classifiers=['Development Status :: 6 - Mature', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', diff -r 72ae4efeefa55befefa2e0bbbc45586e807c4c6d -r c83169fe80e8ad641542727745a0bd4847f34c3a testing/test_junitxml.py --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -171,7 +171,7 @@ assert_attr(node, failures=3, tests=3) for index, char in enumerate("<&'"): - + tnode = node.getElementsByTagName("testcase")[index] assert_attr(tnode, classname="test_failure_escape", @@ -270,7 +270,7 @@ def test_unicode(self, testdir): value = 'hx\xc4\x85\xc4\x87\n' testdir.makepyfile(""" - # coding: utf-8 + # coding: latin1 def test_hello(): print (%r) assert 0 Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Oct 19 15:01:41 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 19 Oct 2012 13:01:41 -0000 Subject: [py-svn] commit/pytest: 3 new changesets Message-ID: <20121019130141.4100.45421@bitbucket21.managed.contegix.com> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/c27a60097767/ changeset: c27a60097767 user: hpk42 date: 2012-10-19 11:12:13 summary: final touches affected #: 19 files diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/Makefile --- a/doc/en/Makefile +++ b/doc/en/Makefile @@ -40,10 +40,10 @@ -rm -rf $(BUILDDIR)/* install: html - rsync -avz _build/html/ pytest.org:/www/pytest.org/dev + rsync -avz _build/html/ pytest.org:/www/pytest.org/latest installpdf: latexpdf - @scp $(BUILDDIR)/latex/pytest.pdf pytest.org:/www/pytest.org/dev + @scp $(BUILDDIR)/latex/pytest.pdf pytest.org:/www/pytest.org/latest installall: clean install installpdf @echo "done" diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/announce/release-2.3.0.txt --- a/doc/en/announce/release-2.3.0.txt +++ b/doc/en/announce/release-2.3.0.txt @@ -27,8 +27,9 @@ to run your test suites and 3rd party plugins that worked with pytest-2.2.4. -If you are interested in the precise reasoning of the pytest-2.3 fixture -evolution, please consult http://pytest.org/latest/funcarg_compare.html +If you are interested in the precise reasoning (including examples) of the +pytest-2.3 fixture evolution, please consult +http://pytest.org/latest/funcarg_compare.html For general info on installation and getting started: diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/capture.txt --- a/doc/en/capture.txt +++ b/doc/en/capture.txt @@ -78,7 +78,7 @@ test_module.py:9: AssertionError ----------------------------- Captured stdout ------------------------------ - setting up + setting up ==================== 1 failed, 1 passed in 0.01 seconds ==================== Accessing captured output from a test function diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/doctest.txt --- a/doc/en/doctest.txt +++ b/doc/en/doctest.txt @@ -49,4 +49,4 @@ mymodule.py . - ========================= 1 passed in 0.04 seconds ========================= + ========================= 1 passed in 0.02 seconds ========================= diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/example/attic_remoteinterpreter.txt --- a/doc/en/example/attic_remoteinterpreter.txt +++ b/doc/en/example/attic_remoteinterpreter.txt @@ -98,7 +98,7 @@ self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport __import__(modname) - File "/tmp/doc-exec-172/conftest.py", line 2, in + File "/tmp/doc-exec-286/conftest.py", line 2, in from .remoteinterpreter import RemoteInterpreter ValueError: Attempted relative import in non-package @@ -185,7 +185,7 @@ self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport __import__(modname) - File "/tmp/doc-exec-172/conftest.py", line 2, in + File "/tmp/doc-exec-286/conftest.py", line 2, in from myapp import MyApp ImportError: No module named myapp diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/example/index.txt --- a/doc/en/example/index.txt +++ b/doc/en/example/index.txt @@ -7,6 +7,11 @@ Here is a (growing) list of examples. :ref:`Contact ` us if you need more examples or have questions. Also take a look at the :ref:`comprehensive documentation ` which contains many example snippets as well. +Also, `pytest on stackoverflow.com `_ +is a primary continously updated source of pytest questions and answers +which often contain examples. New Questions will usually be seen +by pytest users or developers. + .. note:: see :doc:`../getting-started` for basic introductory examples diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -239,7 +239,7 @@ test_someenv.py . - ========================= 1 passed in 0.00 seconds ========================= + ========================= 1 passed in 0.01 seconds ========================= The ``--markers`` option always gives you a list of available markers:: @@ -356,7 +356,7 @@ test_plat.py s.s. ========================= short test summary info ========================== - SKIP [2] /tmp/doc-exec-174/conftest.py:12: cannot run on platform linux2 + SKIP [2] /tmp/doc-exec-288/conftest.py:12: cannot run on platform linux2 =================== 2 passed, 2 skipped in 0.01 seconds ==================== diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/example/nonpython.txt --- a/doc/en/example/nonpython.txt +++ b/doc/en/example/nonpython.txt @@ -80,4 +80,4 @@ - ============================= in 0.02 seconds ============================= + ============================= in 0.03 seconds ============================= diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/example/parametrize.txt --- a/doc/en/example/parametrize.txt +++ b/doc/en/example/parametrize.txt @@ -195,7 +195,7 @@ ================================= FAILURES ================================= _________________________ test_db_initialized[d2] __________________________ - db = + db = def test_db_initialized(db): # a dummy test @@ -250,7 +250,7 @@ ================================= FAILURES ================================= ________________________ TestClass.test_equals[1-2] ________________________ - self = , a = 1, b = 2 + self = , a = 1, b = 2 def test_equals(self, a, b): > assert a == b diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/example/pythoncollection.txt --- a/doc/en/example/pythoncollection.txt +++ b/doc/en/example/pythoncollection.txt @@ -51,7 +51,7 @@ - ============================= in 0.01 seconds ============================= + ============================= in 0.00 seconds ============================= Interpreting cmdline arguments as Python packages ----------------------------------------------------- diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/example/reportingdemo.txt --- a/doc/en/example/reportingdemo.txt +++ b/doc/en/example/reportingdemo.txt @@ -30,7 +30,7 @@ failure_demo.py:15: AssertionError _________________________ TestFailing.test_simple __________________________ - self = + self = def test_simple(self): def f(): @@ -40,13 +40,13 @@ > assert f() == g() E assert 42 == 43 - E + where 42 = () - E + and 43 = () + E + where 42 = () + E + and 43 = () failure_demo.py:28: AssertionError ____________________ TestFailing.test_simple_multiline _____________________ - self = + self = def test_simple_multiline(self): otherfunc_multi( @@ -66,19 +66,19 @@ failure_demo.py:11: AssertionError ___________________________ TestFailing.test_not ___________________________ - self = + self = def test_not(self): def f(): return 42 > assert not f() E assert not 42 - E + where 42 = () + E + where 42 = () failure_demo.py:38: AssertionError _________________ TestSpecialisedExplanations.test_eq_text _________________ - self = + self = def test_eq_text(self): > assert 'spam' == 'eggs' @@ -89,7 +89,7 @@ failure_demo.py:42: AssertionError _____________ TestSpecialisedExplanations.test_eq_similar_text _____________ - self = + self = def test_eq_similar_text(self): > assert 'foo 1 bar' == 'foo 2 bar' @@ -102,7 +102,7 @@ failure_demo.py:45: AssertionError ____________ TestSpecialisedExplanations.test_eq_multiline_text ____________ - self = + self = def test_eq_multiline_text(self): > assert 'foo\nspam\nbar' == 'foo\neggs\nbar' @@ -115,7 +115,7 @@ failure_demo.py:48: AssertionError ______________ TestSpecialisedExplanations.test_eq_long_text _______________ - self = + self = def test_eq_long_text(self): a = '1'*100 + 'a' + '2'*100 @@ -132,7 +132,7 @@ failure_demo.py:53: AssertionError _________ TestSpecialisedExplanations.test_eq_long_text_multiline __________ - self = + self = def test_eq_long_text_multiline(self): a = '1\n'*100 + 'a' + '2\n'*100 @@ -156,7 +156,7 @@ failure_demo.py:58: AssertionError _________________ TestSpecialisedExplanations.test_eq_list _________________ - self = + self = def test_eq_list(self): > assert [0, 1, 2] == [0, 1, 3] @@ -166,7 +166,7 @@ failure_demo.py:61: AssertionError ______________ TestSpecialisedExplanations.test_eq_list_long _______________ - self = + self = def test_eq_list_long(self): a = [0]*100 + [1] + [3]*100 @@ -178,7 +178,7 @@ failure_demo.py:66: AssertionError _________________ TestSpecialisedExplanations.test_eq_dict _________________ - self = + self = def test_eq_dict(self): > assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2} @@ -191,7 +191,7 @@ failure_demo.py:69: AssertionError _________________ TestSpecialisedExplanations.test_eq_set __________________ - self = + self = def test_eq_set(self): > assert set([0, 10, 11, 12]) == set([0, 20, 21]) @@ -207,7 +207,7 @@ failure_demo.py:72: AssertionError _____________ TestSpecialisedExplanations.test_eq_longer_list ______________ - self = + self = def test_eq_longer_list(self): > assert [1,2] == [1,2,3] @@ -217,7 +217,7 @@ failure_demo.py:75: AssertionError _________________ TestSpecialisedExplanations.test_in_list _________________ - self = + self = def test_in_list(self): > assert 1 in [0, 2, 3, 4, 5] @@ -226,7 +226,7 @@ failure_demo.py:78: AssertionError __________ TestSpecialisedExplanations.test_not_in_text_multiline __________ - self = + self = def test_not_in_text_multiline(self): text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail' @@ -244,7 +244,7 @@ failure_demo.py:82: AssertionError ___________ TestSpecialisedExplanations.test_not_in_text_single ____________ - self = + self = def test_not_in_text_single(self): text = 'single foo line' @@ -257,7 +257,7 @@ failure_demo.py:86: AssertionError _________ TestSpecialisedExplanations.test_not_in_text_single_long _________ - self = + self = def test_not_in_text_single_long(self): text = 'head ' * 50 + 'foo ' + 'tail ' * 20 @@ -270,7 +270,7 @@ failure_demo.py:90: AssertionError ______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______ - self = + self = def test_not_in_text_single_long_term(self): text = 'head ' * 50 + 'f'*70 + 'tail ' * 20 @@ -289,7 +289,7 @@ i = Foo() > assert i.b == 2 E assert 1 == 2 - E + where 1 = .b + E + where 1 = .b failure_demo.py:101: AssertionError _________________________ test_attribute_instance __________________________ @@ -299,8 +299,8 @@ b = 1 > assert Foo().b == 2 E assert 1 == 2 - E + where 1 = .b - E + where = () + E + where 1 = .b + E + where = () failure_demo.py:107: AssertionError __________________________ test_attribute_failure __________________________ @@ -316,7 +316,7 @@ failure_demo.py:116: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - self = + self = def _get_b(self): > raise Exception('Failed to get attrib') @@ -332,15 +332,15 @@ b = 2 > assert Foo().b == Bar().b E assert 1 == 2 - E + where 1 = .b - E + where = () - E + and 2 = .b - E + where = () + E + where 1 = .b + E + where = () + E + and 2 = .b + E + where = () failure_demo.py:124: AssertionError __________________________ TestRaises.test_raises __________________________ - self = + self = def test_raises(self): s = 'qwe' @@ -355,7 +355,7 @@ <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:838>:1: ValueError ______________________ TestRaises.test_raises_doesnt _______________________ - self = + self = def test_raises_doesnt(self): > raises(IOError, "int('3')") @@ -364,7 +364,7 @@ failure_demo.py:136: Failed __________________________ TestRaises.test_raise ___________________________ - self = + self = def test_raise(self): > raise ValueError("demo error") @@ -373,7 +373,7 @@ failure_demo.py:139: ValueError ________________________ TestRaises.test_tupleerror ________________________ - self = + self = def test_tupleerror(self): > a,b = [1] @@ -382,7 +382,7 @@ failure_demo.py:142: ValueError ______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______ - self = + self = def test_reinterpret_fails_with_print_for_the_fun_of_it(self): l = [1,2,3] @@ -395,11 +395,11 @@ l is [1, 2, 3] ________________________ TestRaises.test_some_error ________________________ - self = + self = def test_some_error(self): > if namenotexi: - E NameError: global name 'namenotexi' is not defined + E NameError: global name 'namenotexi' is not defined failure_demo.py:150: NameError ____________________ test_dynamic_compile_shows_nicely _____________________ @@ -423,7 +423,7 @@ <2-codegen 'abc-123' /home/hpk/p/pytest/doc/en/example/assertion/failure_demo.py:162>:2: AssertionError ____________________ TestMoreErrors.test_complex_error _____________________ - self = + self = def test_complex_error(self): def f(): @@ -452,7 +452,7 @@ failure_demo.py:5: AssertionError ___________________ TestMoreErrors.test_z1_unpack_error ____________________ - self = + self = def test_z1_unpack_error(self): l = [] @@ -462,7 +462,7 @@ failure_demo.py:179: ValueError ____________________ TestMoreErrors.test_z2_type_error _____________________ - self = + self = def test_z2_type_error(self): l = 3 @@ -472,19 +472,19 @@ failure_demo.py:183: TypeError ______________________ TestMoreErrors.test_startswith ______________________ - self = + self = def test_startswith(self): s = "123" g = "456" > assert s.startswith(g) - E assert ('456') - E + where = '123'.startswith + E assert ('456') + E + where = '123'.startswith failure_demo.py:188: AssertionError __________________ TestMoreErrors.test_startswith_nested ___________________ - self = + self = def test_startswith_nested(self): def f(): @@ -492,15 +492,15 @@ def g(): return "456" > assert f().startswith(g()) - E assert ('456') - E + where = '123'.startswith - E + where '123' = () - E + and '456' = () + E assert ('456') + E + where = '123'.startswith + E + where '123' = () + E + and '456' = () failure_demo.py:195: AssertionError _____________________ TestMoreErrors.test_global_func ______________________ - self = + self = def test_global_func(self): > assert isinstance(globf(42), float) @@ -510,18 +510,18 @@ failure_demo.py:198: AssertionError _______________________ TestMoreErrors.test_instance _______________________ - self = + self = def test_instance(self): self.x = 6*7 > assert self.x != 42 E assert 42 != 42 - E + where 42 = .x + E + where 42 = .x failure_demo.py:202: AssertionError _______________________ TestMoreErrors.test_compare ________________________ - self = + self = def test_compare(self): > assert globf(10) < 5 @@ -531,7 +531,7 @@ failure_demo.py:205: AssertionError _____________________ TestMoreErrors.test_try_finally ______________________ - self = + self = def test_try_finally(self): x = 1 @@ -540,4 +540,4 @@ E assert 1 == 0 failure_demo.py:210: AssertionError - ======================== 39 failed in 0.17 seconds ========================= + ======================== 39 failed in 0.18 seconds ========================= diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/example/simple.txt --- a/doc/en/example/simple.txt +++ b/doc/en/example/simple.txt @@ -155,7 +155,7 @@ test_module.py .s ========================= short test summary info ========================== - SKIP [1] /tmp/doc-exec-179/conftest.py:9: need --runslow option to run + SKIP [1] /tmp/doc-exec-293/conftest.py:9: need --runslow option to run =================== 1 passed, 1 skipped in 0.01 seconds ==================== @@ -327,7 +327,7 @@ ========================= 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 call test_some_are_slow.py::test_funcfast + 0.00s setup test_some_are_slow.py::test_funcfast ========================= 3 passed in 0.31 seconds ========================= incremental testing - test steps @@ -388,7 +388,7 @@ ================================= FAILURES ================================= ____________________ TestUserHandling.test_modification ____________________ - self = + self = def test_modification(self): > assert 0 diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -79,7 +79,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response, msg = smtp.ehlo() @@ -89,7 +89,7 @@ E assert 0 test_smtpsimple.py:12: AssertionError - ========================= 1 failed in 0.54 seconds ========================= + ========================= 1 failed in 0.20 seconds ========================= In the failure traceback we see that the test function was called with a ``smtp`` argument, the ``smtplib.SMTP()`` instance created by the fixture @@ -195,7 +195,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -207,7 +207,7 @@ test_module.py:6: AssertionError ________________________________ test_noop _________________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -216,7 +216,7 @@ E assert 0 test_module.py:11: AssertionError - ========================= 2 failed in 0.21 seconds ========================= + ========================= 2 failed in 0.20 seconds ========================= You see the two ``assert 0`` failing and more importantly you can also see that the same (session-scoped) ``smtp`` object was passed into the two @@ -258,7 +258,7 @@ $ py.test -s -q --tb=no FF - finalizing + finalizing We see that the ``smtp`` instance is finalized after the two tests using it tests executed. If we had specified ``scope='function'`` @@ -328,7 +328,7 @@ ================================= FAILURES ================================= __________________________ test_ehlo[merlinux.eu] __________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -340,7 +340,7 @@ test_module.py:6: AssertionError __________________________ test_noop[merlinux.eu] __________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -351,7 +351,7 @@ test_module.py:11: AssertionError ________________________ test_ehlo[mail.python.org] ________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -362,7 +362,7 @@ test_module.py:5: AssertionError ________________________ test_noop[mail.python.org] ________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -416,16 +416,17 @@ test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED - ========================= 2 passed in 0.25 seconds ========================= + ========================= 2 passed in 0.17 seconds ========================= Due to the parametrization of ``smtp`` the test will run twice with two different ``App`` instances and respective smtp servers. There is no need for the ``app`` fixture to be aware of the ``smtp`` parametrization as pytest will fully analyse the fixture dependency graph. Note also, -that the ``app`` fixture has a scope of ``module`` but uses a -session-scoped ``smtp``: it is fine for fixtures to use "broader" scoped -fixtures but not the other way round: A session-scoped fixture could -not use a module-scoped one in a meaningful way. +that the ``app`` fixture has a scope of ``module`` but may use a +session-scoped ``smtp`` from the example higher up: it is fine for +fixtures to use "broader" scoped fixtures but not the other way round: +A session-scoped fixture could not use a module-scoped one in a +meaningful way. .. _`automatic per-resource grouping`: diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/funcarg_compare.txt --- a/doc/en/funcarg_compare.txt +++ b/doc/en/funcarg_compare.txt @@ -1,7 +1,7 @@ .. _`funcargcompare`: -pytest-2.3: reasoning for the new funcarg and setup functions +pytest-2.3: reasoning for fixture/funcarg evolution ============================================================= **Target audience**: Reading this document requires basic knowledge of diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/getting-started.txt --- a/doc/en/getting-started.txt +++ b/doc/en/getting-started.txt @@ -122,7 +122,7 @@ ================================= FAILURES ================================= ____________________________ TestClass.test_two ____________________________ - self = + self = def test_two(self): x = "hello" @@ -157,7 +157,7 @@ ================================= FAILURES ================================= _____________________________ test_needsfiles ______________________________ - tmpdir = local('/tmp/pytest-1351/test_needsfiles0') + tmpdir = local('/tmp/pytest-1409/test_needsfiles0') def test_needsfiles(tmpdir): print tmpdir @@ -166,7 +166,7 @@ test_tmpdir.py:3: AssertionError ----------------------------- Captured stdout ------------------------------ - /tmp/pytest-1351/test_needsfiles0 + /tmp/pytest-1409/test_needsfiles0 Before the test runs, a unique-per-test-invocation temporary directory was created. More info at :ref:`tmpdir handling`. @@ -182,6 +182,7 @@ * :ref:`cmdline` for command line invocation examples * :ref:`good practises ` for virtualenv, test layout, genscript support +* :ref:`fixtures` for providing a functional baseline to your tests * :ref:`apiref` for documentation and examples on using py.test * :ref:`plugins` managing and writing plugins diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/parametrize.txt --- a/doc/en/parametrize.txt +++ b/doc/en/parametrize.txt @@ -135,8 +135,8 @@ def test_valid_string(stringinput): > assert stringinput.isalpha() - E assert () - E + where = '!'.isalpha + E assert () + E + where = '!'.isalpha test_strings.py:3: AssertionError @@ -149,7 +149,7 @@ $ py.test -q -rs test_strings.py s ========================= short test summary info ========================== - SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:947: got empty parameter set, function test_valid_string at /tmp/doc-exec-145/test_strings.py:1 + SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:947: got empty parameter set, function test_valid_string at /tmp/doc-exec-259/test_strings.py:1 For further examples, you might want to look at :ref:`more parametrization examples `. diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/tmpdir.txt --- a/doc/en/tmpdir.txt +++ b/doc/en/tmpdir.txt @@ -37,7 +37,7 @@ ================================= FAILURES ================================= _____________________________ test_create_file _____________________________ - tmpdir = local('/tmp/pytest-1352/test_create_file0') + tmpdir = local('/tmp/pytest-1410/test_create_file0') def test_create_file(tmpdir): p = tmpdir.mkdir("sub").join("hello.txt") diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b doc/en/unittest.txt --- a/doc/en/unittest.txt +++ b/doc/en/unittest.txt @@ -101,7 +101,7 @@ def test_method1(self): assert hasattr(self, "db") > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:9: AssertionError ___________________________ MyTest.test_method2 ____________________________ @@ -110,7 +110,7 @@ def test_method2(self): > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:12: AssertionError ========================= 2 failed in 0.02 seconds ========================= diff -r c83169fe80e8ad641542727745a0bd4847f34c3a -r c27a60097767c16a54ae56d9669a77925b213b9b tox.ini --- a/tox.ini +++ b/tox.ini @@ -46,7 +46,6 @@ changedir=doc/en deps=:pypi:sphinx :pypi:PyYAML - pytest commands= make clean @@ -57,7 +56,6 @@ changedir=doc/en deps=:pypi:sphinx :pypi:PyYAML - pytest commands= rm -rf /tmp/doc-exec* make regen https://bitbucket.org/hpk42/pytest/changeset/5b2af2637827/ changeset: 5b2af2637827 user: hpk42 date: 2012-10-19 15:01:15 summary: Added tag 2.3.0 for changeset c27a60097767 affected #: 1 file diff -r c27a60097767c16a54ae56d9669a77925b213b9b -r 5b2af2637827b078f46698ed4178e1cd817c3fbb .hgtags --- a/.hgtags +++ b/.hgtags @@ -49,3 +49,4 @@ 92b916483c1e65a80dc80e3f7816b39e84b36a4d 2.2.2 3c11c5c9776f3c678719161e96cc0a08169c1cb8 2.2.3 ad9fe504a371ad8eb613052d58f229aa66f53527 2.2.4 +c27a60097767c16a54ae56d9669a77925b213b9b 2.3.0 https://bitbucket.org/hpk42/pytest/changeset/0d17b885f231/ changeset: 0d17b885f231 user: hpk42 date: 2012-10-19 15:01:29 summary: start new dev cycle affected #: 3 files diff -r 5b2af2637827b078f46698ed4178e1cd817c3fbb -r 0d17b885f23142dead7b91767537f4e7eb51da0a CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +Changes between 2.3.0 and 2.3.dev +----------------------------------- + + Changes between 2.2.4 and 2.3.0 ----------------------------------- diff -r 5b2af2637827b078f46698ed4178e1cd817c3fbb -r 0d17b885f23142dead7b91767537f4e7eb51da0a doc/en/Makefile --- a/doc/en/Makefile +++ b/doc/en/Makefile @@ -12,6 +12,8 @@ PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +SITETARGET=dev + .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest regen: @@ -40,10 +42,10 @@ -rm -rf $(BUILDDIR)/* install: html - rsync -avz _build/html/ pytest.org:/www/pytest.org/latest + rsync -avz _build/html/ pytest.org:/www/pytest.org/$(SITETARGET) installpdf: latexpdf - @scp $(BUILDDIR)/latex/pytest.pdf pytest.org:/www/pytest.org/latest + @scp $(BUILDDIR)/latex/pytest.pdf pytest.org:/www/pytest.org/$(SITETARGET) installall: clean install installpdf @echo "done" diff -r 5b2af2637827b078f46698ed4178e1cd817c3fbb -r 0d17b885f23142dead7b91767537f4e7eb51da0a doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.3.0" +version = release = "2.3.0.dev" import sys, os Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Oct 19 15:01:57 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 19 Oct 2012 13:01:57 -0000 Subject: [py-svn] commit/py: 2 new changesets Message-ID: <20121019130157.807.60633@bitbucket03.managed.contegix.com> 2 new commits in py: https://bitbucket.org/hpk42/py/changeset/7f37ee0aff9b/ changeset: 7f37ee0aff9b user: hpk42 date: 2012-10-19 10:52:12 summary: fix bug, bump version to 1.4.10 for release affected #: 6 files diff -r 605099c880b9cdefca0fbdb1f6eaced561fe04a2 -r 7f37ee0aff9be4b839d6759cfee336f60e8393a4 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -Changes between 1.4.9 and 1.4.10.dev +Changes between 1.4.9 and 1.4.10 ================================================== - terminalwriter: default to encode to UTF8 if no encoding is defined diff -r 605099c880b9cdefca0fbdb1f6eaced561fe04a2 -r 7f37ee0aff9be4b839d6759cfee336f60e8393a4 doc/install.txt --- a/doc/install.txt +++ b/doc/install.txt @@ -7,7 +7,7 @@ **PyPI name**: py_ -**Pythons**: 2.4, 2.5, 2.6, 2.7, 3.0, 3.1.x, Jython-2.5.1, PyPy-1.3 +**Pythons**: 2.5, 2.6, 2.7, 3.0, 3.1.x, Jython-2.5.1, PyPy-1.3 **Operating systems**: Linux, Windows, OSX, Unix diff -r 605099c880b9cdefca0fbdb1f6eaced561fe04a2 -r 7f37ee0aff9be4b839d6759cfee336f60e8393a4 py/__init__.py --- a/py/__init__.py +++ b/py/__init__.py @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.10.dev2' +__version__ = '1.4.10' from py import _apipkg diff -r 605099c880b9cdefca0fbdb1f6eaced561fe04a2 -r 7f37ee0aff9be4b839d6759cfee336f60e8393a4 setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.10.dev2', + version='1.4.10', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 605099c880b9cdefca0fbdb1f6eaced561fe04a2 -r 7f37ee0aff9be4b839d6759cfee336f60e8393a4 testing/code/test_source.py --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -232,7 +232,7 @@ @py.test.mark.skipif("sys.version_info < (2,6)") def test_getstatementrange_out_of_bounds_py3(self): - source = Source("if xxx:\n from .collections import *") + source = Source("if xxx:\n from .collections import something") r = source.getstatementrange(1) assert r == (1,2) diff -r 605099c880b9cdefca0fbdb1f6eaced561fe04a2 -r 7f37ee0aff9be4b839d6759cfee336f60e8393a4 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py26,py27,py31,py32,py27-xdist,py25,py24 +envlist=py26,py27,py31,py32,py27-xdist,py25 #indexserver= # default=http://pypi.testrun.org https://bitbucket.org/hpk42/py/changeset/4532fe5ee826/ changeset: 4532fe5ee826 user: hpk42 date: 2012-10-19 11:00:30 summary: Added tag 1.4.10 for changeset 7f37ee0aff9b affected #: 1 file diff -r 7f37ee0aff9be4b839d6759cfee336f60e8393a4 -r 4532fe5ee826808805b373e9296d2baf4db254d9 .hgtags --- a/.hgtags +++ b/.hgtags @@ -39,3 +39,4 @@ b827dd156a36753e32c7f3f15ce82d6fe9e356c8 1.4.6 f15726f9e5a67cc6221c499affa4840e9d591763 1.4.7 abfabd07a1d328f13c730e8a50d80d2e470afd3b 1.4.9 +7f37ee0aff9be4b839d6759cfee336f60e8393a4 1.4.10 Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Oct 19 15:59:51 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 19 Oct 2012 13:59:51 -0000 Subject: [py-svn] commit/pytest: hpk42: skip pexpect using tests on freebsd Message-ID: <20121019135951.23207.46635@bitbucket02.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/d3c36ac8ebff/ changeset: d3c36ac8ebff user: hpk42 date: 2012-10-19 15:59:29 summary: skip pexpect using tests on freebsd affected #: 4 files diff -r 0d17b885f23142dead7b91767537f4e7eb51da0a -r d3c36ac8ebff5e834b0ea4d0e8b9526b72fd942d CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ Changes between 2.3.0 and 2.3.dev ----------------------------------- +- skip pexpect using tests (test_pdb.py mostly) on freebsd* systems + due to pexpect not supporting it properly (hanging) Changes between 2.2.4 and 2.3.0 ----------------------------------- diff -r 0d17b885f23142dead7b91767537f4e7eb51da0a -r d3c36ac8ebff5e834b0ea4d0e8b9526b72fd942d _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0' +__version__ = '2.3.0.dev1' diff -r 0d17b885f23142dead7b91767537f4e7eb51da0a -r d3c36ac8ebff5e834b0ea4d0e8b9526b72fd942d _pytest/pytester.py --- a/_pytest/pytester.py +++ b/_pytest/pytester.py @@ -532,6 +532,8 @@ pytest.skip("pypy-64 bit not supported") if sys.platform == "darwin": pytest.xfail("pexpect does not work reliably on darwin?!") + if sys.platform.startswith("freebsd"): + pytest.xfail("pexpect does not work reliably on freebsd") logfile = self.tmpdir.join("spawn.out") child = pexpect.spawn(cmd, logfile=logfile.open("w")) child.timeout = expect_timeout diff -r 0d17b885f23142dead7b91767537f4e7eb51da0a -r d3c36ac8ebff5e834b0ea4d0e8b9526b72fd942d setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0', + version='2.3.0.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Fri Oct 19 16:00:39 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 19 Oct 2012 14:00:39 -0000 Subject: [py-svn] commit/pytest: hpk42: proper version number (2.3.1.dev*) Message-ID: <20121019140039.807.20026@bitbucket03.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/6e8541bd4fd2/ changeset: 6e8541bd4fd2 user: hpk42 date: 2012-10-19 16:00:29 summary: proper version number (2.3.1.dev*) affected #: 2 files diff -r d3c36ac8ebff5e834b0ea4d0e8b9526b72fd942d -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.0.dev1' +__version__ = '2.3.1.dev1' diff -r d3c36ac8ebff5e834b0ea4d0e8b9526b72fd942d -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.0.dev1', + version='2.3.1.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Oct 20 10:04:02 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 20 Oct 2012 08:04:02 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20121020080402.27275.77958@bitbucket22.managed.contegix.com> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/fa08012dd30d/ changeset: fa08012dd30d user: hpk42 date: 2012-10-20 09:52:03 summary: some doc refinements affected #: 15 files diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/Makefile --- a/doc/en/Makefile +++ b/doc/en/Makefile @@ -12,7 +12,7 @@ PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -SITETARGET=dev +SITETARGET=latest .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/capture.txt --- a/doc/en/capture.txt +++ b/doc/en/capture.txt @@ -78,7 +78,7 @@ test_module.py:9: AssertionError ----------------------------- Captured stdout ------------------------------ - setting up + setting up ==================== 1 failed, 1 passed in 0.01 seconds ==================== Accessing captured output from a test function diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.3.0.dev" +version = release = "2.3.0.1" import sys, os diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/example/attic_remoteinterpreter.txt --- a/doc/en/example/attic_remoteinterpreter.txt +++ b/doc/en/example/attic_remoteinterpreter.txt @@ -98,7 +98,7 @@ self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport __import__(modname) - File "/tmp/doc-exec-286/conftest.py", line 2, in + File "/tmp/doc-exec-408/conftest.py", line 2, in from .remoteinterpreter import RemoteInterpreter ValueError: Attempted relative import in non-package @@ -185,7 +185,7 @@ self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport __import__(modname) - File "/tmp/doc-exec-286/conftest.py", line 2, in + File "/tmp/doc-exec-408/conftest.py", line 2, in from myapp import MyApp ImportError: No module named myapp diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/example/index.txt --- a/doc/en/example/index.txt +++ b/doc/en/example/index.txt @@ -5,16 +5,22 @@ =========================================== Here is a (growing) list of examples. :ref:`Contact ` us if you -need more examples or have questions. Also take a look at the :ref:`comprehensive documentation ` which contains many example snippets as well. +need more examples or have questions. Also take a look at the +:ref:`comprehensive documentation ` which contains many example +snippets as well. Also, `pytest on stackoverflow.com +`_ often comes with example +answers. -Also, `pytest on stackoverflow.com `_ -is a primary continously updated source of pytest questions and answers -which often contain examples. New Questions will usually be seen -by pytest users or developers. +For basic examples, see -.. note:: +- :doc:`../getting-started` for basic introductory examples +- :ref:`assert` for basic assertion examples +- :ref:`fixtures` for basic fixture/setup examples +- :ref:`parametrize` for basic fixture/setup examples +- :doc:`../unittest` for basic unittest integration +- :doc:`../nose` for basic nosetests integration - see :doc:`../getting-started` for basic introductory examples +The following examples aim at various use cases you might encounter. .. toctree:: :maxdepth: 2 diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -32,7 +32,7 @@ test_server.py:3: test_send_http PASSED =================== 1 tests deselected by "-m 'webtest'" =================== - ================== 1 passed, 1 deselected in 0.00 seconds ================== + ================== 1 passed, 1 deselected in 0.01 seconds ================== Or the inverse, running all tests except the webtest ones:: @@ -239,7 +239,7 @@ test_someenv.py . - ========================= 1 passed in 0.01 seconds ========================= + ========================= 1 passed in 0.00 seconds ========================= The ``--markers`` option always gives you a list of available markers:: @@ -356,7 +356,7 @@ test_plat.py s.s. ========================= short test summary info ========================== - SKIP [2] /tmp/doc-exec-288/conftest.py:12: cannot run on platform linux2 + SKIP [2] /tmp/doc-exec-410/conftest.py:12: cannot run on platform linux2 =================== 2 passed, 2 skipped in 0.01 seconds ==================== diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/example/parametrize.txt --- a/doc/en/example/parametrize.txt +++ b/doc/en/example/parametrize.txt @@ -195,7 +195,7 @@ ================================= FAILURES ================================= _________________________ test_db_initialized[d2] __________________________ - db = + db = def test_db_initialized(db): # a dummy test @@ -250,7 +250,7 @@ ================================= FAILURES ================================= ________________________ TestClass.test_equals[1-2] ________________________ - self = , a = 1, b = 2 + self = , a = 1, b = 2 def test_equals(self, a, b): > assert a == b diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/example/pythoncollection.txt --- a/doc/en/example/pythoncollection.txt +++ b/doc/en/example/pythoncollection.txt @@ -51,7 +51,7 @@ - ============================= in 0.00 seconds ============================= + ============================= in 0.01 seconds ============================= Interpreting cmdline arguments as Python packages ----------------------------------------------------- diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/example/reportingdemo.txt --- a/doc/en/example/reportingdemo.txt +++ b/doc/en/example/reportingdemo.txt @@ -30,7 +30,7 @@ failure_demo.py:15: AssertionError _________________________ TestFailing.test_simple __________________________ - self = + self = def test_simple(self): def f(): @@ -40,13 +40,13 @@ > assert f() == g() E assert 42 == 43 - E + where 42 = () - E + and 43 = () + E + where 42 = () + E + and 43 = () failure_demo.py:28: AssertionError ____________________ TestFailing.test_simple_multiline _____________________ - self = + self = def test_simple_multiline(self): otherfunc_multi( @@ -66,19 +66,19 @@ failure_demo.py:11: AssertionError ___________________________ TestFailing.test_not ___________________________ - self = + self = def test_not(self): def f(): return 42 > assert not f() E assert not 42 - E + where 42 = () + E + where 42 = () failure_demo.py:38: AssertionError _________________ TestSpecialisedExplanations.test_eq_text _________________ - self = + self = def test_eq_text(self): > assert 'spam' == 'eggs' @@ -89,7 +89,7 @@ failure_demo.py:42: AssertionError _____________ TestSpecialisedExplanations.test_eq_similar_text _____________ - self = + self = def test_eq_similar_text(self): > assert 'foo 1 bar' == 'foo 2 bar' @@ -102,7 +102,7 @@ failure_demo.py:45: AssertionError ____________ TestSpecialisedExplanations.test_eq_multiline_text ____________ - self = + self = def test_eq_multiline_text(self): > assert 'foo\nspam\nbar' == 'foo\neggs\nbar' @@ -115,7 +115,7 @@ failure_demo.py:48: AssertionError ______________ TestSpecialisedExplanations.test_eq_long_text _______________ - self = + self = def test_eq_long_text(self): a = '1'*100 + 'a' + '2'*100 @@ -132,7 +132,7 @@ failure_demo.py:53: AssertionError _________ TestSpecialisedExplanations.test_eq_long_text_multiline __________ - self = + self = def test_eq_long_text_multiline(self): a = '1\n'*100 + 'a' + '2\n'*100 @@ -156,7 +156,7 @@ failure_demo.py:58: AssertionError _________________ TestSpecialisedExplanations.test_eq_list _________________ - self = + self = def test_eq_list(self): > assert [0, 1, 2] == [0, 1, 3] @@ -166,7 +166,7 @@ failure_demo.py:61: AssertionError ______________ TestSpecialisedExplanations.test_eq_list_long _______________ - self = + self = def test_eq_list_long(self): a = [0]*100 + [1] + [3]*100 @@ -178,7 +178,7 @@ failure_demo.py:66: AssertionError _________________ TestSpecialisedExplanations.test_eq_dict _________________ - self = + self = def test_eq_dict(self): > assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2} @@ -191,7 +191,7 @@ failure_demo.py:69: AssertionError _________________ TestSpecialisedExplanations.test_eq_set __________________ - self = + self = def test_eq_set(self): > assert set([0, 10, 11, 12]) == set([0, 20, 21]) @@ -207,7 +207,7 @@ failure_demo.py:72: AssertionError _____________ TestSpecialisedExplanations.test_eq_longer_list ______________ - self = + self = def test_eq_longer_list(self): > assert [1,2] == [1,2,3] @@ -217,7 +217,7 @@ failure_demo.py:75: AssertionError _________________ TestSpecialisedExplanations.test_in_list _________________ - self = + self = def test_in_list(self): > assert 1 in [0, 2, 3, 4, 5] @@ -226,7 +226,7 @@ failure_demo.py:78: AssertionError __________ TestSpecialisedExplanations.test_not_in_text_multiline __________ - self = + self = def test_not_in_text_multiline(self): text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail' @@ -244,7 +244,7 @@ failure_demo.py:82: AssertionError ___________ TestSpecialisedExplanations.test_not_in_text_single ____________ - self = + self = def test_not_in_text_single(self): text = 'single foo line' @@ -257,7 +257,7 @@ failure_demo.py:86: AssertionError _________ TestSpecialisedExplanations.test_not_in_text_single_long _________ - self = + self = def test_not_in_text_single_long(self): text = 'head ' * 50 + 'foo ' + 'tail ' * 20 @@ -270,7 +270,7 @@ failure_demo.py:90: AssertionError ______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______ - self = + self = def test_not_in_text_single_long_term(self): text = 'head ' * 50 + 'f'*70 + 'tail ' * 20 @@ -289,7 +289,7 @@ i = Foo() > assert i.b == 2 E assert 1 == 2 - E + where 1 = .b + E + where 1 = .b failure_demo.py:101: AssertionError _________________________ test_attribute_instance __________________________ @@ -299,8 +299,8 @@ b = 1 > assert Foo().b == 2 E assert 1 == 2 - E + where 1 = .b - E + where = () + E + where 1 = .b + E + where = () failure_demo.py:107: AssertionError __________________________ test_attribute_failure __________________________ @@ -316,7 +316,7 @@ failure_demo.py:116: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - self = + self = def _get_b(self): > raise Exception('Failed to get attrib') @@ -332,15 +332,15 @@ b = 2 > assert Foo().b == Bar().b E assert 1 == 2 - E + where 1 = .b - E + where = () - E + and 2 = .b - E + where = () + E + where 1 = .b + E + where = () + E + and 2 = .b + E + where = () failure_demo.py:124: AssertionError __________________________ TestRaises.test_raises __________________________ - self = + self = def test_raises(self): s = 'qwe' @@ -355,7 +355,7 @@ <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:838>:1: ValueError ______________________ TestRaises.test_raises_doesnt _______________________ - self = + self = def test_raises_doesnt(self): > raises(IOError, "int('3')") @@ -364,7 +364,7 @@ failure_demo.py:136: Failed __________________________ TestRaises.test_raise ___________________________ - self = + self = def test_raise(self): > raise ValueError("demo error") @@ -373,7 +373,7 @@ failure_demo.py:139: ValueError ________________________ TestRaises.test_tupleerror ________________________ - self = + self = def test_tupleerror(self): > a,b = [1] @@ -382,7 +382,7 @@ failure_demo.py:142: ValueError ______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______ - self = + self = def test_reinterpret_fails_with_print_for_the_fun_of_it(self): l = [1,2,3] @@ -395,7 +395,7 @@ l is [1, 2, 3] ________________________ TestRaises.test_some_error ________________________ - self = + self = def test_some_error(self): > if namenotexi: @@ -423,7 +423,7 @@ <2-codegen 'abc-123' /home/hpk/p/pytest/doc/en/example/assertion/failure_demo.py:162>:2: AssertionError ____________________ TestMoreErrors.test_complex_error _____________________ - self = + self = def test_complex_error(self): def f(): @@ -452,7 +452,7 @@ failure_demo.py:5: AssertionError ___________________ TestMoreErrors.test_z1_unpack_error ____________________ - self = + self = def test_z1_unpack_error(self): l = [] @@ -462,7 +462,7 @@ failure_demo.py:179: ValueError ____________________ TestMoreErrors.test_z2_type_error _____________________ - self = + self = def test_z2_type_error(self): l = 3 @@ -472,19 +472,19 @@ failure_demo.py:183: TypeError ______________________ TestMoreErrors.test_startswith ______________________ - self = + self = def test_startswith(self): s = "123" g = "456" > assert s.startswith(g) - E assert ('456') - E + where = '123'.startswith + E assert ('456') + E + where = '123'.startswith failure_demo.py:188: AssertionError __________________ TestMoreErrors.test_startswith_nested ___________________ - self = + self = def test_startswith_nested(self): def f(): @@ -492,15 +492,15 @@ def g(): return "456" > assert f().startswith(g()) - E assert ('456') - E + where = '123'.startswith - E + where '123' = () - E + and '456' = () + E assert ('456') + E + where = '123'.startswith + E + where '123' = () + E + and '456' = () failure_demo.py:195: AssertionError _____________________ TestMoreErrors.test_global_func ______________________ - self = + self = def test_global_func(self): > assert isinstance(globf(42), float) @@ -510,18 +510,18 @@ failure_demo.py:198: AssertionError _______________________ TestMoreErrors.test_instance _______________________ - self = + self = def test_instance(self): self.x = 6*7 > assert self.x != 42 E assert 42 != 42 - E + where 42 = .x + E + where 42 = .x failure_demo.py:202: AssertionError _______________________ TestMoreErrors.test_compare ________________________ - self = + self = def test_compare(self): > assert globf(10) < 5 @@ -531,7 +531,7 @@ failure_demo.py:205: AssertionError _____________________ TestMoreErrors.test_try_finally ______________________ - self = + self = def test_try_finally(self): x = 1 diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/example/simple.txt --- a/doc/en/example/simple.txt +++ b/doc/en/example/simple.txt @@ -155,7 +155,7 @@ test_module.py .s ========================= short test summary info ========================== - SKIP [1] /tmp/doc-exec-293/conftest.py:9: need --runslow option to run + SKIP [1] /tmp/doc-exec-415/conftest.py:9: need --runslow option to run =================== 1 passed, 1 skipped in 0.01 seconds ==================== @@ -327,7 +327,7 @@ ========================= 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_funcfast + 0.00s call test_some_are_slow.py::test_funcfast ========================= 3 passed in 0.31 seconds ========================= incremental testing - test steps @@ -388,7 +388,7 @@ ================================= FAILURES ================================= ____________________ TestUserHandling.test_modification ____________________ - self = + self = def test_modification(self): > assert 0 diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -26,8 +26,9 @@ fixtures. * fixture management scales from simple unit to complex - functional testing, allowing to parametrize fixtures or tests according - to configuration and component options. + functional testing, allowing to parametrize fixtures and tests according + to configuration and component options, or to re-use fixtures + across class, module or whole test session scopes. In addition, pytest continues to support :ref:`xunitsetup` which it originally introduced in 2005. You can mix both styles, moving @@ -47,7 +48,7 @@ Test functions can receive fixture objects by naming them as an input argument. For each argument name, a fixture function with that name provides the fixture object. Fixture functions are registered by marking them with -:py:func:`@pytest.fixture `. Let's look at a simple +:py:func:`@pytest.fixture <_pytest.python.fixture>`. Let's look at a simple self-contained test module containing a fixture and a test function using it:: @@ -66,8 +67,8 @@ assert 0 # for demo purposes Here, the ``test_function`` needs the ``smtp`` fixture value. pytest -will discover and call the ``@pytest.fixture`` marked ``smtp`` -fixture function. Running the test looks like this:: +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 =========================== test session starts ============================ @@ -79,7 +80,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response, msg = smtp.ehlo() @@ -89,7 +90,7 @@ E assert 0 test_smtpsimple.py:12: AssertionError - ========================= 1 failed in 0.20 seconds ========================= + ========================= 1 failed in 0.25 seconds ========================= In the failure traceback we see that the test function was called with a ``smtp`` argument, the ``smtplib.SMTP()`` instance created by the fixture @@ -143,25 +144,27 @@ .. _smtpshared: -Working with a session-shared fixture +Working with a module-shared fixture ----------------------------------------------------------------- .. regendoc:wipe Fixtures requiring network access depend on connectivity and are usually time-expensive to create. Extending the previous example, we -can add a ``scope='session'`` parameter to ``@pytest.fixture`` decorator. +can add a ``scope='module'`` parameter to the +:py:func:`@pytest.fixture <_pytest.python.fixture>` invocation to cause the decorated ``smtp`` fixture function to only be invoked once -per test session. Multiple test functions will thus each receive the -same ``smtp`` fixture instance. The next example also extracts -the fixture function into a separate ``conftest.py`` file so that -all tests in the directory can access the fixture function:: +per test module. Multiple test functions in a test module will thus +each receive the same ``smtp`` fixture instance. The next example also +extracts the fixture function into a separate ``conftest.py`` file so +that all tests in test modules in the directory can access the fixture +function:: # content of conftest.py import pytest import smtplib - @pytest.fixture(scope="session") + @pytest.fixture(scope="module") def smtp(): return smtplib.SMTP("merlinux.eu") @@ -195,7 +198,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -207,7 +210,7 @@ test_module.py:6: AssertionError ________________________________ test_noop _________________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -216,28 +219,39 @@ E assert 0 test_module.py:11: AssertionError - ========================= 2 failed in 0.20 seconds ========================= + ========================= 2 failed in 0.17 seconds ========================= You see the two ``assert 0`` failing and more importantly you can also see -that the same (session-scoped) ``smtp`` object was passed into the two +that the same (module-scoped) ``smtp`` object was passed into the two test functions because pytest shows the incoming argument values in the traceback. As a result, the two test functions using ``smtp`` run as quick as a single one because they reuse the same instance. +If you decide that you rather want to have a session-scoped ``smtp`` +instance, you can simply declare it:: + + @pytest.fixture(scope=``session``) + def smtp(...): + # the returned fixture value will be shared for + # all tests needing it + .. _`request-context`: Fixtures can interact with the requesting test context ------------------------------------------------------------- -Using the special :py:class:`request ` argument, -fixture functions can introspect the function, class or module for which -they are invoked or can register finalizing (cleanup) +Fixture functions can themselves use other fixtures by naming +them as an input argument just like test functions do, see +:ref:`interdependent fixtures`. Moreover, pytest +provides a builtin :py:class:`request ` object, +which fixture functions can use to introspect the function, class or module +for which they are invoked or to register finalizing (cleanup) functions which are called when the last test finished execution. -Further extending the previous ``smtp`` fixture example, let's try to -read the server URL from the module namespace, also use module-caching and -register a finalizer that closes the smtp connection after the last -test finished execution:: +Further extending the previous ``smtp`` fixture example, let's +read an optional server URL from the module namespace and register +a finalizer that closes the smtp connection after the last +test in a module finished execution:: # content of conftest.py import pytest @@ -258,12 +272,12 @@ $ py.test -s -q --tb=no FF - finalizing + finalizing We see that the ``smtp`` instance is finalized after the two tests using it tests executed. If we had specified ``scope='function'`` then fixture setup and cleanup would occur around each single test. -Note that the test module itself did not need to change! +Note that either case the test module itself does not need to change! Let's quickly create another test module that actually sets the server URL and has a test to verify the fixture picks it up:: @@ -289,7 +303,7 @@ .. _`fixture-parametrize`: -Parametrizing a session-shared fixture +Parametrizing a fixture ----------------------------------------------------------------- Fixture functions can be parametrized in which case they will be called @@ -308,7 +322,7 @@ import pytest import smtplib - @pytest.fixture(scope="session", + @pytest.fixture(scope="module", params=["merlinux.eu", "mail.python.org"]) def smtp(request): smtp = smtplib.SMTP(request.param) @@ -318,7 +332,8 @@ request.addfinalizer(fin) return smtp -The main change is the declaration of ``params``, a list of values +The main change is the declaration of ``params`` with +:py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values 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:: @@ -328,7 +343,7 @@ ================================= FAILURES ================================= __________________________ test_ehlo[merlinux.eu] __________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -340,7 +355,7 @@ test_module.py:6: AssertionError __________________________ test_noop[merlinux.eu] __________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -351,7 +366,7 @@ test_module.py:11: AssertionError ________________________ test_ehlo[mail.python.org] ________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -362,7 +377,7 @@ test_module.py:5: AssertionError ________________________ test_noop[mail.python.org] ________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -416,15 +431,17 @@ test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED - ========================= 2 passed in 0.17 seconds ========================= + ========================= 2 passed in 0.15 seconds ========================= Due to the parametrization of ``smtp`` the test will run twice with two different ``App`` instances and respective smtp servers. There is no need for the ``app`` fixture to be aware of the ``smtp`` parametrization -as pytest will fully analyse the fixture dependency graph. Note also, -that the ``app`` fixture has a scope of ``module`` but may use a -session-scoped ``smtp`` from the example higher up: it is fine for -fixtures to use "broader" scoped fixtures but not the other way round: +as pytest will fully analyse the fixture dependency graph. + +Note, that the ``app`` fixture has a scope of ``module`` and uses a +module-scoped ``smtp`` fixture. The example would still work if ``smtp`` +was cached on a ``session`` scope: it is fine for fixtures to use +"broader" scoped fixtures but not the other way round: A session-scoped fixture could not use a module-scoped one in a meaningful way. diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/getting-started.txt --- a/doc/en/getting-started.txt +++ b/doc/en/getting-started.txt @@ -122,7 +122,7 @@ ================================= FAILURES ================================= ____________________________ TestClass.test_two ____________________________ - self = + self = def test_two(self): x = "hello" @@ -157,7 +157,7 @@ ================================= FAILURES ================================= _____________________________ test_needsfiles ______________________________ - tmpdir = local('/tmp/pytest-1409/test_needsfiles0') + tmpdir = local('/tmp/pytest-1437/test_needsfiles0') def test_needsfiles(tmpdir): print tmpdir @@ -166,7 +166,7 @@ test_tmpdir.py:3: AssertionError ----------------------------- Captured stdout ------------------------------ - /tmp/pytest-1409/test_needsfiles0 + /tmp/pytest-1437/test_needsfiles0 Before the test runs, a unique-per-test-invocation temporary directory was created. More info at :ref:`tmpdir handling`. diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/parametrize.txt --- a/doc/en/parametrize.txt +++ b/doc/en/parametrize.txt @@ -135,8 +135,8 @@ def test_valid_string(stringinput): > assert stringinput.isalpha() - E assert () - E + where = '!'.isalpha + E assert () + E + where = '!'.isalpha test_strings.py:3: AssertionError @@ -149,7 +149,7 @@ $ py.test -q -rs test_strings.py s ========================= short test summary info ========================== - SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:947: got empty parameter set, function test_valid_string at /tmp/doc-exec-259/test_strings.py:1 + SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:947: got empty parameter set, function test_valid_string at /tmp/doc-exec-381/test_strings.py:1 For further examples, you might want to look at :ref:`more parametrization examples `. diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/tmpdir.txt --- a/doc/en/tmpdir.txt +++ b/doc/en/tmpdir.txt @@ -37,7 +37,7 @@ ================================= FAILURES ================================= _____________________________ test_create_file _____________________________ - tmpdir = local('/tmp/pytest-1410/test_create_file0') + tmpdir = local('/tmp/pytest-1438/test_create_file0') def test_create_file(tmpdir): p = tmpdir.mkdir("sub").join("hello.txt") @@ -48,7 +48,7 @@ E assert 0 test_tmpdir.py:7: AssertionError - ========================= 1 failed in 0.03 seconds ========================= + ========================= 1 failed in 0.04 seconds ========================= .. _`base temporary directory`: diff -r 6e8541bd4fd2a638c021eb38de4b16de01d067b0 -r fa08012dd30da95984b417be00307a034e28e4ee doc/en/unittest.txt --- a/doc/en/unittest.txt +++ b/doc/en/unittest.txt @@ -101,7 +101,7 @@ def test_method1(self): assert hasattr(self, "db") > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:9: AssertionError ___________________________ MyTest.test_method2 ____________________________ @@ -110,7 +110,7 @@ def test_method2(self): > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:12: AssertionError ========================= 2 failed in 0.02 seconds ========================= https://bitbucket.org/hpk42/pytest/changeset/ed21f5c4d1f4/ changeset: ed21f5c4d1f4 user: hpk42 date: 2012-10-20 09:59:20 summary: fix issue203 - fixture functions with a scope=function should have a "self" that points to the actual instance with which the test functions run. affected #: 6 files diff -r fa08012dd30da95984b417be00307a034e28e4ee -r ed21f5c4d1f4bdd8201e9d096d6fddc2083b5f44 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,10 @@ Changes between 2.3.0 and 2.3.dev ----------------------------------- +- fix issue202 - fix regression: using "self" from fixture functions now + works as expected (it's the same "self" instance that a test method + which uses the fixture sees) + - skip pexpect using tests (test_pdb.py mostly) on freebsd* systems due to pexpect not supporting it properly (hanging) diff -r fa08012dd30da95984b417be00307a034e28e4ee -r ed21f5c4d1f4bdd8201e9d096d6fddc2083b5f44 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.1.dev1' +__version__ = '2.3.1.dev2' diff -r fa08012dd30da95984b417be00307a034e28e4ee -r ed21f5c4d1f4bdd8201e9d096d6fddc2083b5f44 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -12,6 +12,16 @@ callable = py.builtin.callable +def getimfunc(func): + try: + return func.__func__ + except AttributeError: + try: + return func.im_func + except AttributeError: + return func + + class FixtureFunctionMarker: def __init__(self, scope, params, autouse=False): self.scope = scope @@ -1621,7 +1631,19 @@ if self.unittest: result = self.func(request.instance, **kwargs) else: - result = self.func(**kwargs) + fixturefunc = self.func + # the fixture function needs to be bound to the actual + # request.instance so that code working with "self" behaves + # as expected. XXX request.instance should maybe return None + # instead of raising AttributeError + try: + if request.instance is not None: + fixturefunc = getimfunc(self.func) + if fixturefunc != self.func: + fixturefunc = fixturefunc.__get__(request.instance) + except AttributeError: + pass + result = fixturefunc(**kwargs) self.active = True self.cached_result = result return result diff -r fa08012dd30da95984b417be00307a034e28e4ee -r ed21f5c4d1f4bdd8201e9d096d6fddc2083b5f44 doc/en/example/index.txt --- a/doc/en/example/index.txt +++ b/doc/en/example/index.txt @@ -16,7 +16,7 @@ - :doc:`../getting-started` for basic introductory examples - :ref:`assert` for basic assertion examples - :ref:`fixtures` for basic fixture/setup examples -- :ref:`parametrize` for basic fixture/setup examples +- :ref:`parametrize` for basic test function parametrization - :doc:`../unittest` for basic unittest integration - :doc:`../nose` for basic nosetests integration diff -r fa08012dd30da95984b417be00307a034e28e4ee -r ed21f5c4d1f4bdd8201e9d096d6fddc2083b5f44 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.1.dev1', + version='2.3.1.dev2', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r fa08012dd30da95984b417be00307a034e28e4ee -r ed21f5c4d1f4bdd8201e9d096d6fddc2083b5f44 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -2037,6 +2037,21 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=2) + def test_request_instance_issue203(self, testdir): + testdir.makepyfile(""" + import pytest + + class TestClass: + @pytest.fixture + def setup1(self, request): + assert self == request.instance + self.arg1 = 1 + def test_hello(self, setup1): + assert self.arg1 == 1 + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) + class TestFixtureManager: def pytest_funcarg__testdir(self, request): testdir = request.getfuncargvalue("testdir") Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Oct 20 13:57:02 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 20 Oct 2012 11:57:02 -0000 Subject: [py-svn] commit/pytest: hpk42: improve --markers output Message-ID: <20121020115702.7033.32981@bitbucket16.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/96990f087779/ changeset: 96990f087779 user: hpk42 date: 2012-10-20 13:56:53 summary: improve --markers output affected #: 3 files diff -r ed21f5c4d1f4bdd8201e9d096d6fddc2083b5f44 -r 96990f087779269e6f009bd56fbc322171e7d6fc CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -8,6 +8,9 @@ - skip pexpect using tests (test_pdb.py mostly) on freebsd* systems due to pexpect not supporting it properly (hanging) +- link to web pages from --markers output which provides help for + pytest.mark.* usage. + Changes between 2.2.4 and 2.3.0 ----------------------------------- diff -r ed21f5c4d1f4bdd8201e9d096d6fddc2083b5f44 -r 96990f087779269e6f009bd56fbc322171e7d6fc _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -110,10 +110,12 @@ "times passing in multiple different argument value sets. 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." ) config.addinivalue_line("markers", "usefixtures(fixturename1, fixturename2, ...): mark tests as needing " - "all of the specified fixtures." + "all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures " ) def pytest_sessionstart(session): diff -r ed21f5c4d1f4bdd8201e9d096d6fddc2083b5f44 -r 96990f087779269e6f009bd56fbc322171e7d6fc _pytest/skipping.py --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -14,7 +14,8 @@ "skipif(*conditions): skip the given test function if evaluation " "of all conditions has 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. " + "skips the test if we are on the win32 platform. see " + "http://pytest.org/latest/skipping.html" ) config.addinivalue_line("markers", "xfail(*conditions, reason=None, run=True): mark the the test function " @@ -22,6 +23,7 @@ "if you don't even want to execute the test function. Any positional " "condition strings will be evaluated (like with skipif) and if one is " "False the marker will not be applied." + "see http://pytest.org/latest/skipping.html" ) def pytest_namespace(): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Oct 20 14:05:45 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 20 Oct 2012 12:05:45 -0000 Subject: [py-svn] commit/pytest: hpk42: prepare a 2.3.1 Message-ID: <20121020120545.29116.78485@bitbucket16.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/c547971be73a/ changeset: c547971be73a user: hpk42 date: 2012-10-20 14:05:33 summary: prepare a 2.3.1 affected #: 20 files diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.1.dev2' +__version__ = '2.3.1' diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df _pytest/skipping.py --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -11,19 +11,18 @@ def pytest_configure(config): config.addinivalue_line("markers", - "skipif(*conditions): skip the given test function if evaluation " - "of all conditions has a True value. Evaluation happens within the " + "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" ) config.addinivalue_line("markers", - "xfail(*conditions, reason=None, run=True): mark the the test function " - "as an expected failure. Optionally specify a reason and run=False " - "if you don't even want to execute the test function. Any positional " - "condition strings will be evaluated (like with skipif) and if one is " - "False the marker will not be applied." - "see http://pytest.org/latest/skipping.html" + "xfail(condition, reason=None, run=True): 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. See " + "http://pytest.org/latest/skipping.html" ) def pytest_namespace(): diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/assert.txt --- a/doc/en/assert.txt +++ b/doc/en/assert.txt @@ -26,7 +26,7 @@ $ py.test test_assert1.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 1 items test_assert1.py F @@ -110,7 +110,7 @@ $ py.test test_assert2.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 1 items test_assert2.py F diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/capture.txt --- a/doc/en/capture.txt +++ b/doc/en/capture.txt @@ -64,7 +64,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 2 items test_module.py .F @@ -78,7 +78,7 @@ test_module.py:9: AssertionError ----------------------------- Captured stdout ------------------------------ - setting up + setting up ==================== 1 failed, 1 passed in 0.01 seconds ==================== Accessing captured output from a test function diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.3.0.1" +version = release = "2.3.1" import sys, os diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/doctest.txt --- a/doc/en/doctest.txt +++ b/doc/en/doctest.txt @@ -44,7 +44,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 1 items mymodule.py . diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/example/attic_remoteinterpreter.txt --- a/doc/en/example/attic_remoteinterpreter.txt +++ /dev/null @@ -1,198 +0,0 @@ - -.. highlightlang:: python - -.. _myapp: - -Building an SSH connecting Application fixture -========================================================== - -The goal of this tutorial-example is to show how you can put efficient -test support and fixture code in one place, allowing test modules and -test functions to stay ignorant of importing, configuration or -setup/teardown details. - -The tutorial implements a simple ``RemoteInterpreter`` object that -allows evaluation of python expressions. We are going to use -the `execnet `_ package for the -underlying cross-python bridge functionality. - - -Step 1: Implementing a first test --------------------------------------------------------------- - -Let's write a simple test function using a not yet defined ``interp`` fixture:: - - # content of test_remoteinterpreter.py - - def test_eval_simple(interp): - assert interp.eval("6*9") == 42 - -The test function needs an argument named `interp` and therefore pytest will -look for a :ref:`fixture function` that matches this name. We'll define it -in a :ref:`local plugin ` to make it available also to other -test modules:: - - # content of conftest.py - - from .remoteinterpreter import RemoteInterpreter - - @pytest.fixture - def interp(request): - import execnet - gw = execnet.makegateway() - return RemoteInterpreter(gw) - -To run the example we furthermore need to implement a RemoteInterpreter -object which working with the injected execnet-gateway connection:: - - # content of remoteintepreter.py - - class RemoteInterpreter: - def __init__(self, gateway): - self.gateway = gateway - - def eval(self, expression): - # execnet open a "gateway" to the remote process - # which enables to remotely execute code and communicate - # to and fro via channels - ch = self.gateway.remote_exec("channel.send(%s)" % expression) - return ch.receive() - -That's it, we can now run the test:: - - $ py.test test_remoteinterpreter.py - Traceback (most recent call last): - File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in - load_entry_point('pytest==2.3.0', 'console_scripts', 'py.test')() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 473, in main - config = _prepareconfig(args, plugins) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig - pluginmanager=_pluginmanager, args=args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 422, in __call__ - return self._docall(methods, kwargs) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 433, in _docall - res = mc.execute() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute - res = method(**kwargs) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/helpconfig.py", line 25, in pytest_cmdline_parse - config = __multicall__.execute() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute - res = method(**kwargs) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 10, in pytest_cmdline_parse - config.parse(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 344, in parse - self._preparse(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 322, in _preparse - self._setinitialconftest(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 301, in _setinitialconftest - self._conftest.setinitial(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 160, in setinitial - self._try_load_conftest(anchor) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 166, in _try_load_conftest - self._path2confmods[None] = self.getconftestmodules(anchor) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 190, in getconftestmodules - clist[:0] = self.getconftestmodules(dp) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 189, in getconftestmodules - clist.append(self.importconftest(conftestpath)) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 218, in importconftest - self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport - __import__(modname) - File "/tmp/doc-exec-408/conftest.py", line 2, in - from .remoteinterpreter import RemoteInterpreter - ValueError: Attempted relative import in non-package - -.. _`tut-cmdlineoption`: - -Step 2: Adding command line configuration ------------------------------------------------------------ - -To add a command line option we update the ``conftest.py`` of -the previous example and add a command line option which -is passed on to the MyApp object:: - - # content of ./conftest.py - import pytest - from myapp import MyApp - - def pytest_addoption(parser): # pytest hook called during initialisation - parser.addoption("--ssh", action="store", default=None, - help="specify ssh host to run tests with") - - @pytest.fixture - def mysetup(request): # "mysetup" factory function - return MySetup(request.config) - - class MySetup: - def __init__(self, config): - self.config = config - self.app = MyApp() - - def getsshconnection(self): - import execnet - host = self.config.option.ssh - if host is None: - pytest.skip("specify ssh host with --ssh") - return execnet.SshGateway(host) - - -Now any test function can use the ``mysetup.getsshconnection()`` method -like this:: - - # content of test_ssh.py - class TestClass: - def test_function(self, mysetup): - conn = mysetup.getsshconnection() - # work with conn - -Running it yields:: - - $ py.test -q test_ssh.py -rs - Traceback (most recent call last): - File "/home/hpk/p/pytest/.tox/regen/bin/py.test", line 9, in - load_entry_point('pytest==2.3.0', 'console_scripts', 'py.test')() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 473, in main - config = _prepareconfig(args, plugins) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 463, in _prepareconfig - pluginmanager=_pluginmanager, args=args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 422, in __call__ - return self._docall(methods, kwargs) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 433, in _docall - res = mc.execute() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute - res = method(**kwargs) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/helpconfig.py", line 25, in pytest_cmdline_parse - config = __multicall__.execute() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/core.py", line 351, in execute - res = method(**kwargs) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 10, in pytest_cmdline_parse - config.parse(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 344, in parse - self._preparse(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 322, in _preparse - self._setinitialconftest(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 301, in _setinitialconftest - self._conftest.setinitial(args) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 160, in setinitial - self._try_load_conftest(anchor) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 166, in _try_load_conftest - self._path2confmods[None] = self.getconftestmodules(anchor) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 190, in getconftestmodules - clist[:0] = self.getconftestmodules(dp) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 189, in getconftestmodules - clist.append(self.importconftest(conftestpath)) - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/config.py", line 218, in importconftest - self._conftestpath2mod[conftestpath] = mod = conftestpath.pyimport() - File "/home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/py/_path/local.py", line 532, in pyimport - __import__(modname) - File "/tmp/doc-exec-408/conftest.py", line 2, in - from myapp import MyApp - ImportError: No module named myapp - -If you specify a command line option like ``py.test --ssh=python.org`` the test will execute as expected. - -Note that neither the ``TestClass`` nor the ``test_function`` need to -know anything about how to setup the test state. It is handled separately -in the ``conftest.py`` file. It is easy -to extend the ``mysetup`` object for further needs in the test code - and for use by any other test functions in the files and directories below the ``conftest.py`` file. - diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -26,19 +26,19 @@ $ py.test -v -m webtest =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:3: test_send_http PASSED =================== 1 tests deselected by "-m 'webtest'" =================== - ================== 1 passed, 1 deselected in 0.01 seconds ================== + ================== 1 passed, 1 deselected in 0.00 seconds ================== Or the inverse, running all tests except the webtest ones:: $ py.test -v -m "not webtest" =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:6: test_something_quick PASSED @@ -65,13 +65,13 @@ $ py.test --markers @pytest.mark.webtest: mark a test as a webtest. - @pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has 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. + @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(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied. + @pytest.mark.xfail(condition, reason=None, run=True): 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. See http://pytest.org/latest/skipping.html - @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2. + @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. 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. - @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. + @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures @pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible. @@ -145,7 +145,7 @@ $ py.test -k send_http # running with the above defined examples =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 4 items test_server.py . @@ -157,7 +157,7 @@ $ py.test -k-send_http =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 4 items test_mark_classlevel.py .. @@ -170,7 +170,7 @@ $ py.test -kTestClass =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 4 items test_mark_classlevel.py .. @@ -223,7 +223,7 @@ $ py.test -E stage2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 1 items test_someenv.py s @@ -234,7 +234,7 @@ $ py.test -E stage1 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 1 items test_someenv.py . @@ -246,13 +246,13 @@ $ py.test --markers @pytest.mark.env(name): mark test to run only on named environment - @pytest.mark.skipif(*conditions): skip the given test function if evaluation of all conditions has 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. + @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(*conditions, reason=None, run=True): mark the the test function as an expected failure. Optionally specify a reason and run=False if you don't even want to execute the test function. Any positional condition strings will be evaluated (like with skipif) and if one is False the marker will not be applied. + @pytest.mark.xfail(condition, reason=None, run=True): 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. See http://pytest.org/latest/skipping.html - @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2. + @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in multiple different argument value sets. 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. - @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. + @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures @pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible. @@ -351,12 +351,12 @@ $ py.test -rs # this option reports skip reasons =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 4 items test_plat.py s.s. ========================= short test summary info ========================== - SKIP [2] /tmp/doc-exec-410/conftest.py:12: cannot run on platform linux2 + SKIP [2] /tmp/doc-exec-592/conftest.py:12: cannot run on platform linux2 =================== 2 passed, 2 skipped in 0.01 seconds ==================== @@ -364,7 +364,7 @@ $ py.test -m linux2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 4 items test_plat.py . diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/example/nonpython.txt --- a/doc/en/example/nonpython.txt +++ b/doc/en/example/nonpython.txt @@ -27,7 +27,7 @@ nonpython $ py.test test_simple.yml =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 2 items test_simple.yml .F @@ -56,7 +56,7 @@ nonpython $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_simple.yml:1: usecase: ok PASSED @@ -74,10 +74,10 @@ nonpython $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 2 items - ============================= in 0.03 seconds ============================= + ============================= in 0.02 seconds ============================= diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/example/parametrize.txt --- a/doc/en/example/parametrize.txt +++ b/doc/en/example/parametrize.txt @@ -104,7 +104,7 @@ $ py.test test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 4 items test_scenarios.py .... @@ -116,7 +116,7 @@ $ py.test --collectonly test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 4 items @@ -180,7 +180,7 @@ $ py.test test_backends.py --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 2 items @@ -195,7 +195,7 @@ ================================= FAILURES ================================= _________________________ test_db_initialized[d2] __________________________ - db = + db = def test_db_initialized(db): # a dummy test @@ -250,7 +250,7 @@ ================================= FAILURES ================================= ________________________ TestClass.test_equals[1-2] ________________________ - self = , a = 1, b = 2 + self = , a = 1, b = 2 def test_equals(self, a, b): > assert a == b diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/example/pythoncollection.txt --- a/doc/en/example/pythoncollection.txt +++ b/doc/en/example/pythoncollection.txt @@ -43,7 +43,7 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 2 items @@ -51,7 +51,7 @@ - ============================= in 0.01 seconds ============================= + ============================= in 0.00 seconds ============================= Interpreting cmdline arguments as Python packages ----------------------------------------------------- @@ -82,7 +82,7 @@ . $ py.test --collectonly pythoncollection.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 3 items @@ -135,7 +135,7 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 1 items diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/example/reportingdemo.txt --- a/doc/en/example/reportingdemo.txt +++ b/doc/en/example/reportingdemo.txt @@ -13,7 +13,7 @@ assertion $ py.test failure_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 39 items failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF @@ -30,7 +30,7 @@ failure_demo.py:15: AssertionError _________________________ TestFailing.test_simple __________________________ - self = + self = def test_simple(self): def f(): @@ -40,13 +40,13 @@ > assert f() == g() E assert 42 == 43 - E + where 42 = () - E + and 43 = () + E + where 42 = () + E + and 43 = () failure_demo.py:28: AssertionError ____________________ TestFailing.test_simple_multiline _____________________ - self = + self = def test_simple_multiline(self): otherfunc_multi( @@ -66,19 +66,19 @@ failure_demo.py:11: AssertionError ___________________________ TestFailing.test_not ___________________________ - self = + self = def test_not(self): def f(): return 42 > assert not f() E assert not 42 - E + where 42 = () + E + where 42 = () failure_demo.py:38: AssertionError _________________ TestSpecialisedExplanations.test_eq_text _________________ - self = + self = def test_eq_text(self): > assert 'spam' == 'eggs' @@ -89,7 +89,7 @@ failure_demo.py:42: AssertionError _____________ TestSpecialisedExplanations.test_eq_similar_text _____________ - self = + self = def test_eq_similar_text(self): > assert 'foo 1 bar' == 'foo 2 bar' @@ -102,7 +102,7 @@ failure_demo.py:45: AssertionError ____________ TestSpecialisedExplanations.test_eq_multiline_text ____________ - self = + self = def test_eq_multiline_text(self): > assert 'foo\nspam\nbar' == 'foo\neggs\nbar' @@ -115,7 +115,7 @@ failure_demo.py:48: AssertionError ______________ TestSpecialisedExplanations.test_eq_long_text _______________ - self = + self = def test_eq_long_text(self): a = '1'*100 + 'a' + '2'*100 @@ -132,7 +132,7 @@ failure_demo.py:53: AssertionError _________ TestSpecialisedExplanations.test_eq_long_text_multiline __________ - self = + self = def test_eq_long_text_multiline(self): a = '1\n'*100 + 'a' + '2\n'*100 @@ -156,7 +156,7 @@ failure_demo.py:58: AssertionError _________________ TestSpecialisedExplanations.test_eq_list _________________ - self = + self = def test_eq_list(self): > assert [0, 1, 2] == [0, 1, 3] @@ -166,7 +166,7 @@ failure_demo.py:61: AssertionError ______________ TestSpecialisedExplanations.test_eq_list_long _______________ - self = + self = def test_eq_list_long(self): a = [0]*100 + [1] + [3]*100 @@ -178,7 +178,7 @@ failure_demo.py:66: AssertionError _________________ TestSpecialisedExplanations.test_eq_dict _________________ - self = + self = def test_eq_dict(self): > assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2} @@ -191,7 +191,7 @@ failure_demo.py:69: AssertionError _________________ TestSpecialisedExplanations.test_eq_set __________________ - self = + self = def test_eq_set(self): > assert set([0, 10, 11, 12]) == set([0, 20, 21]) @@ -207,7 +207,7 @@ failure_demo.py:72: AssertionError _____________ TestSpecialisedExplanations.test_eq_longer_list ______________ - self = + self = def test_eq_longer_list(self): > assert [1,2] == [1,2,3] @@ -217,7 +217,7 @@ failure_demo.py:75: AssertionError _________________ TestSpecialisedExplanations.test_in_list _________________ - self = + self = def test_in_list(self): > assert 1 in [0, 2, 3, 4, 5] @@ -226,7 +226,7 @@ failure_demo.py:78: AssertionError __________ TestSpecialisedExplanations.test_not_in_text_multiline __________ - self = + self = def test_not_in_text_multiline(self): text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail' @@ -244,7 +244,7 @@ failure_demo.py:82: AssertionError ___________ TestSpecialisedExplanations.test_not_in_text_single ____________ - self = + self = def test_not_in_text_single(self): text = 'single foo line' @@ -257,7 +257,7 @@ failure_demo.py:86: AssertionError _________ TestSpecialisedExplanations.test_not_in_text_single_long _________ - self = + self = def test_not_in_text_single_long(self): text = 'head ' * 50 + 'foo ' + 'tail ' * 20 @@ -270,7 +270,7 @@ failure_demo.py:90: AssertionError ______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______ - self = + self = def test_not_in_text_single_long_term(self): text = 'head ' * 50 + 'f'*70 + 'tail ' * 20 @@ -289,7 +289,7 @@ i = Foo() > assert i.b == 2 E assert 1 == 2 - E + where 1 = .b + E + where 1 = .b failure_demo.py:101: AssertionError _________________________ test_attribute_instance __________________________ @@ -299,8 +299,8 @@ b = 1 > assert Foo().b == 2 E assert 1 == 2 - E + where 1 = .b - E + where = () + E + where 1 = .b + E + where = () failure_demo.py:107: AssertionError __________________________ test_attribute_failure __________________________ @@ -316,7 +316,7 @@ failure_demo.py:116: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - self = + self = def _get_b(self): > raise Exception('Failed to get attrib') @@ -332,15 +332,15 @@ b = 2 > assert Foo().b == Bar().b E assert 1 == 2 - E + where 1 = .b - E + where = () - E + and 2 = .b - E + where = () + E + where 1 = .b + E + where = () + E + and 2 = .b + E + where = () failure_demo.py:124: AssertionError __________________________ TestRaises.test_raises __________________________ - self = + self = def test_raises(self): s = 'qwe' @@ -352,10 +352,10 @@ > int(s) E ValueError: invalid literal for int() with base 10: 'qwe' - <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:838>:1: ValueError + <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:850>:1: ValueError ______________________ TestRaises.test_raises_doesnt _______________________ - self = + self = def test_raises_doesnt(self): > raises(IOError, "int('3')") @@ -364,7 +364,7 @@ failure_demo.py:136: Failed __________________________ TestRaises.test_raise ___________________________ - self = + self = def test_raise(self): > raise ValueError("demo error") @@ -373,7 +373,7 @@ failure_demo.py:139: ValueError ________________________ TestRaises.test_tupleerror ________________________ - self = + self = def test_tupleerror(self): > a,b = [1] @@ -382,7 +382,7 @@ failure_demo.py:142: ValueError ______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______ - self = + self = def test_reinterpret_fails_with_print_for_the_fun_of_it(self): l = [1,2,3] @@ -395,7 +395,7 @@ l is [1, 2, 3] ________________________ TestRaises.test_some_error ________________________ - self = + self = def test_some_error(self): > if namenotexi: @@ -423,7 +423,7 @@ <2-codegen 'abc-123' /home/hpk/p/pytest/doc/en/example/assertion/failure_demo.py:162>:2: AssertionError ____________________ TestMoreErrors.test_complex_error _____________________ - self = + self = def test_complex_error(self): def f(): @@ -452,7 +452,7 @@ failure_demo.py:5: AssertionError ___________________ TestMoreErrors.test_z1_unpack_error ____________________ - self = + self = def test_z1_unpack_error(self): l = [] @@ -462,7 +462,7 @@ failure_demo.py:179: ValueError ____________________ TestMoreErrors.test_z2_type_error _____________________ - self = + self = def test_z2_type_error(self): l = 3 @@ -472,19 +472,19 @@ failure_demo.py:183: TypeError ______________________ TestMoreErrors.test_startswith ______________________ - self = + self = def test_startswith(self): s = "123" g = "456" > assert s.startswith(g) - E assert ('456') - E + where = '123'.startswith + E assert ('456') + E + where = '123'.startswith failure_demo.py:188: AssertionError __________________ TestMoreErrors.test_startswith_nested ___________________ - self = + self = def test_startswith_nested(self): def f(): @@ -492,15 +492,15 @@ def g(): return "456" > assert f().startswith(g()) - E assert ('456') - E + where = '123'.startswith - E + where '123' = () - E + and '456' = () + E assert ('456') + E + where = '123'.startswith + E + where '123' = () + E + and '456' = () failure_demo.py:195: AssertionError _____________________ TestMoreErrors.test_global_func ______________________ - self = + self = def test_global_func(self): > assert isinstance(globf(42), float) @@ -510,18 +510,18 @@ failure_demo.py:198: AssertionError _______________________ TestMoreErrors.test_instance _______________________ - self = + self = def test_instance(self): self.x = 6*7 > assert self.x != 42 E assert 42 != 42 - E + where 42 = .x + E + where 42 = .x failure_demo.py:202: AssertionError _______________________ TestMoreErrors.test_compare ________________________ - self = + self = def test_compare(self): > assert globf(10) < 5 @@ -531,7 +531,7 @@ failure_demo.py:205: AssertionError _____________________ TestMoreErrors.test_try_finally ______________________ - self = + self = def test_try_finally(self): x = 1 @@ -540,4 +540,4 @@ E assert 1 == 0 failure_demo.py:210: AssertionError - ======================== 39 failed in 0.18 seconds ========================= + ======================== 39 failed in 0.16 seconds ========================= diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/example/simple.txt --- a/doc/en/example/simple.txt +++ b/doc/en/example/simple.txt @@ -106,7 +106,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 0 items ============================= in 0.00 seconds ============================= @@ -150,12 +150,12 @@ $ py.test -rs # "-rs" means report details on the little 's' =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 2 items test_module.py .s ========================= short test summary info ========================== - SKIP [1] /tmp/doc-exec-415/conftest.py:9: need --runslow option to run + SKIP [1] /tmp/doc-exec-597/conftest.py:9: need --runslow option to run =================== 1 passed, 1 skipped in 0.01 seconds ==================== @@ -163,7 +163,7 @@ $ py.test --runslow =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 2 items test_module.py .. @@ -253,7 +253,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 project deps: mylib-1.1 collected 0 items @@ -276,7 +276,7 @@ $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 -- /home/hpk/p/pytest/.tox/regen/bin/python info1: did you know that ... did you? collecting ... collected 0 items @@ -287,7 +287,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 0 items ============================= in 0.00 seconds ============================= @@ -319,7 +319,7 @@ $ py.test --durations=3 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 3 items test_some_are_slow.py ... @@ -327,7 +327,7 @@ ========================= 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 call test_some_are_slow.py::test_funcfast + 0.00s setup test_some_are_slow.py::test_funcfast ========================= 3 passed in 0.31 seconds ========================= incremental testing - test steps @@ -380,7 +380,7 @@ $ py.test -rx =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 4 items test_step.py .Fx. @@ -388,7 +388,7 @@ ================================= FAILURES ================================= ____________________ TestUserHandling.test_modification ____________________ - self = + self = def test_modification(self): > assert 0 diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -72,7 +72,7 @@ $ py.test test_smtpsimple.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 1 items test_smtpsimple.py F @@ -80,7 +80,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response, msg = smtp.ehlo() @@ -90,7 +90,7 @@ E assert 0 test_smtpsimple.py:12: AssertionError - ========================= 1 failed in 0.25 seconds ========================= + ========================= 1 failed in 0.12 seconds ========================= In the failure traceback we see that the test function was called with a ``smtp`` argument, the ``smtplib.SMTP()`` instance created by the fixture @@ -190,7 +190,7 @@ $ py.test test_module.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 2 items test_module.py FF @@ -198,7 +198,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -210,7 +210,7 @@ test_module.py:6: AssertionError ________________________________ test_noop _________________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -219,7 +219,7 @@ E assert 0 test_module.py:11: AssertionError - ========================= 2 failed in 0.17 seconds ========================= + ========================= 2 failed in 0.13 seconds ========================= You see the two ``assert 0`` failing and more importantly you can also see that the same (module-scoped) ``smtp`` object was passed into the two @@ -272,7 +272,7 @@ $ py.test -s -q --tb=no FF - finalizing + finalizing We see that the ``smtp`` instance is finalized after the two tests using it tests executed. If we had specified ``scope='function'`` @@ -343,7 +343,7 @@ ================================= FAILURES ================================= __________________________ test_ehlo[merlinux.eu] __________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -355,7 +355,7 @@ test_module.py:6: AssertionError __________________________ test_noop[merlinux.eu] __________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -366,7 +366,7 @@ test_module.py:11: AssertionError ________________________ test_ehlo[mail.python.org] ________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -377,7 +377,7 @@ test_module.py:5: AssertionError ________________________ test_noop[mail.python.org] ________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -425,13 +425,13 @@ $ py.test -v test_appsetup.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED - ========================= 2 passed in 0.15 seconds ========================= + ========================= 2 passed in 0.20 seconds ========================= Due to the parametrization of ``smtp`` the test will run twice with two different ``App`` instances and respective smtp servers. There is no @@ -490,7 +490,7 @@ $ py.test -v -s test_module.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 8 items test_module.py:16: test_0[1] PASSED diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/getting-started.txt --- a/doc/en/getting-started.txt +++ b/doc/en/getting-started.txt @@ -23,7 +23,7 @@ To check your installation has installed the correct version:: $ py.test --version - This is py.test version 2.3.0, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc + This is py.test version 2.3.1, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc If you get an error checkout :ref:`installation issues`. @@ -45,7 +45,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 1 items test_sample.py F @@ -122,7 +122,7 @@ ================================= FAILURES ================================= ____________________________ TestClass.test_two ____________________________ - self = + self = def test_two(self): x = "hello" @@ -157,7 +157,7 @@ ================================= FAILURES ================================= _____________________________ test_needsfiles ______________________________ - tmpdir = local('/tmp/pytest-1437/test_needsfiles0') + tmpdir = local('/tmp/pytest-1516/test_needsfiles0') def test_needsfiles(tmpdir): print tmpdir @@ -166,7 +166,7 @@ test_tmpdir.py:3: AssertionError ----------------------------- Captured stdout ------------------------------ - /tmp/pytest-1437/test_needsfiles0 + /tmp/pytest-1516/test_needsfiles0 Before the test runs, a unique-per-test-invocation temporary directory was created. More info at :ref:`tmpdir handling`. diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/parametrize.txt --- a/doc/en/parametrize.txt +++ b/doc/en/parametrize.txt @@ -53,7 +53,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 3 items test_expectation.py ..F @@ -135,8 +135,8 @@ def test_valid_string(stringinput): > assert stringinput.isalpha() - E assert () - E + where = '!'.isalpha + E assert () + E + where = '!'.isalpha test_strings.py:3: AssertionError @@ -149,7 +149,7 @@ $ py.test -q -rs test_strings.py s ========================= short test summary info ========================== - SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:947: got empty parameter set, function test_valid_string at /tmp/doc-exec-381/test_strings.py:1 + SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:959: got empty parameter set, function test_valid_string at /tmp/doc-exec-564/test_strings.py:1 For further examples, you might want to look at :ref:`more parametrization examples `. diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/skipping.txt --- a/doc/en/skipping.txt +++ b/doc/en/skipping.txt @@ -132,7 +132,7 @@ example $ py.test -rx xfail_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 6 items xfail_demo.py xxxxxx diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/tmpdir.txt --- a/doc/en/tmpdir.txt +++ b/doc/en/tmpdir.txt @@ -29,7 +29,7 @@ $ py.test test_tmpdir.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 1 items test_tmpdir.py F @@ -37,7 +37,7 @@ ================================= FAILURES ================================= _____________________________ test_create_file _____________________________ - tmpdir = local('/tmp/pytest-1438/test_create_file0') + tmpdir = local('/tmp/pytest-1517/test_create_file0') def test_create_file(tmpdir): p = tmpdir.mkdir("sub").join("hello.txt") diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df doc/en/unittest.txt --- a/doc/en/unittest.txt +++ b/doc/en/unittest.txt @@ -88,7 +88,7 @@ $ py.test test_unittest_db.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.0 + platform linux2 -- Python 2.7.3 -- pytest-2.3.1 collected 2 items test_unittest_db.py FF @@ -101,7 +101,7 @@ def test_method1(self): assert hasattr(self, "db") > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:9: AssertionError ___________________________ MyTest.test_method2 ____________________________ @@ -110,7 +110,7 @@ def test_method2(self): > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:12: AssertionError ========================= 2 failed in 0.02 seconds ========================= diff -r 96990f087779269e6f009bd56fbc322171e7d6fc -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.1.dev2', + version='2.3.1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Oct 20 14:13:04 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 20 Oct 2012 12:13:04 -0000 Subject: [py-svn] commit/pytest: hpk42: some more fixes Message-ID: <20121020121304.21558.35773@bitbucket12.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/acf0e1477fb1/ changeset: acf0e1477fb1 user: hpk42 date: 2012-10-20 14:10:12 summary: some more fixes affected #: 3 files diff -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df -r acf0e1477fb19a1d35a4e40242b77fa6af32eb17 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -Changes between 2.3.0 and 2.3.dev +Changes between 2.3.0 and 2.3.1 ----------------------------------- - fix issue202 - fix regression: using "self" from fixture functions now @@ -16,8 +16,7 @@ - fix issue202 - better automatic names for parametrized test functions - fix issue139 - introduce @pytest.fixture which allows direct scoping - and parametrization of funcarg factories. Introduce new @pytest.setup - marker to allow the writing of setup functions which accept funcargs. + and parametrization of funcarg factories. - fix issue198 - conftest fixtures were not found on windows32 in some circumstances with nested directory structures due to path manipulation issues - fix issue193 skip test functions with were parametrized with empty @@ -27,7 +26,7 @@ - introduce re-ordering of tests by resource and parametrization setup which takes precedence to the usual file-ordering - fix issue185 monkeypatching time.time does not cause pytest to fail -- fix issue172 duplicate call of pytest.setup-decoratored setup_module +- fix issue172 duplicate call of pytest.fixture decoratored setup_module functions - fix junitxml=path construction so that if tests change the current working directory and the path is a relative path diff -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df -r acf0e1477fb19a1d35a4e40242b77fa6af32eb17 doc/en/announce/release-2.3.1.txt --- /dev/null +++ b/doc/en/announce/release-2.3.1.txt @@ -0,0 +1,39 @@ +pytest-2.3.1: fix regression with factory functions +=========================================================================== + +pytest-2.3.1 is a quick follow-up release: + +- fix issue202 - regression with fixture functions/funcarg factories: + using "self" is now safe again and works as in 2.2.4. Thanks + to Eduard Schettino for the quick bug report. + +- disable pexpect pytest self tests on Freebsd - thanks Koob for the + quick reporting + +- fix/improve interactive docs with --markers + +See + + http://pytest.org/ + +for general information. To install or upgrade pytest: + + pip install -U pytest # or + easy_install -U pytest + +best, +holger krekel + + +Changes between 2.3.0 and 2.3.1 +----------------------------------- + +- fix issue202 - fix regression: using "self" from fixture functions now + works as expected (it's the same "self" instance that a test method + which uses the fixture sees) + +- skip pexpect using tests (test_pdb.py mostly) on freebsd* systems + due to pexpect not supporting it properly (hanging) + +- link to web pages from --markers output which provides help for + pytest.mark.* usage. diff -r c547971be73ae42e4d1d1eea78ee8bc7c1e774df -r acf0e1477fb19a1d35a4e40242b77fa6af32eb17 testing/test_skipping.py --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -551,8 +551,8 @@ def test_default_markers(testdir): result = testdir.runpytest("--markers") result.stdout.fnmatch_lines([ - "*skipif(*conditions)*skip*", - "*xfail(*conditions, reason=None, run=True)*expected failure*", + "*skipif(*condition)*skip*", + "*xfail(*condition, reason=None, run=True)*expected failure*", ]) Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Oct 20 14:33:30 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 20 Oct 2012 12:33:30 -0000 Subject: [py-svn] commit/pytest: hpk42: Added tag 2.3.1 for changeset acf0e1477fb1 Message-ID: <20121020123330.28862.54425@bitbucket02.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/c6d81d30a44d/ changeset: c6d81d30a44d user: hpk42 date: 2012-10-20 14:33:23 summary: Added tag 2.3.1 for changeset acf0e1477fb1 affected #: 1 file diff -r acf0e1477fb19a1d35a4e40242b77fa6af32eb17 -r c6d81d30a44d038c53f0310271485d815b89020c .hgtags --- a/.hgtags +++ b/.hgtags @@ -50,3 +50,4 @@ 3c11c5c9776f3c678719161e96cc0a08169c1cb8 2.2.3 ad9fe504a371ad8eb613052d58f229aa66f53527 2.2.4 c27a60097767c16a54ae56d9669a77925b213b9b 2.3.0 +acf0e1477fb19a1d35a4e40242b77fa6af32eb17 2.3.1 Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Oct 20 17:08:31 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 20 Oct 2012 15:08:31 -0000 Subject: [py-svn] commit/pytest: hpk42: add tox.ini to distribution Message-ID: <20121020150831.31488.29534@bitbucket05.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/91fc055eea01/ changeset: 91fc055eea01 user: hpk42 date: 2012-10-20 17:08:02 summary: add tox.ini to distribution affected #: 4 files diff -r c6d81d30a44d038c53f0310271485d815b89020c -r 91fc055eea01dd7fabc29df1e826eec5f572591e CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +Changes between 2.3.1 and 2.3.2.dev +----------------------------------- + +- add tox.ini to pytest distribution so that ignore-dirs and others config + bits are properly distributed for maintainers who run pytest-own tests + Changes between 2.3.0 and 2.3.1 ----------------------------------- diff -r c6d81d30a44d038c53f0310271485d815b89020c -r 91fc055eea01dd7fabc29df1e826eec5f572591e MANIFEST.in --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,6 +2,7 @@ include README.txt include setup.py include distribute_setup.py +include tox.ini include LICENSE graft doc graft testing diff -r c6d81d30a44d038c53f0310271485d815b89020c -r 91fc055eea01dd7fabc29df1e826eec5f572591e _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.1' +__version__ = '2.3.2.dev1' diff -r c6d81d30a44d038c53f0310271485d815b89020c -r 91fc055eea01dd7fabc29df1e826eec5f572591e setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.1', + version='2.3.2.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sat Oct 20 17:32:33 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 20 Oct 2012 15:32:33 -0000 Subject: [py-svn] commit/pytest: hpk42: allow to run self-tests with "python setup.py test" for pytest tests itself Message-ID: <20121020153233.18664.50626@bitbucket12.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/f5cc42c56496/ changeset: f5cc42c56496 user: hpk42 date: 2012-10-20 17:32:03 summary: allow to run self-tests with "python setup.py test" for pytest tests itself affected #: 3 files diff -r 91fc055eea01dd7fabc29df1e826eec5f572591e -r f5cc42c56496ea702d635c6d8a0002ecb629bddc CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,9 @@ - add tox.ini to pytest distribution so that ignore-dirs and others config bits are properly distributed for maintainers who run pytest-own tests +- add some logic to setup.py such that "python setup.py test" works with + pytest itself + Changes between 2.3.0 and 2.3.1 ----------------------------------- diff -r 91fc055eea01dd7fabc29df1e826eec5f572591e -r f5cc42c56496ea702d635c6d8a0002ecb629bddc _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.2.dev1' +__version__ = '2.3.2.dev2' diff -r 91fc055eea01dd7fabc29df1e826eec5f572591e -r f5cc42c56496ea702d635c6d8a0002ecb629bddc setup.py --- a/setup.py +++ b/setup.py @@ -1,10 +1,10 @@ import os, sys try: - from setuptools import setup + from setuptools import setup, Command except ImportError: from distribute_setup import use_setuptools use_setuptools() - from setuptools import setup + from setuptools import setup, Command long_description = """ cross-project testing tool for Python. @@ -24,13 +24,14 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.2.dev1', + version='2.3.2.dev2', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], author='Holger Krekel, Benjamin Peterson, Ronny Pfannschmidt, Floris Bruynooghe and others', author_email='holger at merlinux.eu', entry_points= make_entry_points(), + cmdclass = {'test': PyTest}, # the following should be enabled for release install_requires=['py>=1.4.10'], classifiers=['Development Status :: 6 - Mature', @@ -69,5 +70,18 @@ l = ["%s = %s" % (x, points[x]) for x in keys] return {'console_scripts': l} + +class PyTest(Command): + user_options = [] + def initialize_options(self): + pass + def finalize_options(self): + pass + def run(self): + import sys,subprocess + os.environ["PYTHONPATH"] = os.environ["PYTHONPATH"] + ":" + os.getcwd() + errno = subprocess.call([sys.executable, 'pytest.py']) + raise SystemExit(errno) + if __name__ == '__main__': main() Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Oct 21 20:38:00 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 21 Oct 2012 18:38:00 -0000 Subject: [py-svn] commit/py: hpk42: fix internal test, thanks thm Message-ID: <20121021183800.4656.37166@bitbucket20.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/1060ea1c96dd/ changeset: 1060ea1c96dd user: hpk42 date: 2012-10-21 20:37:54 summary: fix internal test, thanks thm affected #: 2 files diff -r 4532fe5ee826808805b373e9296d2baf4db254d9 -r 1060ea1c96dd9dfb031bd1987ceb2bf437a096a3 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +Changes between 1.4.10 and 1.4.11.dev +================================================== + +- fix an internal test to not use class-denoted pytest_funcarg__ + + Changes between 1.4.9 and 1.4.10 ================================================== diff -r 4532fe5ee826808805b373e9296d2baf4db254d9 -r 1060ea1c96dd9dfb031bd1987ceb2bf437a096a3 testing/path/test_svnauth.py --- a/testing/path/test_svnauth.py +++ b/testing/path/test_svnauth.py @@ -261,7 +261,10 @@ u.propget('foo') assert '--username="foo" --password="bar"' in u.commands[0] -class pytest_funcarg__setup: +def pytest_funcarg__setup(request): + return Setup(request) + +class Setup: def __init__(self, request): if not svnbin: py.test.skip("svn binary required") Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 22 10:17:58 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 22 Oct 2012 08:17:58 -0000 Subject: [py-svn] commit/pytest: 3 new changesets Message-ID: <20121022081758.13231.87796@bitbucket12.managed.contegix.com> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/51bae917c674/ changeset: 51bae917c674 user: hpk42 date: 2012-10-20 17:39:15 summary: properly handle non-existent PYTHONPATH affected #: 2 files diff -r f5cc42c56496ea702d635c6d8a0002ecb629bddc -r 51bae917c674420b6a9de262581c8d6d57e873a0 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.2.dev2' +__version__ = '2.3.2.dev3' diff -r f5cc42c56496ea702d635c6d8a0002ecb629bddc -r 51bae917c674420b6a9de262581c8d6d57e873a0 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.2.dev2', + version='2.3.2.dev3', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -79,7 +79,9 @@ pass def run(self): import sys,subprocess - os.environ["PYTHONPATH"] = os.environ["PYTHONPATH"] + ":" + os.getcwd() + PPATH=[x for x in os.environ.get("PYTHONPATH", "").split(":") if x] + PPATH.insert(0, os.getcwd()) + os.environ["PYTHONPATH"] = ":".join(PPATH) errno = subprocess.call([sys.executable, 'pytest.py']) raise SystemExit(errno) https://bitbucket.org/hpk42/pytest/changeset/0ad44294bcf7/ changeset: 0ad44294bcf7 user: hpk42 date: 2012-10-22 09:32:41 summary: mention twisted with external plugins affected #: 1 file diff -r 51bae917c674420b6a9de262581c8d6d57e873a0 -r 0ad44294bcf7e050505af99d723926e66a425e45 doc/en/plugins.txt --- a/doc/en/plugins.txt +++ b/doc/en/plugins.txt @@ -71,6 +71,10 @@ * `pytest-django `_: write tests for `django`_ apps, using pytest integration. +* `pytest-twisted `_: write tests + for `twisted `_ apps, starting a reactor and + processing deferreds from test functions. + * `pytest-capturelog `_: to capture and assert about messages from the logging module https://bitbucket.org/hpk42/pytest/changeset/40c73a4d1021/ changeset: 40c73a4d1021 user: hpk42 date: 2012-10-22 10:17:50 summary: fix issue205 - nested conftest to pickup pycollect_makemodule - relates to the two reports of a failing doc/en/example/py2py3. affected #: 5 files diff -r 0ad44294bcf7e050505af99d723926e66a425e45 -r 40c73a4d102182bf1fa9f1d4f4316fe7a1338c2f CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ Changes between 2.3.1 and 2.3.2.dev ----------------------------------- +- fix issue205 - conftests in subdirs customizing + pytest_pycollect_makemodule now work properly + - add tox.ini to pytest distribution so that ignore-dirs and others config bits are properly distributed for maintainers who run pytest-own tests diff -r 0ad44294bcf7e050505af99d723926e66a425e45 -r 40c73a4d102182bf1fa9f1d4f4316fe7a1338c2f _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.2.dev3' +__version__ = '2.3.2.dev4' diff -r 0ad44294bcf7e050505af99d723926e66a425e45 -r 40c73a4d102182bf1fa9f1d4f4316fe7a1338c2f _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -161,8 +161,8 @@ break else: return - return parent.ihook.pytest_pycollect_makemodule( - path=path, parent=parent) + ihook = parent.session.gethookproxy(path) + return ihook.pytest_pycollect_makemodule(path=path, parent=parent) def pytest_pycollect_makemodule(path, parent): return Module(path, parent) diff -r 0ad44294bcf7e050505af99d723926e66a425e45 -r 40c73a4d102182bf1fa9f1d4f4316fe7a1338c2f setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.2.dev3', + version='2.3.2.dev4', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 0ad44294bcf7e050505af99d723926e66a425e45 -r 40c73a4d102182bf1fa9f1d4f4316fe7a1338c2f testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -404,6 +404,21 @@ "* 2 new commits in py: https://bitbucket.org/hpk42/py/changeset/ff122afce5d1/ changeset: ff122afce5d1 user: hpk42 date: 2012-10-21 20:41:02 summary: add --runslowtests to tox xdist invocation affected #: 1 file diff -r 1060ea1c96dd9dfb031bd1987ceb2bf437a096a3 -r ff122afce5d17920ae48b5ca4599d7de30ac43b4 tox.ini --- a/tox.ini +++ b/tox.ini @@ -14,7 +14,7 @@ pytest pytest-xdist commands= - py.test -n3 -rfsxX --confcutdir=.. \ + py.test -n3 -rfsxX --confcutdir=.. --runslowtests \ --junitxml={envlogdir}/junit-{envname}.xml [] [testenv:jython] https://bitbucket.org/hpk42/py/changeset/7577a2cb4785/ changeset: 7577a2cb4785 user: hpk42 date: 2012-10-22 10:30:40 summary: fix readme/a link to bug tracker affected #: 2 files diff -r ff122afce5d17920ae48b5ca4599d7de30ac43b4 -r 7577a2cb47851104ce3400b639c4e6dbb6036e5d CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ ================================================== - fix an internal test to not use class-denoted pytest_funcarg__ +- fix a link to bug tracker Changes between 1.4.9 and 1.4.10 diff -r ff122afce5d17920ae48b5ca4599d7de30ac43b4 -r 7577a2cb47851104ce3400b639c4e6dbb6036e5d README.txt --- a/README.txt +++ b/README.txt @@ -12,9 +12,9 @@ For questions and more information please visit http://pylib.org -Bugs and issues: http://bitbucket.org/hpk42/pylib/issues/ +Bugs and issues: http://bitbucket.org/hpk42/py/issues/ Mailing lists and more contact points: http://pylib.org/contact.html -(c) Holger Krekel and others, 2004-2011 +(c) Holger Krekel and others, 2004-2012 Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 22 10:56:43 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 22 Oct 2012 08:56:43 -0000 Subject: [py-svn] commit/pytest: hpk42: - fix test_nose.py by being more tolerant about the error message Message-ID: <20121022085643.9030.25734@bitbucket15.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/a2b978e586f2/ changeset: a2b978e586f2 user: hpk42 date: 2012-10-22 10:55:59 summary: - fix test_nose.py by being more tolerant about the error message (differs between py32 and py33, thanks Arfrever) - use pypi again now that py is released affected #: 3 files diff -r 40c73a4d102182bf1fa9f1d4f4316fe7a1338c2f -r a2b978e586f2e09a90106ccdabf5860f15b6b928 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,8 @@ - fix issue205 - conftests in subdirs customizing pytest_pycollect_makemodule now work properly +- fix exception message check of test_nose.py to pass on python33 as well + - add tox.ini to pytest distribution so that ignore-dirs and others config bits are properly distributed for maintainers who run pytest-own tests diff -r 40c73a4d102182bf1fa9f1d4f4316fe7a1338c2f -r a2b978e586f2e09a90106ccdabf5860f15b6b928 testing/test_nose.py --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -85,7 +85,7 @@ """) result = testdir.runpytest(p, '-p', 'nose') result.stdout.fnmatch_lines([ - "*TypeError: () takes exactly 1*0 given*" + "*TypeError: ()*" ]) diff -r 40c73a4d102182bf1fa9f1d4f4316fe7a1338c2f -r a2b978e586f2e09a90106ccdabf5860f15b6b928 tox.ini --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ indexserver= pypi = http://pypi.python.org/simple testrun = http://pypi.testrun.org - default = http://pypi.testrun.org + #default = http://pypi.testrun.org [testenv] changedir=testing @@ -74,6 +74,7 @@ [testenv:py33] deps=py>=1.4.0 + nose [testenv:jython] changedir=testing Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 22 11:14:28 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 22 Oct 2012 09:14:28 -0000 Subject: [py-svn] commit/pytest: hpk42: fix issue206 - unset PYTHONDONTWRITEBYTECODE in assertrewrite test Message-ID: <20121022091428.18590.35609@bitbucket15.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/f8223edf9072/ changeset: f8223edf9072 user: hpk42 date: 2012-10-22 11:14:18 summary: fix issue206 - unset PYTHONDONTWRITEBYTECODE in assertrewrite test affected #: 5 files diff -r a2b978e586f2e09a90106ccdabf5860f15b6b928 -r f8223edf907244076e98eeae576f896489354d55 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,9 @@ - fix exception message check of test_nose.py to pass on python33 as well +- fix issue206 - fix test_assertrewrite.py to work when a global + PYTHONDONTWRITEBYTECODE=1 is present + - add tox.ini to pytest distribution so that ignore-dirs and others config bits are properly distributed for maintainers who run pytest-own tests diff -r a2b978e586f2e09a90106ccdabf5860f15b6b928 -r f8223edf907244076e98eeae576f896489354d55 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.2.dev4' +__version__ = '2.3.2.dev5' diff -r a2b978e586f2e09a90106ccdabf5860f15b6b928 -r f8223edf907244076e98eeae576f896489354d55 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.2.dev4', + version='2.3.2.dev5', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r a2b978e586f2e09a90106ccdabf5860f15b6b928 -r f8223edf907244076e98eeae576f896489354d55 testing/test_assertrewrite.py --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -354,18 +354,21 @@ @pytest.mark.skipif('"__pypy__" in sys.modules') def test_pyc_vs_pyo(self, testdir, monkeypatch): testdir.makepyfile(""" -import pytest -def test_optimized(): - "hello" - assert test_optimized.__doc__ is None""") + import pytest + def test_optimized(): + "hello" + assert test_optimized.__doc__ is None""" + ) p = py.path.local.make_numbered_dir(prefix="runpytest-", keep=None, rootdir=testdir.tmpdir) tmp = "--basetemp=%s" % p monkeypatch.setenv("PYTHONOPTIMIZE", "2") + monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False) assert testdir.runpybin("py.test", tmp).ret == 0 tagged = "test_pyc_vs_pyo." + PYTEST_TAG assert tagged + ".pyo" in os.listdir("__pycache__") monkeypatch.undo() + monkeypatch.delenv("PYTHONDONTWRITEBYTECODE", raising=False) assert testdir.runpybin("py.test", tmp).ret == 1 assert tagged + ".pyc" in os.listdir("__pycache__") diff -r a2b978e586f2e09a90106ccdabf5860f15b6b928 -r f8223edf907244076e98eeae576f896489354d55 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] distshare={homedir}/.tox/distshare -envlist=py26,py27,py31,py32,py33,py27-xdist,py25,trial +envlist=py26,py27,py27-nobyte,py31,py32,py33,py27-xdist,py25,trial indexserver= pypi = http://pypi.python.org/simple testrun = http://pypi.testrun.org @@ -24,7 +24,17 @@ deps=pytest-xdist commands= py.test -n3 -rfsxX \ - --ignore .tox --junitxml={envlogdir}/junit-{envname}.xml testing + --junitxml={envlogdir}/junit-{envname}.xml testing + +[testenv:py27-nobyte] +changedir=. +basepython=python2.7 +deps=pytest-xdist +setenv= + PYTHONDONTWRITEBYTECODE=1 +commands= + py.test -n3 -rfsxX \ + --junitxml={envlogdir}/junit-{envname}.xml [] [testenv:trial] changedir=. Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 22 11:52:30 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 22 Oct 2012 09:52:30 -0000 Subject: [py-svn] commit/py: hpk42: try to make terminalwriter printing more robust against ascii files Message-ID: <20121022095230.22723.3150@bitbucket02.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/4316a23e130d/ changeset: 4316a23e130d user: hpk42 date: 2012-10-22 11:52:09 summary: try to make terminalwriter printing more robust against ascii files affected #: 5 files diff -r 7577a2cb47851104ce3400b639c4e6dbb6036e5d -r 4316a23e130da1edcd9fd7c422030fd6e237a486 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,8 @@ - fix an internal test to not use class-denoted pytest_funcarg__ - fix a link to bug tracker +- try to make terminal.write() printing more robust against + unicodeencode/decode problems, amend according test Changes between 1.4.9 and 1.4.10 diff -r 7577a2cb47851104ce3400b639c4e6dbb6036e5d -r 4316a23e130da1edcd9fd7c422030fd6e237a486 py/__init__.py --- a/py/__init__.py +++ b/py/__init__.py @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.10' +__version__ = '1.4.11.dev1' from py import _apipkg diff -r 7577a2cb47851104ce3400b639c4e6dbb6036e5d -r 4316a23e130da1edcd9fd7c422030fd6e237a486 py/_io/terminalwriter.py --- a/py/_io/terminalwriter.py +++ b/py/_io/terminalwriter.py @@ -162,7 +162,7 @@ def write(self, s, **kw): if s: - if not isinstance(self._file, WriteFile): + if not isinstance(self._file, WriteFile) and not getattr(self._file, "encoding", None): s = self._getbytestring(s) if self.hasmarkup and kw: s = self.markup(s, **kw) @@ -172,7 +172,7 @@ def _getbytestring(self, s): # XXX review this and the whole logic if sys.version_info[0] < 3 and isinstance(s, unicode): - return s.encode(self.encoding or "utf8") + return s.encode(self.encoding or "utf8", "replace") elif not isinstance(s, str): try: return str(s) @@ -236,7 +236,7 @@ def write(self, data): if self.encoding: - data = data.encode(self.encoding) + data = data.encode(self.encoding, "replace") self._writemethod(data) def flush(self): diff -r 7577a2cb47851104ce3400b639c4e6dbb6036e5d -r 4316a23e130da1edcd9fd7c422030fd6e237a486 setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.10', + version='1.4.11.dev1', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 7577a2cb47851104ce3400b639c4e6dbb6036e5d -r 4316a23e130da1edcd9fd7c422030fd6e237a486 testing/io_/test_terminalwriter.py --- a/testing/io_/test_terminalwriter.py +++ b/testing/io_/test_terminalwriter.py @@ -78,20 +78,24 @@ tw.line(msg) assert l[0].strip() == msg.encode(encoding) -def test_unicode_on_file_with_no_encoding(tmpdir, monkeypatch): + at pytest.mark.parametrize("encoding", [None, "ascii"]) +def test_unicode_on_file_with_no_encoding(tmpdir, monkeypatch, encoding): try: bytes except NameError: bytes = str msg = py.builtin._totext('?l', "utf8") #pytest.raises(UnicodeEncodeError, lambda: bytes(msg)) - f = py.std.codecs.open(str(tmpdir.join("x")), "w", None) + f = py.std.codecs.open(str(tmpdir.join("x")), "w", encoding, errors="replace") tw = py.io.TerminalWriter(f, encoding=None) tw.encoding = None tw.line(msg) f.close() s = tmpdir.join("x").open("rb").read().strip() - assert s == msg.encode("utf8") + if encoding is None: + assert s == msg.encode(f.encoding or "utf-8") + else: + assert s.decode(encoding) == '?l' class TestTerminalWriter: def pytest_generate_tests(self, metafunc): Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 22 12:24:09 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 22 Oct 2012 10:24:09 -0000 Subject: [py-svn] commit/pytest: hpk42: fix teardown-ordering for parametrized setups/teardowns Message-ID: <20121022102409.22622.64622@bitbucket20.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/3d4ebcbd4079/ changeset: 3d4ebcbd4079 user: hpk42 date: 2012-10-22 12:16:54 summary: fix teardown-ordering for parametrized setups/teardowns affected #: 5 files diff -r f8223edf907244076e98eeae576f896489354d55 -r 3d4ebcbd4079e310abdc4ab53941527ebb6e67fd CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,8 @@ - fix issue205 - conftests in subdirs customizing pytest_pycollect_makemodule now work properly +- fix teardown-ordering for parametrized setups + - fix exception message check of test_nose.py to pass on python33 as well - fix issue206 - fix test_assertrewrite.py to work when a global diff -r f8223edf907244076e98eeae576f896489354d55 -r 3d4ebcbd4079e310abdc4ab53941527ebb6e67fd _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.2.dev5' +__version__ = '2.3.2.dev6' diff -r f8223edf907244076e98eeae576f896489354d55 -r 3d4ebcbd4079e310abdc4ab53941527ebb6e67fd _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1531,7 +1531,7 @@ item.session._setupstate._callfinalizers((name, param)) l = self._arg2finish.get(name) if l is not None: - for fin in l: + for fin in reversed(l): fin() def parsefactories(self, node_or_obj, nodeid=None, unittest=False): diff -r f8223edf907244076e98eeae576f896489354d55 -r 3d4ebcbd4079e310abdc4ab53941527ebb6e67fd setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.2.dev5', + version='2.3.2.dev6', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r f8223edf907244076e98eeae576f896489354d55 -r 3d4ebcbd4079e310abdc4ab53941527ebb6e67fd testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -2763,6 +2763,43 @@ reprec = testdir.inline_run("-v") reprec.assertoutcome(passed=12+1) + def test_parametrized_fixture_teardown_order(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.fixture(params=[1,2], scope="class") + def param1(request): + return request.param + + l = [] + + class TestClass: + @classmethod + @pytest.fixture(scope="class", autouse=True) + def setup1(self, request, param1): + l.append(1) + request.addfinalizer(self.teardown1) + @classmethod + def teardown1(self): + assert l.pop() == 1 + @pytest.fixture(scope="class", autouse=True) + def setup2(self, request, param1): + l.append(2) + request.addfinalizer(self.teardown2) + @classmethod + def teardown2(self): + assert l.pop() == 2 + def test(self): + pass + + def test_finish(): + assert not l + """) + result = testdir.runpytest("-v") + result.stdout.fnmatch_lines(""" + *3 passed* + """) + assert "error" not in result.stdout.str() + def test_parametrize_separated_lifecycle(self, testdir): testdir.makepyfile(""" import pytest Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 22 16:26:03 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 22 Oct 2012 14:26:03 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20121022142603.13060.10150@bitbucket15.managed.contegix.com> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/ef59b755c115/ changeset: ef59b755c115 user: hpk42 date: 2012-10-22 16:12:22 summary: make sure ihook uses a node's fspath - important for hooks e.g. during a Module's collect to pick up conftest.py files residing in the same dir affected #: 6 files diff -r 3d4ebcbd4079e310abdc4ab53941527ebb6e67fd -r ef59b755c115b2d0344404b11ca922220bbbd329 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -2,20 +2,22 @@ ----------------------------------- - fix issue205 - conftests in subdirs customizing - pytest_pycollect_makemodule now work properly + pytest_pycollect_makemodule and pytest_pycollect_makeitem + now work properly - fix teardown-ordering for parametrized setups -- fix exception message check of test_nose.py to pass on python33 as well +- "python setup.py test" now works with pytest itself -- fix issue206 - fix test_assertrewrite.py to work when a global - PYTHONDONTWRITEBYTECODE=1 is present +- fix/improve internal/packaging related bits: -- add tox.ini to pytest distribution so that ignore-dirs and others config - bits are properly distributed for maintainers who run pytest-own tests + - exception message check of test_nose.py now passes on python33 as well -- add some logic to setup.py such that "python setup.py test" works with - pytest itself + - issue206 - fix test_assertrewrite.py to work when a global + PYTHONDONTWRITEBYTECODE=1 is present + + - add tox.ini to pytest distribution so that ignore-dirs and others config + bits are properly distributed for maintainers who run pytest-own tests Changes between 2.3.0 and 2.3.1 ----------------------------------- diff -r 3d4ebcbd4079e310abdc4ab53941527ebb6e67fd -r ef59b755c115b2d0344404b11ca922220bbbd329 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.2.dev6' +__version__ = '2.3.2.dev7' diff -r 3d4ebcbd4079e310abdc4ab53941527ebb6e67fd -r ef59b755c115b2d0344404b11ca922220bbbd329 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -211,14 +211,16 @@ #: filesystem path where this node was collected from (can be None) self.fspath = getattr(parent, 'fspath', None) - #: fspath sensitive hook proxy used to call pytest hooks - self.ihook = self.session.gethookproxy(self.fspath) - #: keywords/markers collected from all scopes self.keywords = NodeKeywords(self) #self.extrainit() + @property + def ihook(self): + """ fspath sensitive hook proxy used to call pytest hooks""" + return self.session.gethookproxy(self.fspath) + #def extrainit(self): # """"extra initialization after Node is initialized. Implemented # by some subclasses. """ diff -r 3d4ebcbd4079e310abdc4ab53941527ebb6e67fd -r ef59b755c115b2d0344404b11ca922220bbbd329 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -288,6 +288,7 @@ return l def makeitem(self, name, obj): + #assert self.ihook.fspath == self.fspath, self return self.ihook.pytest_pycollect_makeitem( collector=self, name=name, obj=obj) diff -r 3d4ebcbd4079e310abdc4ab53941527ebb6e67fd -r ef59b755c115b2d0344404b11ca922220bbbd329 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.2.dev6', + version='2.3.2.dev7', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 3d4ebcbd4079e310abdc4ab53941527ebb6e67fd -r ef59b755c115b2d0344404b11ca922220bbbd329 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -419,6 +419,28 @@ reprec = testdir.inline_run() reprec.assertoutcome(passed=1) + def test_customized_pymakeitem(self, testdir): + b = testdir.mkdir("a").mkdir("b") + b.join("conftest.py").write(py.code.Source(""" + def pytest_pycollect_makeitem(__multicall__): + result = __multicall__.execute() + if result: + for func in result: + func._some123 = "world" + return result + """)) + b.join("test_module.py").write(py.code.Source(""" + import pytest + + @pytest.fixture() + def obj(request): + return request.node._some123 + def test_hello(obj): + assert obj == "world" + """)) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) + def test_pytest_pycollect_makeitem(self, testdir): testdir.makeconftest(""" import pytest https://bitbucket.org/hpk42/pytest/changeset/65a3b9540751/ changeset: 65a3b9540751 user: hpk42 date: 2012-10-22 16:25:09 summary: fix unittest emulation: TestCase.runTest is now ignored if there are test* methods. affected #: 5 files diff -r ef59b755c115b2d0344404b11ca922220bbbd329 -r 65a3b95407512a9492e5cd755768c7f6022ab77f CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,9 @@ - fix teardown-ordering for parametrized setups +- fix unittest behaviour: TestCase.runtest only called if there are + test methods defined + - "python setup.py test" now works with pytest itself - fix/improve internal/packaging related bits: diff -r ef59b755c115b2d0344404b11ca922220bbbd329 -r 65a3b95407512a9492e5cd755768c7f6022ab77f _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.2.dev7' +__version__ = '2.3.2.dev8' diff -r ef59b755c115b2d0344404b11ca922220bbbd329 -r 65a3b95407512a9492e5cd755768c7f6022ab77f _pytest/unittest.py --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -28,6 +28,7 @@ loader = py.std.unittest.TestLoader() module = self.getparent(pytest.Module).obj cls = self.obj + foundsomething = False for name in loader.getTestCaseNames(self.obj): x = getattr(self.obj, name) funcobj = getattr(x, 'im_func', x) @@ -35,9 +36,11 @@ if hasattr(funcobj, 'todo'): pytest.mark.xfail(reason=str(funcobj.todo))(funcobj) yield TestCaseFunction(name, parent=self) + foundsomething = True - if getattr(self.obj, 'runTest', None) is not None: - yield TestCaseFunction('runTest', parent=self) + if not foundsomething: + if getattr(self.obj, 'runTest', None) is not None: + yield TestCaseFunction('runTest', parent=self) def setup(self): meth = getattr(self.obj, 'setUpClass', None) diff -r ef59b755c115b2d0344404b11ca922220bbbd329 -r 65a3b95407512a9492e5cd755768c7f6022ab77f setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.2.dev7', + version='2.3.2.dev8', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r ef59b755c115b2d0344404b11ca922220bbbd329 -r 65a3b95407512a9492e5cd755768c7f6022ab77f testing/test_unittest.py --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -18,12 +18,21 @@ testpath=testdir.makepyfile(""" import unittest pytest_plugins = "pytest_unittest" - class MyTestCase(unittest.TestCase): + class MyTestCaseWithRunTest(unittest.TestCase): def runTest(self): self.assertEquals('foo', 'foo') + class MyTestCaseWithoutRunTest(unittest.TestCase): + def runTest(self): + self.assertEquals('foo', 'foo') + def test_something(self): + pass """) - reprec = testdir.inline_run(testpath) - assert reprec.matchreport('runTest').passed + result = testdir.runpytest("-v") + result.stdout.fnmatch_lines(""" + *MyTestCaseWithRunTest.runTest* + *MyTestCaseWithoutRunTest.test_something* + *2 passed* + """) def test_isclasscheck_issue53(testdir): testpath = testdir.makepyfile(""" Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon Oct 22 19:22:24 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 22 Oct 2012 17:22:24 -0000 Subject: [py-svn] commit/pytest: hpk42: improve support for trial a bit more: don't run trial's empty TestCase.runTest() method Message-ID: <20121022172224.17883.30350@bitbucket01.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/cc9892350f7e/ changeset: cc9892350f7e user: hpk42 date: 2012-10-22 19:22:01 summary: improve support for trial a bit more: don't run trial's empty TestCase.runTest() method affected #: 6 files diff -r 65a3b95407512a9492e5cd755768c7f6022ab77f -r cc9892350f7e43b6b9d84f7bb6ae6f907181b235 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -10,6 +10,9 @@ - fix unittest behaviour: TestCase.runtest only called if there are test methods defined +- improve trial support: don't collect its empty + unittest.TestCase.runTest() method + - "python setup.py test" now works with pytest itself - fix/improve internal/packaging related bits: diff -r 65a3b95407512a9492e5cd755768c7f6022ab77f -r cc9892350f7e43b6b9d84f7bb6ae6f907181b235 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.2.dev8' +__version__ = '2.3.2.dev9' diff -r 65a3b95407512a9492e5cd755768c7f6022ab77f -r cc9892350f7e43b6b9d84f7bb6ae6f907181b235 _pytest/unittest.py --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -39,8 +39,11 @@ foundsomething = True if not foundsomething: - if getattr(self.obj, 'runTest', None) is not None: - yield TestCaseFunction('runTest', parent=self) + runtest = getattr(self.obj, 'runTest', None) + if runtest is not None: + ut = sys.modules.get("twisted.trial.unittest", None) + if ut is None or runtest != ut.TestCase.runTest: + yield TestCaseFunction('runTest', parent=self) def setup(self): meth = getattr(self.obj, 'setUpClass', None) diff -r 65a3b95407512a9492e5cd755768c7f6022ab77f -r cc9892350f7e43b6b9d84f7bb6ae6f907181b235 doc/en/faq.txt --- a/doc/en/faq.txt +++ b/doc/en/faq.txt @@ -25,10 +25,13 @@ Since some time py.test has builtin support for supporting tests written using trial. It does not itself start a reactor, however, -and does not handle Deferreds returned from a test. Someone using -these features might eventually write a dedicated ``pytest-twisted`` -plugin which will surely see strong support from the pytest development -team. +and does not handle Deferreds returned from a test in pytest style. +If you are using trial's unittest.TestCase chances are that you can +just run your tests even if you return Deferreds. In addition, +there also is a dedicated `pytest-twisted + 2 new commits in py: https://bitbucket.org/hpk42/py/changeset/c35f6441aa26/ changeset: c35f6441aa26 user: hpk42 date: 2012-10-24 22:16:49 summary: simplify and improve unicode handling in terminalwriter affected #: 5 files diff -r 4316a23e130da1edcd9fd7c422030fd6e237a486 -r c35f6441aa262da4e136812f94b55339016c9b2f py/__init__.py --- a/py/__init__.py +++ b/py/__init__.py @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.11.dev1' +__version__ = '1.4.11.dev2' from py import _apipkg diff -r 4316a23e130da1edcd9fd7c422030fd6e237a486 -r c35f6441aa262da4e136812f94b55339016c9b2f py/_io/terminalwriter.py --- a/py/_io/terminalwriter.py +++ b/py/_io/terminalwriter.py @@ -7,6 +7,7 @@ import sys, os import py +py3k = sys.version_info[0] >= 3 win32_and_ctypes = False if sys.platform == "win32": @@ -160,26 +161,19 @@ self.line(line, **kw) - def write(self, s, **kw): - if s: - if not isinstance(self._file, WriteFile) and not getattr(self._file, "encoding", None): - s = self._getbytestring(s) - if self.hasmarkup and kw: - s = self.markup(s, **kw) - self._file.write(s) + def write(self, msg, **kw): + if msg: + if self.hasmarkup and kw: + markupmsg = self.markup(msg, **kw) + else: + markupmsg = msg + try: + self._file.write(markupmsg) + except UnicodeEncodeError: + msg = msg.encode("unicode-escape").decode("ascii") + self._file.write(msg) self._file.flush() - def _getbytestring(self, s): - # XXX review this and the whole logic - if sys.version_info[0] < 3 and isinstance(s, unicode): - return s.encode(self.encoding or "utf8", "replace") - elif not isinstance(s, str): - try: - return str(s) - except UnicodeEncodeError: - return "" % type(s).__name__ - return s - def line(self, s='', **kw): self.write(s, **kw) self._checkfill(s) diff -r 4316a23e130da1edcd9fd7c422030fd6e237a486 -r c35f6441aa262da4e136812f94b55339016c9b2f setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.11.dev1', + version='1.4.11.dev2', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 4316a23e130da1edcd9fd7c422030fd6e237a486 -r c35f6441aa262da4e136812f94b55339016c9b2f testing/io_/test_terminalwriter.py --- a/testing/io_/test_terminalwriter.py +++ b/testing/io_/test_terminalwriter.py @@ -1,4 +1,3 @@ -# coding=utf8 import py import os, sys @@ -78,24 +77,18 @@ tw.line(msg) assert l[0].strip() == msg.encode(encoding) - at pytest.mark.parametrize("encoding", [None, "ascii"]) -def test_unicode_on_file_with_no_encoding(tmpdir, monkeypatch, encoding): - try: - bytes - except NameError: - bytes = str - msg = py.builtin._totext('?l', "utf8") + at pytest.mark.parametrize("encoding", ["ascii"]) +def test_unicode_on_file_with_ascii_encoding(tmpdir, monkeypatch, encoding): + msg = py.builtin._totext('hell\xf6', "latin1") #pytest.raises(UnicodeEncodeError, lambda: bytes(msg)) - f = py.std.codecs.open(str(tmpdir.join("x")), "w", encoding, errors="replace") - tw = py.io.TerminalWriter(f, encoding=None) - tw.encoding = None + f = py.std.codecs.open(str(tmpdir.join("x")), "w", encoding) + tw = py.io.TerminalWriter(f) tw.line(msg) f.close() s = tmpdir.join("x").open("rb").read().strip() - if encoding is None: - assert s == msg.encode(f.encoding or "utf-8") - else: - assert s.decode(encoding) == '?l' + assert encoding == "ascii" + assert s == msg.encode("unicode-escape") + class TestTerminalWriter: def pytest_generate_tests(self, metafunc): diff -r 4316a23e130da1edcd9fd7c422030fd6e237a486 -r c35f6441aa262da4e136812f94b55339016c9b2f tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py26,py27,py31,py32,py27-xdist,py25 +envlist=py26,py27,py31,py32,py33,py27-xdist,py25 #indexserver= # default=http://pypi.testrun.org https://bitbucket.org/hpk42/py/changeset/7e29aba2f979/ changeset: 7e29aba2f979 user: hpk42 date: 2012-10-24 22:16:50 summary: remove test_sources and helper whose tests are now covered by tox affected #: 3 files diff -r c35f6441aa262da4e136812f94b55339016c9b2f -r 7e29aba2f9791e942b87149cbb0c4812a7bf2f0a CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -2,11 +2,10 @@ ================================================== - fix an internal test to not use class-denoted pytest_funcarg__ -- fix a link to bug tracker +- fix a doc link to bug tracker - try to make terminal.write() printing more robust against unicodeencode/decode problems, amend according test - Changes between 1.4.9 and 1.4.10 ================================================== diff -r c35f6441aa262da4e136812f94b55339016c9b2f -r 7e29aba2f9791e942b87149cbb0c4812a7bf2f0a testing/root/check_compile.py --- a/testing/root/check_compile.py +++ /dev/null @@ -1,13 +0,0 @@ -import sys - -def main(fn): - print("Testing %s" % (fn,)) - fp = open(fn, "rb") - try: - source = fp.read() - finally: - fp.close() - compile(source, fn, "exec") - -if __name__ == "__main__": - main(sys.argv[1]) diff -r c35f6441aa262da4e136812f94b55339016c9b2f -r 7e29aba2f9791e942b87149cbb0c4812a7bf2f0a testing/root/test_sources.py --- a/testing/root/test_sources.py +++ /dev/null @@ -1,19 +0,0 @@ -import os -import py - - -this_dir = py.path.local(__file__).dirpath() -_compile_checker = this_dir.join("check_compile.py") -_py_root = this_dir.join("..") -del this_dir - - at py.test.mark.multi(pyversion=("2.4", "2.5", "2.6", "3.1")) -def test_syntax(pyversion): - executable = py.path.local.sysfind("python" + pyversion) - if executable is None: - py.test.skip("no python%s found" % (pyversion,)) - for path, dirs, filenames in os.walk(str(_py_root)): - for fn in filenames: - if fn.endswith(".py"): - full = os.path.join(path, fn) - executable.sysexec(_compile_checker, full) Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 25 11:07:18 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 25 Oct 2012 09:07:18 -0000 Subject: [py-svn] commit/pytest: hpk42: fix issue127 - improve pytest_addoption and related documentation Message-ID: <20121025090718.21238.42904@bitbucket25.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/415781a6d022/ changeset: 415781a6d022 user: hpk42 date: 2012-10-25 11:07:07 summary: fix issue127 - improve pytest_addoption and related documentation affected #: 5 files diff -r cc9892350f7e43b6b9d84f7bb6ae6f907181b235 -r 415781a6d022b09b081e63ce40b80ca270596e21 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,9 @@ - fix teardown-ordering for parametrized setups +- fix issue127 - better documentation for pytest_addoption + and related objects. + - fix unittest behaviour: TestCase.runtest only called if there are test methods defined diff -r cc9892350f7e43b6b9d84f7bb6ae6f907181b235 -r 415781a6d022b09b081e63ce40b80ca270596e21 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -19,7 +19,7 @@ fin() class Parser: - """ Parser for command line arguments. """ + """ Parser for command line arguments and ini-file values. """ def __init__(self, usage=None, processopt=None): self._anonymous = OptionGroup("custom options", parser=self) @@ -38,9 +38,14 @@ def getgroup(self, name, description="", after=None): """ get (or create) a named option Group. - :name: unique name of the option group. + :name: name of the option group. :description: long description for --help output. :after: name of other group, used for ordering --help output. + + The returned group object has an ``addoption`` method with the same + signature as :py:func:`parser.addoption + <_pytest.config.Parser.addoption>` but will be shown in the + respective group in the output of ``pytest. --help``. """ for group in self._groups: if group.name == name: @@ -54,7 +59,19 @@ return group def addoption(self, *opts, **attrs): - """ add an optparse-style option. """ + """ register a command line option. + + :opts: option names, can be short or long options. + :attrs: same attributes which the ``add_option()`` function of the + `optparse library + `_ + accepts. + + After command line parsing options are available on the pytest config + object via ``config.option.NAME`` where ``NAME`` is usually set + by passing a ``dest`` attribute, for example + ``addoption("--long", dest="NAME", ...)``. + """ self._anonymous.addoption(*opts, **attrs) def parse(self, args): @@ -75,7 +92,15 @@ return args def addini(self, name, help, type=None, default=None): - """ add an ini-file option with the given name and description. """ + """ register an ini-file option. + + :name: name of the ini-variable + :type: type of the variable, can be ``pathlist``, ``args`` or ``linelist``. + :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") self._inidict[name] = (help, type, default) self._ininames.append(name) @@ -246,8 +271,9 @@ class Config(object): """ access to configuration values, pluginmanager and plugin hooks. """ def __init__(self, pluginmanager=None): - #: command line option values, usually added via parser.addoption(...) - #: or parser.getgroup(...).addoption(...) calls + #: command line option values, which must have been previously added + #: via calls like ``parser.addoption(...)`` or + #: ``parser.getgroup(groupname).addoption(...)`` self.option = CmdOptions() self._parser = Parser( usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]", diff -r cc9892350f7e43b6b9d84f7bb6ae6f907181b235 -r 415781a6d022b09b081e63ce40b80ca270596e21 _pytest/hookspec.py --- a/_pytest/hookspec.py +++ b/_pytest/hookspec.py @@ -23,8 +23,10 @@ """modify command line arguments before option parsing. """ def pytest_addoption(parser): - """add optparse-style options and ini-style config values via calls - to ``parser.addoption`` and ``parser.addini(...)``. + """use the parser to add optparse-style options and ini-style + config values via calls, see :py:func:`parser.addoption(...) + <_pytest.config.Parser.addoption>` + and :py:func:`parser.addini(...) <_pytest.config.Parser.addini>`. """ def pytest_cmdline_main(config): diff -r cc9892350f7e43b6b9d84f7bb6ae6f907181b235 -r 415781a6d022b09b081e63ce40b80ca270596e21 doc/en/announce/index.txt --- a/doc/en/announce/index.txt +++ b/doc/en/announce/index.txt @@ -5,6 +5,7 @@ .. toctree:: :maxdepth: 2 + release-2.3.1 release-2.3.0 release-2.2.4 release-2.2.2 diff -r cc9892350f7e43b6b9d84f7bb6ae6f907181b235 -r 415781a6d022b09b081e63ce40b80ca270596e21 doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.3.1" +version = release = "2.3.2.dev9" import sys, os Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 25 11:51:11 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 25 Oct 2012 09:51:11 -0000 Subject: [py-svn] commit/py: 3 new changesets Message-ID: <20121025095111.18486.95809@bitbucket24.managed.contegix.com> 3 new commits in py: https://bitbucket.org/hpk42/py/changeset/e6cf2e2fee51/ changeset: e6cf2e2fee51 user: hpk42 date: 2012-10-25 09:44:01 summary: convert terminal write-data to unicode if it is not a string/bytes affected #: 6 files diff -r 7e29aba2f9791e942b87149cbb0c4812a7bf2f0a -r e6cf2e2fee5156e174a547beeb7d827d723a944b CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,8 @@ - fix a doc link to bug tracker - try to make terminal.write() printing more robust against unicodeencode/decode problems, amend according test +- introduce py.builtin.text and py.builtin.bytes + to point to respective str/unicode (py2) and bytes/str (py3) types Changes between 1.4.9 and 1.4.10 ================================================== diff -r 7e29aba2f9791e942b87149cbb0c4812a7bf2f0a -r e6cf2e2fee5156e174a547beeb7d827d723a944b py/__init__.py --- a/py/__init__.py +++ b/py/__init__.py @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.11.dev2' +__version__ = '1.4.11.dev4' from py import _apipkg @@ -104,6 +104,8 @@ 'builtins' : '._builtin:builtins', 'execfile' : '._builtin:execfile', 'callable' : '._builtin:callable', + 'bytes' : '._builtin:bytes', + 'text' : '._builtin:text', }, # input-output helping diff -r 7e29aba2f9791e942b87149cbb0c4812a7bf2f0a -r e6cf2e2fee5156e174a547beeb7d827d723a944b py/_builtin.py --- a/py/_builtin.py +++ b/py/_builtin.py @@ -128,6 +128,10 @@ def _istext(x): return isinstance(x, str) + text = str + bytes = bytes + + def _getimself(function): return getattr(function, '__self__', None) @@ -160,6 +164,8 @@ import __builtin__ as builtins _totext = unicode _basestring = basestring + text = unicode + bytes = str execfile = execfile callable = callable def _isbytes(x): diff -r 7e29aba2f9791e942b87149cbb0c4812a7bf2f0a -r e6cf2e2fee5156e174a547beeb7d827d723a944b py/_io/terminalwriter.py --- a/py/_io/terminalwriter.py +++ b/py/_io/terminalwriter.py @@ -8,6 +8,7 @@ import sys, os import py py3k = sys.version_info[0] >= 3 +from py.builtin import text, bytes win32_and_ctypes = False if sys.platform == "win32": @@ -163,6 +164,8 @@ def write(self, msg, **kw): if msg: + if not isinstance(msg, (bytes, text)): + msg = text(msg) if self.hasmarkup and kw: markupmsg = self.markup(msg, **kw) else: diff -r 7e29aba2f9791e942b87149cbb0c4812a7bf2f0a -r e6cf2e2fee5156e174a547beeb7d827d723a944b setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.11.dev2', + version='1.4.11.dev4', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 7e29aba2f9791e942b87149cbb0c4812a7bf2f0a -r e6cf2e2fee5156e174a547beeb7d827d723a944b testing/root/test_builtin.py --- a/testing/root/test_builtin.py +++ b/testing/root/test_builtin.py @@ -133,6 +133,14 @@ def test_totext(): py.builtin._totext("hello", "UTF-8") +def test_bytes_text(): + if sys.version_info[0] < 3: + assert py.builtin.text == unicode + assert py.builtin.bytes == str + else: + assert py.builtin.text == str + assert py.builtin.bytes == bytes + def test_totext_badutf8(): # this was in printouts within the pytest testsuite # totext would fail https://bitbucket.org/hpk42/py/changeset/2ccc7c44763d/ changeset: 2ccc7c44763d user: hpk42 date: 2012-10-25 11:15:01 summary: fix pytest issue207 rewrite heuristic to find statements to use _ast module affected #: 6 files diff -r e6cf2e2fee5156e174a547beeb7d827d723a944b -r 2ccc7c44763d32e794d46e12933053c07962eb52 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ Changes between 1.4.10 and 1.4.11.dev ================================================== +- use _ast to determine statement ranges when printing tracebacks - + avoids multi-second delays on some large test modules - fix an internal test to not use class-denoted pytest_funcarg__ - fix a doc link to bug tracker - try to make terminal.write() printing more robust against diff -r e6cf2e2fee5156e174a547beeb7d827d723a944b -r 2ccc7c44763d32e794d46e12933053c07962eb52 py/__init__.py --- a/py/__init__.py +++ b/py/__init__.py @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.11.dev4' +__version__ = '1.4.11.dev5' from py import _apipkg diff -r e6cf2e2fee5156e174a547beeb7d827d723a944b -r 2ccc7c44763d32e794d46e12933053c07962eb52 py/_code/code.py --- a/py/_code/code.py +++ b/py/_code/code.py @@ -166,20 +166,12 @@ if source is None: return None start = self.getfirstlinesource() - end = self.lineno try: - _, end = source.getstatementrange(end) - except (IndexError, ValueError): + _, end = source.getstatementrange(self.lineno) + except SyntaxError: end = self.lineno + 1 - # heuristic to stop displaying source on e.g. - # if something: # assume this causes a NameError - # # _this_ lines and the one - # below we don't want from entry.getsource() - for i in range(self.lineno, end): - if source[i].rstrip().endswith(':'): - end = i + 1 - break return source[start:end] + source = property(getsource) def ishidden(self): diff -r e6cf2e2fee5156e174a547beeb7d827d723a944b -r 2ccc7c44763d32e794d46e12933053c07962eb52 py/_code/source.py --- a/py/_code/source.py +++ b/py/_code/source.py @@ -108,47 +108,10 @@ def getstatementrange(self, lineno, 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 there must be a better than these heuristic ways ... - # XXX there may even be better heuristics :-) if not (0 <= lineno < len(self)): raise IndexError("lineno out of range") - - # 1. find the start of the statement - from codeop import compile_command - for start in range(lineno, -1, -1): - if assertion: - line = self.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 = self.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(self)+1): - trysource = self[start:end] - if trysource.isparseable(): - return start, end - raise IndexError("no valid source range around line %d " % (lineno,)) - - def getblockend(self, lineno): - # XXX - lines = [x + '\n' for x in self.lines[lineno:]] - blocklines = inspect.getblock(lines) - #print blocklines - return lineno + len(blocklines) - 1 + return getstatementrange_ast(lineno, self) def deindent(self, offset=None): """ return a new source object deindented by offset. @@ -352,3 +315,65 @@ # Add any lines we didn't see. E.g. if an exception was raised. newlines.extend(lines[len(newlines):]) return newlines + +def get_statement_startend(lineno, nodelist): + from bisect import bisect_right, bisect_left + # lineno starts at 0 + nextlineno = None + while 1: + lineno_list = [x.lineno-1 for x in nodelist] # ast indexes start at 1 + insert_index = bisect_right(lineno_list, lineno) + if insert_index >= len(nodelist): + insert_index -= 1 + elif lineno < (nodelist[insert_index].lineno - 1) and insert_index > 0: + insert_index -= 1 + assert lineno >= (nodelist[insert_index].lineno - 1) + nextnode = nodelist[insert_index] + + try: + nextlineno = nodelist[insert_index+1].lineno - 1 + except IndexError: + pass + lastnodelist = nodelist + nodelist = getnodelist(nextnode) + if not nodelist: + start, end = nextnode.lineno-1, nextlineno + start = min(lineno, start) + assert start <= lineno and (end is None or lineno < end) + #print "returning", start, end + return start, end + +def getnodelist(node): + import _ast + l = [] + #print "node", node, "fields", node._fields, "lineno", getattr(node, "lineno", 0) - 1 + for subname in "test", "type", "body", "handlers", "orelse", "finalbody": + attr = getattr(node, subname, None) + if attr is not None: + if isinstance(attr, list): + l.extend(attr) + elif hasattr(attr, "lineno"): + l.append(attr) + #print "returning nodelist", l + return l + +def getstatementrange_ast(lineno, source): + content = "\n".join(source.lines) + #print "compiling lines", len(source.lines) + astnode = compile(content, "source", "exec", 1024) # 1024 for AST + start, end = get_statement_startend(lineno, getnodelist(astnode)) + # we need to correct the end: + # - ast-parsing strips comments + # - else statements do not have a separate lineno + # - there might be empty lines + if end is None: + end = len(source.lines) + while end: + line = source.lines[end-1].lstrip() + if (not line or line.startswith("#") or line.startswith("else:") or + line.startswith("finally:")): + end -= 1 + else: + break + return start, end + diff -r e6cf2e2fee5156e174a547beeb7d827d723a944b -r 2ccc7c44763d32e794d46e12933053c07962eb52 setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.11.dev4', + version='1.4.11.dev5', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r e6cf2e2fee5156e174a547beeb7d827d723a944b -r 2ccc7c44763d32e794d46e12933053c07962eb52 testing/code/test_source.py --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -178,10 +178,12 @@ def test_getstatementrange_triple_quoted(self): #print str(self.source) - source = Source("""''' - '''""") + source = Source("""hello(''' + ''')""") + s = source.getstatement(0) + assert s == str(source) s = source.getstatement(1) - assert eval(str(s)) + assert s == str(source) def test_getstatementrange_within_constructs(self): source = Source("""\ @@ -194,12 +196,13 @@ 42 """) assert len(source) == 7 - assert source.getstatementrange(0) == (0, 7) - assert source.getstatementrange(1) == (1, 5) + # check all lineno's that could occur in a traceback + #assert source.getstatementrange(0) == (0, 7) + #assert source.getstatementrange(1) == (1, 5) assert source.getstatementrange(2) == (2, 3) - assert source.getstatementrange(3) == (1, 5) + assert source.getstatementrange(3) == (3, 4) assert source.getstatementrange(4) == (4, 5) - assert source.getstatementrange(5) == (0, 7) + #assert source.getstatementrange(5) == (0, 7) assert source.getstatementrange(6) == (6, 7) def test_getstatementrange_bug(self): @@ -238,7 +241,7 @@ def test_getstatementrange_with_syntaxerror_issue7(self): source = Source(":") - py.test.raises(IndexError, lambda: source.getstatementrange(0)) + py.test.raises(SyntaxError, lambda: source.getstatementrange(0)) @py.test.mark.skipif("sys.version_info < (2,6)") def test_compile_to_ast(self): @@ -455,3 +458,123 @@ def __call__(self): pass py.test.raises(TypeError, lambda: py.code.Code(Hello)) + + +def getstatement(lineno, source): + from py._code.source import getstatementrange_ast + source = py.code.Source(source, deindent=False) + start, end = getstatementrange_ast(lineno, source) + return source[start:end] + +def test_oneline(): + source = getstatement(0, "raise ValueError") + assert str(source) == "raise ValueError" + +def test_oneline_and_comment(): + source = getstatement(0, "raise ValueError\n#hello") + assert str(source) == "raise ValueError" + +def XXXtest_multiline(): + source = getstatement(0, """\ +raise ValueError( + 23 +) +x = 3 +""") + assert str(source) == "raise ValueError(\n 23\n)" + +class TestTry: + source = """\ +try: + raise ValueError +except Something: + raise IndexError(1) +else: + raise KeyError() +""" + + def test_body(self): + source = getstatement(1, self.source) + assert str(source) == " raise ValueError" + + def test_except_line(self): + source = getstatement(2, self.source) + assert str(source) == "except Something:" + + def test_except_body(self): + source = getstatement(3, self.source) + assert str(source) == " raise IndexError(1)" + + def test_else(self): + source = getstatement(5, self.source) + assert str(source) == " raise KeyError()" + +class TestTryFinally: + source = """\ +try: + raise ValueError +finally: + raise IndexError(1) +""" + + def test_body(self): + source = getstatement(1, self.source) + assert str(source) == " raise ValueError" + + def test_finally(self): + source = getstatement(3, self.source) + assert str(source) == " raise IndexError(1)" + + + +class TestIf: + source = """\ +if 1: + y = 3 +elif False: + y = 5 +else: + y = 7 +""" + + def test_body(self): + source = getstatement(1, self.source) + assert str(source) == " y = 3" + + def test_elif_clause(self): + source = getstatement(2, self.source) + assert str(source) == "elif False:" + + def test_elif(self): + source = getstatement(3, self.source) + assert str(source) == " y = 5" + + def test_else(self): + source = getstatement(5, self.source) + assert str(source) == " y = 7" + +def test_semicolon(): + s = """\ +hello ; pytest.skip() +""" + source = getstatement(0, s) + assert str(source) == s.strip() + +def test_def_online(): + s = """\ +def func(): raise ValueError(42) + +def something(): + pass +""" + source = getstatement(0, s) + assert str(source) == "def func(): raise ValueError(42)" + +def XXX_test_expression_multiline(): + source = """\ +something +''' +'''""" + result = getstatement(1, source) + assert str(result) == "'''\n'''" + https://bitbucket.org/hpk42/py/changeset/2481690036ba/ changeset: 2481690036ba user: hpk42 date: 2012-10-25 11:38:25 summary: introduce an optional ASTcache passed in by exception-info printing to avoid reparsing the same module over and over affected #: 5 files diff -r 2ccc7c44763d32e794d46e12933053c07962eb52 -r 2481690036bae55d4cb613cba41490ed3839105e py/__init__.py --- a/py/__init__.py +++ b/py/__init__.py @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.11.dev5' +__version__ = '1.4.11.dev6' from py import _apipkg diff -r 2ccc7c44763d32e794d46e12933053c07962eb52 -r 2481690036bae55d4cb613cba41490ed3839105e py/_code/code.py --- a/py/_code/code.py +++ b/py/_code/code.py @@ -160,16 +160,28 @@ # on Jython this firstlineno can be -1 apparently return max(self.frame.code.firstlineno, 0) - def getsource(self): + def getsource(self, astcache=None): """ return failing source code. """ + # we use the passed in astcache to not reparse asttrees + # within exception info printing + from py._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: - _, end = source.getstatementrange(self.lineno) + 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) @@ -391,6 +403,7 @@ self.tbfilter = tbfilter self.funcargs = funcargs self.abspath = abspath + self.astcache = {} def _getindent(self, source): # figure out indent for given source @@ -408,7 +421,7 @@ return 4 + (len(s) - len(s.lstrip())) def _getentrysource(self, entry): - source = entry.getsource() + source = entry.getsource(self.astcache) if source is not None: source = source.deindent() return source diff -r 2ccc7c44763d32e794d46e12933053c07962eb52 -r 2481690036bae55d4cb613cba41490ed3839105e py/_code/source.py --- a/py/_code/source.py +++ b/py/_code/source.py @@ -111,7 +111,8 @@ """ if not (0 <= lineno < len(self)): raise IndexError("lineno out of range") - return getstatementrange_ast(lineno, self) + ast, start, end = getstatementrange_ast(lineno, self) + return start, end def deindent(self, offset=None): """ return a new source object deindented by offset. @@ -357,10 +358,10 @@ #print "returning nodelist", l return l -def getstatementrange_ast(lineno, source): - content = "\n".join(source.lines) - #print "compiling lines", len(source.lines) - astnode = compile(content, "source", "exec", 1024) # 1024 for AST +def getstatementrange_ast(lineno, source, astnode=None): + if astnode is None: + content = "\n".join(source.lines) + astnode = compile(content, "source", "exec", 1024) # 1024 for AST start, end = get_statement_startend(lineno, getnodelist(astnode)) # we need to correct the end: # - ast-parsing strips comments @@ -375,5 +376,5 @@ end -= 1 else: break - return start, end + return astnode, start, end diff -r 2ccc7c44763d32e794d46e12933053c07962eb52 -r 2481690036bae55d4cb613cba41490ed3839105e setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.11.dev5', + version='1.4.11.dev6', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 2ccc7c44763d32e794d46e12933053c07962eb52 -r 2481690036bae55d4cb613cba41490ed3839105e testing/code/test_source.py --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -463,7 +463,7 @@ def getstatement(lineno, source): from py._code.source import getstatementrange_ast source = py.code.Source(source, deindent=False) - start, end = getstatementrange_ast(lineno, source) + ast, start, end = getstatementrange_ast(lineno, source) return source[start:end] def test_oneline(): Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 25 12:08:23 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 25 Oct 2012 10:08:23 -0000 Subject: [py-svn] commit/pytest: hpk42: fix issue208 and fix issue29 - avoid long pauses in traceback printing Message-ID: <20121025100823.7851.94337@bitbucket23.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/44a7f54ec1ea/ changeset: 44a7f54ec1ea user: hpk42 date: 2012-10-25 12:08:11 summary: fix issue208 and fix issue29 - avoid long pauses in traceback printing by using the new getstatementrange() code of the py lib which uses AST-parsing rather than the previous heuristic which had O(n^2) complexity (with n = len(sourcelines)) - require new (in-dev) py version to affected #: 3 files diff -r 415781a6d022b09b081e63ce40b80ca270596e21 -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ Changes between 2.3.1 and 2.3.2.dev ----------------------------------- +- fix issue208 and fix issue29 use new py version to avoid long pauses + when printing tracebacks in long modules + - fix issue205 - conftests in subdirs customizing pytest_pycollect_makemodule and pytest_pycollect_makeitem now work properly diff -r 415781a6d022b09b081e63ce40b80ca270596e21 -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 setup.py --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ entry_points= make_entry_points(), cmdclass = {'test': PyTest}, # the following should be enabled for release - install_requires=['py>=1.4.10'], + install_requires=['py>=1.4.11.dev6'], classifiers=['Development Status :: 6 - Mature', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', diff -r 415781a6d022b09b081e63ce40b80ca270596e21 -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 tox.ini --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ indexserver= pypi = http://pypi.python.org/simple testrun = http://pypi.testrun.org - #default = http://pypi.testrun.org + default = http://pypi.testrun.org [testenv] changedir=testing @@ -84,7 +84,7 @@ [testenv:py33] deps=py>=1.4.0 - nose + :pypi:nose [testenv:jython] changedir=testing Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 25 12:42:04 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 25 Oct 2012 10:42:04 -0000 Subject: [py-svn] commit/py: hpk42: bump version to 1.4.11 Message-ID: <20121025104204.26889.4133@bitbucket25.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/acb835fc2967/ changeset: acb835fc2967 user: hpk42 date: 2012-10-25 12:11:31 summary: bump version to 1.4.11 affected #: 3 files diff -r 2481690036bae55d4cb613cba41490ed3839105e -r acb835fc29676018b2c643b83e0980ae3427fc62 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,8 @@ -Changes between 1.4.10 and 1.4.11.dev +Changes between 1.4.10 and 1.4.11 ================================================== - use _ast to determine statement ranges when printing tracebacks - - avoids multi-second delays on some large test modules + avoiding multi-second delays on some large test modules - fix an internal test to not use class-denoted pytest_funcarg__ - fix a doc link to bug tracker - try to make terminal.write() printing more robust against diff -r 2481690036bae55d4cb613cba41490ed3839105e -r acb835fc29676018b2c643b83e0980ae3427fc62 py/__init__.py --- a/py/__init__.py +++ b/py/__init__.py @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.11.dev6' +__version__ = '1.4.11' from py import _apipkg diff -r 2481690036bae55d4cb613cba41490ed3839105e -r acb835fc29676018b2c643b83e0980ae3427fc62 setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.11.dev6', + version='1.4.11', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 25 12:50:49 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 25 Oct 2012 10:50:49 -0000 Subject: [py-svn] commit/py: hpk42: fix windows writing wrt to unicode handling Message-ID: <20121025105049.16666.74797@bitbucket05.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/56d95d9647e3/ changeset: 56d95d9647e3 user: hpk42 date: 2012-10-25 12:50:05 summary: fix windows writing wrt to unicode handling affected #: 1 file diff -r acb835fc29676018b2c643b83e0980ae3427fc62 -r 56d95d9647e37a55ecdbc799f8176b62f2417706 py/_io/terminalwriter.py --- a/py/_io/terminalwriter.py +++ b/py/_io/terminalwriter.py @@ -196,8 +196,10 @@ self.write(" " * diff2last) class Win32ConsoleWriter(TerminalWriter): - def write(self, s, **kw): - if s: + def write(self, msg, **kw): + if msg: + if not isinstance(msg, (bytes, text)): + msg = text(s) oldcolors = None if self.hasmarkup and kw: handle = GetStdHandle(STD_OUTPUT_HANDLE) @@ -219,9 +221,11 @@ attr |= oldcolors & 0x0007 SetConsoleTextAttribute(handle, attr) - if not isinstance(self._file, WriteFile): - s = self._getbytestring(s) - self._file.write(s) + try: + self._file.write(msg) + except UnicodeEncodeError: + msg = msg.encode("unicode-escape").decode("ascii") + self._file.write(msg) self._file.flush() if oldcolors: SetConsoleTextAttribute(handle, oldcolors) Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 25 13:21:58 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 25 Oct 2012 11:21:58 -0000 Subject: [py-svn] commit/py: hpk42: fix python33 path handling errors Message-ID: <20121025112158.8615.82466@bitbucket05.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/fe4593263efa/ changeset: fe4593263efa user: hpk42 date: 2012-10-25 13:14:31 summary: fix python33 path handling errors affected #: 2 files diff -r 56d95d9647e37a55ecdbc799f8176b62f2417706 -r fe4593263efa10ea7ba014db6e3379e0b82368a2 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -9,6 +9,7 @@ unicodeencode/decode problems, amend according test - introduce py.builtin.text and py.builtin.bytes to point to respective str/unicode (py2) and bytes/str (py3) types +- fix error handling on win32/py33 for ENODIR Changes between 1.4.9 and 1.4.10 ================================================== diff -r 56d95d9647e37a55ecdbc799f8176b62f2417706 -r fe4593263efa10ea7ba014db6e3379e0b82368a2 py/_error.py --- a/py/_error.py +++ b/py/_error.py @@ -25,6 +25,7 @@ 17: errno.EEXIST, 13: errno.EBUSY, # empty cd drive, but ENOMEDIUM seems unavailiable 22: errno.ENOTDIR, + 20: errno.ENOTDIR, 267: errno.ENOTDIR, 5: errno.EACCES, # anything better? } @@ -63,7 +64,7 @@ return func(*args, **kwargs) except self.Error: raise - except EnvironmentError: + except (OSError, EnvironmentError): cls, value, tb = sys.exc_info() if not hasattr(value, 'errno'): raise @@ -82,5 +83,6 @@ raise value raise cls("%s%r" % (func.__name__, args)) __tracebackhide__ = True + error = ErrorMaker() Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 25 13:32:18 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 25 Oct 2012 11:32:18 -0000 Subject: [py-svn] commit/py: hpk42: Added tag 1.4.11 for changeset fe4593263efa Message-ID: <20121025113218.18478.3245@bitbucket13.managed.contegix.com> 1 new commit in py: https://bitbucket.org/hpk42/py/changeset/147f3801f9f2/ changeset: 147f3801f9f2 user: hpk42 date: 2012-10-25 13:32:09 summary: Added tag 1.4.11 for changeset fe4593263efa affected #: 1 file diff -r fe4593263efa10ea7ba014db6e3379e0b82368a2 -r 147f3801f9f2839c693dd1e460b8456cf08b2d73 .hgtags --- a/.hgtags +++ b/.hgtags @@ -40,3 +40,4 @@ f15726f9e5a67cc6221c499affa4840e9d591763 1.4.7 abfabd07a1d328f13c730e8a50d80d2e470afd3b 1.4.9 7f37ee0aff9be4b839d6759cfee336f60e8393a4 1.4.10 +fe4593263efa10ea7ba014db6e3379e0b82368a2 1.4.11 Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 25 13:33:11 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 25 Oct 2012 11:33:11 -0000 Subject: [py-svn] commit/py: 2 new changesets Message-ID: <20121025113311.29899.30224@bitbucket21.managed.contegix.com> 2 new commits in py: https://bitbucket.org/hpk42/py/changeset/da92c1e4b0c3/ changeset: da92c1e4b0c3 user: hpk42 date: 2012-10-25 13:32:42 summary: add tox.ini to distribution affected #: 1 file diff -r 147f3801f9f2839c693dd1e460b8456cf08b2d73 -r da92c1e4b0c3bc53e2e8967fb93faaa6973ba56e MANIFEST.in --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,5 +4,6 @@ include distribute_setup.py include LICENSE include conftest.py +include tox.ini graft doc graft testing https://bitbucket.org/hpk42/py/changeset/8cca692742d7/ changeset: 8cca692742d7 user: hpk42 date: 2012-10-25 13:33:05 summary: add tox.ini to distribution affected #: 1 file diff -r da92c1e4b0c3bc53e2e8967fb93faaa6973ba56e -r 8cca692742d7c89697b25aaac7ee7dd9ff5343fc CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +Changes between 1.4.11 and 1.4.12.dev +================================================== + +- add tox.ini to distribution + Changes between 1.4.10 and 1.4.11 ================================================== Repository URL: https://bitbucket.org/hpk42/py/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 25 14:13:53 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 25 Oct 2012 12:13:53 -0000 Subject: [py-svn] commit/pytest: 3 new changesets Message-ID: <20121025121353.19350.28812@bitbucket25.managed.contegix.com> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/8738b828dec5/ changeset: 8738b828dec5 user: hpk42 date: 2012-10-25 13:48:31 summary: bump version to 2.3.2, regen docs and changelog affected #: 23 files diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,4 @@ -Changes between 2.3.1 and 2.3.2.dev +Changes between 2.3.1 and 2.3.2 ----------------------------------- - fix issue208 and fix issue29 use new py version to avoid long pauses diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.2.dev9' +__version__ = '2.3.2' diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/announce/index.txt --- a/doc/en/announce/index.txt +++ b/doc/en/announce/index.txt @@ -5,6 +5,7 @@ .. toctree:: :maxdepth: 2 + release-2.3.2 release-2.3.1 release-2.3.0 release-2.2.4 diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/assert.txt --- a/doc/en/assert.txt +++ b/doc/en/assert.txt @@ -26,7 +26,7 @@ $ py.test test_assert1.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 1 items test_assert1.py F @@ -110,7 +110,7 @@ $ py.test test_assert2.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 1 items test_assert2.py F diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/capture.txt --- a/doc/en/capture.txt +++ b/doc/en/capture.txt @@ -64,7 +64,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 2 items test_module.py .F @@ -78,7 +78,7 @@ test_module.py:9: AssertionError ----------------------------- Captured stdout ------------------------------ - setting up + setting up ==================== 1 failed, 1 passed in 0.01 seconds ==================== Accessing captured output from a test function diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.3.2.dev9" +version = release = "2.4.2" import sys, os @@ -53,7 +53,7 @@ # General information about the project. project = u'pytest' -copyright = u'2011, holger krekel et alii' +copyright = u'2012, holger krekel' @@ -197,7 +197,7 @@ # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('contents', 'pytest.tex', u'pytest Documentation', - u'holger krekel et alii', 'manual'), + u'holger krekel, http://merlinux.eu', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -239,7 +239,7 @@ epub_title = u'pytest' epub_author = u'holger krekel at merlinux eu' epub_publisher = u'holger krekel at merlinux eu' -epub_copyright = u'2011, holger krekel et alii' +epub_copyright = u'2012, holger krekel et alii' # The language of the text. It defaults to the language option # or en if the language is not set. diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/doctest.txt --- a/doc/en/doctest.txt +++ b/doc/en/doctest.txt @@ -44,7 +44,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 1 items mymodule.py . diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -26,25 +26,25 @@ $ py.test -v -m webtest =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:3: test_send_http PASSED =================== 1 tests deselected by "-m 'webtest'" =================== - ================== 1 passed, 1 deselected in 0.00 seconds ================== + ================== 1 passed, 1 deselected in 0.01 seconds ================== Or the inverse, running all tests except the webtest ones:: $ py.test -v -m "not webtest" =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_server.py:6: test_something_quick PASSED ================= 1 tests deselected by "-m 'not webtest'" ================= - ================== 1 passed, 1 deselected in 0.00 seconds ================== + ================== 1 passed, 1 deselected in 0.01 seconds ================== Registering markers ------------------------------------- @@ -145,7 +145,7 @@ $ py.test -k send_http # running with the above defined examples =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 4 items test_server.py . @@ -157,7 +157,7 @@ $ py.test -k-send_http =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 4 items test_mark_classlevel.py .. @@ -170,7 +170,7 @@ $ py.test -kTestClass =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 4 items test_mark_classlevel.py .. @@ -223,23 +223,23 @@ $ py.test -E stage2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 1 items test_someenv.py s - ======================== 1 skipped in 0.00 seconds ========================= + ======================== 1 skipped in 0.01 seconds ========================= and here is one that specifies exactly the environment needed:: $ py.test -E stage1 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 1 items test_someenv.py . - ========================= 1 passed in 0.00 seconds ========================= + ========================= 1 passed in 0.01 seconds ========================= The ``--markers`` option always gives you a list of available markers:: @@ -351,12 +351,12 @@ $ py.test -rs # this option reports skip reasons =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 4 items test_plat.py s.s. ========================= short test summary info ========================== - SKIP [2] /tmp/doc-exec-592/conftest.py:12: cannot run on platform linux2 + SKIP [2] /tmp/doc-exec-99/conftest.py:12: cannot run on platform linux2 =================== 2 passed, 2 skipped in 0.01 seconds ==================== @@ -364,7 +364,7 @@ $ py.test -m linux2 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 4 items test_plat.py . diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/example/nonpython.txt --- a/doc/en/example/nonpython.txt +++ b/doc/en/example/nonpython.txt @@ -27,7 +27,7 @@ nonpython $ py.test test_simple.yml =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 2 items test_simple.yml .F @@ -56,7 +56,7 @@ nonpython $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_simple.yml:1: usecase: ok PASSED @@ -74,7 +74,7 @@ nonpython $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 2 items diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/example/parametrize.txt --- a/doc/en/example/parametrize.txt +++ b/doc/en/example/parametrize.txt @@ -104,7 +104,7 @@ $ py.test test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 4 items test_scenarios.py .... @@ -116,7 +116,7 @@ $ py.test --collectonly test_scenarios.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 4 items @@ -180,7 +180,7 @@ $ py.test test_backends.py --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 2 items @@ -195,7 +195,7 @@ ================================= FAILURES ================================= _________________________ test_db_initialized[d2] __________________________ - db = + db = def test_db_initialized(db): # a dummy test @@ -250,7 +250,7 @@ ================================= FAILURES ================================= ________________________ TestClass.test_equals[1-2] ________________________ - self = , a = 1, b = 2 + self = , a = 1, b = 2 def test_equals(self, a, b): > assert a == b diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/example/pythoncollection.txt --- a/doc/en/example/pythoncollection.txt +++ b/doc/en/example/pythoncollection.txt @@ -43,7 +43,7 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 2 items @@ -51,7 +51,7 @@ - ============================= in 0.00 seconds ============================= + ============================= in 0.01 seconds ============================= Interpreting cmdline arguments as Python packages ----------------------------------------------------- @@ -82,7 +82,7 @@ . $ py.test --collectonly pythoncollection.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 3 items @@ -135,7 +135,7 @@ $ py.test --collectonly =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 1 items diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/example/reportingdemo.txt --- a/doc/en/example/reportingdemo.txt +++ b/doc/en/example/reportingdemo.txt @@ -13,7 +13,7 @@ assertion $ py.test failure_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 39 items failure_demo.py FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF @@ -30,7 +30,7 @@ failure_demo.py:15: AssertionError _________________________ TestFailing.test_simple __________________________ - self = + self = def test_simple(self): def f(): @@ -40,13 +40,13 @@ > assert f() == g() E assert 42 == 43 - E + where 42 = () - E + and 43 = () + E + where 42 = () + E + and 43 = () failure_demo.py:28: AssertionError ____________________ TestFailing.test_simple_multiline _____________________ - self = + self = def test_simple_multiline(self): otherfunc_multi( @@ -66,19 +66,19 @@ failure_demo.py:11: AssertionError ___________________________ TestFailing.test_not ___________________________ - self = + self = def test_not(self): def f(): return 42 > assert not f() E assert not 42 - E + where 42 = () + E + where 42 = () failure_demo.py:38: AssertionError _________________ TestSpecialisedExplanations.test_eq_text _________________ - self = + self = def test_eq_text(self): > assert 'spam' == 'eggs' @@ -89,7 +89,7 @@ failure_demo.py:42: AssertionError _____________ TestSpecialisedExplanations.test_eq_similar_text _____________ - self = + self = def test_eq_similar_text(self): > assert 'foo 1 bar' == 'foo 2 bar' @@ -102,7 +102,7 @@ failure_demo.py:45: AssertionError ____________ TestSpecialisedExplanations.test_eq_multiline_text ____________ - self = + self = def test_eq_multiline_text(self): > assert 'foo\nspam\nbar' == 'foo\neggs\nbar' @@ -115,7 +115,7 @@ failure_demo.py:48: AssertionError ______________ TestSpecialisedExplanations.test_eq_long_text _______________ - self = + self = def test_eq_long_text(self): a = '1'*100 + 'a' + '2'*100 @@ -132,7 +132,7 @@ failure_demo.py:53: AssertionError _________ TestSpecialisedExplanations.test_eq_long_text_multiline __________ - self = + self = def test_eq_long_text_multiline(self): a = '1\n'*100 + 'a' + '2\n'*100 @@ -156,7 +156,7 @@ failure_demo.py:58: AssertionError _________________ TestSpecialisedExplanations.test_eq_list _________________ - self = + self = def test_eq_list(self): > assert [0, 1, 2] == [0, 1, 3] @@ -166,7 +166,7 @@ failure_demo.py:61: AssertionError ______________ TestSpecialisedExplanations.test_eq_list_long _______________ - self = + self = def test_eq_list_long(self): a = [0]*100 + [1] + [3]*100 @@ -178,7 +178,7 @@ failure_demo.py:66: AssertionError _________________ TestSpecialisedExplanations.test_eq_dict _________________ - self = + self = def test_eq_dict(self): > assert {'a': 0, 'b': 1} == {'a': 0, 'b': 2} @@ -191,7 +191,7 @@ failure_demo.py:69: AssertionError _________________ TestSpecialisedExplanations.test_eq_set __________________ - self = + self = def test_eq_set(self): > assert set([0, 10, 11, 12]) == set([0, 20, 21]) @@ -207,7 +207,7 @@ failure_demo.py:72: AssertionError _____________ TestSpecialisedExplanations.test_eq_longer_list ______________ - self = + self = def test_eq_longer_list(self): > assert [1,2] == [1,2,3] @@ -217,7 +217,7 @@ failure_demo.py:75: AssertionError _________________ TestSpecialisedExplanations.test_in_list _________________ - self = + self = def test_in_list(self): > assert 1 in [0, 2, 3, 4, 5] @@ -226,7 +226,7 @@ failure_demo.py:78: AssertionError __________ TestSpecialisedExplanations.test_not_in_text_multiline __________ - self = + self = def test_not_in_text_multiline(self): text = 'some multiline\ntext\nwhich\nincludes foo\nand a\ntail' @@ -244,7 +244,7 @@ failure_demo.py:82: AssertionError ___________ TestSpecialisedExplanations.test_not_in_text_single ____________ - self = + self = def test_not_in_text_single(self): text = 'single foo line' @@ -257,7 +257,7 @@ failure_demo.py:86: AssertionError _________ TestSpecialisedExplanations.test_not_in_text_single_long _________ - self = + self = def test_not_in_text_single_long(self): text = 'head ' * 50 + 'foo ' + 'tail ' * 20 @@ -270,7 +270,7 @@ failure_demo.py:90: AssertionError ______ TestSpecialisedExplanations.test_not_in_text_single_long_term _______ - self = + self = def test_not_in_text_single_long_term(self): text = 'head ' * 50 + 'f'*70 + 'tail ' * 20 @@ -289,7 +289,7 @@ i = Foo() > assert i.b == 2 E assert 1 == 2 - E + where 1 = .b + E + where 1 = .b failure_demo.py:101: AssertionError _________________________ test_attribute_instance __________________________ @@ -299,8 +299,8 @@ b = 1 > assert Foo().b == 2 E assert 1 == 2 - E + where 1 = .b - E + where = () + E + where 1 = .b + E + where = () failure_demo.py:107: AssertionError __________________________ test_attribute_failure __________________________ @@ -316,7 +316,7 @@ failure_demo.py:116: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ - self = + self = def _get_b(self): > raise Exception('Failed to get attrib') @@ -332,15 +332,15 @@ b = 2 > assert Foo().b == Bar().b E assert 1 == 2 - E + where 1 = .b - E + where = () - E + and 2 = .b - E + where = () + E + where 1 = .b + E + where = () + E + and 2 = .b + E + where = () failure_demo.py:124: AssertionError __________________________ TestRaises.test_raises __________________________ - self = + self = def test_raises(self): s = 'qwe' @@ -352,10 +352,10 @@ > int(s) E ValueError: invalid literal for int() with base 10: 'qwe' - <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:850>:1: ValueError + <0-codegen /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:851>:1: ValueError ______________________ TestRaises.test_raises_doesnt _______________________ - self = + self = def test_raises_doesnt(self): > raises(IOError, "int('3')") @@ -364,7 +364,7 @@ failure_demo.py:136: Failed __________________________ TestRaises.test_raise ___________________________ - self = + self = def test_raise(self): > raise ValueError("demo error") @@ -373,7 +373,7 @@ failure_demo.py:139: ValueError ________________________ TestRaises.test_tupleerror ________________________ - self = + self = def test_tupleerror(self): > a,b = [1] @@ -382,7 +382,7 @@ failure_demo.py:142: ValueError ______ TestRaises.test_reinterpret_fails_with_print_for_the_fun_of_it ______ - self = + self = def test_reinterpret_fails_with_print_for_the_fun_of_it(self): l = [1,2,3] @@ -395,7 +395,7 @@ l is [1, 2, 3] ________________________ TestRaises.test_some_error ________________________ - self = + self = def test_some_error(self): > if namenotexi: @@ -423,7 +423,7 @@ <2-codegen 'abc-123' /home/hpk/p/pytest/doc/en/example/assertion/failure_demo.py:162>:2: AssertionError ____________________ TestMoreErrors.test_complex_error _____________________ - self = + self = def test_complex_error(self): def f(): @@ -452,7 +452,7 @@ failure_demo.py:5: AssertionError ___________________ TestMoreErrors.test_z1_unpack_error ____________________ - self = + self = def test_z1_unpack_error(self): l = [] @@ -462,7 +462,7 @@ failure_demo.py:179: ValueError ____________________ TestMoreErrors.test_z2_type_error _____________________ - self = + self = def test_z2_type_error(self): l = 3 @@ -472,19 +472,19 @@ failure_demo.py:183: TypeError ______________________ TestMoreErrors.test_startswith ______________________ - self = + self = def test_startswith(self): s = "123" g = "456" > assert s.startswith(g) - E assert ('456') - E + where = '123'.startswith + E assert ('456') + E + where = '123'.startswith failure_demo.py:188: AssertionError __________________ TestMoreErrors.test_startswith_nested ___________________ - self = + self = def test_startswith_nested(self): def f(): @@ -492,15 +492,15 @@ def g(): return "456" > assert f().startswith(g()) - E assert ('456') - E + where = '123'.startswith - E + where '123' = () - E + and '456' = () + E assert ('456') + E + where = '123'.startswith + E + where '123' = () + E + and '456' = () failure_demo.py:195: AssertionError _____________________ TestMoreErrors.test_global_func ______________________ - self = + self = def test_global_func(self): > assert isinstance(globf(42), float) @@ -510,18 +510,18 @@ failure_demo.py:198: AssertionError _______________________ TestMoreErrors.test_instance _______________________ - self = + self = def test_instance(self): self.x = 6*7 > assert self.x != 42 E assert 42 != 42 - E + where 42 = .x + E + where 42 = .x failure_demo.py:202: AssertionError _______________________ TestMoreErrors.test_compare ________________________ - self = + self = def test_compare(self): > assert globf(10) < 5 @@ -531,7 +531,7 @@ failure_demo.py:205: AssertionError _____________________ TestMoreErrors.test_try_finally ______________________ - self = + self = def test_try_finally(self): x = 1 @@ -540,4 +540,4 @@ E assert 1 == 0 failure_demo.py:210: AssertionError - ======================== 39 failed in 0.16 seconds ========================= + ======================== 39 failed in 0.21 seconds ========================= diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/example/simple.txt --- a/doc/en/example/simple.txt +++ b/doc/en/example/simple.txt @@ -106,7 +106,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 0 items ============================= in 0.00 seconds ============================= @@ -150,12 +150,12 @@ $ py.test -rs # "-rs" means report details on the little 's' =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 2 items test_module.py .s ========================= short test summary info ========================== - SKIP [1] /tmp/doc-exec-597/conftest.py:9: need --runslow option to run + SKIP [1] /tmp/doc-exec-104/conftest.py:9: need --runslow option to run =================== 1 passed, 1 skipped in 0.01 seconds ==================== @@ -163,7 +163,7 @@ $ py.test --runslow =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 2 items test_module.py .. @@ -253,7 +253,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 project deps: mylib-1.1 collected 0 items @@ -276,7 +276,7 @@ $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 -- /home/hpk/p/pytest/.tox/regen/bin/python info1: did you know that ... did you? collecting ... collected 0 items @@ -287,7 +287,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 0 items ============================= in 0.00 seconds ============================= @@ -319,7 +319,7 @@ $ py.test --durations=3 =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 3 items test_some_are_slow.py ... @@ -327,7 +327,7 @@ ========================= 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_funcfast + 0.00s call test_some_are_slow.py::test_funcfast ========================= 3 passed in 0.31 seconds ========================= incremental testing - test steps @@ -380,7 +380,7 @@ $ py.test -rx =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 4 items test_step.py .Fx. @@ -388,7 +388,7 @@ ================================= FAILURES ================================= ____________________ TestUserHandling.test_modification ____________________ - self = + self = def test_modification(self): > assert 0 diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -30,11 +30,10 @@ to configuration and component options, or to re-use fixtures across class, module or whole test session scopes. -In addition, pytest continues to support :ref:`xunitsetup` which it -originally introduced in 2005. You can mix both styles, moving -incrementally from classic to new style, if you prefer. You can also -start out from existing :ref:`unittest.TestCase style ` -or :ref:`nose based ` projects. +In addition, pytest continues to support :ref:`xunitsetup`. You can mix +both styles, moving incrementally from classic to new style, as you +prefer. You can also start out from existing :ref:`unittest.TestCase +style ` or :ref:`nose based ` projects. .. _`funcargs`: .. _`funcarg mechanism`: @@ -72,7 +71,7 @@ $ py.test test_smtpsimple.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 1 items test_smtpsimple.py F @@ -80,7 +79,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response, msg = smtp.ehlo() @@ -90,7 +89,7 @@ E assert 0 test_smtpsimple.py:12: AssertionError - ========================= 1 failed in 0.12 seconds ========================= + ========================= 1 failed in 0.16 seconds ========================= In the failure traceback we see that the test function was called with a ``smtp`` argument, the ``smtplib.SMTP()`` instance created by the fixture @@ -190,7 +189,7 @@ $ py.test test_module.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 2 items test_module.py FF @@ -198,7 +197,7 @@ ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -210,7 +209,7 @@ test_module.py:6: AssertionError ________________________________ test_noop _________________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -219,7 +218,7 @@ E assert 0 test_module.py:11: AssertionError - ========================= 2 failed in 0.13 seconds ========================= + ========================= 2 failed in 0.17 seconds ========================= You see the two ``assert 0`` failing and more importantly you can also see that the same (module-scoped) ``smtp`` object was passed into the two @@ -272,7 +271,7 @@ $ py.test -s -q --tb=no FF - finalizing + finalizing We see that the ``smtp`` instance is finalized after the two tests using it tests executed. If we had specified ``scope='function'`` @@ -343,7 +342,7 @@ ================================= FAILURES ================================= __________________________ test_ehlo[merlinux.eu] __________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -355,7 +354,7 @@ test_module.py:6: AssertionError __________________________ test_noop[merlinux.eu] __________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -366,7 +365,7 @@ test_module.py:11: AssertionError ________________________ test_ehlo[mail.python.org] ________________________ - smtp = + smtp = def test_ehlo(smtp): response = smtp.ehlo() @@ -377,7 +376,7 @@ test_module.py:5: AssertionError ________________________ test_noop[mail.python.org] ________________________ - smtp = + smtp = def test_noop(smtp): response = smtp.noop() @@ -425,13 +424,13 @@ $ py.test -v test_appsetup.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 2 items test_appsetup.py:12: test_smtp_exists[merlinux.eu] PASSED test_appsetup.py:12: test_smtp_exists[mail.python.org] PASSED - ========================= 2 passed in 0.20 seconds ========================= + ========================= 2 passed in 5.45 seconds ========================= Due to the parametrization of ``smtp`` the test will run twice with two different ``App`` instances and respective smtp servers. There is no @@ -490,7 +489,7 @@ $ py.test -v -s test_module.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 -- /home/hpk/p/pytest/.tox/regen/bin/python + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 -- /home/hpk/p/pytest/.tox/regen/bin/python collecting ... collected 8 items test_module.py:16: test_0[1] PASSED diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/getting-started.txt --- a/doc/en/getting-started.txt +++ b/doc/en/getting-started.txt @@ -23,7 +23,7 @@ To check your installation has installed the correct version:: $ py.test --version - This is py.test version 2.3.1, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc + This is py.test version 2.3.2, imported from /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/pytest.pyc If you get an error checkout :ref:`installation issues`. @@ -45,7 +45,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 1 items test_sample.py F @@ -122,7 +122,7 @@ ================================= FAILURES ================================= ____________________________ TestClass.test_two ____________________________ - self = + self = def test_two(self): x = "hello" @@ -157,7 +157,7 @@ ================================= FAILURES ================================= _____________________________ test_needsfiles ______________________________ - tmpdir = local('/tmp/pytest-1516/test_needsfiles0') + tmpdir = local('/tmp/pytest-917/test_needsfiles0') def test_needsfiles(tmpdir): print tmpdir @@ -166,7 +166,7 @@ test_tmpdir.py:3: AssertionError ----------------------------- Captured stdout ------------------------------ - /tmp/pytest-1516/test_needsfiles0 + /tmp/pytest-917/test_needsfiles0 Before the test runs, a unique-per-test-invocation temporary directory was created. More info at :ref:`tmpdir handling`. diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/index.txt --- a/doc/en/index.txt +++ b/doc/en/index.txt @@ -34,8 +34,9 @@ **integrates many common testing methods**: - - can run many ``nose``, ``unittest.py`` and ``doctest.py`` style - tests, including running testcases made for Django and trial + - multi-paradigm: pytest can run many ``nose``, ``unittest.py`` and + ``doctest.py`` style test suites, including running testcases made for + Django and trial - supports :ref:`good integration practises ` - supports extended :ref:`xUnit style setup ` - supports domain-specific :ref:`non-python tests` diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/parametrize.txt --- a/doc/en/parametrize.txt +++ b/doc/en/parametrize.txt @@ -53,7 +53,7 @@ $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 3 items test_expectation.py ..F @@ -135,8 +135,8 @@ def test_valid_string(stringinput): > assert stringinput.isalpha() - E assert () - E + where = '!'.isalpha + E assert () + E + where = '!'.isalpha test_strings.py:3: AssertionError @@ -149,7 +149,7 @@ $ py.test -q -rs test_strings.py s ========================= short test summary info ========================== - SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:959: got empty parameter set, function test_valid_string at /tmp/doc-exec-564/test_strings.py:1 + SKIP [1] /home/hpk/p/pytest/.tox/regen/local/lib/python2.7/site-packages/_pytest/python.py:960: got empty parameter set, function test_valid_string at /tmp/doc-exec-69/test_strings.py:1 For further examples, you might want to look at :ref:`more parametrization examples `. diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/skipping.txt --- a/doc/en/skipping.txt +++ b/doc/en/skipping.txt @@ -132,7 +132,7 @@ example $ py.test -rx xfail_demo.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 6 items xfail_demo.py xxxxxx @@ -149,7 +149,7 @@ XFAIL xfail_demo.py::test_hello6 reason: reason - ======================== 6 xfailed in 0.03 seconds ========================= + ======================== 6 xfailed in 0.04 seconds ========================= .. _`evaluation of skipif/xfail conditions`: diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/tmpdir.txt --- a/doc/en/tmpdir.txt +++ b/doc/en/tmpdir.txt @@ -29,7 +29,7 @@ $ py.test test_tmpdir.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 1 items test_tmpdir.py F @@ -37,7 +37,7 @@ ================================= FAILURES ================================= _____________________________ test_create_file _____________________________ - tmpdir = local('/tmp/pytest-1517/test_create_file0') + tmpdir = local('/tmp/pytest-918/test_create_file0') def test_create_file(tmpdir): p = tmpdir.mkdir("sub").join("hello.txt") diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/unittest.txt --- a/doc/en/unittest.txt +++ b/doc/en/unittest.txt @@ -88,7 +88,7 @@ $ py.test test_unittest_db.py =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.1 + platform linux2 -- Python 2.7.3 -- pytest-2.3.2 collected 2 items test_unittest_db.py FF @@ -101,7 +101,7 @@ def test_method1(self): assert hasattr(self, "db") > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:9: AssertionError ___________________________ MyTest.test_method2 ____________________________ @@ -110,7 +110,7 @@ def test_method2(self): > assert 0, self.db # fail for demo purposes - E AssertionError: + E AssertionError: test_unittest_db.py:12: AssertionError ========================= 2 failed in 0.02 seconds ========================= diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 doc/en/xunit_setup.txt --- a/doc/en/xunit_setup.txt +++ b/doc/en/xunit_setup.txt @@ -5,13 +5,16 @@ classic xunit-style setup ======================================== -.. note:: - - This section describes the classic way how you can implement setup and - teardown on a per-module/class/function basis. It remains fully - supported but it is recommended to rather use the more flexible, - more modular and more scalable :ref:`fixture functions - ` for implementing for fixing test state for your tests. +This section describes a classic and popular way how you can implement +fixtures (setup and teardown test state) on a per-module/class/function basis. +pytest started supporting these methods around 2005 and subsequently +nose and the standard library introduced them (under slightly different +names). While these setup/teardown methods are and will remain fully +supported you may also use pytest's more powerful :ref:`fixture mechanism +` which leverages the concept of dependency injection, allowing +for a more modular and more scalable approach for managing test state, +especially for larger projects and for functional testing. It is safe +to mix both fixture mechanisms. Module level setup/teardown -------------------------------------- diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.2.dev9', + version='2.3.2', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -33,7 +33,7 @@ entry_points= make_entry_points(), cmdclass = {'test': PyTest}, # the following should be enabled for release - install_requires=['py>=1.4.11.dev6'], + install_requires=['py>=1.4.11'], classifiers=['Development Status :: 6 - Mature', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', diff -r 44a7f54ec1ea7a15d6afd96bd9fa5f1fa6e698e6 -r 8738b828dec53937765db71951ef955cca4c51f6 tox.ini --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ indexserver= pypi = http://pypi.python.org/simple testrun = http://pypi.testrun.org - default = http://pypi.testrun.org + #default = http://pypi.testrun.org [testenv] changedir=testing @@ -16,7 +16,6 @@ [testenv:genscript] changedir=. commands= py.test --genscript=pytest1 -deps=py>=1.4.0 [testenv:py27-xdist] changedir=. @@ -41,7 +40,6 @@ basepython=python2.6 deps=:pypi:twisted :pypi:pexpect - py>=1.4.5.dev1 commands= py.test -rsxf testing/test_unittest.py \ --junitxml={envlogdir}/junit-{envname}.xml {posargs:testing/test_unittest.py} https://bitbucket.org/hpk42/pytest/changeset/b0d79ce164d0/ changeset: b0d79ce164d0 user: hpk42 date: 2012-10-25 14:13:06 summary: Added tag 2.3.2 for changeset 8738b828dec5 affected #: 1 file diff -r 8738b828dec53937765db71951ef955cca4c51f6 -r b0d79ce164d0fdaffa9224afb83eae3508152de2 .hgtags --- a/.hgtags +++ b/.hgtags @@ -51,3 +51,4 @@ ad9fe504a371ad8eb613052d58f229aa66f53527 2.2.4 c27a60097767c16a54ae56d9669a77925b213b9b 2.3.0 acf0e1477fb19a1d35a4e40242b77fa6af32eb17 2.3.1 +8738b828dec53937765db71951ef955cca4c51f6 2.3.2 https://bitbucket.org/hpk42/pytest/changeset/49bdddc2ef8d/ changeset: 49bdddc2ef8d user: hpk42 date: 2012-10-25 14:13:43 summary: add release announce affected #: 1 file diff -r b0d79ce164d0fdaffa9224afb83eae3508152de2 -r 49bdddc2ef8df63fdc33a29c9168937a4fe0b3a1 doc/en/announce/release-2.3.2.txt --- /dev/null +++ b/doc/en/announce/release-2.3.2.txt @@ -0,0 +1,57 @@ +pytest-2.3.2: some fixes and more traceback-printing speed +=========================================================================== + +pytest-2.3.2 is a another stabilization release: + +- issue 205: fixes a regression with conftest detection +- issue 208/29: fixes traceback-printing speed in some bad cases +- fix teardown-ordering for parametrized setups +- fix unittest and trial compat behaviour with respect to runTest() methods +- issue 206 and others: some improvements to packaging +- fix issue127 and others: improve some docs + +See + + http://pytest.org/ + +for general information. To install or upgrade pytest: + + pip install -U pytest # or + easy_install -U pytest + +best, +holger krekel + + +Changes between 2.3.1 and 2.3.2 +----------------------------------- + +- fix issue208 and fix issue29 use new py version to avoid long pauses + when printing tracebacks in long modules + +- fix issue205 - conftests in subdirs customizing + pytest_pycollect_makemodule and pytest_pycollect_makeitem + now work properly + +- fix teardown-ordering for parametrized setups + +- fix issue127 - better documentation for pytest_addoption + and related objects. + +- fix unittest behaviour: TestCase.runtest only called if there are + test methods defined + +- improve trial support: don't collect its empty + unittest.TestCase.runTest() method + +- "python setup.py test" now works with pytest itself + +- fix/improve internal/packaging related bits: + + - exception message check of test_nose.py now passes on python33 as well + + - issue206 - fix test_assertrewrite.py to work when a global + PYTHONDONTWRITEBYTECODE=1 is present + + - add tox.ini to pytest distribution so that ignore-dirs and others config + bits are properly distributed for maintainers who run pytest-own tests Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu Oct 25 15:24:07 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 25 Oct 2012 13:24:07 -0000 Subject: [py-svn] commit/pytest-pep8: 2 new changesets Message-ID: <20121025132407.27226.50413@bitbucket22.managed.contegix.com> 2 new commits in pytest-pep8: https://bitbucket.org/hpk42/pytest-pep8/changeset/ee2bdf096449/ changeset: ee2bdf096449 user: hpk42 date: 2012-10-18 13:51:51 summary: pytest-2.3 will not have a .markers but the old backward-compat rw-keywords attribute affected #: 1 file diff -r e75a84750400eff3732ae3008eeefc6d42c151b9 -r ee2bdf0964490f42f036b3418c48635a160c1e23 pytest_pep8.py --- a/pytest_pep8.py +++ b/pytest_pep8.py @@ -51,10 +51,7 @@ def __init__(self, path, parent, pep8ignore, max_line_length): super(Pep8Item, self).__init__(path, parent) - if hasattr(self, "markers"): - self.markers.pep8 = pytest.mark.pep8 - else: - self.keywords["pep8"] = pytest.mark.pep8 + self.keywords["pep8"] = pytest.mark.pep8 self.pep8ignore = pep8ignore self.max_line_length = max_line_length https://bitbucket.org/hpk42/pytest-pep8/changeset/47c4298aedfd/ changeset: 47c4298aedfd user: hpk42 date: 2012-10-25 15:24:00 summary: add MANIFEST.in to include test and tox.ini affected #: 2 files diff -r ee2bdf0964490f42f036b3418c48635a160c1e23 -r 47c4298aedfd090458d914c80c35a6ad3dfce991 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ - added pep8maxlinelength ini setting to configure the maximum allowed line length. +- add tox.ini and test_pep8.py to distribution 1.0.2 ---------------------------------------------- diff -r ee2bdf0964490f42f036b3418c48635a160c1e23 -r 47c4298aedfd090458d914c80c35a6ad3dfce991 MANIFEST.in --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,8 @@ +include CHANGELOG +include README.txt +include setup.py +include tox.ini +include LICENSE +include test_pep8.py +graft doc +graft test_pep8.py Repository URL: https://bitbucket.org/hpk42/pytest-pep8/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Oct 28 10:13:44 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 28 Oct 2012 09:13:44 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20121028091344.9018.59027@bitbucket20.managed.contegix.com> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/375658534add/ changeset: 375658534add user: hpk42 date: 2012-10-28 10:12:36 summary: remove unused code affected #: 1 file diff -r 49bdddc2ef8df63fdc33a29c9168937a4fe0b3a1 -r 375658534addf02aaf54eb21147c160f23fdbdd2 _pytest/config.py --- a/_pytest/config.py +++ b/_pytest/config.py @@ -477,14 +477,3 @@ return iniconfig['pytest'] return {} -def findupwards(current, basename): - current = py.path.local(current) - while 1: - p = current.join(basename) - if p.check(): - return p - p = current.dirpath() - if p == current: - return - current = p - https://bitbucket.org/hpk42/pytest/changeset/253d5159436a/ changeset: 253d5159436a user: hpk42 date: 2012-10-28 10:13:37 summary: fix wrong document version on pytest.org (closes #210) affected #: 1 file diff -r 375658534addf02aaf54eb21147c160f23fdbdd2 -r 253d5159436ad40955dabf59740e402476e3935c doc/en/conf.py --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -17,7 +17,7 @@ # # The full version, including alpha/beta/rc tags. # The short X.Y version. -version = release = "2.4.2" +version = release = "2.3.2" import sys, os Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Oct 28 11:26:00 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 28 Oct 2012 10:26:00 -0000 Subject: [py-svn] commit/pytest: hpk42: i think "helps you write better programs" fits better than "makes" Message-ID: <20121028102600.9018.41967@bitbucket20.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/5de7185c2891/ changeset: 5de7185c2891 user: hpk42 date: 2012-10-28 11:25:53 summary: i think "helps you write better programs" fits better than "makes" affected #: 1 file diff -r 253d5159436ad40955dabf59740e402476e3935c -r 5de7185c2891e69ff23594c80682b46536d43d68 doc/en/index.txt --- a/doc/en/index.txt +++ b/doc/en/index.txt @@ -1,7 +1,7 @@ .. _features: -pytest: makes you write better programs +pytest: helps you write better programs ============================================= **a mature full-featured Python testing tool** Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Oct 28 14:53:51 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 28 Oct 2012 13:53:51 -0000 Subject: [py-svn] commit/pytest: hpk42: re-allow to parametrize with values that don't support __eq__ (closes issue213) Message-ID: <20121028135351.21883.33266@bitbucket23.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/239a30027f7a/ changeset: 239a30027f7a user: hpk42 date: 2012-10-28 14:52:43 summary: re-allow to parametrize with values that don't support __eq__ (closes issue213) affected #: 3 files diff -r 5de7185c2891e69ff23594c80682b46536d43d68 -r 239a30027f7a83cf62ae24fef26fe9d97120bf3e CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,9 @@ +Changes between 2.3.2 and 2.3.3.dev +----------------------------------- + +- fix issue213 - allow to parametrize with values like numpy arrays that + do not support an __eq__ operator + Changes between 2.3.1 and 2.3.2 ----------------------------------- diff -r 5de7185c2891e69ff23594c80682b46536d43d68 -r 239a30027f7a83cf62ae24fef26fe9d97120bf3e _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -598,7 +598,7 @@ if valtype == "funcargs": self.params[arg] = id self._arg2scopenum[arg] = scopenum - if val == _notexists: + if val is _notexists: self._emptyparamspecified = True self._idlist.append(id) diff -r 5de7185c2891e69ff23594c80682b46536d43d68 -r 239a30027f7a83cf62ae24fef26fe9d97120bf3e testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -313,6 +313,19 @@ reprec = testdir.inline_run() reprec.assertoutcome(skipped=1) + def test_issue213_parametrize_value_no_equal(self, testdir): + testdir.makepyfile(""" + import pytest + class A: + def __eq__(self, other): + raise ValueError("not possible") + @pytest.mark.parametrize('arg', [A()]) + def test_function(arg): + assert arg.__class__.__name__ == "A" + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) + def test_function_equality_with_callspec(self, testdir, tmpdir): items = testdir.getitems(""" import pytest Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun Oct 28 14:55:16 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sun, 28 Oct 2012 13:55:16 -0000 Subject: [py-svn] commit/pytest: hpk42: fix wrong reference in basic fixture example, thanks for reporting! (closes #212) Message-ID: <20121028135516.13214.30302@bitbucket02.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/678655781ea3/ changeset: 678655781ea3 user: hpk42 date: 2012-10-28 14:54:49 summary: fix wrong reference in basic fixture example, thanks for reporting! (closes #212) affected #: 1 file diff -r 239a30027f7a83cf62ae24fef26fe9d97120bf3e -r 678655781ea359b14d96829c205322db47018239 doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -65,7 +65,7 @@ assert "merlinux" in msg assert 0 # for demo purposes -Here, the ``test_function`` needs the ``smtp`` fixture value. pytest +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:: Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Oct 31 17:00:52 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 31 Oct 2012 16:00:52 -0000 Subject: [py-svn] commit/pytest: RonnyPfannschmidt: fix issue 214 - gracefully handle proxy objects that look like fixtures Message-ID: <20121031160052.20475.49536@bitbucket12.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/changeset/08f2675ec7d4/ changeset: 08f2675ec7d4 user: RonnyPfannschmidt date: 2012-10-31 17:00:43 summary: fix issue 214 - gracefully handle proxy objects that look like fixtures affected #: 3 files diff -r 678655781ea359b14d96829c205322db47018239 -r 08f2675ec7d4efc28f101fd5df6f197c75691e08 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ Changes between 2.3.2 and 2.3.3.dev ----------------------------------- +- fix issue 214: gracefully handle proxy objects that + look like fixtures but raise exceptions on introspection - fix issue213 - allow to parametrize with values like numpy arrays that do not support an __eq__ operator diff -r 678655781ea359b14d96829c205322db47018239 -r 08f2675ec7d4efc28f101fd5df6f197c75691e08 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1551,7 +1551,15 @@ continue # fixture functions have a pytest_funcarg__ prefix (pre-2.3 style) # or are "@pytest.fixture" marked - marker = getattr(obj, "_pytestfixturefunction", None) + try: + marker = getattr(obj, "_pytestfixturefunction", None) + except RuntimeError: + # some proxy objects raise RuntimeError + # flasks request globals are one example + # those aren't fixture functions, so we can ignore + # XXX: maybe trace it when it happens? + marker = None + if marker is None: if not name.startswith(self._argprefix): continue diff -r 678655781ea359b14d96829c205322db47018239 -r 08f2675ec7d4efc28f101fd5df6f197c75691e08 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1611,6 +1611,34 @@ ]) assert "INTERNAL" not in result.stdout.str() + +def test_funcarg_fixture_discovery_failure_issue214(testdir): + # some proxy objects raise RuntimeError on getattr + # for example flask.request + p = testdir.makepyfile(""" + + class EvilObject(object): + def __call__(self): + #needed to trick discovery + pass + def __getattr__(self, arg): + raise RuntimeError('uhm ' + arg) + + + fixture = EvilObject() + + def test_1(): + pass + """) + result = testdir.runpytest('--fulltrace') + result.stdout.fnmatch_lines([ + '*1 passed*' + ]) + assert "INTERNAL" not in result.stdout.str() + assert "ERROR" not in result.stdout.str() + + + class TestReportInfo: def test_itemreport_reportinfo(self, testdir, linecomp): testdir.makeconftest(""" Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed Oct 31 17:06:16 2012 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 31 Oct 2012 16:06:16 -0000 Subject: [py-svn] commit/pytest: 3 new changesets Message-ID: <20121031160616.32200.96523@bitbucket23.managed.contegix.com> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/changeset/c52af7d3cfb5/ changeset: c52af7d3cfb5 user: hpk42 date: 2012-10-28 17:40:30 summary: remove issue that doesn't make sense anymore affected #: 1 file diff -r 678655781ea359b14d96829c205322db47018239 -r c52af7d3cfb50cd43f7907bd39fe3227960c64fe ISSUES.txt --- a/ISSUES.txt +++ b/ISSUES.txt @@ -48,59 +48,6 @@ not sufficient to always allow non-matches? -unify item/request classes, generalize items ---------------------------------------------------------------- -tags: 2.4 wish - -in lieu of extended parametrization and the new way to specify resource -factories in terms of the parametrize decorator, consider unification -of the item and request class. This also is connected with allowing -funcargs in setup functions. Example of new item API: - - item.getresource("db") # alias for request.getfuncargvalue - item.addfinalizer(...) - item.cached_setup(...) - item.applymarker(...) - -test classes/modules could then use this api via:: - - def pytest_runtest_setup(item): - use item API ... - -introduction of this new method needs to be _fully_ backward compatible - -and the documentation needs to change along to mention this new way of -doing things. - -impl note: probably Request._fillfixtures would be called from the -python plugins own pytest_runtest_setup(item) and would call -item.getresource(X) for all X in the funcargs of a function. - -XXX is it possible to even put the above item API to Nodes, i.e. also -to Directorty/module/file/class collectors? Problem is that current -funcarg factories presume they are called with a per-function (even -per-funcarg-per-function) scope. Could there be small tweaks to the new -API that lift this restriction? - -consider:: - - def setup_class(cls, tmpdir): - # would get a per-class tmpdir because tmpdir parametrization - # would know that it is called with a class scope - # - # - # -this looks very difficult because those setup functions are also used -by nose etc. Rather consider introduction of a new setup hook: - - def setup_test(self, item): - self.db = item.cached_setup(..., scope='class') - self.tmpdir = item.getresource("tmpdir") - -this should be compatible to unittest/nose and provide much of what -"testresources" provide. XXX This would not allow full parametrization -such that test function could be run multiple times with different -values. See "parametrized attributes" issue. - allow parametrized attributes on classes -------------------------------------------------- https://bitbucket.org/hpk42/pytest/changeset/9a0bff73c95e/ changeset: 9a0bff73c95e user: hpk42 date: 2012-10-31 17:01:24 summary: merge affected #: 3 files diff -r c52af7d3cfb50cd43f7907bd39fe3227960c64fe -r 9a0bff73c95e37a9752936e9eed791e6770acbae CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ Changes between 2.3.2 and 2.3.3.dev ----------------------------------- +- fix issue 214: gracefully handle proxy objects that + look like fixtures but raise exceptions on introspection - fix issue213 - allow to parametrize with values like numpy arrays that do not support an __eq__ operator diff -r c52af7d3cfb50cd43f7907bd39fe3227960c64fe -r 9a0bff73c95e37a9752936e9eed791e6770acbae _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1551,7 +1551,15 @@ continue # fixture functions have a pytest_funcarg__ prefix (pre-2.3 style) # or are "@pytest.fixture" marked - marker = getattr(obj, "_pytestfixturefunction", None) + try: + marker = getattr(obj, "_pytestfixturefunction", None) + except RuntimeError: + # some proxy objects raise RuntimeError + # flasks request globals are one example + # those aren't fixture functions, so we can ignore + # XXX: maybe trace it when it happens? + marker = None + if marker is None: if not name.startswith(self._argprefix): continue diff -r c52af7d3cfb50cd43f7907bd39fe3227960c64fe -r 9a0bff73c95e37a9752936e9eed791e6770acbae testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1611,6 +1611,34 @@ ]) assert "INTERNAL" not in result.stdout.str() + +def test_funcarg_fixture_discovery_failure_issue214(testdir): + # some proxy objects raise RuntimeError on getattr + # for example flask.request + p = testdir.makepyfile(""" + + class EvilObject(object): + def __call__(self): + #needed to trick discovery + pass + def __getattr__(self, arg): + raise RuntimeError('uhm ' + arg) + + + fixture = EvilObject() + + def test_1(): + pass + """) + result = testdir.runpytest('--fulltrace') + result.stdout.fnmatch_lines([ + '*1 passed*' + ]) + assert "INTERNAL" not in result.stdout.str() + assert "ERROR" not in result.stdout.str() + + + class TestReportInfo: def test_itemreport_reportinfo(self, testdir, linecomp): testdir.makeconftest(""" https://bitbucket.org/hpk42/pytest/changeset/a751a14ccfb3/ changeset: a751a14ccfb3 user: hpk42 date: 2012-10-31 17:00:55 summary: extended - fix issue214 - ignore attribute-access errors with objects in test modules that can blow up (for example flask's request object) affected #: 6 files diff -r 9a0bff73c95e37a9752936e9eed791e6770acbae -r a751a14ccfb360f4f72ab138d5a9647e89213a82 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,10 +1,12 @@ Changes between 2.3.2 and 2.3.3.dev ----------------------------------- -- fix issue 214: gracefully handle proxy objects that - look like fixtures but raise exceptions on introspection +- fix issue214 - parse modules that contain special objects like e. g. + flask's request object which blows up on getattr access if no request + is active. - fix issue213 - allow to parametrize with values like numpy arrays that - do not support an __eq__ operator + do not support an __eq__ operator + Changes between 2.3.1 and 2.3.2 ----------------------------------- diff -r 9a0bff73c95e37a9752936e9eed791e6770acbae -r a751a14ccfb360f4f72ab138d5a9647e89213a82 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.2' +__version__ = '2.3.3.dev1' diff -r 9a0bff73c95e37a9752936e9eed791e6770acbae -r a751a14ccfb360f4f72ab138d5a9647e89213a82 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1552,12 +1552,12 @@ # fixture functions have a pytest_funcarg__ prefix (pre-2.3 style) # or are "@pytest.fixture" marked try: - marker = getattr(obj, "_pytestfixturefunction", None) - except RuntimeError: - # some proxy objects raise RuntimeError - # flasks request globals are one example - # those aren't fixture functions, so we can ignore - # XXX: maybe trace it when it happens? + marker = obj._pytestfixturefunction + except KeyboardInterrupt: + raise + except Exception: + # some objects raise errors like request (from flask import request) + # we don't expect them to be fixture functions marker = None if marker is None: diff -r 9a0bff73c95e37a9752936e9eed791e6770acbae -r a751a14ccfb360f4f72ab138d5a9647e89213a82 doc/en/example/nonpython/conftest.py --- a/doc/en/example/nonpython/conftest.py +++ b/doc/en/example/nonpython/conftest.py @@ -2,10 +2,10 @@ import pytest -def pytest_collect_file(path, parent): +def pytest_collect_file(parent, path): if path.ext == ".yml" and path.basename.startswith("test"): return YamlFile(path, parent) - + class YamlFile(pytest.File): def collect(self): import yaml # we need a yaml parser, e.g. PyYAML @@ -17,7 +17,7 @@ def __init__(self, name, parent, spec): super(YamlItem, self).__init__(name, parent) self.spec = spec - + def runtest(self): for name, value in self.spec.items(): # some custom test execution (dumb example follows) diff -r 9a0bff73c95e37a9752936e9eed791e6770acbae -r a751a14ccfb360f4f72ab138d5a9647e89213a82 setup.py --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.2', + version='2.3.3.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 9a0bff73c95e37a9752936e9eed791e6770acbae -r a751a14ccfb360f4f72ab138d5a9647e89213a82 testing/test_python.py --- a/testing/test_python.py +++ b/testing/test_python.py @@ -1612,33 +1612,6 @@ assert "INTERNAL" not in result.stdout.str() -def test_funcarg_fixture_discovery_failure_issue214(testdir): - # some proxy objects raise RuntimeError on getattr - # for example flask.request - p = testdir.makepyfile(""" - - class EvilObject(object): - def __call__(self): - #needed to trick discovery - pass - def __getattr__(self, arg): - raise RuntimeError('uhm ' + arg) - - - fixture = EvilObject() - - def test_1(): - pass - """) - result = testdir.runpytest('--fulltrace') - result.stdout.fnmatch_lines([ - '*1 passed*' - ]) - assert "INTERNAL" not in result.stdout.str() - assert "ERROR" not in result.stdout.str() - - - class TestReportInfo: def test_itemreport_reportinfo(self, testdir, linecomp): testdir.makeconftest(""" @@ -2145,6 +2118,20 @@ """) return testdir + def test_parsefactories_evil_objects_issue214(self, testdir): + testdir.makepyfile(""" + class A: + def __call__(self): + pass + def __getattr__(self, name): + raise RuntimeError() + a = A() + def test_hello(): + pass + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1, failed=0) + def test_parsefactories_conftest(self, testdir): testdir.makepyfile(""" def test_hello(item, fm): Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.