From 6461295ab400c52e640d47bd60aa3146374af3c0 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Sun, 7 Nov 2010 10:19:58 +0100 Subject: [PATCH] probably the last major internal cleanup action: rename collection to session which now is the root collection node. This means that session, collection and config objects have a more defined relationship (previously there was no way to get from a collection node or even from a runtest hook to the session object which was strange). --- doc/assert.txt | 6 ++-- doc/doctest.txt | 4 +-- doc/example/builtin.txt | 4 +-- doc/example/controlskip.txt | 6 ++-- doc/example/mysetup.txt | 10 +++--- doc/example/nonpython.txt | 19 +++++----- doc/example/pythoncollection.txt | 1 - doc/example/simple.txt | 2 +- doc/faq.txt | 16 ++++----- doc/funcargs.txt | 7 ++-- doc/getting-started.txt | 16 ++++----- doc/mark.txt | 14 ++++---- doc/monkeypatch.txt | 4 +-- doc/plugins.txt | 4 +-- doc/tmpdir.txt | 6 ++-- doc/unittest.txt | 4 +-- doc/usage.txt | 1 - pytest/__init__.py | 2 +- pytest/hookspec.py | 10 +++--- pytest/plugin/pytester.py | 26 +++++++------- pytest/plugin/python.py | 18 +++++----- pytest/plugin/session.py | 62 ++++++++++++++++++++------------ pytest/plugin/terminal.py | 5 +-- setup.py | 2 +- testing/plugin/test_python.py | 18 +++++----- testing/plugin/test_resultlog.py | 8 ++--- testing/plugin/test_runner.py | 2 +- testing/plugin/test_terminal.py | 2 +- testing/test_collection.py | 27 +++++++------- 29 files changed, 163 insertions(+), 143 deletions(-) diff --git a/doc/assert.txt b/doc/assert.txt index 32438134e..5e7cb08ce 100644 --- a/doc/assert.txt +++ b/doc/assert.txt @@ -21,7 +21,7 @@ assertion fails you will see the value of ``x``:: $ py.test test_assert1.py =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 test path 1: test_assert1.py test_assert1.py F @@ -35,7 +35,7 @@ assertion fails you will see the value of ``x``:: E + where 3 = f() test_assert1.py:5: AssertionError - ========================= 1 failed in 0.02 seconds ========================= + ========================= 1 failed in 0.05 seconds ========================= Reporting details about the failing assertion is achieved by re-evaluating the assert expression and recording intermediate values. @@ -101,7 +101,7 @@ if you run this module:: $ py.test test_assert2.py =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 test path 1: test_assert2.py test_assert2.py F diff --git a/doc/doctest.txt b/doc/doctest.txt index 5120a5392..90a0a7f02 100644 --- a/doc/doctest.txt +++ b/doc/doctest.txt @@ -44,7 +44,7 @@ then you can just invoke ``py.test`` without command line options:: $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 - test path 1: /tmp/doc-exec-400 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 + test path 1: /tmp/doc-exec-519 ============================= in 0.00 seconds ============================= diff --git a/doc/example/builtin.txt b/doc/example/builtin.txt index 008d4eec2..71d8c4e98 100644 --- a/doc/example/builtin.txt +++ b/doc/example/builtin.txt @@ -27,10 +27,10 @@ Let's run our little function:: F ================================= FAILURES ================================= ______________________________ test_something ______________________________ - + def test_something(): > checkconfig(42) E Failed: not configured: 42 - + test_checkconfig.py:8: Failed 1 failed in 0.02 seconds diff --git a/doc/example/controlskip.txt b/doc/example/controlskip.txt index 3194034f4..cad3bb509 100644 --- a/doc/example/controlskip.txt +++ b/doc/example/controlskip.txt @@ -36,12 +36,12 @@ and when running it will see a skipped "slow" test:: $ py.test test_module.py -rs # "-rs" means report on the little 's' =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 test path 1: test_module.py test_module.py .s ========================= short test summary info ========================== - SKIP [1] /tmp/doc-exec-435/conftest.py:9: need --runslow option to run + SKIP [1] /tmp/doc-exec-557/conftest.py:9: need --runslow option to run =================== 1 passed, 1 skipped in 0.02 seconds ==================== @@ -49,7 +49,7 @@ Or run it including the ``slow`` marked test:: $ py.test test_module.py --runslow =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 test path 1: test_module.py test_module.py .. diff --git a/doc/example/mysetup.txt b/doc/example/mysetup.txt index ef325b441..d8fb1e24c 100644 --- a/doc/example/mysetup.txt +++ b/doc/example/mysetup.txt @@ -49,7 +49,7 @@ You can now run the test:: $ py.test test_sample.py =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 test path 1: test_sample.py test_sample.py F @@ -57,7 +57,7 @@ You can now run the test:: ================================= FAILURES ================================= _______________________________ test_answer ________________________________ - mysetup = + mysetup = def test_answer(mysetup): app = mysetup.myapp() @@ -122,14 +122,14 @@ Running it yields:: $ py.test test_ssh.py -rs =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 test path 1: test_ssh.py test_ssh.py s ========================= short test summary info ========================== - SKIP [1] /tmp/doc-exec-438/conftest.py:22: specify ssh host with --ssh + SKIP [1] /tmp/doc-exec-560/conftest.py:22: specify ssh host with --ssh - ======================== 1 skipped in 0.02 seconds ========================= + ======================== 1 skipped in 0.03 seconds ========================= If you specify a command line option like ``py.test --ssh=python.org`` the test will execute as expected. diff --git a/doc/example/nonpython.txt b/doc/example/nonpython.txt index 98433c7ea..52e172663 100644 --- a/doc/example/nonpython.txt +++ b/doc/example/nonpython.txt @@ -27,17 +27,19 @@ now execute the test specification:: nonpython $ py.test test_simple.yml =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 test path 1: test_simple.yml - + test_simple.yml .F - + ================================= FAILURES ================================= ______________________________ usecase: hello ______________________________ usecase execution failed spec failed: 'some': 'other' no further details known at this point. - ==================== 1 failed, 1 passed in 0.42 seconds ==================== + ========================= short test summary info ========================== + FAIL test_simple.yml::hello + ==================== 1 failed, 1 passed in 0.43 seconds ==================== You get one dot for the passing ``sub1: sub1`` check and one failure. Obviously in the above ``conftest.py`` you'll want to implement a more @@ -56,24 +58,25 @@ reporting in ``verbose`` mode:: nonpython $ py.test -v =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 -- /home/hpk/venv/0/bin/python + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 -- /home/hpk/venv/0/bin/python test path 1: /home/hpk/p/pytest/doc/example/nonpython - + test_simple.yml <- test_simple.yml:1: usecase: ok PASSED test_simple.yml <- test_simple.yml:1: usecase: hello FAILED - + ================================= FAILURES ================================= ______________________________ usecase: hello ______________________________ usecase execution failed spec failed: 'some': 'other' no further details known at this point. + ========================= short test summary info ========================== + FAIL test_simple.yml::hello ==================== 1 failed, 1 passed in 0.07 seconds ==================== While developing your custom test collection and execution it's also interesting to just look at the collection tree:: nonpython $ py.test --collectonly - diff --git a/doc/example/pythoncollection.txt b/doc/example/pythoncollection.txt index a39cd169e..0e51d9c4c 100644 --- a/doc/example/pythoncollection.txt +++ b/doc/example/pythoncollection.txt @@ -31,7 +31,6 @@ finding out what is collected You can always peek at the collection tree without running tests like this:: . $ py.test --collectonly collectonly.py - diff --git a/doc/example/simple.txt b/doc/example/simple.txt index 06e525bed..4e4d634cf 100644 --- a/doc/example/simple.txt +++ b/doc/example/simple.txt @@ -130,7 +130,7 @@ let's run the full monty:: E assert 4 < 4 test_compute.py:3: AssertionError - 1 failed, 4 passed in 0.02 seconds + 1 failed, 4 passed in 0.03 seconds As expected when running the full range of ``param1`` values we'll get an error on the last one. diff --git a/doc/faq.txt b/doc/faq.txt index b868c6d59..80aba92dc 100644 --- a/doc/faq.txt +++ b/doc/faq.txt @@ -12,11 +12,11 @@ On naming, nosetests, licensing and magic XXX Why a ``py.test`` instead of a ``pytest`` command? ++++++++++++++++++++++++++++++++++++++++++++++++++ -Some historic, some practical reasons: ``py.test`` used to be part of +Some historic, some practical reasons: ``py.test`` used to be part of the ``py`` package which provided several developer utitilities, all starting with ``py.``, providing nice TAB-completion. If you install ``pip install pycmd`` you get these tools from a separate -package. These days the command line tool could be ``pytest`` +package. These days the command line tool could be ``pytest`` but then many people have gotten used to the old name and there also is another tool with this same which would lead to some clashes. @@ -37,11 +37,11 @@ What's this "magic" with py.test? Around 2007 (version ``0.8``) some several people claimed that py.test was using too much "magic". It has been refactored a lot. It is today -probably one of the smallest, most universally runnable and most -customizable testing frameworks for Python. It remains true -that ``py.test`` uses metaprogramming techniques, i.e. it views -test code similar to how compilers view programs, using a -somewhat abstract internal model. +probably one of the smallest, most universally runnable and most +customizable testing frameworks for Python. It remains true +that ``py.test`` uses metaprogramming techniques, i.e. it views +test code similar to how compilers view programs, using a +somewhat abstract internal model. It's also true that the no-boilerplate testing is implemented by making use of the Python assert statement through "re-interpretation": @@ -64,7 +64,7 @@ function arguments, parametrized tests and setup Is using funcarg- versus xUnit setup a style question? +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -For simple applications and for people experienced with nose_ or +For simple applications and for people experienced with nose_ or unittest-style test setup using `xUnit style setup`_ feels natural. For larger test suites, parametrized testing or setup of complex test resources using funcargs_ is recommended. diff --git a/doc/funcargs.txt b/doc/funcargs.txt index 9f1650ee8..7b73e5ac2 100644 --- a/doc/funcargs.txt +++ b/doc/funcargs.txt @@ -34,7 +34,7 @@ Running the test looks like this:: $ py.test test_simplefactory.py =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 test path 1: test_simplefactory.py test_simplefactory.py F @@ -136,7 +136,7 @@ Running this:: $ py.test test_example.py =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 test path 1: test_example.py test_example.py .........F @@ -158,7 +158,6 @@ the test collection phase which is separate from the actual test running. Let's just look at what is collected:: $ py.test --collectonly test_example.py - @@ -175,7 +174,7 @@ If you want to select only the run with the value ``7`` you could do:: $ py.test -v -k 7 test_example.py # or -k test_func[7] =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 -- /home/hpk/venv/0/bin/python + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 -- /home/hpk/venv/0/bin/python test path 1: test_example.py test_example.py <- test_example.py:6: test_func[7] PASSED diff --git a/doc/getting-started.txt b/doc/getting-started.txt index c2f2741cd..637dadc50 100644 --- a/doc/getting-started.txt +++ b/doc/getting-started.txt @@ -14,7 +14,7 @@ Installation options:: To check your installation has installed the correct version:: $ py.test --version - This is py.test version 2.0.0.dev19, imported from /home/hpk/p/pytest/pytest + This is py.test version 2.0.0.dev22, imported from /home/hpk/p/pytest/pytest If you get an error checkout :ref:`installation issues`. @@ -34,8 +34,8 @@ That's it. You can execute the test function now:: $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 - test path 1: /tmp/doc-exec-404 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 + test path 1: /tmp/doc-exec-523 test_sample.py F @@ -121,7 +121,7 @@ run the module by passing its filename:: ================================= FAILURES ================================= ____________________________ TestClass.test_two ____________________________ - self = + self = def test_two(self): x = "hello" @@ -129,7 +129,7 @@ run the module by passing its filename:: E assert hasattr('hello', 'check') test_class.py:8: AssertionError - 1 failed, 1 passed in 0.02 seconds + 1 failed, 1 passed in 0.03 seconds The first test passed, the second failed. Again we can easily see the intermediate values used in the assertion, helping us to @@ -157,7 +157,7 @@ before performing the test function call. Let's just run it:: ================================= FAILURES ================================= _____________________________ test_needsfiles ______________________________ - tmpdir = local('/tmp/pytest-240/test_needsfiles0') + tmpdir = local('/tmp/pytest-446/test_needsfiles0') def test_needsfiles(tmpdir): print tmpdir @@ -166,8 +166,8 @@ before performing the test function call. Let's just run it:: test_tmpdir.py:3: AssertionError ----------------------------- Captured stdout ------------------------------ - /tmp/pytest-240/test_needsfiles0 - 1 failed in 0.04 seconds + /tmp/pytest-446/test_needsfiles0 + 1 failed in 0.07 seconds Before the test runs, a unique-per-test-invocation temporary directory was created. More info at :ref:`tmpdir handling`. diff --git a/doc/mark.txt b/doc/mark.txt index 3556b238a..af629407d 100644 --- a/doc/mark.txt +++ b/doc/mark.txt @@ -88,20 +88,20 @@ You can use the ``-k`` command line option to select tests:: $ py.test -k webtest # running with the above defined examples yields =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 - test path 1: /tmp/doc-exec-407 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 + test path 1: /tmp/doc-exec-527 test_mark.py .. test_mark_classlevel.py .. - ========================= 4 passed in 0.01 seconds ========================= + ========================= 4 passed in 0.02 seconds ========================= And you can also run all tests except the ones that match the keyword:: $ py.test -k-webtest =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 - test path 1: /tmp/doc-exec-407 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 + test path 1: /tmp/doc-exec-527 ===================== 4 tests deselected by '-webtest' ===================== ======================= 4 deselected in 0.01 seconds ======================= @@ -110,8 +110,8 @@ Or to only select the class:: $ py.test -kTestClass =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 - test path 1: /tmp/doc-exec-407 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 + test path 1: /tmp/doc-exec-527 test_mark_classlevel.py .. diff --git a/doc/monkeypatch.txt b/doc/monkeypatch.txt index 548debad2..b91b098d8 100644 --- a/doc/monkeypatch.txt +++ b/doc/monkeypatch.txt @@ -39,8 +39,8 @@ will be undone. .. background check: $ py.test =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 - test path 1: /tmp/doc-exec-408 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 + test path 1: /tmp/doc-exec-528 ============================= in 0.00 seconds ============================= diff --git a/doc/plugins.txt b/doc/plugins.txt index 20ddaaf66..fcc38d2d6 100644 --- a/doc/plugins.txt +++ b/doc/plugins.txt @@ -16,7 +16,7 @@ conftest.py: local per-directory plugins -------------------------------------------------------------- local ``conftest.py`` plugins contain directory-specific hook -implementations. Collection and test running activities will +implementations. Session and test running activities will invoke all hooks defined in "higher up" ``conftest.py`` files. Example: Assume the following layout and content of files:: @@ -268,7 +268,7 @@ you can use the following hook: reporting hooks ------------------------------ -Collection related reporting hooks: +Session related reporting hooks: .. autofunction: pytest_collectstart .. autofunction: pytest_itemcollected diff --git a/doc/tmpdir.txt b/doc/tmpdir.txt index 68d7b3f39..7ba6c82c9 100644 --- a/doc/tmpdir.txt +++ b/doc/tmpdir.txt @@ -28,7 +28,7 @@ Running this would result in a passed test except for the last $ py.test test_tmpdir.py =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 test path 1: test_tmpdir.py test_tmpdir.py F @@ -36,7 +36,7 @@ Running this would result in a passed test except for the last ================================= FAILURES ================================= _____________________________ test_create_file _____________________________ - tmpdir = local('/tmp/pytest-243/test_create_file0') + tmpdir = local('/tmp/pytest-447/test_create_file0') def test_create_file(tmpdir): p = tmpdir.mkdir("sub").join("hello.txt") @@ -47,7 +47,7 @@ Running this would result in a passed test except for the last E assert 0 test_tmpdir.py:7: AssertionError - ========================= 1 failed in 0.04 seconds ========================= + ========================= 1 failed in 0.15 seconds ========================= .. _`base temporary directory`: diff --git a/doc/unittest.txt b/doc/unittest.txt index 23ef882bf..0ee81c832 100644 --- a/doc/unittest.txt +++ b/doc/unittest.txt @@ -24,7 +24,7 @@ Running it yields:: $ py.test test_unittest.py =========================== test session starts ============================ - platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev19 + platform linux2 -- Python 2.6.5 -- pytest-2.0.0.dev22 test path 1: test_unittest.py test_unittest.py F @@ -56,7 +56,7 @@ Running it yields:: /usr/lib/python2.6/unittest.py:350: AssertionError ----------------------------- Captured stdout ------------------------------ hello - ========================= 1 failed in 0.02 seconds ========================= + ========================= 1 failed in 0.12 seconds ========================= .. _`unittest.py style`: http://docs.python.org/library/unittest.html diff --git a/doc/usage.txt b/doc/usage.txt index 067a6f54b..cb0696a18 100644 --- a/doc/usage.txt +++ b/doc/usage.txt @@ -175,5 +175,4 @@ Running it will exit quickly:: $ python myinvoke.py ERROR: hi from our plugin - .. include:: links.inc diff --git a/pytest/__init__.py b/pytest/__init__.py index 82530191c..eea7487cb 100644 --- a/pytest/__init__.py +++ b/pytest/__init__.py @@ -5,7 +5,7 @@ see http://pytest.org for documentation and details (c) Holger Krekel and others, 2004-2010 """ -__version__ = '2.0.0.dev22' +__version__ = '2.0.0.dev23' __all__ = ['config', 'cmdline'] diff --git a/pytest/hookspec.py b/pytest/hookspec.py index cd778a522..674e6384a 100644 --- a/pytest/hookspec.py +++ b/pytest/hookspec.py @@ -38,7 +38,8 @@ def pytest_unconfigure(config): """ called before test process is exited. """ def pytest_runtestloop(session): - """ called for performing the main runtest loop (after collection. """ + """ called for performing the main runtest loop + (after collection finished). """ pytest_runtestloop.firstresult = True # ------------------------------------------------------------------------- @@ -49,11 +50,11 @@ def pytest_collection(session): """ perform the collection protocol for the given session. """ pytest_collection.firstresult = True -def pytest_collection_modifyitems(config, items): +def pytest_collection_modifyitems(session, config, items): """ called after collection has been performed, may filter or re-order the items in-place.""" -def pytest_collection_finish(collection): +def pytest_collection_finish(session): """ called after collection has been performed and modified. """ def pytest_ignore_collect(path, config): @@ -64,8 +65,7 @@ def pytest_ignore_collect(path, config): pytest_ignore_collect.firstresult = True def pytest_collect_directory(path, parent): - """ return collection Node or None for the given path. Any new node - needs to have the specified ``parent`` as a parent.""" + """ called before traversing a directory for collection files. """ pytest_collect_directory.firstresult = True def pytest_collect_file(path, parent): diff --git a/pytest/plugin/pytester.py b/pytest/plugin/pytester.py index d3eb76d03..11d9ec0d3 100644 --- a/pytest/plugin/pytester.py +++ b/pytest/plugin/pytester.py @@ -6,7 +6,7 @@ import re import inspect import time from fnmatch import fnmatch -from pytest.plugin.session import Collection +from pytest.plugin.session import Session from py.builtin import print_ from pytest.main import HookRelay @@ -273,34 +273,34 @@ class TmpTestdir: p.ensure("__init__.py") return p - Collection = Collection + Session = Session def getnode(self, config, arg): - collection = Collection(config) + session = Session(config) assert '::' not in str(arg) p = py.path.local(arg) - x = collection.fspath.bestrelpath(p) - return collection.perform_collect([x], genitems=False)[0] + x = session.fspath.bestrelpath(p) + return session.perform_collect([x], genitems=False)[0] def getpathnode(self, path): config = self.parseconfig(path) - collection = Collection(config) - x = collection.fspath.bestrelpath(path) - return collection.perform_collect([x], genitems=False)[0] + session = Session(config) + x = session.fspath.bestrelpath(path) + return session.perform_collect([x], genitems=False)[0] def genitems(self, colitems): - collection = colitems[0].collection + session = colitems[0].session result = [] for colitem in colitems: - result.extend(collection.genitems(colitem)) + result.extend(session.genitems(colitem)) return result def inline_genitems(self, *args): #config = self.parseconfig(*args) config = self.parseconfigure(*args) rec = self.getreportrecorder(config) - collection = Collection(config) - collection.perform_collect() - return collection.items, rec + session = Session(config) + session.perform_collect() + return session.items, rec def runitem(self, source): # used from runner functional tests diff --git a/pytest/plugin/python.py b/pytest/plugin/python.py index 6648d0b64..6399a3011 100644 --- a/pytest/plugin/python.py +++ b/pytest/plugin/python.py @@ -47,7 +47,7 @@ def pytest_collect_file(path, parent): ext = path.ext pb = path.purebasename if ext == ".py" and (pb.startswith("test_") or pb.endswith("_test") or - parent.collection.isinitpath(path)): + parent.session.isinitpath(path)): return parent.ihook.pytest_pycollect_makemodule( path=path, parent=parent) @@ -393,9 +393,9 @@ class Function(FunctionMixin, pytest.collect.Item): """ _genid = None def __init__(self, name, parent=None, args=None, config=None, - callspec=None, callobj=_dummy, keywords=None, collection=None): + callspec=None, callobj=_dummy, keywords=None, session=None): super(Function, self).__init__(name, parent, - config=config, collection=collection) + config=config, session=session) self._args = args if self._isyieldedfunction(): assert not callspec, ( @@ -711,13 +711,13 @@ class FuncargRequest: raise self.LookupError(msg) def showfuncargs(config): - from pytest.plugin.session import Collection - collection = Collection(config) - collection.perform_collect() - if collection.items: - plugins = getplugins(collection.items[0]) + from pytest.plugin.session import Session + session = Session(config) + session.perform_collect() + if session.items: + plugins = getplugins(session.items[0]) else: - plugins = getplugins(collection) + plugins = getplugins(session) curdir = py.path.local() tw = py.io.TerminalWriter() verbose = config.getvalue("verbose") diff --git a/pytest/plugin/session.py b/pytest/plugin/session.py index 69de9c116..8645e4315 100644 --- a/pytest/plugin/session.py +++ b/pytest/plugin/session.py @@ -1,4 +1,4 @@ -""" core implementation of testing process: init, collection, runtest loop. """ +""" core implementation of testing process: init, session, runtest loop. """ import py import pytest @@ -54,7 +54,7 @@ def pytest_configure(config): config.option.maxfail = 1 def pytest_cmdline_main(config): - """ default command line protocol for initialization, collection, + """ default command line protocol for initialization, session, running tests and reporting. """ session = Session(config) session.exitstatus = EXIT_OK @@ -83,20 +83,17 @@ def pytest_cmdline_main(config): return session.exitstatus def pytest_collection(session): - collection = session.collection - assert not hasattr(collection, 'items') - - collection.perform_collect() + session.perform_collect() hook = session.config.hook - items = collection.items - hook.pytest_collection_modifyitems(config=session.config, items=items) - hook.pytest_collection_finish(collection=collection) + hook.pytest_collection_modifyitems(session=session, + config=session.config, items=session.items) + hook.pytest_collection_finish(session=session) return True def pytest_runtestloop(session): if session.config.option.collectonly: return True - for item in session.collection.items: + for item in session.session.items: item.config.hook.pytest_runtest_protocol(item=item) if session.shouldstop: raise session.Interrupted(session.shouldstop) @@ -121,7 +118,7 @@ class Session(object): self.config.pluginmanager.register(self, name="session", prepend=True) self._testsfailed = 0 self.shouldstop = False - self.collection = Collection(config) # XXX move elswehre + self.session = Session(config) # XXX move elswehre def pytest_collectstart(self): if self.shouldstop: @@ -154,7 +151,7 @@ def compatproperty(name): def fget(self): #print "retrieving %r property from %s" %(name, self.fspath) py.log._apiwarn("2.0", "use py.test.collect.%s for " - "Collection classes" % name) + "Session classes" % name) return getattr(pytest.collect, name) return property(fget) @@ -162,7 +159,7 @@ class Node(object): """ base class for all Nodes in the collection tree. Collector subclasses have children, Items are terminal nodes.""" - def __init__(self, name, parent=None, config=None, collection=None): + def __init__(self, name, parent=None, config=None, session=None): #: a unique name with the scope of the parent self.name = name @@ -173,11 +170,11 @@ class Node(object): self.config = config or parent.config #: the collection this node is part of - self.collection = collection or parent.collection + self.session = session or parent.session #: filesystem path where this node was collected from self.fspath = getattr(parent, 'fspath', None) - self.ihook = self.collection.gethookproxy(self.fspath) + self.ihook = self.session.gethookproxy(self.fspath) self.keywords = {self.name: True} Module = compatproperty("Module") @@ -312,16 +309,16 @@ class Collector(Node): excinfo.traceback = ntraceback.filter() class FSCollector(Collector): - def __init__(self, fspath, parent=None, config=None, collection=None): + def __init__(self, fspath, parent=None, config=None, session=None): fspath = py.path.local(fspath) # xxx only for test_resultlog.py? name = parent and fspath.relto(parent.fspath) or fspath.basename - super(FSCollector, self).__init__(name, parent, config, collection) + super(FSCollector, self).__init__(name, parent, config, session) self.fspath = fspath def _makeid(self): - if self == self.collection: + if self == self.session: return "." - relpath = self.collection.fspath.bestrelpath(self.fspath) + relpath = self.session.fspath.bestrelpath(self.fspath) if os.sep != "/": relpath = relpath.replace(os.sep, "/") return relpath @@ -346,13 +343,33 @@ class Item(Node): self._location = location return location -class Collection(FSCollector): +class Session(FSCollector): + class Interrupted(KeyboardInterrupt): + """ signals an interrupted test run. """ + __module__ = 'builtins' # for py3 + def __init__(self, config): - super(Collection, self).__init__(py.path.local(), parent=None, - config=config, collection=self) + super(Session, self).__init__(py.path.local(), parent=None, + config=config, session=self) + self.config.pluginmanager.register(self, name="session", prepend=True) + self._testsfailed = 0 + self.shouldstop = False self.trace = config.trace.root.get("collection") self._norecursepatterns = config.getini("norecursedirs") + def pytest_collectstart(self): + if self.shouldstop: + raise self.Interrupted(self.shouldstop) + + def pytest_runtest_logreport(self, report): + if report.failed and 'xfail' not in getattr(report, 'keywords', []): + self._testsfailed += 1 + maxfail = self.config.getvalue("maxfail") + if maxfail and self._testsfailed >= maxfail: + self.shouldstop = "stopping after %d failures" % ( + self._testsfailed) + pytest_collectreport = pytest_runtest_logreport + def isinitpath(self, path): return path in self._initialpaths @@ -509,3 +526,4 @@ class Collection(FSCollector): yield x node.ihook.pytest_collectreport(report=rep) +Session = Session diff --git a/pytest/plugin/terminal.py b/pytest/plugin/terminal.py index da7414351..dc1a0727e 100644 --- a/pytest/plugin/terminal.py +++ b/pytest/plugin/terminal.py @@ -376,8 +376,9 @@ class CollectonlyReporter: self._tw.line("INTERNALERROR> " + line) def pytest_collectstart(self, collector): - self.outindent(collector) - self.indent += self.INDENT + if collector.session != collector: + self.outindent(collector) + self.indent += self.INDENT def pytest_itemcollected(self, item): self.outindent(item) diff --git a/setup.py b/setup.py index 33a5e01ea..7630b5050 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ def main(): name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.0.0.dev22', + version='2.0.0.dev23', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], diff --git a/testing/plugin/test_python.py b/testing/plugin/test_python.py index ac8f7135a..57aadfe6f 100644 --- a/testing/plugin/test_python.py +++ b/testing/plugin/test_python.py @@ -205,15 +205,15 @@ class TestFunction: def test_function_equality(self, testdir, tmpdir): config = testdir.reparseconfig() - collection = testdir.Collection(config) + session = testdir.Session(config) f1 = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=isinstance, collection=collection) + args=(1,), callobj=isinstance, session=session) f2 = py.test.collect.Function(name="name",config=config, - args=(1,), callobj=py.builtin.callable, collection=collection) + args=(1,), callobj=py.builtin.callable, session=session) assert not f1 == f2 assert f1 != f2 f3 = py.test.collect.Function(name="name", config=config, - args=(1,2), callobj=py.builtin.callable, collection=collection) + args=(1,2), callobj=py.builtin.callable, session=session) assert not f3 == f2 assert f3 != f2 @@ -221,7 +221,7 @@ class TestFunction: assert f3 != f1 f1_b = py.test.collect.Function(name="name", config=config, - args=(1,), callobj=isinstance, collection=collection) + args=(1,), callobj=isinstance, session=session) assert f1 == f1_b assert not f1 != f1_b @@ -235,11 +235,11 @@ class TestFunction: param = 1 funcargs = {} id = "world" - collection = testdir.Collection(config) + session = testdir.Session(config) f5 = py.test.collect.Function(name="name", config=config, - callspec=callspec1, callobj=isinstance, collection=collection) + callspec=callspec1, callobj=isinstance, session=session) f5b = py.test.collect.Function(name="name", config=config, - callspec=callspec2, callobj=isinstance, collection=collection) + callspec=callspec2, callobj=isinstance, session=session) assert f5 != f5b assert not (f5 == f5b) @@ -396,7 +396,7 @@ def test_generate_tests_only_done_in_subdir(testdir): def test_modulecol_roundtrip(testdir): modcol = testdir.getmodulecol("pass", withinit=True) trail = modcol.nodeid - newcol = modcol.collection.perform_collect([trail], genitems=0)[0] + newcol = modcol.session.perform_collect([trail], genitems=0)[0] assert modcol.name == newcol.name diff --git a/testing/plugin/test_resultlog.py b/testing/plugin/test_resultlog.py index 489adcb08..0842fc32c 100644 --- a/testing/plugin/test_resultlog.py +++ b/testing/plugin/test_resultlog.py @@ -5,10 +5,10 @@ from pytest.plugin.resultlog import generic_path, ResultLog, \ from pytest.plugin.session import Node, Item, FSCollector def test_generic_path(testdir): - from pytest.plugin.session import Collection + from pytest.plugin.session import Session config = testdir.parseconfig() - collection = Collection(config) - p1 = Node('a', config=config, collection=collection) + session = Session(config) + p1 = Node('a', config=config, session=session) #assert p1.fspath is None p2 = Node('B', parent=p1) p3 = Node('()', parent = p2) @@ -17,7 +17,7 @@ def test_generic_path(testdir): res = generic_path(item) assert res == 'a.B().c' - p0 = FSCollector('proj/test', config=config, collection=collection) + p0 = FSCollector('proj/test', config=config, session=session) p1 = FSCollector('proj/test/a', parent=p0) p2 = Node('B', parent=p1) p3 = Node('()', parent = p2) diff --git a/testing/plugin/test_runner.py b/testing/plugin/test_runner.py index 2c595b7dd..a84ffb1a4 100644 --- a/testing/plugin/test_runner.py +++ b/testing/plugin/test_runner.py @@ -244,7 +244,7 @@ class TestExecutionForked(BaseFunctionalTests): assert rep.failed assert rep.when == "???" -class TestCollectionReports: +class TestSessionReports: def test_collect_result(self, testdir): col = testdir.getmodulecol(""" def test_func1(): diff --git a/testing/plugin/test_terminal.py b/testing/plugin/test_terminal.py index 610942e5b..74e7f9313 100644 --- a/testing/plugin/test_terminal.py +++ b/testing/plugin/test_terminal.py @@ -516,7 +516,7 @@ def test_traceconfig(testdir, monkeypatch): def test_debug(testdir, monkeypatch): result = testdir.runpytest("--debug") result.stderr.fnmatch_lines([ - "*registered*session*", + "*pytest_sessionstart*session*", ]) assert result.ret == 0 diff --git a/testing/test_collection.py b/testing/test_collection.py index 451279ac4..72e84ec32 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1,6 +1,6 @@ import py -from pytest.plugin.session import Collection +from pytest.plugin.session import Session class TestCollector: def test_collect_versus_item(self): @@ -86,7 +86,7 @@ class TestCollector: node = testdir.getpathnode(hello) assert isinstance(node, py.test.collect.File) assert node.name == "hello.xxx" - nodes = node.collection.perform_collect([node.nodeid], genitems=False) + nodes = node.session.perform_collect([node.nodeid], genitems=False) assert len(nodes) == 1 assert isinstance(nodes[0], py.test.collect.File) @@ -292,7 +292,7 @@ class TestCustomConftests: "*test_x*" ]) -class TestCollection: +class TestSession: def test_parsearg(self, testdir): p = testdir.makepyfile("def test_func(): pass") subdir = testdir.mkdir("sub") @@ -302,7 +302,7 @@ class TestCollection: testdir.chdir() subdir.chdir() config = testdir.parseconfig(p.basename) - rcol = Collection(config=config) + rcol = Session(config=config) assert rcol.fspath == subdir parts = rcol._parsearg(p.basename) @@ -318,7 +318,7 @@ class TestCollection: id = "::".join([p.basename, "test_func"]) config = testdir.parseconfig(id) topdir = testdir.tmpdir - rcol = Collection(config) + rcol = Session(config) assert topdir == rcol.fspath rootid = rcol.nodeid #root2 = rcol.perform_collect([rcol.nodeid], genitems=False)[0] @@ -333,7 +333,7 @@ class TestCollection: id = "::".join([p.basename, "test_func"]) config = testdir.parseconfig(id) topdir = testdir.tmpdir - rcol = Collection(config) + rcol = Session(config) assert topdir == rcol.fspath hookrec = testdir.getreportrecorder(config) rcol.perform_collect() @@ -367,7 +367,7 @@ class TestCollection: normid, ]: config = testdir.parseconfig(id) - rcol = Collection(config=config) + rcol = Session(config=config) rcol.perform_collect() items = rcol.items assert len(items) == 1 @@ -392,7 +392,7 @@ class TestCollection: id = p.basename config = testdir.parseconfig(id) - rcol = Collection(config) + rcol = Session(config) hookrec = testdir.getreportrecorder(config) rcol.perform_collect() items = rcol.items @@ -400,7 +400,7 @@ class TestCollection: assert len(items) == 2 hookrec.hookrecorder.contains([ ("pytest_collectstart", - "collector.fspath == collector.collection.fspath"), + "collector.fspath == collector.session.fspath"), ("pytest_collectstart", "collector.__class__.__name__ == 'SpecialFile'"), ("pytest_collectstart", @@ -417,7 +417,7 @@ class TestCollection: test_aaa = aaa.join("test_aaa.py") p.move(test_aaa) config = testdir.parseconfig() - rcol = Collection(config) + rcol = Session(config) hookrec = testdir.getreportrecorder(config) rcol.perform_collect() items = rcol.items @@ -441,7 +441,7 @@ class TestCollection: id = "." config = testdir.parseconfig(id) - rcol = Collection(config) + rcol = Session(config) hookrec = testdir.getreportrecorder(config) rcol.perform_collect() items = rcol.items @@ -459,12 +459,13 @@ class TestCollection: def test_serialization_byid(self, testdir): p = testdir.makepyfile("def test_func(): pass") config = testdir.parseconfig() - rcol = Collection(config) + rcol = Session(config) rcol.perform_collect() items = rcol.items assert len(items) == 1 item, = items - newcol = Collection(config) + rcol.config.pluginmanager.unregister(name="session") + newcol = Session(config) item2, = newcol.perform_collect([item.nodeid], genitems=False) assert item2.name == item.name assert item2.fspath == item.fspath