From eliswilson at hushmail.com Wed May 1 01:23:05 2013 From: eliswilson at hushmail.com (eliswilson at hushmail.com) Date: Tue, 30 Apr 2013 19:23:05 -0400 Subject: [Pytest-commit] Biggest Fake Conference in Computer Science Message-ID: <20130430232306.09581E6736@smtp.hushmail.com> Biggest Fake Conference in Computer Science We are researchers from different parts of the world and conducted a study on the world?s biggest bogus computer science conference WORLDCOMP http://sites.google.com/site/worlddump1 organized by Prof. Hamid Arabnia from University of Georgia, USA. We submitted a fake paper to WORLDCOMP 2011 and again (the same paper with a modified title) to WORLDCOMP 2012. This paper had numerous fundamental mistakes. Sample statements from that paper include: (1). Binary logic is fuzzy logic and vice versa (2). Pascal developed fuzzy logic (3). Object oriented languages do not exhibit any polymorphism or inheritance (4). TCP and IP are synonyms and are part of OSI model (5). Distributed systems deal with only one computer (6). Laptop is an example for a super computer (7). Operating system is an example for computer hardware Also, our paper did not express any conceptual meaning. However, it was accepted both the times without any modifications (and without any reviews) and we were invited to submit the final paper and a payment of $500+ fee to present the paper. We decided to use the fee for better purposes than making Prof. Hamid Arabnia richer. After that, we received few reminders from WORLDCOMP to pay the fee but we never responded. This fake paper is different from the two fake papers already published (see https://sites.google.com/site/worlddump4 for details) in WORLDCOMP. We MUST say that you should look at the above website if you have any thoughts of participating in WORLDCOMP. DBLP and other indexing agencies have stopped indexing WORLDCOMP?s proceedings since 2011 due to its fakeness. See http://www.informatik.uni-trier.de/~ley/db/conf/icai/index.html for of one of the conferences of WORLDCOMP and notice that there is no listing after 2010. See Section 2 of http://sites.google.com/site/dumpconf for comments from well-known researchers about WORLDCOMP. The status of your WORLDCOMP papers can be changed from scientific to other (i.e., junk or non-technical) at any time. Better not to have a paper than having it in WORLDCOMP and spoil the resume and peace of mind forever! Our study revealed that WORLDCOMP is money making business, using University of Georgia mask, for Prof. Hamid Arabnia. He is throwing out a small chunk of that money (around 20 dollars per paper published in WORLDCOMP?s proceedings) to his puppet (Mr. Ashu Solo or A.M.G. Solo) who publicizes WORLDCOMP and also defends it at various forums, using fake/anonymous names. The puppet uses fake names and defames other conferences to divert traffic to WORLDCOMP. He also makes anonymous phone calls and threatens the critiques of WORLDCOMP (See Item 7 of Section 5 of above website). That is, the puppet does all his best to get a maximum number of papers published at WORLDCOMP to get more money into his (and Prof. Hamid Arabnia?s) pockets. Prof. Hamid Arabnia makes a lot of tricks. For example, he appeared in a newspaper to fool the public, claiming him a victim of cyber-attack (see Item 8 in Section 5 of above website). Monte Carlo Resort (the venue of WORLDCOMP for more than 10 years, until 2012) has refused to provide the venue for WORLDCOMP?13 because of the fears of their image being tarnished due to WORLDCOMP?s fraudulent activities. That is why WORLDCOMP?13 is taking place at a different resort. WORLDCOMP will not be held after 2013. The draft paper submission deadline is over but still there are no committee members, no reviewers, and there is no conference Chairman. The only contact details available on WORLDCOMP?s website is just an email address! We ask Prof. Hamid Arabnia to publish all reviews for all the papers (after blocking identifiable details) since 2000 conference. Reveal the names and affiliations of all the reviewers (for each year) and how many papers each reviewer had reviewed on average. We also ask him to look at the Open Challenge (Section 6) at https://sites.google.com/site/moneycomp1 and respond if he has any professional values. Sorry for posting to multiple lists. Spreading the word is the only way to stop this bogus conference. Please forward this message to other mailing lists and people. We are shocked with Prof. Hamid Arabnia and his puppet?s activities at http://worldcomp-fake-bogus.blogspot.com Search Google using the keyword worldcomp fake for additional links. From issues-reply at bitbucket.org Wed May 1 13:03:24 2013 From: issues-reply at bitbucket.org (jenisys) Date: Wed, 01 May 2013 11:03:24 -0000 Subject: [Pytest-commit] [hpk42/tox] Environment variable usage schema {env:XXX} is not working (issue #99) Message-ID: <20130501110324.445.99664@bitbucket22.managed.contegix.com> New issue 99: Environment variable usage schema {env:XXX} is not working https://bitbucket.org/hpk42/tox/issue/99/environment-variable-usage-schema-env-xxx jenisys: Version info: tox-1.4.3, tox-1.5.dev7 (current head/tip) The documentation describes that environment variables can be used/referenced as shown above. But there seems to be a number of issues. PROBLEMS: * Environment variables that are setup via "setenv= ..." can not be referenced in "commands= ..." part. * Option --showconfig does not evaluate "setenv= ..." parts in testenv section before the output is shown. ``` #!ini [testenv:example] commands= echo ENVIRONMENT-VAR foo: {env:foo} echo ENVIRONMENT-VAR bar: {env:bar} setenv = foo = Alice bar = Bob deps= ``` ADDITIONAL NOTE: Current repository tip has a problem with {homedir} placeholder when ``indexserver= local=file://{homedir}/.pip_downloads`` is used. This is working in box-1.4.3, but no longer working in tox-1.5.dev7. From commits-noreply at bitbucket.org Sun May 5 14:24:11 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 05 May 2013 12:24:11 -0000 Subject: [Pytest-commit] commit/pytest: 2 new changesets Message-ID: <20130505122411.17860.60928@bitbucket25.managed.contegix.com> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/8199400776b6/ Changeset: 8199400776b6 User: hpk42 Date: 2013-04-30 16:41:01 Summary: Added tag 2.3.5 for changeset fc3a793e87ec Affected #: 1 file diff -r fc3a793e87ec907000a47ea0d3a372a2fe218c0a -r 8199400776b61be0dea0b9fe1d5ce9ab144adcf6 .hgtags --- a/.hgtags +++ b/.hgtags @@ -54,3 +54,4 @@ 8738b828dec53937765db71951ef955cca4c51f6 2.3.2 7fe44182c434f8ac89149a3c340479872a5d5ccb 2.3.3 ef299e57f24218dbdd949498d7e660723636bcc3 2.3.4 +fc3a793e87ec907000a47ea0d3a372a2fe218c0a 2.3.5 https://bitbucket.org/hpk42/pytest/commits/f693c65bfa4d/ Changeset: f693c65bfa4d User: hpk42 Date: 2013-05-05 14:23:47 Summary: change version Affected #: 1 file diff -r 8199400776b61be0dea0b9fe1d5ce9ab144adcf6 -r f693c65bfa4dd90625eb231e2bcd2ba593103c61 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.4.1" +version = release = "2.3.5" 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 May 5 14:56:28 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 05 May 2013 12:56:28 -0000 Subject: [Pytest-commit] commit/pytest: 2 new changesets Message-ID: <20130505125628.21562.7308@bitbucket04.managed.contegix.com> 2 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/af707032e4da/ Changeset: af707032e4da User: hpk42 Date: 2013-05-05 14:48:17 Summary: bump version Affected #: 2 files diff -r f693c65bfa4dd90625eb231e2bcd2ba593103c61 -r af707032e4da0d1f391f2683b017cc698ce620d9 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.5' +__version__ = '2.3.6.dev1' diff -r f693c65bfa4dd90625eb231e2bcd2ba593103c61 -r af707032e4da0d1f391f2683b017cc698ce620d9 setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.5', + version='2.3.6.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], https://bitbucket.org/hpk42/pytest/commits/b93ac0cdae02/ Changeset: b93ac0cdae02 User: hpk42 Date: 2013-05-05 14:48:37 Summary: allow fixture functions to be implemented as context managers: @pytest.fixture def myfix(): # setup yield 1 # teardown Affected #: 5 files diff -r af707032e4da0d1f391f2683b017cc698ce620d9 -r b93ac0cdae02effaa3c136a681cc45bba757fe46 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,12 @@ -Changes between 2.3.4 and 2.3.5dev +Changes between 2.3.5 and DEV +----------------------------------- + +- (experimental) allow fixture functions to be + implemented as context managers + + + +Changes between 2.3.4 and 2.3.5 ----------------------------------- - never consider a fixture function for test function collection diff -r af707032e4da0d1f391f2683b017cc698ce620d9 -r b93ac0cdae02effaa3c136a681cc45bba757fe46 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.6.dev1' +__version__ = '2.3.6.dev2' diff -r af707032e4da0d1f391f2683b017cc698ce620d9 -r b93ac0cdae02effaa3c136a681cc45bba757fe46 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -1613,6 +1613,29 @@ except ValueError: pass +def call_fixture_func(fixturefunc, request, kwargs): + if is_generator(fixturefunc): + iter = fixturefunc(**kwargs) + next = getattr(iter, "__next__", None) + if next is None: + next = getattr(iter, "next") + res = next() + def teardown(): + try: + next() + except StopIteration: + pass + else: + fs, lineno = getfslineno(fixturefunc) + location = "%s:%s" % (fs, lineno+1) + pytest.fail( + "fixture function %s has more than one 'yield': \n%s" % + (fixturefunc.__name__, location), pytrace=False) + request.addfinalizer(teardown) + else: + res = fixturefunc(**kwargs) + return res + class FixtureDef: """ A container for a factory definition. """ def __init__(self, fixturemanager, baseid, argname, func, scope, params, @@ -1663,7 +1686,7 @@ fixturefunc = fixturefunc.__get__(request.instance) except AttributeError: pass - result = fixturefunc(**kwargs) + result = call_fixture_func(fixturefunc, request, kwargs) assert not hasattr(self, "cached_result") self.cached_result = result return result diff -r af707032e4da0d1f391f2683b017cc698ce620d9 -r b93ac0cdae02effaa3c136a681cc45bba757fe46 setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.6.dev1', + version='2.3.6.dev2', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r af707032e4da0d1f391f2683b017cc698ce620d9 -r b93ac0cdae02effaa3c136a681cc45bba757fe46 testing/python/fixture.py --- a/testing/python/fixture.py +++ b/testing/python/fixture.py @@ -1795,3 +1795,101 @@ *hello world* """) + + +class TestContextManagerFixtureFuncs: + def test_simple(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.fixture + def arg1(): + print ("setup") + yield 1 + print ("teardown") + def test_1(arg1): + print ("test1 %s" % arg1) + def test_2(arg1): + print ("test2 %s" % arg1) + assert 0 + """) + result = testdir.runpytest("-s") + result.stdout.fnmatch_lines(""" + setup + test1 1 + teardown + setup + test2 1 + teardown + """) + + def test_scoped(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.fixture(scope="module") + def arg1(): + print ("setup") + yield 1 + print ("teardown") + def test_1(arg1): + print ("test1 %s" % arg1) + def test_2(arg1): + print ("test2 %s" % arg1) + """) + result = testdir.runpytest("-s") + result.stdout.fnmatch_lines(""" + setup + test1 1 + test2 1 + teardown + """) + + def test_setup_exception(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.fixture(scope="module") + def arg1(): + pytest.fail("setup") + yield 1 + def test_1(arg1): + pass + """) + result = testdir.runpytest("-s") + result.stdout.fnmatch_lines(""" + *pytest.fail*setup* + *1 error* + """) + + def test_teardown_exception(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.fixture(scope="module") + def arg1(): + yield 1 + pytest.fail("teardown") + def test_1(arg1): + pass + """) + result = testdir.runpytest("-s") + result.stdout.fnmatch_lines(""" + *pytest.fail*teardown* + *1 passed*1 error* + """) + + + def test_yields_more_than_one(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.fixture(scope="module") + def arg1(): + yield 1 + yield 2 + def test_1(arg1): + pass + """) + result = testdir.runpytest("-s") + result.stdout.fnmatch_lines(""" + *fixture function* + *test_yields*: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 Mon May 6 09:58:12 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Mon, 06 May 2013 07:58:12 -0000 Subject: [Pytest-commit] commit/tox: 2 new changesets Message-ID: <20130506075812.25896.58973@bitbucket16.managed.contegix.com> 2 new commits in tox: https://bitbucket.org/hpk42/tox/commits/45bfbce11300/ Changeset: 45bfbce11300 User: kfekete Date: 2013-05-06 00:31:31 Summary: Test for #99 regression on multiple substitutions per line Affected #: 1 file diff -r 1aadfbd68cfff77e57ef36ffb8ec5a54b40217d8 -r 45bfbce1130042a5f3b0794cbf051539603534cd tests/test_config.py --- a/tests/test_config.py +++ b/tests/test_config.py @@ -806,6 +806,20 @@ assert config.indexserver["default"].url == "xzy" assert config.indexserver["name1"].url == "xzy" + def test_multiple_homedir_relative_local_indexservers(self, newconfig): + inisource = """ + [tox] + indexserver = + default = file://{homedir}/.pip/downloads/simple + local1 = file://{homedir}/.pip/downloads/simple + local2 = file://{toxinidir}/downloads/simple + pypi = http://pypi.python.org/simple + """ + config = newconfig([], inisource) + homedir = str(py.path.local._gethomedir()) + assert config.indexserver['default'].url == "file://{h}/.pip/downloads/simple".format(h=homedir) + assert config.indexserver['local1'].url == config.indexserver['default'].url + class TestParseEnv: def test_parse_recreate(self, newconfig): https://bitbucket.org/hpk42/tox/commits/8dd1345f8c32/ Changeset: 8dd1345f8c32 User: kfekete Date: 2013-05-06 00:31:32 Summary: Fix #99 regression - modify regular expression to be non-greedy Affected #: 1 file diff -r 45bfbce1130042a5f3b0794cbf051539603534cd -r 8dd1345f8c327a471adc09695823df668f9e23a4 tox/_config.py --- a/tox/_config.py +++ b/tox/_config.py @@ -345,8 +345,15 @@ self.name = name self.url = url -RE_ITEM_REF = re.compile(r'\{(?:(?P[^[:]+):)?(?P.*)\}') -# temporary workaround for sublime text syntax highlight bug: ]))' +RE_ITEM_REF = re.compile( + ''' + [{] + (?:(?P[^[:{}]+):)? # optional sub_type for special rules + (?P[^{}]*) # substitution key + [}] + ''', + re.VERBOSE) + class IniReader: def __init__(self, cfgparser, fallbacksections=None): Repository URL: https://bitbucket.org/hpk42/tox/ -- 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 May 7 10:48:21 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Tue, 07 May 2013 08:48:21 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: Added tag 1.4.14 for changeset b93ac0cdae02 Message-ID: <20130507084821.22455.25532@bitbucket21.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/0c08bfe320c1/ Changeset: 0c08bfe320c1 User: hpk42 Date: 2013-05-07 10:48:13 Summary: Added tag 1.4.14 for changeset b93ac0cdae02 Affected #: 1 file diff -r b93ac0cdae02effaa3c136a681cc45bba757fe46 -r 0c08bfe320c178b768987e0b3e775ff2118ca69c .hgtags --- a/.hgtags +++ b/.hgtags @@ -55,3 +55,4 @@ 7fe44182c434f8ac89149a3c340479872a5d5ccb 2.3.3 ef299e57f24218dbdd949498d7e660723636bcc3 2.3.4 fc3a793e87ec907000a47ea0d3a372a2fe218c0a 2.3.5 +b93ac0cdae02effaa3c136a681cc45bba757fe46 1.4.14 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 May 7 10:55:53 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Tue, 07 May 2013 08:55:53 -0000 Subject: [Pytest-commit] commit/pytest: 4 new changesets Message-ID: <20130507085553.11370.61078@bitbucket05.managed.contegix.com> 4 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/a46857b89f02/ Changeset: a46857b89f02 User: hpk42 Date: 2013-05-07 10:53:31 Summary: implemented as context managers. Thanks Andreas Pelme, ladimir Keleshev. fix issue245 by depending on the released py-1.4.14 which fixes py.io.dupfile to work with files with no mode. Thanks Jason R. Coombs. Affected #: 3 files diff -r 0c08bfe320c178b768987e0b3e775ff2118ca69c -r a46857b89f02cac923a861698f4b449654a8912b AUTHORS --- a/AUTHORS +++ b/AUTHORS @@ -6,6 +6,7 @@ Ronny Pfannschmidt Benjamin Peterson Floris Bruynooghe +Jason R. Coombs Samuele Pedroni Carl Friedrich Bolz Armin Rigo diff -r 0c08bfe320c178b768987e0b3e775ff2118ca69c -r a46857b89f02cac923a861698f4b449654a8912b CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -2,8 +2,12 @@ ----------------------------------- - (experimental) allow fixture functions to be - implemented as context managers + implemented as context managers. Thanks Andreas Pelme, + ladimir Keleshev. +- fix issue245 by depending on the released py-1.4.14 + which fixes py.io.dupfile to work with files with no + mode. Thanks Jason R. Coombs. Changes between 2.3.4 and 2.3.5 diff -r 0c08bfe320c178b768987e0b3e775ff2118ca69c -r a46857b89f02cac923a861698f4b449654a8912b setup.py --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ entry_points= make_entry_points(), cmdclass = {'test': PyTest}, # the following should be enabled for release - install_requires=['py>=1.4.13dev6'], + install_requires=['py>=1.4.14'], classifiers=['Development Status :: 6 - Mature', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', https://bitbucket.org/hpk42/pytest/commits/728d5dc5c1d7/ Changeset: 728d5dc5c1d7 User: hpk42 Date: 2013-05-07 10:54:05 Summary: fix issue245 by depending on py-1.4.14 which fixes py.io.dupfile to not assume file.mode is present. Affected #: 2 files diff -r a46857b89f02cac923a861698f4b449654a8912b -r 728d5dc5c1d7445ff9ad806a4e5b0b60b730793c _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.6.dev2' +__version__ = '2.3.6.dev3' diff -r a46857b89f02cac923a861698f4b449654a8912b -r 728d5dc5c1d7445ff9ad806a4e5b0b60b730793c setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.6.dev2', + version='2.3.6.dev3', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], https://bitbucket.org/hpk42/pytest/commits/7e0e46f0aa50/ Changeset: 7e0e46f0aa50 User: hpk42 Date: 2013-05-07 10:54:46 Summary: Removed tag 1.4.14 Affected #: 1 file diff -r 728d5dc5c1d7445ff9ad806a4e5b0b60b730793c -r 7e0e46f0aa5003ccdbe30ff20f2a0049dfad66a8 .hgtags --- a/.hgtags +++ b/.hgtags @@ -56,3 +56,5 @@ ef299e57f24218dbdd949498d7e660723636bcc3 2.3.4 fc3a793e87ec907000a47ea0d3a372a2fe218c0a 2.3.5 b93ac0cdae02effaa3c136a681cc45bba757fe46 1.4.14 +b93ac0cdae02effaa3c136a681cc45bba757fe46 1.4.14 +0000000000000000000000000000000000000000 1.4.14 https://bitbucket.org/hpk42/pytest/commits/37ebb8278004/ Changeset: 37ebb8278004 User: hpk42 Date: 2013-05-07 10:55:41 Summary: Removed tag 1.4.14 Affected #: 1 file diff -r 7e0e46f0aa5003ccdbe30ff20f2a0049dfad66a8 -r 37ebb8278004f6aa99f4deafe032c790c5cc5e99 .hgtags --- a/.hgtags +++ b/.hgtags @@ -58,3 +58,5 @@ b93ac0cdae02effaa3c136a681cc45bba757fe46 1.4.14 b93ac0cdae02effaa3c136a681cc45bba757fe46 1.4.14 0000000000000000000000000000000000000000 1.4.14 +0000000000000000000000000000000000000000 1.4.14 +0000000000000000000000000000000000000000 1.4.14 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 May 8 07:43:23 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 08 May 2013 05:43:23 -0000 Subject: [Pytest-commit] commit/pytest: 5 new changesets Message-ID: <20130508054323.31794.63564@bitbucket03.managed.contegix.com> 5 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/8dfa43a47cca/ Changeset: 8dfa43a47cca User: hpk42 Date: 2013-05-07 16:26:56 Summary: don't use indexservers anymore Affected #: 1 file diff -r 37ebb8278004f6aa99f4deafe032c790c5cc5e99 -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 tox.ini --- a/tox.ini +++ b/tox.ini @@ -1,17 +1,13 @@ [tox] distshare={homedir}/.tox/distshare envlist=py25,py26,py27,py27-nobyte,py32,py33,py27-xdist,trial -indexserver= - pypi = https://pypi.python.org/simple - testrun = http://pypi.testrun.org - default = http://pypi.testrun.org [testenv] changedir=testing commands= py.test --lsof -rfsxX --junitxml={envlogdir}/junit-{envname}.xml [] deps= - :pypi:pexpect - :pypi:nose + pexpect + nose [testenv:genscript] changedir=. @@ -21,8 +17,8 @@ changedir=. basepython=python2.7 deps=pytest-xdist - :pypi:mock - :pypi:nose + mock + nose commands= py.test -n3 -rfsxX \ --junitxml={envlogdir}/junit-{envname}.xml testing @@ -39,8 +35,8 @@ [testenv:trial] changedir=. -deps=:pypi:twisted - :pypi:pexpect +deps=twisted + pexpect commands= py.test -rsxf testing/test_unittest.py \ --junitxml={envlogdir}/junit-{envname}.xml {posargs:testing/test_unittest.py} @@ -51,17 +47,17 @@ [testenv:py32] deps= - :pypi:nose + nose [testenv:py33] deps= - :pypi:nose + nose [testenv:doc] basepython=python changedir=doc/en -deps=:pypi:sphinx - :pypi:PyYAML +deps=sphinx + PyYAML commands= make clean @@ -70,15 +66,15 @@ [testenv:regen] basepython=python changedir=doc/en -deps=:pypi:sphinx - :pypi:PyYAML +deps=sphinx + PyYAML commands= rm -rf /tmp/doc-exec* #pip install pytest==2.3.4 make regen [testenv:py31] -deps=:pypi:nose>=1.0 +deps=nose>=1.0 [testenv:py31-xdist] deps=pytest-xdist https://bitbucket.org/hpk42/pytest/commits/35e73638a6e4/ Changeset: 35e73638a6e4 User: hpk42 Date: 2013-05-07 18:40:26 Summary: support boolean condition expressions in skipif/xfail change documentation to prefer it over string expressions Affected #: 6 files diff -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 -r 35e73638a6e4a1ae8b9b14237da4469960085d05 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,9 +1,15 @@ -Changes between 2.3.5 and DEV +Changes between 2.3.5 and 2.4.DEV ----------------------------------- - (experimental) allow fixture functions to be implemented as context managers. Thanks Andreas Pelme, - ladimir Keleshev. + Vladimir Keleshev. + +- (experimental) allow boolean expression directly with skipif/xfail + if a "reason" is also specified. Rework skipping documentation + to recommend "condition as booleans" because it prevents surprises + when importing markers between modules. Specifying conditions + as strings will remain fully supported. - fix issue245 by depending on the released py-1.4.14 which fixes py.io.dupfile to work with files with no diff -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 -r 35e73638a6e4a1ae8b9b14237da4469960085d05 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.3.6.dev3' +__version__ = '2.4.0.dev1' diff -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 -r 35e73638a6e4a1ae8b9b14237da4469960085d05 _pytest/skipping.py --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -89,7 +89,11 @@ if isinstance(expr, py.builtin._basestring): result = cached_eval(self.item.config, expr, d) else: - pytest.fail("expression is not a string") + if self.get("reason") is None: + # XXX better be checked at collection time + pytest.fail("you need to specify reason=STRING " + "when using booleans as conditions.") + result = bool(expr) if result: self.result = True self.expr = expr diff -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 -r 35e73638a6e4a1ae8b9b14237da4469960085d05 doc/en/skipping.txt --- a/doc/en/skipping.txt +++ b/doc/en/skipping.txt @@ -9,86 +9,110 @@ or that you expect to fail you can mark them accordingly or you may call helper functions during execution of setup or test functions. -A *skip* means that you expect your test to pass unless a certain -configuration or condition (e.g. wrong Python interpreter, missing -dependency) prevents it to run. And *xfail* means that your test -can run but you expect it to fail because there is an implementation problem. +A *skip* means that you expect your test to pass unless the environment +(e.g. wrong Python interpreter, missing dependency) prevents it to run. +And *xfail* means that your test can run but you expect it to fail +because there is an implementation problem. -py.test counts and lists *skip* and *xfail* tests separately. However, -detailed information about skipped/xfailed tests is not shown by default -to avoid cluttering the output. You can use the ``-r`` option to see -details corresponding to the "short" letters shown in the test -progress:: +py.test counts and lists *skip* and *xfail* tests separately. Detailed +information about skipped/xfailed tests is not shown by default to avoid +cluttering the output. You can use the ``-r`` option to see details +corresponding to the "short" letters shown in the test progress:: py.test -rxs # show extra info on skips and xfails (See :ref:`how to change command line options defaults`) .. _skipif: +.. _`condition booleans`: Marking a test function to be skipped ------------------------------------------- +.. versionadded:: 2.4 + Here is an example of marking a test function to be skipped -when run on a Python3 interpreter:: +when run on a Python3.3 interpreter:: import sys - @pytest.mark.skipif("sys.version_info >= (3,0)") + @pytest.mark.skipif(sys.version_info >= (3,3), + reason="requires python3.3") def test_function(): ... -During test function setup the skipif condition is -evaluated by calling ``eval('sys.version_info >= (3,0)', namespace)``. -(*New in version 2.0.2*) The namespace contains all the module globals of the test function so that -you can for example check for versions of a module you are using:: +During test function setup the condition ("sys.version_info >= (3,3)") is +checked. If it evaluates to True, the test function will be skipped +with the specified reason. Note that pytest enforces specifying a reason +in order to report meaningful "skip reasons" (e.g. when using ``-rs``). + +You can share skipif markers between modules. Consider this test module:: + + # content of test_mymodule.py import mymodule - - @pytest.mark.skipif("mymodule.__version__ < '1.2'") - def test_function(): - ... - -The test function will not be run ("skipped") if -``mymodule`` is below the specified version. The reason -for specifying the condition as a string is mainly that -py.test can report a summary of skip conditions. -For information on the construction of the ``namespace`` -see `evaluation of skipif/xfail conditions`_. - -You can of course create a shortcut for your conditional skip -decorator at module level like this:: - - win32only = pytest.mark.skipif("sys.platform != 'win32'") - - @win32only + minversion = pytest.mark.skipif(mymodule.__versioninfo__ >= (1,1), + reason="at least mymodule-1.1 required") + @minversion def test_function(): ... -Skip all test functions of a class --------------------------------------- +You can import it from another test module:: + + # test_myothermodule.py + from test_mymodule import minversion + + @minversion + def test_anotherfunction(): + ... + +For larger test suites it's usually a good idea to have one file +where you define the markers which you then consistently apply +throughout your test suite. + +Alternatively, the pre pytest-2.4 way to specify `condition strings `_ instead of booleans will remain fully supported in future +versions of pytest. It couldn't be easily used for importing markers +between test modules so it's no longer advertised as the primary method. + + +Skip all test functions of a class or module +--------------------------------------------- As with all function :ref:`marking ` you can skip test functions at the -`whole class- or module level`_. Here is an example -for skipping all methods of a test class based on the platform:: +`whole class- or module level`_. If your code targets python2.6 or above you +use the skipif decorator (and any other marker) on classes:: - class TestPosixCalls: - pytestmark = pytest.mark.skipif("sys.platform == 'win32'") - - def test_function(self): - "will not be setup or run under 'win32' platform" - -The ``pytestmark`` special name tells py.test to apply it to each test -function in the class. If your code targets python2.6 or above you can -more naturally use the skipif decorator (and any other marker) on -classes:: - - @pytest.mark.skipif("sys.platform == 'win32'") + @pytest.mark.skipif(sys.platform == 'win32', + reason="requires windows") class TestPosixCalls: def test_function(self): "will not be setup or run under 'win32' platform" -Using multiple "skipif" decorators on a single function is generally fine - it means that if any of the conditions apply the function execution will be skipped. +If the condition is true, this marker will produce a skip result for +each of the test methods. + +If your code targets python2.5 where class-decorators are not available, +you can set the ``pytestmark`` attribute of a class:: + + class TestPosixCalls: + pytestmark = pytest.mark.skipif(sys.platform == 'win32', + reason="requires Windows") + + def test_function(self): + "will not be setup or run under 'win32' platform" + +As with the class-decorator, the ``pytestmark`` special name tells +py.test to apply it to each test function in the class. + +If you want to skip all test functions of a module, you must use +the ``pytestmark`` name on the global level:: + + # test_module.py + + pytestmark = pytest.mark.skipif(...) + +If multiple "skipif" decorators are applied to a test function, it +will be skipped if any of the skip conditions is true. .. _`whole class- or module level`: mark.html#scoped-marking @@ -118,7 +142,8 @@ As with skipif_ you can also mark your expectation of a failure on a particular platform:: - @pytest.mark.xfail("sys.version_info >= (3,0)") + @pytest.mark.xfail(sys.version_info >= (3,3), + reason="python3.3 api changes") def test_function(): ... @@ -151,41 +176,19 @@ ======================== 6 xfailed in 0.05 seconds ========================= -.. _`evaluation of skipif/xfail conditions`: - -Evaluation of skipif/xfail expressions ----------------------------------------------------- - -.. versionadded:: 2.0.2 - -The evaluation of a condition string in ``pytest.mark.skipif(conditionstring)`` -or ``pytest.mark.xfail(conditionstring)`` takes place in a namespace -dictionary which is constructed as follows: - -* the namespace is initialized by putting the ``sys`` and ``os`` modules - and the pytest ``config`` object into it. - -* updated with the module globals of the test function for which the - expression is applied. - -The pytest ``config`` object allows you to skip based on a test configuration value -which you might have added:: - - @pytest.mark.skipif("not config.getvalue('db')") - def test_function(...): - ... - Imperative xfail from within a test or setup function ------------------------------------------------------ -If you cannot declare xfail-conditions at import time -you can also imperatively produce an XFail-outcome from -within test or setup code. Example:: +If you cannot declare xfail- of skipif conditions at import +time you can also imperatively produce an according outcome +imperatively, in test or setup code:: def test_function(): if not valid_config(): - pytest.xfail("unsupported configuration") + pytest.xfail("failing configuration (but should work)") + # or + pytest.skipif("unsupported configuration") Skipping on a missing import dependency @@ -202,16 +205,61 @@ docutils = pytest.importorskip("docutils", minversion="0.3") -The version will be read from the specified module's ``__version__`` attribute. +The version will be read from the specified +module's ``__version__`` attribute. -Imperative skip from within a test or setup function ------------------------------------------------------- -If for some reason you cannot declare skip-conditions -you can also imperatively produce a skip-outcome from -within test or setup code. Example:: +.. _`string conditions`: +specifying conditions as strings versus booleans +---------------------------------------------------------- + +Prior to pytest-2.4 the only way to specify skipif/xfail conditions was +to use strings:: + + import sys + @pytest.mark.skipif("sys.version_info >= (3,3)") def test_function(): - if not valid_config(): - pytest.skip("unsupported configuration") + ... +During test function setup the skipif condition is evaluated by calling +``eval('sys.version_info >= (3,0)', namespace)``. The namespace contains +all the module globals, and ``os`` and ``sys`` as a minimum. + +Since pytest-2.4 `condition booleans`_ are considered preferable +because markers can then be freely imported between test modules. +With strings you need to import not only the marker but all variables +everything used by the marker, which violates encapsulation. + +The reason for specifying the condition as a string was that py.test can +report a summary of skip conditions based purely on the condition string. +With conditions as booleans you are required to specify a ``reason`` string. + +Note that string conditions will remain fully supported and you are free +to use them if you have no need for cross-importing markers. + +The evaluation of a condition string in ``pytest.mark.skipif(conditionstring)`` +or ``pytest.mark.xfail(conditionstring)`` takes place in a namespace +dictionary which is constructed as follows: + +* the namespace is initialized by putting the ``sys`` and ``os`` modules + and the pytest ``config`` object into it. + +* updated with the module globals of the test function for which the + expression is applied. + +The pytest ``config`` object allows you to skip based on a test +configuration value which you might have added:: + + @pytest.mark.skipif("not config.getvalue('db')") + def test_function(...): + ... + +The equivalent with "boolean conditions" is:: + + @pytest.mark.skipif(not pytest.config.getvalue("db"), + reason="--db was not specified") + def test_function(...): + pass + + diff -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 -r 35e73638a6e4a1ae8b9b14237da4469960085d05 setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.3.6.dev3', + version='2.4.0.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 8dfa43a47ccac80dbf252d35c2156ae9e0d38637 -r 35e73638a6e4a1ae8b9b14237da4469960085d05 testing/test_skipping.py --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -569,7 +569,6 @@ "*xfail(*condition, reason=None, run=True)*expected failure*", ]) - def test_xfail_test_setup_exception(testdir): testdir.makeconftest(""" def pytest_runtest_setup(): @@ -610,3 +609,44 @@ """) +class TestBooleanCondition: + def test_skipif(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.mark.skipif(True, reason="True123") + def test_func1(): + pass + @pytest.mark.skipif(False, reason="True123") + def test_func2(): + pass + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines(""" + *1 passed*1 skipped* + """) + + def test_skipif_noreason(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.mark.skipif(True) + def test_func(): + pass + """) + result = testdir.runpytest("-rs") + result.stdout.fnmatch_lines(""" + *1 error* + """) + + def test_xfail(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.mark.xfail(True, reason="True123") + def test_func(): + assert 0 + """) + result = testdir.runpytest("-rxs") + result.stdout.fnmatch_lines(""" + *XFAIL* + *True123* + *1 xfail* + """) https://bitbucket.org/hpk42/pytest/commits/cccbdc95b3fa/ Changeset: cccbdc95b3fa User: hpk42 Date: 2013-05-07 21:34:59 Summary: enhance index page, fix announcement index Affected #: 3 files diff -r 35e73638a6e4a1ae8b9b14237da4469960085d05 -r cccbdc95b3fa4b1a111449fdcef7319b34502e76 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.5 release-2.3.4 release-2.3.3 release-2.3.2 diff -r 35e73638a6e4a1ae8b9b14237da4469960085d05 -r cccbdc95b3fa4b1a111449fdcef7319b34502e76 doc/en/index.txt --- a/doc/en/index.txt +++ b/doc/en/index.txt @@ -11,8 +11,11 @@ - runs on Posix/Windows, Python 2.4-3.3, PyPy and Jython-2.5.1 - :ref:`comprehensive online ` and `PDF documentation `_ + - many :ref:`third party plugins ` and + :ref:`builtin helpers ` - used in :ref:`many projects and organisations `, in test - suites ranging from 10 to 10s of thousands of tests + suites with up to twenty thousand tests + - strict policy of remaining backward compatible across releases - comes with many :ref:`tested examples ` **provides easy no-boilerplate testing** @@ -26,13 +29,13 @@ **scales from simple unit to complex functional testing** - - (new in 2.3) :ref:`modular parametrizeable fixtures ` + - :ref:`modular parametrizeable fixtures ` (new in 2.3, + improved in 2.4) - :ref:`parametrized test functions ` - :ref:`mark` - - :ref:`skipping` + - :ref:`skipping` (improved in 2.4) - can :ref:`distribute tests to multiple CPUs ` through :ref:`xdist plugin ` - can :ref:`continuously re-run failing tests ` - - many :ref:`builtin helpers ` and :ref:`plugins ` - flexible :ref:`Python test discovery` **integrates many common testing methods**: @@ -50,8 +53,8 @@ **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 - - it is easy to add command line options or do other kind of add-ons and customizations. + - customizations can be per-directory, per-project or per PyPI released plugin + - it is easy to add command line options or customize existing behaviour .. _`Javascript unit- and functional testing`: http://pypi.python.org/pypi/oejskit diff -r 35e73638a6e4a1ae8b9b14237da4469960085d05 -r cccbdc95b3fa4b1a111449fdcef7319b34502e76 doc/en/plugins.txt --- a/doc/en/plugins.txt +++ b/doc/en/plugins.txt @@ -78,12 +78,22 @@ * `pytest-capturelog `_: to capture and assert about messages from the logging module +* `pytest-cov `_: + coverage reporting, compatible with distributed testing + * `pytest-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-instafail `_: + to report failures while the test run is happening. + +* `pytest-bdd `_ and + `pytest-konira `_ + to write tests using behaviour-driven testing. + * `pytest-timeout `_: to timeout tests based on function marks or global definitions. @@ -91,9 +101,6 @@ 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 - * `pytest-pep8 `_: a ``--pep8`` option to enable PEP8 compliance checking. https://bitbucket.org/hpk42/pytest/commits/5db7ac12892a/ Changeset: 5db7ac12892a User: hpk42 Date: 2013-05-07 21:37:08 Summary: document context fixtures, also improve plugin docs Affected #: 2 files diff -r cccbdc95b3fa4b1a111449fdcef7319b34502e76 -r 5db7ac12892adca887ce08cbf01f5683dc24a75f CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -3,7 +3,7 @@ - (experimental) allow fixture functions to be implemented as context managers. Thanks Andreas Pelme, - Vladimir Keleshev. + Vladimir Keleshev. - (experimental) allow boolean expression directly with skipif/xfail if a "reason" is also specified. Rework skipping documentation diff -r cccbdc95b3fa4b1a111449fdcef7319b34502e76 -r 5db7ac12892adca887ce08cbf01f5683dc24a75f doc/en/fixture.txt --- a/doc/en/fixture.txt +++ b/doc/en/fixture.txt @@ -7,14 +7,14 @@ .. currentmodule:: _pytest.python -.. versionadded:: 2.0/2.3 +.. versionadded:: 2.0/2.3/2.4 .. _`xUnit`: http://en.wikipedia.org/wiki/XUnit -.. _`general purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software +.. _`purpose of test fixtures`: http://en.wikipedia.org/wiki/Test_fixture#Software .. _`Dependency injection`: http://en.wikipedia.org/wiki/Dependency_injection#Definition -The `general purpose of test fixtures`_ is to provide a fixed baseline -upon which tests can reliably and repeatedly execute. pytest-2.3 fixtures +The `purpose of test fixtures`_ is to provide a fixed baseline +upon which tests can reliably and repeatedly execute. pytest fixtures offer dramatic improvements over the classic xUnit style of setup/teardown functions: @@ -22,8 +22,7 @@ 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. + triggers a *fixture function* which can itself use other fixtures. * fixture management scales from simple unit to complex functional testing, allowing to parametrize fixtures and tests according @@ -129,10 +128,10 @@ 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 +also in docs today. It now refers to the specific case of injecting 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. +more possibilities to use fixtures but "funcargs" remain as the main way +as they allow to directly state the dependencies of a test function. As the following examples show in more detail, funcargs allow test functions to easily receive and work against specific pre-initialized @@ -154,10 +153,10 @@ :py:func:`@pytest.fixture <_pytest.python.fixture>` invocation to cause the decorated ``smtp`` fixture function to only be invoked once 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:: +each receive the same ``smtp`` fixture instance. The next example puts +the fixture function into a separate ``conftest.py`` file so +that tests from multiple test modules in the directory can +access the fixture function:: # content of conftest.py import pytest @@ -233,24 +232,91 @@ def smtp(...): # the returned fixture value will be shared for # all tests needing it + +.. _`contextfixtures`: +fixture finalization / teardowns +------------------------------------------------------------- + +pytest supports two styles of fixture finalization: + +- (new in pytest-2.4) by writing a contextmanager fixture + generator where a fixture value is "yielded" and the remainder + of the function serves as the teardown code. This integrates + very well with existing context managers. + +- by making a fixture function accept a ``request`` argument + with which it can call ``request.addfinalizer(teardownfunction)`` + to register a teardown callback function. + +Both methods are strictly equivalent from pytest's view and will +remain supported in the future. + +Because a number of people prefer the new contextmanager style +we describe it first:: + + # content of test_ctxfixture.py + + import smtplib + import pytest + + @pytest.fixture(scope="module") + def smtp(): + smtp = smtplib.SMTP("merlinux.eu") + yield smtp # provide the fixture value + print ("teardown smtp") + smtp.close() + +pytest detects that you are using a ``yield`` in your fixture function, +turns it into a generator and: + +a) iterates once into it for producing the value +b) iterates a second time for tearing the fixture down, expecting + a StopIteration (which is produced automatically from the Python + runtime when the generator returns). + +.. note:: + + The teardown will execute independently of the status of test functions. + You do not need to write the teardown code into a ``try-finally`` clause + like you would usually do with ``contextlib.contextmanager`` decorated + functions. + + If the fixture generator yields a second value pytest will report + an error. Yielding cannot be used for parametrization. We'll describe + ways to implement parametrization further below. + +Prior to pytest-2.4 you always needed to register a finalizer by accepting +a ``request`` object into your fixture function and calling +``request.addfinalizer`` with a teardown function:: + + import smtplib + import pytest + + @pytest.fixture(scope="module") + def smtp(request): + smtp = smtplib.SMTP("merlinux.eu") + def fin(): + print ("teardown smtp") + smtp.close() + return smtp # provide the fixture value + +This method of registering a finalizer reads more indirect +than the new contextmanager style syntax because ``fin`` +is a callback function. + + .. _`request-context`: Fixtures can interact with the requesting test context ------------------------------------------------------------- -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, +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. +for which they are invoked. 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:: +read an optional server URL from the module namespace:: # content of conftest.py import pytest @@ -260,26 +326,25 @@ def smtp(request): server = getattr(request.module, "smtpserver", "merlinux.eu") smtp = smtplib.SMTP(server) - def fin(): - print ("finalizing %s" % smtp) - smtp.close() - request.addfinalizer(fin) - return smtp + yield smtp # provide the fixture + print ("finalizing %s" % smtp) + smtp.close() -The registered ``fin`` function will be called when the last test -using it has executed:: +The finalizing part after the ``yield smtp`` statement will execute +when the last test using the ``smtp`` fixture has executed:: $ py.test -s -q --tb=no FF 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 either case the test module itself does not need to change! +tests which use it finished executin. If we rather specify +``scope='function'`` then fixture setup and cleanup occurs +around each single test. Note that in 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:: +server URL in its module namespace:: # content of test_anothersmtp.py @@ -298,6 +363,10 @@ > assert 0, smtp.helo() E AssertionError: (250, 'mail.python.org') +voila! The ``smtp`` fixture function picked up our mail server name +from the module namespace. + + .. _`fixture-parametrize`: Parametrizing a fixture @@ -323,11 +392,9 @@ 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 + yield smtp + print ("finalizing %s" % smtp) + smtp.close() The main change is the declaration of ``params`` with :py:func:`@pytest.fixture <_pytest.python.fixture>`, a list of values @@ -467,10 +534,8 @@ def modarg(request): param = request.param print "create", param - def fin(): - print "fin", param - request.addfinalizer(fin) - return param + yield param + print ("fin %s" % param) @pytest.fixture(scope="function", params=[1,2]) def otherarg(request): @@ -517,9 +582,9 @@ 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`: - using fixtures from classes, modules or projects ---------------------------------------------------------------------- https://bitbucket.org/hpk42/pytest/commits/17f3904c6e8e/ Changeset: 17f3904c6e8e User: hpk42 Date: 2013-05-07 21:39:30 Summary: bump version Affected #: 2 files Diff not available. 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 May 9 15:32:35 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 09 May 2013 13:32:35 -0000 Subject: [Pytest-commit] commit/pytest: 3 new changesets Message-ID: <20130509133235.29948.84056@bitbucket24.managed.contegix.com> 3 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/44765914d448/ Changeset: 44765914d448 User: hg Date: 2013-05-05 22:15:06 Summary: #299 Affected #: 1 file diff -r b93ac0cdae02effaa3c136a681cc45bba757fe46 -r 44765914d448f7675c404af4ed56f5daaaf3f916 _pytest/runner.py --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -198,7 +198,7 @@ if call.when == "call": longrepr = item.repr_failure(excinfo) else: # exception in setup or teardown - longrepr = item._repr_failure_py(excinfo) + longrepr = item._repr_failure_py(excinfo,style=item.config.option.tbstyle) return TestReport(item.nodeid, item.location, keywords, outcome, longrepr, when, duration=duration) https://bitbucket.org/hpk42/pytest/commits/406ceacf7d22/ Changeset: 406ceacf7d22 User: maho Date: 2013-05-08 17:01:20 Summary: #299 - polishing Affected #: 2 files diff -r 44765914d448f7675c404af4ed56f5daaaf3f916 -r 406ceacf7d227cf30ef0357c1030f5934f04ac01 _pytest/runner.py --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -198,7 +198,8 @@ if call.when == "call": longrepr = item.repr_failure(excinfo) else: # exception in setup or teardown - longrepr = item._repr_failure_py(excinfo,style=item.config.option.tbstyle) + longrepr = item._repr_failure_py(excinfo, + style=item.config.option.tbstyle) return TestReport(item.nodeid, item.location, keywords, outcome, longrepr, when, duration=duration) diff -r 44765914d448f7675c404af4ed56f5daaaf3f916 -r 406ceacf7d227cf30ef0357c1030f5934f04ac01 testing/test_terminal.py --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -665,3 +665,19 @@ result.stdout.fnmatch_lines([ "*2 passed*" ]) + + +def test_tbstyle_native_setup_error(testdir): + p = testdir.makepyfile(""" + import pytest + @pytest.fixture + def setup_error_fixture(): + raise Exception("error in exception") + + def test_error_fixture(setup_error_fixture): + pass + """) + result = testdir.runpytest("--tb=native") + result.stdout.fnmatch_lines([ + '*File *test_tbstyle_native_setup_error.py", line *, in setup_error_fixture*' + ]) https://bitbucket.org/hpk42/pytest/commits/0418f5c7b7b5/ Changeset: 0418f5c7b7b5 User: hpk42 Date: 2013-05-09 15:32:29 Summary: Merged in maho/pytest (pull request #31) #299 Affected #: 2 files diff -r 17f3904c6e8e7c55575ac20dcf06e90124f17842 -r 0418f5c7b7b5740ebb349ff3af0bc5073797c55d _pytest/runner.py --- a/_pytest/runner.py +++ b/_pytest/runner.py @@ -198,7 +198,8 @@ if call.when == "call": longrepr = item.repr_failure(excinfo) else: # exception in setup or teardown - longrepr = item._repr_failure_py(excinfo) + longrepr = item._repr_failure_py(excinfo, + style=item.config.option.tbstyle) return TestReport(item.nodeid, item.location, keywords, outcome, longrepr, when, duration=duration) diff -r 17f3904c6e8e7c55575ac20dcf06e90124f17842 -r 0418f5c7b7b5740ebb349ff3af0bc5073797c55d testing/test_terminal.py --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -665,3 +665,19 @@ result.stdout.fnmatch_lines([ "*2 passed*" ]) + + +def test_tbstyle_native_setup_error(testdir): + p = testdir.makepyfile(""" + import pytest + @pytest.fixture + def setup_error_fixture(): + raise Exception("error in exception") + + def test_error_fixture(setup_error_fixture): + pass + """) + result = testdir.runpytest("--tb=native") + result.stdout.fnmatch_lines([ + '*File *test_tbstyle_native_setup_error.py", line *, in setup_error_fixture*' + ]) 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 May 9 15:38:02 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 09 May 2013 13:38:02 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: add maho as contributor Message-ID: <20130509133802.3370.64658@bitbucket03.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/fb4c4834bcf6/ Changeset: fb4c4834bcf6 User: hpk42 Date: 2013-05-09 15:37:51 Summary: add maho as contributor Affected #: 1 file diff -r 0418f5c7b7b5740ebb349ff3af0bc5073797c55d -r fb4c4834bcf6c95238b608ea2304a53caea3eb9d AUTHORS --- a/AUTHORS +++ b/AUTHORS @@ -10,6 +10,7 @@ Samuele Pedroni Carl Friedrich Bolz Armin Rigo +Maho Maciek Fijalkowski Guido Wesdorp Brian Dorsey 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 May 10 08:06:38 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Fri, 10 May 2013 06:06:38 -0000 Subject: [Pytest-commit] commit/pytest: 4 new changesets Message-ID: <20130510060638.30653.8401@bitbucket24.managed.contegix.com> 4 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/ea7c9699dc47/ Changeset: ea7c9699dc47 User: jaapz Date: 2013-05-08 15:15:43 Summary: Fix junitxml generation when using special characters in parametrized tests. Affected #: 2 files diff -r 17f3904c6e8e7c55575ac20dcf06e90124f17842 -r ea7c9699dc473a5aa8d875f5b948f02185bc785a _pytest/junitxml.py --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -36,7 +36,8 @@ # | [#x10000-#x10FFFF] _legal_chars = (0x09, 0x0A, 0x0d) _legal_ranges = ( - (0x20, 0xD7FF), + (0x20, 0x7E), + (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF), ) @@ -103,7 +104,7 @@ classnames.insert(0, self.prefix) self.tests.append(Junit.testcase( classname=".".join(classnames), - name=names[-1], + name=bin_xml_escape(names[-1]), time=getattr(report, 'duration', 0) )) https://bitbucket.org/hpk42/pytest/commits/e91ec1fd68ec/ Changeset: e91ec1fd68ec User: jaapz Date: 2013-05-08 16:11:55 Summary: Fix pytest.py permissions. Affected #: 1 file https://bitbucket.org/hpk42/pytest/commits/c1547d89c7fe/ Changeset: c1547d89c7fe User: jaapz Date: 2013-05-09 21:16:57 Summary: Implemented a test for xml control character fail. Affected #: 1 file diff -r e91ec1fd68ec9252537442c6d2cd1e75c6212112 -r c1547d89c7fe3c319286b06e6f33c4f229368186 testing/test_junitxml.py --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -450,3 +450,16 @@ assert result.ret == 0 assert testdir.tmpdir.join("a/x.xml").check() +def test_escaped_parametrized_names_xml(testdir): + testdir.makepyfile(""" + import pytest + @pytest.mark.parametrize('char', ["\\x00"]) + def test_func(char): + assert char + """) + result, dom = runandparse(testdir) + assert result.ret == 0 + node = dom.getElementsByTagName("testcase")[0] + assert_attr(node, + name="test_func[#x00]") + https://bitbucket.org/hpk42/pytest/commits/1a0e829d03d9/ Changeset: 1a0e829d03d9 User: hpk42 Date: 2013-05-10 08:06:31 Summary: Merged in jaapz/pytest-xml-escape-control-chars (pull request #32) Fix junitxml generation when using special characters in parametrized tests. Affected #: 2 files diff -r fb4c4834bcf6c95238b608ea2304a53caea3eb9d -r 1a0e829d03d9fd537b7be24bb86a2c7eeee532a7 _pytest/junitxml.py --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -36,7 +36,8 @@ # | [#x10000-#x10FFFF] _legal_chars = (0x09, 0x0A, 0x0d) _legal_ranges = ( - (0x20, 0xD7FF), + (0x20, 0x7E), + (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF), ) @@ -103,7 +104,7 @@ classnames.insert(0, self.prefix) self.tests.append(Junit.testcase( classname=".".join(classnames), - name=names[-1], + name=bin_xml_escape(names[-1]), time=getattr(report, 'duration', 0) )) diff -r fb4c4834bcf6c95238b608ea2304a53caea3eb9d -r 1a0e829d03d9fd537b7be24bb86a2c7eeee532a7 testing/test_junitxml.py --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -450,3 +450,16 @@ assert result.ret == 0 assert testdir.tmpdir.join("a/x.xml").check() +def test_escaped_parametrized_names_xml(testdir): + testdir.makepyfile(""" + import pytest + @pytest.mark.parametrize('char', ["\\x00"]) + def test_func(char): + assert char + """) + result, dom = runandparse(testdir) + assert result.ret == 0 + node = dom.getElementsByTagName("testcase")[0] + assert_attr(node, + name="test_func[#x00]") + 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 May 10 08:15:15 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Fri, 10 May 2013 06:15:15 -0000 Subject: [Pytest-commit] commit/pytest: 4 new changesets Message-ID: <20130510061515.22827.33321@bitbucket04.managed.contegix.com> 4 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/89e5be038d30/ Changeset: 89e5be038d30 User: hpk42 Date: 2013-05-09 15:50:09 Summary: honor --tb style for setup/teardown errors as well. Affected #: 1 file diff -r fb4c4834bcf6c95238b608ea2304a53caea3eb9d -r 89e5be038d30539715ff55e85ad013027ba2cfe1 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,7 @@ which fixes py.io.dupfile to work with files with no mode. Thanks Jason R. Coombs. +- honor --tb style for setup/teardown errors as well. Changes between 2.3.4 and 2.3.5 ----------------------------------- https://bitbucket.org/hpk42/pytest/commits/13e9038463fc/ Changeset: 13e9038463fc User: hpk42 Date: 2013-05-09 15:50:28 Summary: mention --tb style change in changelog Affected #: 1 file diff -r 89e5be038d30539715ff55e85ad013027ba2cfe1 -r 13e9038463fce9202efd89967a5288c107e81944 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -15,7 +15,7 @@ which fixes py.io.dupfile to work with files with no mode. Thanks Jason R. Coombs. -- honor --tb style for setup/teardown errors as well. +- honor --tb style for setup/teardown errors as well. Thanks Maho. Changes between 2.3.4 and 2.3.5 ----------------------------------- https://bitbucket.org/hpk42/pytest/commits/ca3f3ad80a2b/ Changeset: ca3f3ad80a2b User: hpk42 Date: 2013-05-10 08:13:35 Summary: fix junitxml generation when test output contains control characters, addressing issue267 Affected #: 3 files diff -r 13e9038463fce9202efd89967a5288c107e81944 -r ca3f3ad80a2be9eae5a878b5e588a4bcc1d5b1fe CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -15,6 +15,9 @@ which fixes py.io.dupfile to work with files with no mode. Thanks Jason R. Coombs. +- fix junitxml generation when test output contains control characters, + addressing issue267 + - honor --tb style for setup/teardown errors as well. Thanks Maho. Changes between 2.3.4 and 2.3.5 diff -r 13e9038463fce9202efd89967a5288c107e81944 -r ca3f3ad80a2be9eae5a878b5e588a4bcc1d5b1fe _pytest/junitxml.py --- a/_pytest/junitxml.py +++ b/_pytest/junitxml.py @@ -36,7 +36,8 @@ # | [#x10000-#x10FFFF] _legal_chars = (0x09, 0x0A, 0x0d) _legal_ranges = ( - (0x20, 0xD7FF), + (0x20, 0x7E), + (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF), ) @@ -103,7 +104,7 @@ classnames.insert(0, self.prefix) self.tests.append(Junit.testcase( classname=".".join(classnames), - name=names[-1], + name=bin_xml_escape(names[-1]), time=getattr(report, 'duration', 0) )) diff -r 13e9038463fce9202efd89967a5288c107e81944 -r ca3f3ad80a2be9eae5a878b5e588a4bcc1d5b1fe testing/test_junitxml.py --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -450,3 +450,16 @@ assert result.ret == 0 assert testdir.tmpdir.join("a/x.xml").check() +def test_escaped_parametrized_names_xml(testdir): + testdir.makepyfile(""" + import pytest + @pytest.mark.parametrize('char', ["\\x00"]) + def test_func(char): + assert char + """) + result, dom = runandparse(testdir) + assert result.ret == 0 + node = dom.getElementsByTagName("testcase")[0] + assert_attr(node, + name="test_func[#x00]") + https://bitbucket.org/hpk42/pytest/commits/7c468f83e347/ Changeset: 7c468f83e347 User: hpk42 Date: 2013-05-10 08:14:39 Summary: add Jaap Broekhuizen for junitxml gen Affected #: 2 files diff -r ca3f3ad80a2be9eae5a878b5e588a4bcc1d5b1fe -r 7c468f83e347c21fbed87eeae457f645bfcc7a66 AUTHORS --- a/AUTHORS +++ b/AUTHORS @@ -11,6 +11,7 @@ Carl Friedrich Bolz Armin Rigo Maho +Jaap Broekhuizen Maciek Fijalkowski Guido Wesdorp Brian Dorsey diff -r ca3f3ad80a2be9eae5a878b5e588a4bcc1d5b1fe -r 7c468f83e347c21fbed87eeae457f645bfcc7a66 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -16,7 +16,7 @@ mode. Thanks Jason R. Coombs. - fix junitxml generation when test output contains control characters, - addressing issue267 + addressing issue267, thanks Jaap Broekhuizen - honor --tb style for setup/teardown errors as well. Thanks Maho. 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 issues-reply at bitbucket.org Mon May 13 16:28:05 2013 From: issues-reply at bitbucket.org (Laurens Van Houtven) Date: Mon, 13 May 2013 14:28:05 -0000 Subject: [Pytest-commit] Issue #100: Old distribute version makes pudb uninstallable (hpk42/tox) Message-ID: <20130513142805.9841.5468@bitbucket23.managed.contegix.com> New issue 100: Old distribute version makes pudb uninstallable https://bitbucket.org/hpk42/tox/issue/100/old-distribute-version-makes-pudb Laurens Van Houtven: Hi :) I've ran into an issue installing pudb into the tox virtualenv which I believe to be a consequence of pudb requriing distribute >= 0.6.35, with tox installing distribute == 0.6.34. Here's an excerpt: ``` Downloading/unpacking pudb (from -r /Users/lvh/Code/exponent/requirements-testing.txt (line 3)) Using version 2013.1 (newest of versions: 2013.1, 2012.3, 2012.2.1, 2012.2, 2012.1, 2011.3.1, 2011.3, 2011.2, 2011.1, 0.93.1, 0.93, 0.92.15, 0.92.14, 0.92.13, 0.92.12, 0.92.11, 0.92.10, 0.92.9, 0.92.8, 0.92.7, 0.92.6, 0.92.5, 0.92.4, 0.92.3, 0.92.2, 0.92.1, 0.92, 0.91.5, 0.91.4, 0.91.3, 0.91.2, 0.91.1, 0.91, 0.90.6, 0.90.5, 0.90.4, 0.90.3, 0.90.2, 0.90.1, 0.90) Using download cache from /Users/lvh/.pip/cache/https%3A%2F%2Fpypi.python.org%2Fpackages%2Fsource%2Fp%2Fpudb%2Fpudb-2013.1.tar.gz Running setup.py egg_info for package pudb The required version of distribute (>=0.6.35) is not available, and can't be installed while this script is running. Please install a more recent version first, using 'easy_install -U distribute'. (Currently using distribute 0.6.34 (/Users/lvh/Code/exponent/.tox/pypy/site-packages/distribute-0.6.34-py2.7.egg)) Complete output from command python setup.py egg_info: The required version of distribute (>=0.6.35) is not available, and can't be installed while this script is running. Please install a more recent version first, using 'easy_install -U distribute'. ``` I've fixed this by adding a pip install --upgrade distribute line right before attempting to install that, but I'm guessing the appropriate thing would be for tox to install the latest version of distribute instead? From commits-noreply at bitbucket.org Tue May 14 12:48:36 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Tue, 14 May 2013 10:48:36 -0000 Subject: [Pytest-commit] commit/tox: lukaszb: Show full command that is being run during tests Message-ID: <20130514104836.19853.2824@bitbucket16.managed.contegix.com> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/1c746939df7f/ Changeset: 1c746939df7f User: lukaszb Date: 2013-05-09 19:52:25 Summary: Show full command that is being run during tests Affected #: 2 files diff -r 8dd1345f8c327a471adc09695823df668f9e23a4 -r 1c746939df7f57bae11fe81010446d5c8dc3dae6 tests/test_venv.py --- a/tests/test_venv.py +++ b/tests/test_venv.py @@ -262,6 +262,16 @@ venv.update() mocksession.report.expect("verbosity0", "*recreate*") +def test_test_runtests_action_command_is_in_output(newmocksession): + mocksession = newmocksession([], ''' + [testenv] + commands = echo foo bar + ''') + venv = mocksession.getenv('python') + venv.update() + venv.test() + mocksession.report.expect("verbosity0", "*runtests*commands?0? | echo foo bar") + def test_install_error(newmocksession, monkeypatch): mocksession = newmocksession(['--recreate'], """ [testenv] diff -r 8dd1345f8c327a471adc09695823df668f9e23a4 -r 1c746939df7f57bae11fe81010446d5c8dc3dae6 tox/_venv.py --- a/tox/_venv.py +++ b/tox/_venv.py @@ -269,7 +269,8 @@ self.session.make_emptydir(self.envconfig.envtmpdir) cwd = self.envconfig.changedir for i, argv in enumerate(self.envconfig.commands): - action.setactivity("runtests", "commands[%s]" % i) + message = "commands[%s] | %s" % (i, ' '.join(argv)) + action.setactivity("runtests", message) try: self._pcall(argv, cwd=cwd, action=action, redirect=redirect) except tox.exception.InvocationError: Repository URL: https://bitbucket.org/hpk42/tox/ -- 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 May 14 12:50:44 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Tue, 14 May 2013 10:50:44 -0000 Subject: [Pytest-commit] commit/tox: 2 new changesets Message-ID: <20130514105044.24142.90131@bitbucket15.managed.contegix.com> 2 new commits in tox: https://bitbucket.org/hpk42/tox/commits/065fd13f5963/ Changeset: 065fd13f5963 User: hpk42 Date: 2013-05-14 12:50:25 Summary: show (test) command that is being executed, thanks Lukasz Balcerzak Affected #: 1 file diff -r 1c746939df7f57bae11fe81010446d5c8dc3dae6 -r 065fd13f5963363820ed362fd3d07fa12bc9da0d CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,9 @@ - fix issue92 - fix {envsitepackagesdir} to actually work again +- show (test) command that is being executed, thanks + Lukasz Balcerzak + - re-license tox to MIT license - depend on virtualenv-1.9.1 https://bitbucket.org/hpk42/tox/commits/bb8cfbe8a188/ Changeset: bb8cfbe8a188 User: hpk42 Date: 2013-05-14 12:50:36 Summary: mention command showing in changelog Affected #: 3 files diff -r 065fd13f5963363820ed362fd3d07fa12bc9da0d -r bb8cfbe8a1881d62c10e9e6714af171c7de7c642 setup.py --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ description='virtualenv-based automation of test activities', long_description=long_description, url='http://tox.testrun.org/', - version='1.5.dev7', + version='1.5.dev8', license='http://opensource.org/licenses/MIT', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], author='holger krekel', diff -r 065fd13f5963363820ed362fd3d07fa12bc9da0d -r bb8cfbe8a1881d62c10e9e6714af171c7de7c642 tox/__init__.py --- a/tox/__init__.py +++ b/tox/__init__.py @@ -1,5 +1,5 @@ # -__version__ = '1.5.dev7' +__version__ = '1.5.dev8' class exception: class Error(Exception): diff -r 065fd13f5963363820ed362fd3d07fa12bc9da0d -r bb8cfbe8a1881d62c10e9e6714af171c7de7c642 toxbootstrap.py --- a/toxbootstrap.py +++ b/toxbootstrap.py @@ -58,7 +58,7 @@ """ -__version__ = '1.5.dev7' +__version__ = '1.5.dev8' import sys import os Repository URL: https://bitbucket.org/hpk42/tox/ -- 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 May 16 10:00:21 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 16 May 2013 08:00:21 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: fix issue307 - use yaml.safe_load instead of yaml.load, thanks Mark Eichin. Message-ID: <20130516080021.20919.78331@bitbucket24.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/7db6af80a1de/ Changeset: 7db6af80a1de User: hpk42 Date: 2013-05-16 09:59:48 Summary: fix issue307 - use yaml.safe_load instead of yaml.load, thanks Mark Eichin. Affected #: 2 files diff -r 7c468f83e347c21fbed87eeae457f645bfcc7a66 -r 7db6af80a1defcc27d9016a0258b5be31386d82e CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -20,6 +20,8 @@ - honor --tb style for setup/teardown errors as well. Thanks Maho. +- fix issue307 - use yaml.safe_load in example, thanks Mark Eichin. + Changes between 2.3.4 and 2.3.5 ----------------------------------- diff -r 7c468f83e347c21fbed87eeae457f645bfcc7a66 -r 7db6af80a1defcc27d9016a0258b5be31386d82e doc/en/example/nonpython/conftest.py --- a/doc/en/example/nonpython/conftest.py +++ b/doc/en/example/nonpython/conftest.py @@ -9,7 +9,7 @@ class YamlFile(pytest.File): def collect(self): import yaml # we need a yaml parser, e.g. PyYAML - raw = yaml.load(self.fspath.open()) + raw = yaml.safe_load(self.fspath.open()) for name, spec in raw.items(): yield YamlItem(name, self, spec) 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 May 17 20:40:14 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Fri, 17 May 2013 18:40:14 -0000 Subject: [Pytest-commit] commit/pytest: danilobellini: zero to many doctests from module instead of one Message-ID: <20130517184014.7051.69234@bitbucket02.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/b2108fd98d3b/ Changeset: b2108fd98d3b User: danilobellini Date: 2013-05-17 17:18:22 Summary: zero to many doctests from module instead of one Affected #: 2 files diff -r 7db6af80a1defcc27d9016a0258b5be31386d82e -r b2108fd98d3b4a4ed8ad6f43a1cddebf5a5b7212 _pytest/doctest.py --- a/_pytest/doctest.py +++ b/_pytest/doctest.py @@ -34,6 +34,14 @@ self.reprlocation.toterminal(tw) class DoctestItem(pytest.Item): + def __init__(self, name, parent, runner=None, dtest=None): + super(DoctestItem, self).__init__(name, parent) + self.runner = runner + self.dtest = dtest + + def runtest(self): + self.runner.run(self.dtest) + def repr_failure(self, excinfo): doctest = py.std.doctest if excinfo.errisinstance((doctest.DocTestFailure, @@ -76,7 +84,7 @@ return super(DoctestItem, self).repr_failure(excinfo) def reportinfo(self): - return self.fspath, None, "[doctest]" + return self.fspath, None, "[doctest] %s" % self.name class DoctestTextfile(DoctestItem, pytest.File): def runtest(self): @@ -91,8 +99,8 @@ extraglobs=dict(getfixture=fixture_request.getfuncargvalue), raise_on_error=True, verbose=0) -class DoctestModule(DoctestItem, pytest.File): - def runtest(self): +class DoctestModule(pytest.File): + def collect(self): doctest = py.std.doctest if self.fspath.basename == "conftest.py": module = self.config._conftest.importconftest(self.fspath) @@ -102,7 +110,11 @@ self.funcargs = {} self._fixtureinfo = FuncFixtureInfo((), [], {}) fixture_request = FixtureRequest(self) - failed, tot = doctest.testmod( - module, raise_on_error=True, verbose=0, - extraglobs=dict(getfixture=fixture_request.getfuncargvalue), - optionflags=doctest.ELLIPSIS) + doctest_globals = dict(getfixture=fixture_request.getfuncargvalue) + # uses internal doctest module parsing mechanism + finder = doctest.DocTestFinder() + runner = doctest.DebugRunner(verbose=0, optionflags=doctest.ELLIPSIS) + for test in finder.find(module, module.__name__, + extraglobs=doctest_globals): + if test.examples: # skip empty doctests + yield DoctestItem(test.name, self, runner, test) diff -r 7db6af80a1defcc27d9016a0258b5be31386d82e -r b2108fd98d3b4a4ed8ad6f43a1cddebf5a5b7212 testing/test_doctest.py --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -1,4 +1,4 @@ -from _pytest.doctest import DoctestModule, DoctestTextfile +from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile import py, pytest class TestDoctests: @@ -19,13 +19,61 @@ items, reprec = testdir.inline_genitems(w) assert len(items) == 1 - def test_collect_module(self, testdir): + def test_collect_module_empty(self, testdir): path = testdir.makepyfile(whatever="#") for p in (path, testdir.tmpdir): items, reprec = testdir.inline_genitems(p, '--doctest-modules') + assert len(items) == 0 + + def test_collect_module_single_modulelevel_doctest(self, testdir): + path = testdir.makepyfile(whatever='""">>> pass"""') + for p in (path, testdir.tmpdir): + items, reprec = testdir.inline_genitems(p, + '--doctest-modules') assert len(items) == 1 - assert isinstance(items[0], DoctestModule) + assert isinstance(items[0], DoctestItem) + assert isinstance(items[0].parent, DoctestModule) + + def test_collect_module_two_doctest_one_modulelevel(self, testdir): + path = testdir.makepyfile(whatever=""" + '>>> x = None' + def my_func(): + ">>> magic = 42 " + """) + for p in (path, testdir.tmpdir): + items, reprec = testdir.inline_genitems(p, + '--doctest-modules') + assert len(items) == 2 + assert isinstance(items[0], DoctestItem) + assert isinstance(items[1], DoctestItem) + assert isinstance(items[0].parent, DoctestModule) + assert items[0].parent is items[1].parent + + def test_collect_module_two_doctest_no_modulelevel(self, testdir): + path = testdir.makepyfile(whatever=""" + '# Empty' + def my_func(): + ">>> magic = 42 " + def unuseful(): + ''' + # This is a function + # >>> # it doesn't have any doctest + ''' + def another(): + ''' + # This is another function + >>> import os # this one does have a doctest + ''' + """) + for p in (path, testdir.tmpdir): + items, reprec = testdir.inline_genitems(p, + '--doctest-modules') + assert len(items) == 2 + assert isinstance(items[0], DoctestItem) + assert isinstance(items[1], DoctestItem) + assert isinstance(items[0].parent, DoctestModule) + assert items[0].parent is items[1].parent def test_simple_doctestfile(self, testdir): p = testdir.maketxtfile(test_doc=""" @@ -164,3 +212,47 @@ """) reprec = testdir.inline_run(p, "--doctest-modules") reprec.assertoutcome(passed=1) + + def test_doctestmodule_three_tests(self, testdir): + p = testdir.makepyfile(""" + ''' + >>> dir = getfixture('tmpdir') + >>> type(dir).__name__ + 'LocalPath' + ''' + def my_func(): + ''' + >>> magic = 42 + >>> magic - 42 + 0 + ''' + def unuseful(): + pass + def another(): + ''' + >>> import os + >>> os is os + True + ''' + """) + reprec = testdir.inline_run(p, "--doctest-modules") + reprec.assertoutcome(passed=3) + + def test_doctestmodule_two_tests_one_fail(self, testdir): + p = testdir.makepyfile(""" + class MyClass: + def bad_meth(self): + ''' + >>> magic = 42 + >>> magic + 0 + ''' + def nice_meth(self): + ''' + >>> magic = 42 + >>> magic - 42 + 0 + ''' + """) + reprec = testdir.inline_run(p, "--doctest-modules") + reprec.assertoutcome(failed=1, passed=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 Fri May 17 20:49:02 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Fri, 17 May 2013 18:49:02 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: added changelog for improved doctest counting Message-ID: <20130517184902.27310.66437@bitbucket25.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/ed60f7d8bcff/ Changeset: ed60f7d8bcff User: hpk42 Date: 2013-05-17 20:48:51 Summary: added changelog for improved doctest counting Affected #: 1 file diff -r b2108fd98d3b4a4ed8ad6f43a1cddebf5a5b7212 -r ed60f7d8bcff1ced6774cbadb9b0771e7416f9d4 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,11 @@ when importing markers between modules. Specifying conditions as strings will remain fully supported. +- improved doctest counting for doctests in python modules -- + files without any doctest items will not show up anymore + and doctest examples are counted as separate test items. + thanks Danilo Bellini. + - fix issue245 by depending on the released py-1.4.14 which fixes py.io.dupfile to work with files with no mode. Thanks Jason R. Coombs. 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 May 21 13:47:55 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Tue, 21 May 2013 11:47:55 -0000 Subject: [Pytest-commit] commit/tox: hpk42: introduce white list for commands Message-ID: <20130521114755.13448.25867@bitbucket16.managed.contegix.com> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/8d323c83d4d8/ Changeset: 8d323c83d4d8 User: hpk42 Date: 2013-05-21 13:47:32 Summary: introduce white list for commands Affected #: 9 files diff -r bb8cfbe8a1881d62c10e9e6714af171c7de7c642 -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,10 @@ 1.5.0.dev ----------------- +- re-fix issue2 - add command_whitelist to be used in ``[testenv*]`` + sections, allowing to avoid warnings for commands such as ``make``, + used from the commands value. + - fix issue97 - allow substitutions to reference from other sections (thanks Krisztian Fekete) diff -r bb8cfbe8a1881d62c10e9e6714af171c7de7c642 -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e doc/config.txt --- a/doc/config.txt +++ b/doc/config.txt @@ -75,6 +75,16 @@ For eventually performing a call to ``subprocess.Popen(args, ...)`` ``args`` are determined by splitting the whole command by whitespace. +.. confval:: whitelist_externals=MULTI-LINE-LIST + + each line specifies a command name (in glob-style pattern format) + which can be used in the ``commands`` section without triggering + a "not installed in virtualenv" warning. Example: if you use the + unix ``make`` for running tests you can list ``whitelist_externals=make`` + or ``whitelist_externals=/usr/bin/make`` if you want more precision. + If you don't want tox to issue a warning in any case, just use + ``whitelist_externals=*`` which will match all commands (not recommended). + .. confval:: changedir=path change to this working directory when executing the test command. diff -r bb8cfbe8a1881d62c10e9e6714af171c7de7c642 -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e setup.py --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ description='virtualenv-based automation of test activities', long_description=long_description, url='http://tox.testrun.org/', - version='1.5.dev8', + version='1.5.dev9', license='http://opensource.org/licenses/MIT', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], author='holger krekel', diff -r bb8cfbe8a1881d62c10e9e6714af171c7de7c642 -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e tests/test_config.py --- a/tests/test_config.py +++ b/tests/test_config.py @@ -368,6 +368,24 @@ envconfig = config.envconfigs['py'] assert envconfig.commands == [["abc"]] + def test_whitelist_externals(self, tmpdir, newconfig): + config = newconfig(""" + [testenv] + whitelist_externals = xyz + commands=xyz + [testenv:x] + + [testenv:py] + whitelist_externals = xyz2 + commands=abc + """) + assert len(config.envconfigs) == 2 + envconfig = config.envconfigs['py'] + assert envconfig.commands == [["abc"]] + assert envconfig.whitelist_externals == ["xyz2"] + envconfig = config.envconfigs['x'] + assert envconfig.whitelist_externals == ["xyz"] + def test_changedir(self, tmpdir, newconfig): config = newconfig(""" [testenv] diff -r bb8cfbe8a1881d62c10e9e6714af171c7de7c642 -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e tests/test_venv.py --- a/tests/test_venv.py +++ b/tests/test_venv.py @@ -295,6 +295,21 @@ mocksession.report.expect("warning", "*test command found but not*") assert venv.status == "commands failed" +def test_install_command_whitelisted(newmocksession, monkeypatch): + mocksession = newmocksession(['--recreate'], """ + [testenv] + whitelist_externals = py.test + xy* + commands= + py.test + xyz + """) + venv = mocksession.getenv('python') + venv.test() + mocksession.report.expect("warning", "*test command found but not*", + invert=True) + assert venv.status == "commands failed" + @pytest.mark.skipif("not sys.platform.startswith('linux')") def test_install_command_not_installed(newmocksession): mocksession = newmocksession(['--recreate'], """ diff -r bb8cfbe8a1881d62c10e9e6714af171c7de7c642 -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e tox/__init__.py --- a/tox/__init__.py +++ b/tox/__init__.py @@ -1,5 +1,5 @@ # -__version__ = '1.5.dev8' +__version__ = '1.5.dev9' class exception: class Error(Exception): diff -r bb8cfbe8a1881d62c10e9e6714af171c7de7c642 -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e tox/_config.py --- a/tox/_config.py +++ b/tox/_config.py @@ -291,6 +291,8 @@ vc.setenv = None vc.commands = reader.getargvlist(section, "commands") + vc.whitelist_externals = reader.getlist(section, + "whitelist_externals") vc.deps = [] for depline in reader.getlist(section, "deps"): m = re.match(r":(\w+):\s*(\S+)", depline) diff -r bb8cfbe8a1881d62c10e9e6714af171c7de7c642 -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e tox/_venv.py --- a/tox/_venv.py +++ b/tox/_venv.py @@ -81,12 +81,16 @@ if p.relto(self.envconfig.envdir): return p if venv: - self.session.report.warning( - "test command found but not installed in testenv\n" - " cmd: %s\n" - " env: %s\n" - "Maybe forgot to specify a dependency?" % (p, - self.envconfig.envdir)) + for x in self.envconfig.whitelist_externals: + if p.fnmatch(x): + break + else: + self.session.report.warning( + "test command found but not installed in testenv\n" + " cmd: %s\n" + " env: %s\n" + "Maybe forgot to specify a dependency?" % (p, + self.envconfig.envdir)) return str(p) # will not be rewritten for reporting def _ispython3(self): diff -r bb8cfbe8a1881d62c10e9e6714af171c7de7c642 -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e toxbootstrap.py --- a/toxbootstrap.py +++ b/toxbootstrap.py @@ -58,7 +58,7 @@ """ -__version__ = '1.5.dev8' +__version__ = '1.5.dev9' import sys import os Repository URL: https://bitbucket.org/hpk42/tox/ -- 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 May 21 16:05:42 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Tue, 21 May 2013 14:05:42 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: add holger's gittip account, would also like to add ronny's Message-ID: <20130521140542.30597.82081@bitbucket03.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/db9c8ef9f6e0/ Changeset: db9c8ef9f6e0 User: hpk42 Date: 2013-05-21 16:05:32 Summary: add holger's gittip account, would also like to add ronny's Affected #: 1 file diff -r ed60f7d8bcff1ced6774cbadb9b0771e7416f9d4 -r db9c8ef9f6e01273a0dcc23b9ba55f954aab662a doc/en/_templates/layout.html --- a/doc/en/_templates/layout.html +++ b/doc/en/_templates/layout.html @@ -3,6 +3,12 @@ {% block relbaritems %} {{ super() }} + + + + {% endblock %} {% block footer %} 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 May 21 16:50:57 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Tue, 21 May 2013 14:50:57 -0000 Subject: [Pytest-commit] commit/tox: hpk42: make sure virtualenv commands are found first Message-ID: <20130521145057.20102.23187@bitbucket02.managed.contegix.com> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/638b892f6fad/ Changeset: 638b892f6fad User: hpk42 Date: 2013-05-21 16:46:31 Summary: make sure virtualenv commands are found first Affected #: 10 files diff -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e -r 638b892f6fadcbacbb31168c889827171a6a54eb CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,10 @@ 1.5.0.dev ----------------- +- make sure test commands are searched first in the virtualenv + +- rename README.txt to README.rst to make bitbucket happier + - re-fix issue2 - add command_whitelist to be used in ``[testenv*]`` sections, allowing to avoid warnings for commands such as ``make``, used from the commands value. diff -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e -r 638b892f6fadcbacbb31168c889827171a6a54eb README.rst --- /dev/null +++ b/README.rst @@ -0,0 +1,22 @@ + +What is Tox? +-------------------- + +Tox as is a generic virtualenv_ management and test command line tool you can use for: + +* checking your package installs correctly with different Python versions and + interpreters + +* running your tests in each of the environments, configuring your test tool of choice + +* acting as a frontend to Continuous Integration servers, greatly + reducing boilerplate and merging CI and shell-based testing. + +For more information, docs and many examples please checkout: + + http://tox.testrun.org + +have fun, + +holger krekel, May 2013 + diff -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e -r 638b892f6fadcbacbb31168c889827171a6a54eb README.txt --- a/README.txt +++ /dev/null @@ -1,22 +0,0 @@ - -What is Tox? --------------------- - -Tox as is a generic virtualenv_ management and test command line tool you can use for: - -* checking your package installs correctly with different Python versions and - interpreters - -* running your tests in each of the environments, configuring your test tool of choice - -* acting as a frontend to Continuous Integration servers, greatly - reducing boilerplate and merging CI and shell-based testing. - -For more information, docs and many examples please checkout: - - http://tox.testrun.org - -have fun, - -holger krekel, May 2012 - diff -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e -r 638b892f6fadcbacbb31168c889827171a6a54eb doc/config.txt --- a/doc/config.txt +++ b/doc/config.txt @@ -83,7 +83,7 @@ unix ``make`` for running tests you can list ``whitelist_externals=make`` or ``whitelist_externals=/usr/bin/make`` if you want more precision. If you don't want tox to issue a warning in any case, just use - ``whitelist_externals=*`` which will match all commands (not recommended). + ``whitelist_externals=*`` which will match all commands (not recommended). .. confval:: changedir=path diff -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e -r 638b892f6fadcbacbb31168c889827171a6a54eb setup.py --- a/setup.py +++ b/setup.py @@ -2,29 +2,6 @@ from setuptools import setup from setuptools.command.test import test as TestCommand -long_description = """ -What is Tox? -========================== - -Tox as is a generic virtualenv management and test command line tool you can -use for: - -* checking your package installs correctly with different - Python versions and interpreters - -* running your tests in each of the - environments, configuring your test tool of choice - -* acting as a frontend to Continuous Integration - servers, greatly reducing boilerplate and merging - CI and shell-based testing. - -For more information, docs and many examples please checkout the `home page`_: - - http://tox.testrun.org/ - -.. _`home page`: http://tox.testrun.org/ -""" class Tox(TestCommand): @@ -47,9 +24,9 @@ setup( name='tox', description='virtualenv-based automation of test activities', - long_description=long_description, + long_description=open("README.rst").read(), url='http://tox.testrun.org/', - version='1.5.dev9', + version='1.5.dev11', license='http://opensource.org/licenses/MIT', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], author='holger krekel', diff -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e -r 638b892f6fadcbacbb31168c889827171a6a54eb tests/test_venv.py --- a/tests/test_venv.py +++ b/tests/test_venv.py @@ -102,6 +102,20 @@ assert interp == venv.getconfigexecutable() assert venv.path_config.check(exists=False) + at pytest.mark.skipif("sys.platform == 'win32'") +def test_commandpath_venv_precendence(tmpdir, monkeypatch, + mocksession, newconfig): + config = newconfig([], """ + [testenv:py123] + """) + envconfig = config.envconfigs['py123'] + venv = VirtualEnv(envconfig, session=mocksession) + tmpdir.ensure("easy_install") + monkeypatch.setenv("PATH", str(tmpdir), prepend=os.pathsep) + envconfig.envbindir.ensure("easy_install") + p = venv.getcommandpath("easy_install") + assert py.path.local(p).relto(envconfig.envbindir), p + def test_create_distribute(monkeypatch, mocksession, newconfig): config = newconfig([], """ [testenv:py123] diff -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e -r 638b892f6fadcbacbb31168c889827171a6a54eb tests/test_z_cmdline.py --- a/tests/test_z_cmdline.py +++ b/tests/test_z_cmdline.py @@ -2,7 +2,7 @@ import py import pytest import sys -from conftest import ReportExpectMock +from tox._pytestplugin import ReportExpectMock pytest_plugins = "pytester" diff -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e -r 638b892f6fadcbacbb31168c889827171a6a54eb tox/__init__.py --- a/tox/__init__.py +++ b/tox/__init__.py @@ -1,5 +1,5 @@ # -__version__ = '1.5.dev9' +__version__ = '1.5.dev11' class exception: class Error(Exception): diff -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e -r 638b892f6fadcbacbb31168c889827171a6a54eb tox/_venv.py --- a/tox/_venv.py +++ b/tox/_venv.py @@ -74,12 +74,15 @@ p = cwd.join(name) if p.check(): return str(p) + p = None + if venv: + p = py.path.local.sysfind(name, paths=[self.envconfig.envbindir]) + if p is not None: + return p p = py.path.local.sysfind(name) if p is None: - raise tox.exception.InvocationError("could not find executable %r" - % (name,)) - if p.relto(self.envconfig.envdir): - return p + raise tox.exception.InvocationError( + "could not find executable %r" % (name,)) if venv: for x in self.envconfig.whitelist_externals: if p.fnmatch(x): diff -r 8d323c83d4d87511dd8fc12652b32d8cf9b5409e -r 638b892f6fadcbacbb31168c889827171a6a54eb toxbootstrap.py --- a/toxbootstrap.py +++ b/toxbootstrap.py @@ -58,7 +58,7 @@ """ -__version__ = '1.5.dev9' +__version__ = '1.5.dev11' import sys import os Repository URL: https://bitbucket.org/hpk42/tox/ -- 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 May 22 13:36:49 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 22 May 2013 11:36:49 -0000 Subject: [Pytest-commit] commit/pytest: 7 new changesets Message-ID: <20130522113649.12238.2110@bitbucket01.managed.contegix.com> 7 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/0b9d82e69dca/ Changeset: 0b9d82e69dca User: pfctdayelise Date: 2013-05-17 10:46:36 Summary: issue #308 first attempt, mark individual parametrize test instances with other marks (like xfail) Affected #: 2 files diff -r 7c468f83e347c21fbed87eeae457f645bfcc7a66 -r 0b9d82e69dcaf5d73e7b9824723fcbe7c6c61038 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -4,6 +4,7 @@ import sys import pytest from _pytest.main import getfslineno +from _pytest.mark import MarkDecorator, MarkInfo from _pytest.monkeypatch import monkeypatch from py._code.code import TerminalRepr @@ -565,11 +566,13 @@ self._globalid_args = set() self._globalparam = _notexists self._arg2scopenum = {} # used for sorting parametrized resources + self.keywords = {} def copy(self, metafunc): cs = CallSpec2(self.metafunc) cs.funcargs.update(self.funcargs) cs.params.update(self.params) + cs.keywords.update(self.keywords) cs._arg2scopenum.update(self._arg2scopenum) cs._idlist = list(self._idlist) cs._globalid = self._globalid @@ -593,7 +596,7 @@ def id(self): return "-".join(map(str, filter(None, self._idlist))) - def setmulti(self, valtype, argnames, valset, id, scopenum=0): + def setmulti(self, valtype, argnames, valset, id, keywords, scopenum=0): for arg,val in zip(argnames, valset): self._checkargnotcontained(arg) getattr(self, valtype)[arg] = val @@ -605,6 +608,7 @@ if val is _notexists: self._emptyparamspecified = True self._idlist.append(id) + self.keywords.update(keywords) def setall(self, funcargs, id, param): for x in funcargs: @@ -673,6 +677,18 @@ if not argvalues: argvalues = [(_notexists,) * len(argnames)] + # these marks/keywords will be applied in Function init + newkeywords = {} + for i, argval in enumerate(argvalues): + newkeywords[i] = {} + if isinstance(argval, MarkDecorator): + # convert into a mark without the test content mixed in + newmark = MarkDecorator(argval.markname, argval.args[:-1], argval.kwargs) + newkeywords[i] = {newmark.markname: newmark} + + argvalues = [av.args[-1] if isinstance(av, MarkDecorator) else av + for av in argvalues] + if scope is None: scope = "subfunction" scopenum = scopes.index(scope) @@ -691,7 +707,7 @@ assert len(valset) == len(argnames) newcallspec = callspec.copy(self) newcallspec.setmulti(valtype, argnames, valset, ids[i], - scopenum) + newkeywords[i], scopenum) newcalls.append(newcallspec) self._calls = newcalls @@ -908,6 +924,9 @@ for name, val in (py.builtin._getfuncdict(self.obj) or {}).items(): self.keywords[name] = val + if callspec: + for name, val in callspec.keywords.items(): + self.keywords[name] = val if keywords: for name, val in keywords.items(): self.keywords[name] = val diff -r 7c468f83e347c21fbed87eeae457f645bfcc7a66 -r 0b9d82e69dcaf5d73e7b9824723fcbe7c6c61038 testing/python/metafunc.py --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -577,4 +577,175 @@ "*3 passed*" ]) + @pytest.mark.issue308 + def test_mark_on_individual_parametrize_instance(self, testdir): + s = """ + import pytest + @pytest.mark.foo + @pytest.mark.parametrize(("input", "expected"), [ + (1, 2), + pytest.mark.bar((1, 3)), + (2, 3), + ]) + def test_increment(input, expected): + assert input + 1 == expected + """ + items = testdir.getitems(s) + assert len(items) == 3 + for item in items: + assert 'foo' in item.keywords + assert 'bar' not in items[0].keywords + assert 'bar' in items[1].keywords + assert 'bar' not in items[2].keywords + + @pytest.mark.issue308 + def test_select_individual_parametrize_instance_based_on_mark(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("input", "expected"), [ + (1, 2), + pytest.mark.foo((2, 3)), + (3, 4), + ]) + def test_increment(input, expected): + assert input + 1 == expected + """ + testdir.makepyfile(s) + rec = testdir.inline_run("-m", 'foo') + passed, skipped, fail = rec.listoutcomes() + assert len(passed) == 1 + assert len(skipped) == 0 + assert len(fail) == 0 + + @pytest.mark.xfail("is this important to support??") + @pytest.mark.issue308 + def test_nested_marks_on_individual_parametrize_instance(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("input", "expected"), [ + (1, 2), + pytest.mark.foo(pytest.mark.bar((1, 3))), + (2, 3), + ]) + def test_increment(input, expected): + assert input + 1 == expected + """ + items = testdir.getitems(s) + assert len(items) == 3 + for mark in ['foo', 'bar']: + assert mark not in items[0].keywords + assert mark in items[1].keywords + assert mark not in items[2].keywords + + @pytest.mark.xfail(reason="is this important to support??") + @pytest.mark.issue308 + def test_nested_marks_on_individual_parametrize_instance(self, testdir): + s = """ + import pytest + mastermark = pytest.mark.foo(pytest.mark.bar) + + @pytest.mark.parametrize(("input", "expected"), [ + (1, 2), + mastermark((1, 3)), + (2, 3), + ]) + def test_increment(input, expected): + assert input + 1 == expected + """ + items = testdir.getitems(s) + assert len(items) == 3 + for mark in ['foo', 'bar']: + assert mark not in items[0].keywords + assert mark in items[1].keywords + assert mark not in items[2].keywords + + @pytest.mark.issue308 + def test_simple_xfail_on_individual_parametrize_instance(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("input", "expected"), [ + (1, 2), + pytest.mark.xfail((1, 3)), + (2, 3), + ]) + def test_increment(input, expected): + assert input + 1 == expected + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + # xfail is skip?? + reprec.assertoutcome(passed=2, skipped=1) + + @pytest.mark.issue308 + def test_xfail_with_arg_on_individual_parametrize_instance(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("input", "expected"), [ + (1, 2), + pytest.mark.xfail("sys.version > 0")((1, 3)), + (2, 3), + ]) + def test_increment(input, expected): + assert input + 1 == expected + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2, skipped=1) + + @pytest.mark.issue308 + def test_xfail_with_kwarg_on_individual_parametrize_instance(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("input", "expected"), [ + (1, 2), + pytest.mark.xfail(reason="some bug")((1, 3)), + (2, 3), + ]) + def test_increment(input, expected): + assert input + 1 == expected + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2, skipped=1) + + @pytest.mark.issue308 + def test_xfail_with_arg_and_kwarg_on_individual_parametrize_instance(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("input", "expected"), [ + (1, 2), + pytest.mark.xfail("sys.version > 0", reason="some bug")((1, 3)), + (2, 3), + ]) + def test_increment(input, expected): + assert input + 1 == expected + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2, skipped=1) + + @pytest.mark.issue308 + def test_xfail_is_xpass_on_individual_parametrize_instance(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("input", "expected"), [ + (1, 2), + pytest.mark.xfail("sys.version > 0", reason="some bug")((2, 3)), + (3, 4), + ]) + def test_increment(input, expected): + assert input + 1 == expected + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + # xpass is fail, obviously :) + reprec.assertoutcome(passed=2, failed=1) + https://bitbucket.org/hpk42/pytest/commits/3c892457f19e/ Changeset: 3c892457f19e User: hpk42 Date: 2013-05-17 11:32:52 Summary: Merged hpk42/pytest into default Affected #: 2 files diff -r 0b9d82e69dcaf5d73e7b9824723fcbe7c6c61038 -r 3c892457f19ed9c676d7ddf26b7b49e151eb579f CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -20,6 +20,8 @@ - honor --tb style for setup/teardown errors as well. Thanks Maho. +- fix issue307 - use yaml.safe_load in example, thanks Mark Eichin. + Changes between 2.3.4 and 2.3.5 ----------------------------------- diff -r 0b9d82e69dcaf5d73e7b9824723fcbe7c6c61038 -r 3c892457f19ed9c676d7ddf26b7b49e151eb579f doc/en/example/nonpython/conftest.py --- a/doc/en/example/nonpython/conftest.py +++ b/doc/en/example/nonpython/conftest.py @@ -9,7 +9,7 @@ class YamlFile(pytest.File): def collect(self): import yaml # we need a yaml parser, e.g. PyYAML - raw = yaml.load(self.fspath.open()) + raw = yaml.safe_load(self.fspath.open()) for name, spec in raw.items(): yield YamlItem(name, self, spec) https://bitbucket.org/hpk42/pytest/commits/c38344905336/ Changeset: c38344905336 User: pfctdayelise Date: 2013-05-20 04:52:20 Summary: issue #308 address some comments by @hpk42 on 0b9d82e : - move tests into their own class, rename - add test showing metafunc.parametrize called in pytest_generate_tests rather than as decorator - add test and fix single-argname case - convert two loops into one in parametrize() also - renamed 'input' to 'n', since 'input' is a built-in Affected #: 2 files diff -r 0b9d82e69dcaf5d73e7b9824723fcbe7c6c61038 -r c38344905336154f7dd36cc05f84f1fb26cde9c4 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -671,24 +671,27 @@ It will also override any fixture-function defined scope, allowing to set a dynamic scope using test context or configuration. """ + # remove any marks applied to individual tests instances + # these marks will be applied in Function init + newkeywords = {} + strippedargvalues = [] + for i, argval in enumerate(argvalues): + if isinstance(argval, MarkDecorator): + # convert into a mark without the test content mixed in + newmark = MarkDecorator(argval.markname, argval.args[:-1], argval.kwargs) + newkeywords[i] = {newmark.markname: newmark} + strippedargvalues.append(argval.args[-1]) + else: + newkeywords[i] = {} + strippedargvalues.append(argval) + argvalues = strippedargvalues + if not isinstance(argnames, (tuple, list)): argnames = (argnames,) argvalues = [(val,) for val in argvalues] if not argvalues: argvalues = [(_notexists,) * len(argnames)] - # these marks/keywords will be applied in Function init - newkeywords = {} - for i, argval in enumerate(argvalues): - newkeywords[i] = {} - if isinstance(argval, MarkDecorator): - # convert into a mark without the test content mixed in - newmark = MarkDecorator(argval.markname, argval.args[:-1], argval.kwargs) - newkeywords[i] = {newmark.markname: newmark} - - argvalues = [av.args[-1] if isinstance(av, MarkDecorator) else av - for av in argvalues] - if scope is None: scope = "subfunction" scopenum = scopes.index(scope) diff -r 0b9d82e69dcaf5d73e7b9824723fcbe7c6c61038 -r c38344905336154f7dd36cc05f84f1fb26cde9c4 testing/python/metafunc.py --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -577,19 +577,21 @@ "*3 passed*" ]) - @pytest.mark.issue308 - def test_mark_on_individual_parametrize_instance(self, testdir): + + at pytest.mark.issue308 +class TestMarkersWithParametrization: + def test_simple_mark(self, testdir): s = """ import pytest @pytest.mark.foo - @pytest.mark.parametrize(("input", "expected"), [ + @pytest.mark.parametrize(("n", "expected"), [ (1, 2), pytest.mark.bar((1, 3)), (2, 3), ]) - def test_increment(input, expected): - assert input + 1 == expected + def test_increment(n, expected): + assert n + 1 == expected """ items = testdir.getitems(s) assert len(items) == 3 @@ -599,18 +601,17 @@ assert 'bar' in items[1].keywords assert 'bar' not in items[2].keywords - @pytest.mark.issue308 - def test_select_individual_parametrize_instance_based_on_mark(self, testdir): + def test_select_based_on_mark(self, testdir): s = """ import pytest - @pytest.mark.parametrize(("input", "expected"), [ + @pytest.mark.parametrize(("n", "expected"), [ (1, 2), pytest.mark.foo((2, 3)), (3, 4), ]) - def test_increment(input, expected): - assert input + 1 == expected + def test_increment(n, expected): + assert n + 1 == expected """ testdir.makepyfile(s) rec = testdir.inline_run("-m", 'foo') @@ -619,19 +620,19 @@ assert len(skipped) == 0 assert len(fail) == 0 - @pytest.mark.xfail("is this important to support??") - @pytest.mark.issue308 - def test_nested_marks_on_individual_parametrize_instance(self, testdir): + @pytest.mark.xfail(reason="is this important to support??") + def test_nested_marks(self, testdir): s = """ import pytest + mastermark = pytest.mark.foo(pytest.mark.bar) - @pytest.mark.parametrize(("input", "expected"), [ + @pytest.mark.parametrize(("n", "expected"), [ (1, 2), - pytest.mark.foo(pytest.mark.bar((1, 3))), + mastermark((1, 3)), (2, 3), ]) - def test_increment(input, expected): - assert input + 1 == expected + def test_increment(n, expected): + assert n + 1 == expected """ items = testdir.getitems(s) assert len(items) == 3 @@ -640,112 +641,123 @@ assert mark in items[1].keywords assert mark not in items[2].keywords - @pytest.mark.xfail(reason="is this important to support??") - @pytest.mark.issue308 - def test_nested_marks_on_individual_parametrize_instance(self, testdir): - s = """ - import pytest - mastermark = pytest.mark.foo(pytest.mark.bar) - - @pytest.mark.parametrize(("input", "expected"), [ - (1, 2), - mastermark((1, 3)), - (2, 3), - ]) - def test_increment(input, expected): - assert input + 1 == expected - """ - items = testdir.getitems(s) - assert len(items) == 3 - for mark in ['foo', 'bar']: - assert mark not in items[0].keywords - assert mark in items[1].keywords - assert mark not in items[2].keywords - - @pytest.mark.issue308 - def test_simple_xfail_on_individual_parametrize_instance(self, testdir): + def test_simple_xfail(self, testdir): s = """ import pytest - @pytest.mark.parametrize(("input", "expected"), [ + @pytest.mark.parametrize(("n", "expected"), [ (1, 2), pytest.mark.xfail((1, 3)), (2, 3), ]) - def test_increment(input, expected): - assert input + 1 == expected + def test_increment(n, expected): + assert n + 1 == expected """ testdir.makepyfile(s) reprec = testdir.inline_run() # xfail is skip?? reprec.assertoutcome(passed=2, skipped=1) - @pytest.mark.issue308 - def test_xfail_with_arg_on_individual_parametrize_instance(self, testdir): + def test_simple_xfail_single_argname(self, testdir): s = """ import pytest - @pytest.mark.parametrize(("input", "expected"), [ - (1, 2), - pytest.mark.xfail("sys.version > 0")((1, 3)), - (2, 3), + @pytest.mark.parametrize("n", [ + 2, + pytest.mark.xfail(3), + 4, ]) - def test_increment(input, expected): - assert input + 1 == expected + def test_isEven(n): + assert n % 2 == 0 """ testdir.makepyfile(s) reprec = testdir.inline_run() reprec.assertoutcome(passed=2, skipped=1) - @pytest.mark.issue308 - def test_xfail_with_kwarg_on_individual_parametrize_instance(self, testdir): + def test_xfail_with_arg(self, testdir): s = """ import pytest - @pytest.mark.parametrize(("input", "expected"), [ + @pytest.mark.parametrize(("n", "expected"), [ (1, 2), - pytest.mark.xfail(reason="some bug")((1, 3)), + pytest.mark.xfail("sys.version > 0")((1, 3)), (2, 3), ]) - def test_increment(input, expected): - assert input + 1 == expected + def test_increment(n, expected): + assert n + 1 == expected """ testdir.makepyfile(s) reprec = testdir.inline_run() reprec.assertoutcome(passed=2, skipped=1) - @pytest.mark.issue308 - def test_xfail_with_arg_and_kwarg_on_individual_parametrize_instance(self, testdir): + def test_xfail_with_kwarg(self, testdir): s = """ import pytest - @pytest.mark.parametrize(("input", "expected"), [ + @pytest.mark.parametrize(("n", "expected"), [ (1, 2), - pytest.mark.xfail("sys.version > 0", reason="some bug")((1, 3)), + pytest.mark.xfail(reason="some bug")((1, 3)), (2, 3), ]) - def test_increment(input, expected): - assert input + 1 == expected + def test_increment(n, expected): + assert n + 1 == expected """ testdir.makepyfile(s) reprec = testdir.inline_run() reprec.assertoutcome(passed=2, skipped=1) - @pytest.mark.issue308 - def test_xfail_is_xpass_on_individual_parametrize_instance(self, testdir): + def test_xfail_with_arg_and_kwarg(self, testdir): s = """ import pytest - @pytest.mark.parametrize(("input", "expected"), [ + @pytest.mark.parametrize(("n", "expected"), [ + (1, 2), + pytest.mark.xfail("sys.version > 0", reason="some bug")((1, 3)), + (2, 3), + ]) + def test_increment(n, expected): + assert n + 1 == expected + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2, skipped=1) + + def test_xfail_passing_is_xpass(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("n", "expected"), [ (1, 2), pytest.mark.xfail("sys.version > 0", reason="some bug")((2, 3)), (3, 4), ]) - def test_increment(input, expected): - assert input + 1 == expected + def test_increment(n, expected): + assert n + 1 == expected """ testdir.makepyfile(s) reprec = testdir.inline_run() # xpass is fail, obviously :) reprec.assertoutcome(passed=2, failed=1) + def test_parametrize_called_in_generate_tests(self, testdir): + s = """ + import pytest + + + def pytest_generate_tests(metafunc): + passingTestData = [(1, 2), + (2, 3)] + failingTestData = [(1, 3), + (2, 2)] + + testData = passingTestData + [pytest.mark.xfail(d) + for d in failingTestData] + metafunc.parametrize(("n", "expected"), testData) + + + def test_increment(n, expected): + assert n + 1 == expected + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2, skipped=2) https://bitbucket.org/hpk42/pytest/commits/598409cef71a/ Changeset: 598409cef71a User: pfctdayelise Date: 2013-05-20 04:56:30 Summary: ? pull/merge Affected #: 2 files diff -r c38344905336154f7dd36cc05f84f1fb26cde9c4 -r 598409cef71aeb988c97bdb83fbf01782ac42b75 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -20,6 +20,8 @@ - honor --tb style for setup/teardown errors as well. Thanks Maho. +- fix issue307 - use yaml.safe_load in example, thanks Mark Eichin. + Changes between 2.3.4 and 2.3.5 ----------------------------------- diff -r c38344905336154f7dd36cc05f84f1fb26cde9c4 -r 598409cef71aeb988c97bdb83fbf01782ac42b75 doc/en/example/nonpython/conftest.py --- a/doc/en/example/nonpython/conftest.py +++ b/doc/en/example/nonpython/conftest.py @@ -9,7 +9,7 @@ class YamlFile(pytest.File): def collect(self): import yaml # we need a yaml parser, e.g. PyYAML - raw = yaml.load(self.fspath.open()) + raw = yaml.safe_load(self.fspath.open()) for name, spec in raw.items(): yield YamlItem(name, self, spec) https://bitbucket.org/hpk42/pytest/commits/9210cf8ca829/ Changeset: 9210cf8ca829 User: pfctdayelise Date: 2013-05-21 03:12:45 Summary: issue #308 + docs Affected #: 3 files diff -r 598409cef71aeb988c97bdb83fbf01782ac42b75 -r 9210cf8ca82927da6f835cbebddbfe2367ddc817 doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -185,6 +185,29 @@ in which case it will be applied to all functions and methods defined in the module. +.. _`marking individual tests when using parametrize`: + +Marking individual tests when using parametrize +----------------------------------------------- + +When using parametrize, applying a mark will make it apply +to each individual test. However it is also possible to +apply a marker to an individual test instance:: + + import pytest + + @pytest.mark.foo + @pytest.mark.parametrize(("n", "expected"), [ + (1, 2), + pytest.mark.bar((1, 3)), + (2, 3), + ]) + def test_increment(n, expected): + assert n + 1 == expected + +In this example the mark "foo" will apply to each of the three +tests, whereas the "bar" mark is only applied to the second test. +Skip and xfail marks can also be applied in this way, see :ref:`skip/xfail with parametrize`. .. _`adding a custom marker from a plugin`: diff -r 598409cef71aeb988c97bdb83fbf01782ac42b75 -r 9210cf8ca82927da6f835cbebddbfe2367ddc817 doc/en/parametrize.txt --- a/doc/en/parametrize.txt +++ b/doc/en/parametrize.txt @@ -82,6 +82,18 @@ Note that there ways how you can mark a class or a module, see :ref:`mark`. +It is also possible to mark individual test instances within parametrize:: + + # content of test_expectation.py + import pytest + @pytest.mark.parametrize(("input", "expected"), [ + ("3+5", 8), + ("2+4", 6), + pytest.mark.xfail(("6*9", 42)), + ]) + def test_eval(input, expected): + assert eval(input) == expected + .. _`pytest_generate_tests`: diff -r 598409cef71aeb988c97bdb83fbf01782ac42b75 -r 9210cf8ca82927da6f835cbebddbfe2367ddc817 doc/en/skipping.txt --- a/doc/en/skipping.txt +++ b/doc/en/skipping.txt @@ -176,6 +176,28 @@ ======================== 6 xfailed in 0.05 seconds ========================= +.. _`skip/xfail with parametrize`: + +Skip/xfail with parametrize +--------------------------- + +It is possible to apply markers like skip and xfail to individual +test instances when using parametrize: + + import pytest + + @pytest.mark.parametrize(("n", "expected"), [ + (1, 2), + pytest.mark.xfail((1, 0)), + pytest.mark.xfail(reason="some bug")((1, 3)), + (2, 3), + (3, 4), + (4, 5), + pytest.mark.skipif("sys.version_info >= (3,0)")((10, 11)), + ]) + def test_increment(n, expected): + assert n + 1 == expected + Imperative xfail from within a test or setup function ------------------------------------------------------ https://bitbucket.org/hpk42/pytest/commits/2917dfdb26d3/ Changeset: 2917dfdb26d3 User: pfctdayelise Date: 2013-05-21 03:18:37 Summary: Merged hpk42/pytest into default Affected #: 3 files diff -r 9210cf8ca82927da6f835cbebddbfe2367ddc817 -r 2917dfdb26d3152ec0ac40590cbcbe64bb764ec0 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -11,6 +11,11 @@ when importing markers between modules. Specifying conditions as strings will remain fully supported. +- improved doctest counting for doctests in python modules -- + files without any doctest items will not show up anymore + and doctest examples are counted as separate test items. + thanks Danilo Bellini. + - fix issue245 by depending on the released py-1.4.14 which fixes py.io.dupfile to work with files with no mode. Thanks Jason R. Coombs. diff -r 9210cf8ca82927da6f835cbebddbfe2367ddc817 -r 2917dfdb26d3152ec0ac40590cbcbe64bb764ec0 _pytest/doctest.py --- a/_pytest/doctest.py +++ b/_pytest/doctest.py @@ -34,6 +34,14 @@ self.reprlocation.toterminal(tw) class DoctestItem(pytest.Item): + def __init__(self, name, parent, runner=None, dtest=None): + super(DoctestItem, self).__init__(name, parent) + self.runner = runner + self.dtest = dtest + + def runtest(self): + self.runner.run(self.dtest) + def repr_failure(self, excinfo): doctest = py.std.doctest if excinfo.errisinstance((doctest.DocTestFailure, @@ -76,7 +84,7 @@ return super(DoctestItem, self).repr_failure(excinfo) def reportinfo(self): - return self.fspath, None, "[doctest]" + return self.fspath, None, "[doctest] %s" % self.name class DoctestTextfile(DoctestItem, pytest.File): def runtest(self): @@ -91,8 +99,8 @@ extraglobs=dict(getfixture=fixture_request.getfuncargvalue), raise_on_error=True, verbose=0) -class DoctestModule(DoctestItem, pytest.File): - def runtest(self): +class DoctestModule(pytest.File): + def collect(self): doctest = py.std.doctest if self.fspath.basename == "conftest.py": module = self.config._conftest.importconftest(self.fspath) @@ -102,7 +110,11 @@ self.funcargs = {} self._fixtureinfo = FuncFixtureInfo((), [], {}) fixture_request = FixtureRequest(self) - failed, tot = doctest.testmod( - module, raise_on_error=True, verbose=0, - extraglobs=dict(getfixture=fixture_request.getfuncargvalue), - optionflags=doctest.ELLIPSIS) + doctest_globals = dict(getfixture=fixture_request.getfuncargvalue) + # uses internal doctest module parsing mechanism + finder = doctest.DocTestFinder() + runner = doctest.DebugRunner(verbose=0, optionflags=doctest.ELLIPSIS) + for test in finder.find(module, module.__name__, + extraglobs=doctest_globals): + if test.examples: # skip empty doctests + yield DoctestItem(test.name, self, runner, test) diff -r 9210cf8ca82927da6f835cbebddbfe2367ddc817 -r 2917dfdb26d3152ec0ac40590cbcbe64bb764ec0 testing/test_doctest.py --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -1,4 +1,4 @@ -from _pytest.doctest import DoctestModule, DoctestTextfile +from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile import py, pytest class TestDoctests: @@ -19,13 +19,61 @@ items, reprec = testdir.inline_genitems(w) assert len(items) == 1 - def test_collect_module(self, testdir): + def test_collect_module_empty(self, testdir): path = testdir.makepyfile(whatever="#") for p in (path, testdir.tmpdir): items, reprec = testdir.inline_genitems(p, '--doctest-modules') + assert len(items) == 0 + + def test_collect_module_single_modulelevel_doctest(self, testdir): + path = testdir.makepyfile(whatever='""">>> pass"""') + for p in (path, testdir.tmpdir): + items, reprec = testdir.inline_genitems(p, + '--doctest-modules') assert len(items) == 1 - assert isinstance(items[0], DoctestModule) + assert isinstance(items[0], DoctestItem) + assert isinstance(items[0].parent, DoctestModule) + + def test_collect_module_two_doctest_one_modulelevel(self, testdir): + path = testdir.makepyfile(whatever=""" + '>>> x = None' + def my_func(): + ">>> magic = 42 " + """) + for p in (path, testdir.tmpdir): + items, reprec = testdir.inline_genitems(p, + '--doctest-modules') + assert len(items) == 2 + assert isinstance(items[0], DoctestItem) + assert isinstance(items[1], DoctestItem) + assert isinstance(items[0].parent, DoctestModule) + assert items[0].parent is items[1].parent + + def test_collect_module_two_doctest_no_modulelevel(self, testdir): + path = testdir.makepyfile(whatever=""" + '# Empty' + def my_func(): + ">>> magic = 42 " + def unuseful(): + ''' + # This is a function + # >>> # it doesn't have any doctest + ''' + def another(): + ''' + # This is another function + >>> import os # this one does have a doctest + ''' + """) + for p in (path, testdir.tmpdir): + items, reprec = testdir.inline_genitems(p, + '--doctest-modules') + assert len(items) == 2 + assert isinstance(items[0], DoctestItem) + assert isinstance(items[1], DoctestItem) + assert isinstance(items[0].parent, DoctestModule) + assert items[0].parent is items[1].parent def test_simple_doctestfile(self, testdir): p = testdir.maketxtfile(test_doc=""" @@ -164,3 +212,47 @@ """) reprec = testdir.inline_run(p, "--doctest-modules") reprec.assertoutcome(passed=1) + + def test_doctestmodule_three_tests(self, testdir): + p = testdir.makepyfile(""" + ''' + >>> dir = getfixture('tmpdir') + >>> type(dir).__name__ + 'LocalPath' + ''' + def my_func(): + ''' + >>> magic = 42 + >>> magic - 42 + 0 + ''' + def unuseful(): + pass + def another(): + ''' + >>> import os + >>> os is os + True + ''' + """) + reprec = testdir.inline_run(p, "--doctest-modules") + reprec.assertoutcome(passed=3) + + def test_doctestmodule_two_tests_one_fail(self, testdir): + p = testdir.makepyfile(""" + class MyClass: + def bad_meth(self): + ''' + >>> magic = 42 + >>> magic + 0 + ''' + def nice_meth(self): + ''' + >>> magic = 42 + >>> magic - 42 + 0 + ''' + """) + reprec = testdir.inline_run(p, "--doctest-modules") + reprec.assertoutcome(failed=1, passed=1) https://bitbucket.org/hpk42/pytest/commits/fff62647d862/ Changeset: fff62647d862 User: hpk42 Date: 2013-05-22 13:36:39 Summary: Merged in pfctdayelise/pytest (pull request #36) issue 308 Affected #: 5 files diff -r db9c8ef9f6e01273a0dcc23b9ba55f954aab662a -r fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -4,6 +4,7 @@ import sys import pytest from _pytest.main import getfslineno +from _pytest.mark import MarkDecorator, MarkInfo from _pytest.monkeypatch import monkeypatch from py._code.code import TerminalRepr @@ -565,11 +566,13 @@ self._globalid_args = set() self._globalparam = _notexists self._arg2scopenum = {} # used for sorting parametrized resources + self.keywords = {} def copy(self, metafunc): cs = CallSpec2(self.metafunc) cs.funcargs.update(self.funcargs) cs.params.update(self.params) + cs.keywords.update(self.keywords) cs._arg2scopenum.update(self._arg2scopenum) cs._idlist = list(self._idlist) cs._globalid = self._globalid @@ -593,7 +596,7 @@ def id(self): return "-".join(map(str, filter(None, self._idlist))) - def setmulti(self, valtype, argnames, valset, id, scopenum=0): + def setmulti(self, valtype, argnames, valset, id, keywords, scopenum=0): for arg,val in zip(argnames, valset): self._checkargnotcontained(arg) getattr(self, valtype)[arg] = val @@ -605,6 +608,7 @@ if val is _notexists: self._emptyparamspecified = True self._idlist.append(id) + self.keywords.update(keywords) def setall(self, funcargs, id, param): for x in funcargs: @@ -667,6 +671,21 @@ It will also override any fixture-function defined scope, allowing to set a dynamic scope using test context or configuration. """ + # remove any marks applied to individual tests instances + # these marks will be applied in Function init + newkeywords = {} + strippedargvalues = [] + for i, argval in enumerate(argvalues): + if isinstance(argval, MarkDecorator): + # convert into a mark without the test content mixed in + newmark = MarkDecorator(argval.markname, argval.args[:-1], argval.kwargs) + newkeywords[i] = {newmark.markname: newmark} + strippedargvalues.append(argval.args[-1]) + else: + newkeywords[i] = {} + strippedargvalues.append(argval) + argvalues = strippedargvalues + if not isinstance(argnames, (tuple, list)): argnames = (argnames,) argvalues = [(val,) for val in argvalues] @@ -691,7 +710,7 @@ assert len(valset) == len(argnames) newcallspec = callspec.copy(self) newcallspec.setmulti(valtype, argnames, valset, ids[i], - scopenum) + newkeywords[i], scopenum) newcalls.append(newcallspec) self._calls = newcalls @@ -908,6 +927,9 @@ for name, val in (py.builtin._getfuncdict(self.obj) or {}).items(): self.keywords[name] = val + if callspec: + for name, val in callspec.keywords.items(): + self.keywords[name] = val if keywords: for name, val in keywords.items(): self.keywords[name] = val diff -r db9c8ef9f6e01273a0dcc23b9ba55f954aab662a -r fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f doc/en/example/markers.txt --- a/doc/en/example/markers.txt +++ b/doc/en/example/markers.txt @@ -185,6 +185,29 @@ in which case it will be applied to all functions and methods defined in the module. +.. _`marking individual tests when using parametrize`: + +Marking individual tests when using parametrize +----------------------------------------------- + +When using parametrize, applying a mark will make it apply +to each individual test. However it is also possible to +apply a marker to an individual test instance:: + + import pytest + + @pytest.mark.foo + @pytest.mark.parametrize(("n", "expected"), [ + (1, 2), + pytest.mark.bar((1, 3)), + (2, 3), + ]) + def test_increment(n, expected): + assert n + 1 == expected + +In this example the mark "foo" will apply to each of the three +tests, whereas the "bar" mark is only applied to the second test. +Skip and xfail marks can also be applied in this way, see :ref:`skip/xfail with parametrize`. .. _`adding a custom marker from a plugin`: diff -r db9c8ef9f6e01273a0dcc23b9ba55f954aab662a -r fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f doc/en/parametrize.txt --- a/doc/en/parametrize.txt +++ b/doc/en/parametrize.txt @@ -82,6 +82,18 @@ Note that there ways how you can mark a class or a module, see :ref:`mark`. +It is also possible to mark individual test instances within parametrize:: + + # content of test_expectation.py + import pytest + @pytest.mark.parametrize(("input", "expected"), [ + ("3+5", 8), + ("2+4", 6), + pytest.mark.xfail(("6*9", 42)), + ]) + def test_eval(input, expected): + assert eval(input) == expected + .. _`pytest_generate_tests`: diff -r db9c8ef9f6e01273a0dcc23b9ba55f954aab662a -r fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f doc/en/skipping.txt --- a/doc/en/skipping.txt +++ b/doc/en/skipping.txt @@ -176,6 +176,28 @@ ======================== 6 xfailed in 0.05 seconds ========================= +.. _`skip/xfail with parametrize`: + +Skip/xfail with parametrize +--------------------------- + +It is possible to apply markers like skip and xfail to individual +test instances when using parametrize: + + import pytest + + @pytest.mark.parametrize(("n", "expected"), [ + (1, 2), + pytest.mark.xfail((1, 0)), + pytest.mark.xfail(reason="some bug")((1, 3)), + (2, 3), + (3, 4), + (4, 5), + pytest.mark.skipif("sys.version_info >= (3,0)")((10, 11)), + ]) + def test_increment(n, expected): + assert n + 1 == expected + Imperative xfail from within a test or setup function ------------------------------------------------------ diff -r db9c8ef9f6e01273a0dcc23b9ba55f954aab662a -r fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f testing/python/metafunc.py --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -578,3 +578,186 @@ ]) + at pytest.mark.issue308 +class TestMarkersWithParametrization: + def test_simple_mark(self, testdir): + s = """ + import pytest + + @pytest.mark.foo + @pytest.mark.parametrize(("n", "expected"), [ + (1, 2), + pytest.mark.bar((1, 3)), + (2, 3), + ]) + def test_increment(n, expected): + assert n + 1 == expected + """ + items = testdir.getitems(s) + assert len(items) == 3 + for item in items: + assert 'foo' in item.keywords + assert 'bar' not in items[0].keywords + assert 'bar' in items[1].keywords + assert 'bar' not in items[2].keywords + + def test_select_based_on_mark(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("n", "expected"), [ + (1, 2), + pytest.mark.foo((2, 3)), + (3, 4), + ]) + def test_increment(n, expected): + assert n + 1 == expected + """ + testdir.makepyfile(s) + rec = testdir.inline_run("-m", 'foo') + passed, skipped, fail = rec.listoutcomes() + assert len(passed) == 1 + assert len(skipped) == 0 + assert len(fail) == 0 + + @pytest.mark.xfail(reason="is this important to support??") + def test_nested_marks(self, testdir): + s = """ + import pytest + mastermark = pytest.mark.foo(pytest.mark.bar) + + @pytest.mark.parametrize(("n", "expected"), [ + (1, 2), + mastermark((1, 3)), + (2, 3), + ]) + def test_increment(n, expected): + assert n + 1 == expected + """ + items = testdir.getitems(s) + assert len(items) == 3 + for mark in ['foo', 'bar']: + assert mark not in items[0].keywords + assert mark in items[1].keywords + assert mark not in items[2].keywords + + def test_simple_xfail(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("n", "expected"), [ + (1, 2), + pytest.mark.xfail((1, 3)), + (2, 3), + ]) + def test_increment(n, expected): + assert n + 1 == expected + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + # xfail is skip?? + reprec.assertoutcome(passed=2, skipped=1) + + def test_simple_xfail_single_argname(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize("n", [ + 2, + pytest.mark.xfail(3), + 4, + ]) + def test_isEven(n): + assert n % 2 == 0 + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2, skipped=1) + + def test_xfail_with_arg(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("n", "expected"), [ + (1, 2), + pytest.mark.xfail("sys.version > 0")((1, 3)), + (2, 3), + ]) + def test_increment(n, expected): + assert n + 1 == expected + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2, skipped=1) + + def test_xfail_with_kwarg(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("n", "expected"), [ + (1, 2), + pytest.mark.xfail(reason="some bug")((1, 3)), + (2, 3), + ]) + def test_increment(n, expected): + assert n + 1 == expected + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2, skipped=1) + + def test_xfail_with_arg_and_kwarg(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("n", "expected"), [ + (1, 2), + pytest.mark.xfail("sys.version > 0", reason="some bug")((1, 3)), + (2, 3), + ]) + def test_increment(n, expected): + assert n + 1 == expected + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2, skipped=1) + + def test_xfail_passing_is_xpass(self, testdir): + s = """ + import pytest + + @pytest.mark.parametrize(("n", "expected"), [ + (1, 2), + pytest.mark.xfail("sys.version > 0", reason="some bug")((2, 3)), + (3, 4), + ]) + def test_increment(n, expected): + assert n + 1 == expected + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + # xpass is fail, obviously :) + reprec.assertoutcome(passed=2, failed=1) + + def test_parametrize_called_in_generate_tests(self, testdir): + s = """ + import pytest + + + def pytest_generate_tests(metafunc): + passingTestData = [(1, 2), + (2, 3)] + failingTestData = [(1, 3), + (2, 2)] + + testData = passingTestData + [pytest.mark.xfail(d) + for d in failingTestData] + metafunc.parametrize(("n", "expected"), testData) + + + def test_increment(n, expected): + assert n + 1 == expected + """ + testdir.makepyfile(s) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2, skipped=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 Wed May 22 15:25:09 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 22 May 2013 13:25:09 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: - add Brianna (@pfctdayelise ) to changelog and contributors Message-ID: <20130522132509.9853.42717@bitbucket04.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/6865f468f152/ Changeset: 6865f468f152 User: hpk42 Date: 2013-05-22 15:24:58 Summary: - add Brianna (@pfctdayelise ) to changelog and contributors - fix some broken tests on py32/py33 (related to issue308 merge) - re-format docstrings - Affected #: 5 files diff -r fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f -r 6865f468f15242e1a399fd20e9bc9e5ad44cb02d AUTHORS --- a/AUTHORS +++ b/AUTHORS @@ -8,6 +8,7 @@ Floris Bruynooghe Jason R. Coombs Samuele Pedroni +Brianna Laugher Carl Friedrich Bolz Armin Rigo Maho diff -r fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f -r 6865f468f15242e1a399fd20e9bc9e5ad44cb02d CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ Changes between 2.3.5 and 2.4.DEV ----------------------------------- +- fix issue 308 - allow to mark/xfail/skip individual parameter sets + when parametrizing. Thanks Brianna Laugher. + - (experimental) allow fixture functions to be implemented as context managers. Thanks Andreas Pelme, Vladimir Keleshev. diff -r fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f -r 6865f468f15242e1a399fd20e9bc9e5ad44cb02d _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -651,11 +651,12 @@ :arg argnames: an argument name or a list of argument names - :arg argvalues: The list of argvalues determines how often a test is invoked - with different argument values. If only one argname was specified argvalues - is a list of simple values. If N argnames were specified, argvalues must - be a list of N-tuples, where each tuple-element specifies a value for its - respective argname. + :arg argvalues: The list of argvalues determines how often a + test is invoked with different argument values. If only one + argname was specified argvalues is a list of simple values. If N + argnames were specified, argvalues must be a list of N-tuples, + where each tuple-element specifies a value for its respective + argname. :arg indirect: if True each argvalue corresponding to an argname will be passed as request.param to its respective argname fixture @@ -671,20 +672,20 @@ It will also override any fixture-function defined scope, allowing to set a dynamic scope using test context or configuration. """ - # remove any marks applied to individual tests instances - # these marks will be applied in Function init + + # individual parametrized argument sets can be wrapped in a + # marker in which case we unwrap the values and apply the mark + # at Function init newkeywords = {} - strippedargvalues = [] + unwrapped_argvalues = [] for i, argval in enumerate(argvalues): if isinstance(argval, MarkDecorator): - # convert into a mark without the test content mixed in - newmark = MarkDecorator(argval.markname, argval.args[:-1], argval.kwargs) + newmark = MarkDecorator(argval.markname, + argval.args[:-1], argval.kwargs) newkeywords[i] = {newmark.markname: newmark} - strippedargvalues.append(argval.args[-1]) - else: - newkeywords[i] = {} - strippedargvalues.append(argval) - argvalues = strippedargvalues + argval = argval.args[-1] + unwrapped_argvalues.append(argval) + argvalues = unwrapped_argvalues if not isinstance(argnames, (tuple, list)): argnames = (argnames,) @@ -710,7 +711,7 @@ assert len(valset) == len(argnames) newcallspec = callspec.copy(self) newcallspec.setmulti(valtype, argnames, valset, ids[i], - newkeywords[i], scopenum) + newkeywords.get(i, {}), scopenum) newcalls.append(newcallspec) self._calls = newcalls diff -r fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f -r 6865f468f15242e1a399fd20e9bc9e5ad44cb02d testing/python/metafunc.py --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -578,8 +578,8 @@ ]) - at pytest.mark.issue308 class TestMarkersWithParametrization: + pytestmark = pytest.mark.issue308 def test_simple_mark(self, testdir): s = """ import pytest @@ -680,7 +680,7 @@ @pytest.mark.parametrize(("n", "expected"), [ (1, 2), - pytest.mark.xfail("sys.version > 0")((1, 3)), + pytest.mark.xfail("True")((1, 3)), (2, 3), ]) def test_increment(n, expected): @@ -712,7 +712,7 @@ @pytest.mark.parametrize(("n", "expected"), [ (1, 2), - pytest.mark.xfail("sys.version > 0", reason="some bug")((1, 3)), + pytest.mark.xfail("True", reason="some bug")((1, 3)), (2, 3), ]) def test_increment(n, expected): diff -r fff62647d8622e95f5f0e28f1e4a25aeeb4b0a8f -r 6865f468f15242e1a399fd20e9bc9e5ad44cb02d tox.ini --- a/tox.ini +++ b/tox.ini @@ -31,7 +31,7 @@ PYTHONDONTWRITEBYTECODE=1 commands= py.test -n3 -rfsxX \ - --junitxml={envlogdir}/junit-{envname}.xml [] + --junitxml={envlogdir}/junit-{envname}.xml {posargs:testing} [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 Thu May 23 17:24:11 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 23 May 2013 15:24:11 -0000 Subject: [Pytest-commit] commit/tox: hpk42: fix barries name Message-ID: <20130523152411.29122.80347@bitbucket01.managed.contegix.com> 1 new commit in tox: https://bitbucket.org/hpk42/tox/commits/fc53c4e02616/ Changeset: fc53c4e02616 User: hpk42 Date: 2013-05-23 17:23:08 Summary: fix barries name Affected #: 2 files diff -r 638b892f6fadcbacbb31168c889827171a6a54eb -r fc53c4e02616e58ea5ab482409b25746220b1738 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -70,7 +70,7 @@ - change tests to not rely on os.path.expanduser (closes #60), also make mock session return args[1:] for more precise checking (closes #61) - thanks to Barry Warszaw for both. + thanks to Barry Warsaw for both. 1.4.2 ----------------- diff -r 638b892f6fadcbacbb31168c889827171a6a54eb -r fc53c4e02616e58ea5ab482409b25746220b1738 CONTRIBUTORS --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -4,6 +4,7 @@ Krisztian Fekete Marc Abramowitz Sridhar Ratnakumar +Barry Warsaw Chris Rose Jannis Leidl Ronny Pfannschmidt Repository URL: https://bitbucket.org/hpk42/tox/ -- 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 May 27 21:40:52 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Mon, 27 May 2013 19:40:52 -0000 Subject: [Pytest-commit] commit/pytest: 7 new changesets Message-ID: <20130527194052.16621.33074@bitbucket16.managed.contegix.com> 7 new commits in pytest: https://bitbucket.org/hpk42/pytest/commits/191ff82647ca/ Changeset: 191ff82647ca User: Wouter van Ackooy Date: 2013-05-20 14:37:58 Summary: Fixed issue #306: Keywords and markers are now matched in a defined way. Also applied some pep8 formatting while fixing. Affected #: 3 files diff -r 7c468f83e347c21fbed87eeae457f645bfcc7a66 -r 191ff82647ca3e61116861483efb8fe2c2985e82 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -216,6 +216,9 @@ #: keywords/markers collected from all scopes self.keywords = NodeKeywords(self) + #: allow adding of extra keywords to use for matching + self.extra_keyword_matches = [] + #self.extrainit() @property @@ -307,6 +310,15 @@ chain.reverse() return chain + def listextrakeywords(self): + """ Return a list of all extra keywords in self and any parents.""" + extra_keywords = [] + item = self + while item is not None: + extra_keywords.extend(item.extra_keyword_matches) + item = item.parent + return extra_keywords + def listnames(self): return [x.name for x in self.listchain()] diff -r 7c468f83e347c21fbed87eeae457f645bfcc7a66 -r 191ff82647ca3e61116861483efb8fe2c2985e82 _pytest/mark.py --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -1,44 +1,56 @@ """ generic mechanism for marking and selecting python functions. """ import pytest, py + def pytest_namespace(): return {'mark': MarkGenerator()} + def pytest_addoption(parser): group = parser.getgroup("general") - group._addoption('-k', + group._addoption( + '-k', action="store", dest="keyword", default='', metavar="EXPRESSION", help="only run tests which match the given substring expression. " "An expression is a python evaluatable expression " - "where all names are substring-matched against test names " - "and keywords. Example: -k 'test_method or test_other' " - "matches all test functions whose name contains " - "'test_method' or 'test_other'.") + "where all names are substring-matched against test names" + "and their parent classes. Example: -k 'test_method or test " + "other' matches all test functions and classes whose name " + "contains 'test_method' or 'test_other'. " + "Additionally keywords are matched to classes and functions " + "containing extra names in their 'extra_keyword_matches' list, " + "as well as functions which have names assigned directly to them." + ) - group._addoption("-m", + group._addoption( + "-m", action="store", dest="markexpr", default="", metavar="MARKEXPR", help="only run tests matching given mark expression. " "example: -m 'mark1 and not mark2'." - ) + ) - group.addoption("--markers", action="store_true", help= - "show markers (builtin, plugin and per-project ones).") + group.addoption( + "--markers", action="store_true", + help="show markers (builtin, plugin and per-project ones)." + ) parser.addini("markers", "markers for test functions", 'linelist') + def pytest_cmdline_main(config): if config.option.markers: config.pluginmanager.do_configure(config) tw = py.io.TerminalWriter() for line in config.getini("markers"): name, rest = line.split(":", 1) - tw.write("@pytest.mark.%s:" % name, bold=True) + tw.write("@pytest.mark.%s:" % name, bold=True) tw.line(rest) tw.line() config.pluginmanager.do_unconfigure(config) return 0 pytest_cmdline_main.tryfirst = True + def pytest_collection_modifyitems(items, config): keywordexpr = config.option.keyword matchexpr = config.option.markexpr @@ -67,32 +79,76 @@ config.hook.pytest_deselected(items=deselected) items[:] = remaining -class BoolDict: - def __init__(self, mydict): - self._mydict = mydict - def __getitem__(self, name): - return name in self._mydict -class SubstringDict: - def __init__(self, mydict): - self._mydict = mydict - def __getitem__(self, name): - for key in self._mydict: - if name in key: +class MarkMapping: + """Provides a local mapping for markers. + Only the marker names from the given :class:`NodeKeywords` will be mapped, + so the names are taken only from :class:`MarkInfo` or + :class:`MarkDecorator` items. + """ + def __init__(self, keywords): + mymarks = [] + for key, value in keywords.items(): + if isinstance(value, MarkInfo) or isinstance(value, MarkDecorator): + mymarks.append(key) + self._mymarks = mymarks + + def __getitem__(self, markname): + return markname in self._mymarks + + +class KeywordMapping: + """Provides a local mapping for keywords. + Given a list of names, map any substring of one of these names to True. + """ + def __init__(self, names): + self._names = names + + def __getitem__(self, subname): + for name in self._names: + if subname in name: return True return False -def matchmark(colitem, matchexpr): - return eval(matchexpr, {}, BoolDict(colitem.keywords)) + +def matchmark(colitem, markexpr): + """Tries to match on any marker names, attached to the given colitem.""" + return eval(markexpr, {}, MarkMapping(colitem.keywords)) + def matchkeyword(colitem, keywordexpr): + """Tries to match given keyword expression to given collector item. + + Will match on the name of colitem, including the names of its parents. + Only matches names of items which are either a :class:`Class` or a + :class:`Function`. + Additionally, matches on names in the 'extra_keyword_matches' list of + any item, as well as names directly assigned to test functions. + """ keywordexpr = keywordexpr.replace("-", "not ") - return eval(keywordexpr, {}, SubstringDict(colitem.keywords)) + mapped_names = [] + + # Add the names of the current item and any parent items + for item in colitem.listchain(): + if isinstance(item, pytest.Class) or isinstance(item, pytest.Function): + mapped_names.append(item.name) + + # Add the names added as extra keywords to current or parent items + for name in colitem.listextrakeywords(): + mapped_names.append(name) + + # Add the names attached to the current function through direct assignment + for name in colitem.function.func_dict: + mapped_names.append(name) + + return eval(keywordexpr, {}, KeywordMapping(mapped_names)) + def pytest_configure(config): if config.option.strict: pytest.mark._config = config + class MarkGenerator: """ Factory for :class:`MarkDecorator` objects - exposed as a ``py.test.mark`` singleton instance. Example:: @@ -126,6 +182,7 @@ if name not in self._markers: raise AttributeError("%r not a registered marker" % (name,)) + class MarkDecorator: """ A decorator for test functions and test classes. When applied it will create :class:`MarkInfo` objects which may be @@ -149,7 +206,7 @@ def __repr__(self): d = self.__dict__.copy() name = d.pop('markname') - return "" %(name, d) + return "" % (name, d) def __call__(self, *args, **kwargs): """ if passed a single callable argument: decorate it with mark info. @@ -162,15 +219,17 @@ if hasattr(func, 'pytestmark'): l = func.pytestmark if not isinstance(l, list): - func.pytestmark = [l, self] + func.pytestmark = [l, self] else: - l.append(self) + l.append(self) else: - func.pytestmark = [self] + func.pytestmark = [self] else: holder = getattr(func, self.markname, None) if holder is None: - holder = MarkInfo(self.markname, self.args, self.kwargs) + holder = MarkInfo( + self.markname, self.args, self.kwargs + ) setattr(func, self.markname, holder) else: holder.add(self.args, self.kwargs) @@ -180,6 +239,7 @@ args = self.args + args return self.__class__(self.markname, args=args, kwargs=kw) + class MarkInfo: """ Marking object created by :class:`MarkDecorator` instances. """ def __init__(self, name, args, kwargs): @@ -193,7 +253,8 @@ def __repr__(self): return "" % ( - self.name, self.args, self.kwargs) + self.name, self.args, self.kwargs + ) def add(self, args, kwargs): """ add a MarkInfo with the given args and kwargs. """ @@ -205,4 +266,3 @@ """ yield MarkInfo objects each relating to a marking-call. """ for args, kwargs in self._arglist: yield MarkInfo(self.name, args, kwargs) - diff -r 7c468f83e347c21fbed87eeae457f645bfcc7a66 -r 191ff82647ca3e61116861483efb8fe2c2985e82 testing/test_mark.py --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -382,7 +382,6 @@ assert len(reprec.getcalls('pytest_deselected')) == 1 for keyword in ['test_one', 'est_on']: - #yield check, keyword, 'test_one' check(keyword, 'test_one') check('TestClass and test', 'test_method_one') @@ -401,7 +400,7 @@ def pytest_pycollect_makeitem(__multicall__, name): if name == "TestClass": item = __multicall__.execute() - item.keywords["xxx"] = True + item.extra_keyword_matches.append("xxx") return item """) reprec = testdir.inline_run(p.dirpath(), '-s', '-k', keyword) https://bitbucket.org/hpk42/pytest/commits/64a8b6828ebc/ Changeset: 64a8b6828ebc User: Wouter van Ackooy Date: 2013-05-22 07:41:46 Summary: Added lost space. Affected #: 1 file diff -r 191ff82647ca3e61116861483efb8fe2c2985e82 -r 64a8b6828ebcf2e8cd9066bd44c6c83b7b89a9b1 _pytest/mark.py --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -13,7 +13,7 @@ action="store", dest="keyword", default='', metavar="EXPRESSION", help="only run tests which match the given substring expression. " "An expression is a python evaluatable expression " - "where all names are substring-matched against test names" + "where all names are substring-matched against test names " "and their parent classes. Example: -k 'test_method or test " "other' matches all test functions and classes whose name " "contains 'test_method' or 'test_other'. " https://bitbucket.org/hpk42/pytest/commits/88815889e4a3/ Changeset: 88815889e4a3 User: Wouter van Ackooy Date: 2013-05-23 09:12:50 Summary: Added a test to check there is no matching on magic values. Affected #: 1 file diff -r 64a8b6828ebcf2e8cd9066bd44c6c83b7b89a9b1 -r 88815889e4a3dd27082f20187232050f17f05424 testing/test_mark.py --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -439,3 +439,21 @@ reprec = testdir.inline_run("-k", "mykeyword", p) passed, skipped, failed = reprec.countoutcomes() assert failed == 1 + + def test_no_magic_values(self, testdir): + """Make sure the tests do not match on magic values, + no double underscored values, like '__dict__', + and no instance values, like '()'. + """ + p = testdir.makepyfile(""" + def test_one(): assert 1 + """) + def assert_test_is_not_selected(keyword): + reprec = testdir.inline_run("-k", keyword, p) + passed, skipped, failed = reprec.countoutcomes() + dlist = reprec.getcalls("pytest_deselected") + assert passed + skipped + failed == 0 + assert len(dlist) == 1 + + assert_test_is_not_selected("__") + assert_test_is_not_selected("()") https://bitbucket.org/hpk42/pytest/commits/13c86754a55e/ Changeset: 13c86754a55e User: Wouter van Ackooy Date: 2013-05-23 12:21:40 Summary: Added new test to check on matching markers to full test names, which was possible before. Also adjusted check on number of deselected tests. Affected #: 1 file diff -r 88815889e4a3dd27082f20187232050f17f05424 -r 13c86754a55e774c40cff5c2d256c7f3cba56b3d testing/test_mark.py --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -345,6 +345,24 @@ assert l[0].args == ("pos0",) assert l[1].args == ("pos1",) + def test_no_marker_match_on_unmarked_names(self, testdir): + p = testdir.makepyfile(""" + import pytest + @pytest.mark.shouldmatch + def test_marked(): + assert 1 + + def test_unmarked(): + assert 1 + """) + reprec = testdir.inline_run("-m", "test_unmarked", p) + passed, skipped, failed = reprec.listoutcomes() + assert len(passed) + len(skipped) + len(failed) == 0 + dlist = reprec.getcalls("pytest_deselected") + deselected_tests = dlist[0].items + assert len(deselected_tests) == 2 + + def test_keywords_at_node_level(self, testdir): p = testdir.makepyfile(""" import pytest @@ -453,7 +471,8 @@ passed, skipped, failed = reprec.countoutcomes() dlist = reprec.getcalls("pytest_deselected") assert passed + skipped + failed == 0 - assert len(dlist) == 1 + deselected_tests = dlist[0].items + assert len(deselected_tests) == 1 assert_test_is_not_selected("__") assert_test_is_not_selected("()") https://bitbucket.org/hpk42/pytest/commits/1a919511ae4f/ Changeset: 1a919511ae4f User: Wouter van Ackooy Date: 2013-05-27 17:58:39 Summary: Issue 306: Use the names of all the parents in the chain for matching, except the Instance objects. Affected #: 1 file diff -r 13c86754a55e774c40cff5c2d256c7f3cba56b3d -r 1a919511ae4ffbdfd0aadfce7bf4924436c83290 _pytest/mark.py --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -130,7 +130,7 @@ # Add the names of the current item and any parent items for item in colitem.listchain(): - if isinstance(item, pytest.Class) or isinstance(item, pytest.Function): + if not isinstance(item, pytest.Instance): mapped_names.append(item.name) # Add the names added as extra keywords to current or parent items https://bitbucket.org/hpk42/pytest/commits/c3bc70bc6704/ Changeset: c3bc70bc6704 User: Wouter van Ackooy Date: 2013-05-27 18:14:35 Summary: Issue 306: Used a set for the extra_keywords, and used listchain for parent iteration. Affected #: 3 files diff -r 1a919511ae4ffbdfd0aadfce7bf4924436c83290 -r c3bc70bc6704b95ed26b1072ab719c696cde68a4 _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -217,7 +217,7 @@ self.keywords = NodeKeywords(self) #: allow adding of extra keywords to use for matching - self.extra_keyword_matches = [] + self.extra_keyword_matches = set() #self.extrainit() @@ -311,12 +311,11 @@ return chain def listextrakeywords(self): - """ Return a list of all extra keywords in self and any parents.""" - extra_keywords = [] + """ Return a set of all extra keywords in self and any parents.""" + extra_keywords = set() item = self - while item is not None: - extra_keywords.extend(item.extra_keyword_matches) - item = item.parent + for item in self.listchain(): + extra_keywords.update(item.extra_keyword_matches) return extra_keywords def listnames(self): diff -r 1a919511ae4ffbdfd0aadfce7bf4924436c83290 -r c3bc70bc6704b95ed26b1072ab719c696cde68a4 _pytest/mark.py --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -18,7 +18,7 @@ "other' matches all test functions and classes whose name " "contains 'test_method' or 'test_other'. " "Additionally keywords are matched to classes and functions " - "containing extra names in their 'extra_keyword_matches' list, " + "containing extra names in their 'extra_keyword_matches' set, " "as well as functions which have names assigned directly to them." ) @@ -87,10 +87,10 @@ :class:`MarkDecorator` items. """ def __init__(self, keywords): - mymarks = [] + mymarks = set() for key, value in keywords.items(): if isinstance(value, MarkInfo) or isinstance(value, MarkDecorator): - mymarks.append(key) + mymarks.add(key) self._mymarks = mymarks def __getitem__(self, markname): @@ -122,24 +122,24 @@ Will match on the name of colitem, including the names of its parents. Only matches names of items which are either a :class:`Class` or a :class:`Function`. - Additionally, matches on names in the 'extra_keyword_matches' list of + Additionally, matches on names in the 'extra_keyword_matches' set of any item, as well as names directly assigned to test functions. """ keywordexpr = keywordexpr.replace("-", "not ") - mapped_names = [] + mapped_names = set() # Add the names of the current item and any parent items for item in colitem.listchain(): if not isinstance(item, pytest.Instance): - mapped_names.append(item.name) + mapped_names.add(item.name) # Add the names added as extra keywords to current or parent items for name in colitem.listextrakeywords(): - mapped_names.append(name) + mapped_names.add(name) # Add the names attached to the current function through direct assignment for name in colitem.function.func_dict: - mapped_names.append(name) + mapped_names.add(name) return eval(keywordexpr, {}, KeywordMapping(mapped_names)) diff -r 1a919511ae4ffbdfd0aadfce7bf4924436c83290 -r c3bc70bc6704b95ed26b1072ab719c696cde68a4 testing/test_mark.py --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -418,7 +418,7 @@ def pytest_pycollect_makeitem(__multicall__, name): if name == "TestClass": item = __multicall__.execute() - item.extra_keyword_matches.append("xxx") + item.extra_keyword_matches.add("xxx") return item """) reprec = testdir.inline_run(p.dirpath(), '-s', '-k', keyword) https://bitbucket.org/hpk42/pytest/commits/5ce621f0b3d2/ Changeset: 5ce621f0b3d2 User: hpk42 Date: 2013-05-27 21:40:41 Summary: Merged in w00t0r/pytest-fixes (pull request #35) Fixed issue #306: Keywords and markers are now matched in a defined way. Also applied some pep8 formatting while fixing. Affected #: 3 files diff -r 6865f468f15242e1a399fd20e9bc9e5ad44cb02d -r 5ce621f0b3d2857ce7035b765cd18836ec8ea73f _pytest/main.py --- a/_pytest/main.py +++ b/_pytest/main.py @@ -216,6 +216,9 @@ #: keywords/markers collected from all scopes self.keywords = NodeKeywords(self) + #: allow adding of extra keywords to use for matching + self.extra_keyword_matches = set() + #self.extrainit() @property @@ -307,6 +310,14 @@ chain.reverse() return chain + def listextrakeywords(self): + """ Return a set of all extra keywords in self and any parents.""" + extra_keywords = set() + item = self + for item in self.listchain(): + extra_keywords.update(item.extra_keyword_matches) + return extra_keywords + def listnames(self): return [x.name for x in self.listchain()] diff -r 6865f468f15242e1a399fd20e9bc9e5ad44cb02d -r 5ce621f0b3d2857ce7035b765cd18836ec8ea73f _pytest/mark.py --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -1,44 +1,56 @@ """ generic mechanism for marking and selecting python functions. """ import pytest, py + def pytest_namespace(): return {'mark': MarkGenerator()} + def pytest_addoption(parser): group = parser.getgroup("general") - group._addoption('-k', + group._addoption( + '-k', action="store", dest="keyword", default='', metavar="EXPRESSION", help="only run tests which match the given substring expression. " "An expression is a python evaluatable expression " "where all names are substring-matched against test names " - "and keywords. Example: -k 'test_method or test_other' " - "matches all test functions whose name contains " - "'test_method' or 'test_other'.") + "and their parent classes. Example: -k 'test_method or test " + "other' matches all test functions and classes whose name " + "contains 'test_method' or 'test_other'. " + "Additionally keywords are matched to classes and functions " + "containing extra names in their 'extra_keyword_matches' set, " + "as well as functions which have names assigned directly to them." + ) - group._addoption("-m", + group._addoption( + "-m", action="store", dest="markexpr", default="", metavar="MARKEXPR", help="only run tests matching given mark expression. " "example: -m 'mark1 and not mark2'." - ) + ) - group.addoption("--markers", action="store_true", help= - "show markers (builtin, plugin and per-project ones).") + group.addoption( + "--markers", action="store_true", + help="show markers (builtin, plugin and per-project ones)." + ) parser.addini("markers", "markers for test functions", 'linelist') + def pytest_cmdline_main(config): if config.option.markers: config.pluginmanager.do_configure(config) tw = py.io.TerminalWriter() for line in config.getini("markers"): name, rest = line.split(":", 1) - tw.write("@pytest.mark.%s:" % name, bold=True) + tw.write("@pytest.mark.%s:" % name, bold=True) tw.line(rest) tw.line() config.pluginmanager.do_unconfigure(config) return 0 pytest_cmdline_main.tryfirst = True + def pytest_collection_modifyitems(items, config): keywordexpr = config.option.keyword matchexpr = config.option.markexpr @@ -67,32 +79,76 @@ config.hook.pytest_deselected(items=deselected) items[:] = remaining -class BoolDict: - def __init__(self, mydict): - self._mydict = mydict - def __getitem__(self, name): - return name in self._mydict -class SubstringDict: - def __init__(self, mydict): - self._mydict = mydict - def __getitem__(self, name): - for key in self._mydict: - if name in key: +class MarkMapping: + """Provides a local mapping for markers. + Only the marker names from the given :class:`NodeKeywords` will be mapped, + so the names are taken only from :class:`MarkInfo` or + :class:`MarkDecorator` items. + """ + def __init__(self, keywords): + mymarks = set() + for key, value in keywords.items(): + if isinstance(value, MarkInfo) or isinstance(value, MarkDecorator): + mymarks.add(key) + self._mymarks = mymarks + + def __getitem__(self, markname): + return markname in self._mymarks + + +class KeywordMapping: + """Provides a local mapping for keywords. + Given a list of names, map any substring of one of these names to True. + """ + def __init__(self, names): + self._names = names + + def __getitem__(self, subname): + for name in self._names: + if subname in name: return True return False -def matchmark(colitem, matchexpr): - return eval(matchexpr, {}, BoolDict(colitem.keywords)) + +def matchmark(colitem, markexpr): + """Tries to match on any marker names, attached to the given colitem.""" + return eval(markexpr, {}, MarkMapping(colitem.keywords)) + def matchkeyword(colitem, keywordexpr): + """Tries to match given keyword expression to given collector item. + + Will match on the name of colitem, including the names of its parents. + Only matches names of items which are either a :class:`Class` or a + :class:`Function`. + Additionally, matches on names in the 'extra_keyword_matches' set of + any item, as well as names directly assigned to test functions. + """ keywordexpr = keywordexpr.replace("-", "not ") - return eval(keywordexpr, {}, SubstringDict(colitem.keywords)) + mapped_names = set() + + # Add the names of the current item and any parent items + for item in colitem.listchain(): + if not isinstance(item, pytest.Instance): + mapped_names.add(item.name) + + # Add the names added as extra keywords to current or parent items + for name in colitem.listextrakeywords(): + mapped_names.add(name) + + # Add the names attached to the current function through direct assignment + for name in colitem.function.func_dict: + mapped_names.add(name) + + return eval(keywordexpr, {}, KeywordMapping(mapped_names)) + def pytest_configure(config): if config.option.strict: pytest.mark._config = config + class MarkGenerator: """ Factory for :class:`MarkDecorator` objects - exposed as a ``py.test.mark`` singleton instance. Example:: @@ -126,6 +182,7 @@ if name not in self._markers: raise AttributeError("%r not a registered marker" % (name,)) + class MarkDecorator: """ A decorator for test functions and test classes. When applied it will create :class:`MarkInfo` objects which may be @@ -149,7 +206,7 @@ def __repr__(self): d = self.__dict__.copy() name = d.pop('markname') - return "" %(name, d) + return "" % (name, d) def __call__(self, *args, **kwargs): """ if passed a single callable argument: decorate it with mark info. @@ -162,15 +219,17 @@ if hasattr(func, 'pytestmark'): l = func.pytestmark if not isinstance(l, list): - func.pytestmark = [l, self] + func.pytestmark = [l, self] else: - l.append(self) + l.append(self) else: - func.pytestmark = [self] + func.pytestmark = [self] else: holder = getattr(func, self.markname, None) if holder is None: - holder = MarkInfo(self.markname, self.args, self.kwargs) + holder = MarkInfo( + self.markname, self.args, self.kwargs + ) setattr(func, self.markname, holder) else: holder.add(self.args, self.kwargs) @@ -180,6 +239,7 @@ args = self.args + args return self.__class__(self.markname, args=args, kwargs=kw) + class MarkInfo: """ Marking object created by :class:`MarkDecorator` instances. """ def __init__(self, name, args, kwargs): @@ -193,7 +253,8 @@ def __repr__(self): return "" % ( - self.name, self.args, self.kwargs) + self.name, self.args, self.kwargs + ) def add(self, args, kwargs): """ add a MarkInfo with the given args and kwargs. """ @@ -205,4 +266,3 @@ """ yield MarkInfo objects each relating to a marking-call. """ for args, kwargs in self._arglist: yield MarkInfo(self.name, args, kwargs) - diff -r 6865f468f15242e1a399fd20e9bc9e5ad44cb02d -r 5ce621f0b3d2857ce7035b765cd18836ec8ea73f testing/test_mark.py --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -345,6 +345,24 @@ assert l[0].args == ("pos0",) assert l[1].args == ("pos1",) + def test_no_marker_match_on_unmarked_names(self, testdir): + p = testdir.makepyfile(""" + import pytest + @pytest.mark.shouldmatch + def test_marked(): + assert 1 + + def test_unmarked(): + assert 1 + """) + reprec = testdir.inline_run("-m", "test_unmarked", p) + passed, skipped, failed = reprec.listoutcomes() + assert len(passed) + len(skipped) + len(failed) == 0 + dlist = reprec.getcalls("pytest_deselected") + deselected_tests = dlist[0].items + assert len(deselected_tests) == 2 + + def test_keywords_at_node_level(self, testdir): p = testdir.makepyfile(""" import pytest @@ -382,7 +400,6 @@ assert len(reprec.getcalls('pytest_deselected')) == 1 for keyword in ['test_one', 'est_on']: - #yield check, keyword, 'test_one' check(keyword, 'test_one') check('TestClass and test', 'test_method_one') @@ -401,7 +418,7 @@ def pytest_pycollect_makeitem(__multicall__, name): if name == "TestClass": item = __multicall__.execute() - item.keywords["xxx"] = True + item.extra_keyword_matches.add("xxx") return item """) reprec = testdir.inline_run(p.dirpath(), '-s', '-k', keyword) @@ -440,3 +457,22 @@ reprec = testdir.inline_run("-k", "mykeyword", p) passed, skipped, failed = reprec.countoutcomes() assert failed == 1 + + def test_no_magic_values(self, testdir): + """Make sure the tests do not match on magic values, + no double underscored values, like '__dict__', + and no instance values, like '()'. + """ + p = testdir.makepyfile(""" + def test_one(): assert 1 + """) + def assert_test_is_not_selected(keyword): + reprec = testdir.inline_run("-k", keyword, p) + passed, skipped, failed = reprec.countoutcomes() + dlist = reprec.getcalls("pytest_deselected") + assert passed + skipped + failed == 0 + deselected_tests = dlist[0].items + assert len(deselected_tests) == 1 + + assert_test_is_not_selected("__") + assert_test_is_not_selected("()") 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 May 27 23:05:01 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Mon, 27 May 2013 21:05:01 -0000 Subject: [Pytest-commit] commit/pytest: gutworth: use __dict__ not func_dict for Python 3 compatibility Message-ID: <20130527210501.11517.74296@bitbucket22.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/705c35908c83/ Changeset: 705c35908c83 User: gutworth Date: 2013-05-27 23:04:53 Summary: use __dict__ not func_dict for Python 3 compatibility Affected #: 1 file diff -r 5ce621f0b3d2857ce7035b765cd18836ec8ea73f -r 705c35908c8351c5f30977553f7af48ea3b578fa _pytest/mark.py --- a/_pytest/mark.py +++ b/_pytest/mark.py @@ -138,7 +138,7 @@ mapped_names.add(name) # Add the names attached to the current function through direct assignment - for name in colitem.function.func_dict: + for name in colitem.function.__dict__: mapped_names.add(name) return eval(keywordexpr, {}, KeywordMapping(mapped_names)) 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 May 28 10:33:24 2013 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Tue, 28 May 2013 08:33:24 -0000 Subject: [Pytest-commit] commit/pytest: hpk42: allow to specify parametrize inputs as a comma-separated string Message-ID: <20130528083324.26429.78739@bitbucket22.managed.contegix.com> 1 new commit in pytest: https://bitbucket.org/hpk42/pytest/commits/5d0b6123d654/ Changeset: 5d0b6123d654 User: hpk42 Date: 2013-05-28 10:32:54 Summary: allow to specify parametrize inputs as a comma-separated string add Wouter to changelog and to authors Affected #: 7 files diff -r 705c35908c8351c5f30977553f7af48ea3b578fa -r 5d0b6123d6541ad497cc592d72b9ed50a7c26915 AUTHORS --- a/AUTHORS +++ b/AUTHORS @@ -7,6 +7,7 @@ Benjamin Peterson Floris Bruynooghe Jason R. Coombs +Wouter van Ackooy Samuele Pedroni Brianna Laugher Carl Friedrich Bolz diff -r 705c35908c8351c5f30977553f7af48ea3b578fa -r 5d0b6123d6541ad497cc592d72b9ed50a7c26915 CHANGELOG --- a/CHANGELOG +++ b/CHANGELOG @@ -4,6 +4,13 @@ - fix issue 308 - allow to mark/xfail/skip individual parameter sets when parametrizing. Thanks Brianna Laugher. +- simplify parametrize() signature: allow to pass a CSV-separated string + to specify argnames. For example: ``pytest.mark.parametrize("input,expected", [(1,2), (2,3)])`` is possible now in addition to the prior + ``pytest.mark.parametrize(("input", "expected"), ...)``. + +- fix issue 306 - cleanup of -k/-m options to only match markers/test + names/keywords respectively. Thanks Wouter van Ackooy. + - (experimental) allow fixture functions to be implemented as context managers. Thanks Andreas Pelme, Vladimir Keleshev. diff -r 705c35908c8351c5f30977553f7af48ea3b578fa -r 5d0b6123d6541ad497cc592d72b9ed50a7c26915 _pytest/__init__.py --- a/_pytest/__init__.py +++ b/_pytest/__init__.py @@ -1,2 +1,2 @@ # -__version__ = '2.4.0.dev2' +__version__ = '2.4.0.dev3' diff -r 705c35908c8351c5f30977553f7af48ea3b578fa -r 5d0b6123d6541ad497cc592d72b9ed50a7c26915 _pytest/python.py --- a/_pytest/python.py +++ b/_pytest/python.py @@ -649,7 +649,8 @@ during the collection phase. If you need to setup expensive resources 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 argnames: a comma-separated string denoting one or more argument + names, or a list/tuple of argument strings. :arg argvalues: The list of argvalues determines how often a test is invoked with different argument values. If only one @@ -688,7 +689,8 @@ argvalues = unwrapped_argvalues if not isinstance(argnames, (tuple, list)): - argnames = (argnames,) + argnames = [x.strip() for x in argnames.split(",") if x.strip()] + if len(argnames) == 1: argvalues = [(val,) for val in argvalues] if not argvalues: argvalues = [(_notexists,) * len(argnames)] diff -r 705c35908c8351c5f30977553f7af48ea3b578fa -r 5d0b6123d6541ad497cc592d72b9ed50a7c26915 doc/en/parametrize.txt --- a/doc/en/parametrize.txt +++ b/doc/en/parametrize.txt @@ -30,7 +30,7 @@ .. regendoc: wipe -.. versionadded:: 2.2 +.. versionadded:: 2.2, improved in 2.4 The builtin ``pytest.mark.parametrize`` decorator enables parametrization of arguments for a test function. Here is a typical example @@ -39,7 +39,7 @@ # content of test_expectation.py import pytest - @pytest.mark.parametrize(("input", "expected"), [ + @pytest.mark.parametrize("input,expected", [ ("3+5", 8), ("2+4", 6), ("6*9", 42), @@ -47,23 +47,24 @@ def test_eval(input, expected): assert eval(input) == expected -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:: +Here, the ``@parametrize`` decorator defines three different ``(input,output)`` +tuples so that that the ``test_eval`` function will run three times using +them in turn:: $ py.test - =========================== test session starts ============================ - platform linux2 -- Python 2.7.3 -- pytest-2.3.5 + ============================= test session starts ============================== + platform linux2 -- Python 2.7.3 -- pytest-2.4.0.dev3 + plugins: xdist, cache, cli, pep8, xprocess, cov, capturelog, bdd-splinter, rerunfailures, instafail, localserver collected 3 items test_expectation.py ..F - ================================= FAILURES ================================= - ____________________________ test_eval[6*9-42] _____________________________ + =================================== FAILURES =================================== + ______________________________ test_eval[6*9-42] _______________________________ input = '6*9', expected = 42 - @pytest.mark.parametrize(("input", "expected"), [ + @pytest.mark.parametrize("input,expected", [ ("3+5", 8), ("2+4", 6), ("6*9", 42), @@ -74,19 +75,21 @@ E + where 54 = eval('6*9') test_expectation.py:8: AssertionError - ==================== 1 failed, 2 passed in 0.01 seconds ==================== + ====================== 1 failed, 2 passed in 0.02 seconds ====================== -As expected only one pair of input/output values fails the simple test function. -And as usual with test function arguments, you can see the ``input`` and ``output`` values in the traceback. +As designed in this example, only one pair of input/output values fails +the simple test function. And as usual with test function arguments, +you can see the ``input`` and ``output`` values in the traceback. -Note that there ways how you can mark a class or a module, -see :ref:`mark`. +Note that you could also use the parametrize marker on a class or a module +(see :ref:`mark`) which would invoke several functions with the argument sets. -It is also possible to mark individual test instances within parametrize:: +It is also possible to mark individual test instances within parametrize, +for example with the builtin ``mark.xfail``:: # content of test_expectation.py import pytest - @pytest.mark.parametrize(("input", "expected"), [ + @pytest.mark.parametrize("input,expected", [ ("3+5", 8), ("2+4", 6), pytest.mark.xfail(("6*9", 42)), @@ -94,6 +97,27 @@ def test_eval(input, expected): assert eval(input) == expected +Let's run this:: + + $ py.test + ============================= test session starts ============================== + platform linux2 -- Python 2.7.3 -- pytest-2.4.0.dev3 + plugins: xdist, cache, cli, pep8, xprocess, cov, capturelog, bdd-splinter, rerunfailures, instafail, localserver + collected 3 items + + test_expectation.py ..x + + ===================== 2 passed, 1 xfailed in 0.02 seconds ====================== + +The one parameter set which caused a failure previously now +shows up as an "xfailed (expected to fail)" test. + +.. note:: + + In versions prior to 2.4 one needed to specify the argument + names as a tuple. This remains valid but the simpler ``"name1,name2,..."`` + comma-separated-string syntax is now advertised fist because + it's easier to write, produces less line noise. .. _`pytest_generate_tests`: @@ -140,15 +164,15 @@ $ py.test -q --stringinput="!" test_strings.py F - ================================= FAILURES ================================= - ___________________________ test_valid_string[!] ___________________________ + =================================== FAILURES =================================== + _____________________________ test_valid_string[!] _____________________________ stringinput = '!' def test_valid_string(stringinput): > assert stringinput.isalpha() - E assert () - E + where = '!'.isalpha + E assert () + E + where = '!'.isalpha test_strings.py:3: AssertionError @@ -160,8 +184,8 @@ $ 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:974: got empty parameter set, function test_valid_string at /tmp/doc-exec-240/test_strings.py:1 + =========================== short test summary info ============================ + SKIP [1] /home/hpk/p/pytest/_pytest/python.py:999: got empty parameter set, function test_valid_string at /tmp/doc-exec-2/test_strings.py:1 For further examples, you might want to look at :ref:`more parametrization examples `. diff -r 705c35908c8351c5f30977553f7af48ea3b578fa -r 5d0b6123d6541ad497cc592d72b9ed50a7c26915 setup.py --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.4.0.dev2', + version='2.4.0.dev3', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff -r 705c35908c8351c5f30977553f7af48ea3b578fa -r 5d0b6123d6541ad497cc592d72b9ed50a7c26915 testing/python/metafunc.py --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -221,6 +221,16 @@ "*6 fail*", ]) + def test_parametrize_CSV(self, testdir): + testdir.makepyfile(""" + import pytest + @pytest.mark.parametrize("x, y,", [(1,2), (2,3)]) + def test_func(x, y): + assert x+1 == y + """) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=2) + def test_parametrize_class_scenarios(self, testdir): testdir.makepyfile(""" # same as doc/en/example/parametrize scenario example 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.