[Python-modules-commits] [pytest] 01/07: Import pytest_3.1.3.orig.tar.gz

Sebastian Ramacher sramacher at moszumanska.debian.org
Thu Jul 13 11:24:48 UTC 2017


This is an automated email from the git hooks/post-receive script.

sramacher pushed a commit to branch master
in repository pytest.

commit 1f3ae240b868a1fffc4506451270d91075308281
Author: Sebastian Ramacher <sramacher at debian.org>
Date:   Thu Jul 13 13:23:20 2017 +0200

    Import pytest_3.1.3.orig.tar.gz
---
 .github/ISSUE_TEMPLATE.md         |   8 ++
 .github/PULL_REQUEST_TEMPLATE.md  |  15 +++
 .travis.yml                       |  51 ++++++++
 CHANGELOG.rst                     |  52 +++++++-
 LICENSE                           |   2 +-
 MANIFEST.in                       |  39 ------
 PKG-INFO                          |   4 +-
 README.rst                        |   2 +-
 _pytest/_code/code.py             |   7 +-
 _pytest/_version.py               |   2 +-
 _pytest/assertion/rewrite.py      |   4 -
 _pytest/doctest.py                |   9 +-
 _pytest/fixtures.py               |  13 +-
 _pytest/hookspec.py               |  59 ++++++---
 _pytest/impl                      | 254 ++++++++++++++++++++++++++++++++++++++
 _pytest/main.py                   |  11 +-
 _pytest/recwarn.py                |  65 +++++-----
 _pytest/terminal.py               |   2 +-
 appveyor.yml                      |  42 +++++++
 changelog/_template.rst           |   4 +-
 doc/en/announce/index.rst         |   1 +
 doc/en/announce/release-3.1.3.rst |  23 ++++
 doc/en/assert.rst                 |   4 +-
 doc/en/doctest.rst                |   2 +-
 doc/en/example/markers.rst        |  10 +-
 doc/en/example/parametrize.rst    |   2 +-
 doc/en/fixture.rst                | 100 +++++++++------
 doc/en/getting-started.rst        |   2 +-
 doc/en/index.rst                  |   6 +-
 doc/en/license.rst                |   2 +-
 doc/en/tmpdir.rst                 |   2 +-
 doc/en/warnings.rst               |   2 +-
 doc/en/writing_plugins.rst        |   6 +-
 pyproject.toml                    |   4 +-
 pytest.egg-info/PKG-INFO          |   4 +-
 pytest.egg-info/SOURCES.txt       |   9 +-
 scripts/check-manifest.py         |  20 ---
 tasks/__init__.py                 |   8 +-
 tasks/requirements.txt            |   3 +-
 tasks/vendoring.py                |  23 ++++
 testing/acceptance_test.py        |   4 +-
 testing/code/test_excinfo.py      |  23 ++++
 testing/python/fixture.py         |  33 +++++
 testing/test_collection.py        |  19 ++-
 testing/test_doctest.py           |  19 +++
 testing/test_recwarn.py           |  58 +++++++--
 testing/test_runner.py            |   4 +-
 testing/test_terminal.py          |   9 ++
 tox.ini                           |   2 -
 49 files changed, 829 insertions(+), 220 deletions(-)

diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..fbcbb16
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,8 @@
+Thanks for submitting an issue!
+
+Here's a quick checklist in what to include:
+
+- [ ] Include a detailed description of the bug or suggestion
+- [ ] `pip list` of the virtual environment you are using
+- [ ] pytest and operating system versions
+- [ ] Minimal example if possible
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..ad3fea6
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,15 @@
+Thanks for submitting a PR, your contribution is really appreciated!
+
+Here's a quick checklist that should be present in PRs:
+
+- [ ] Add a new news fragment into the changelog folder
+  * name it `$issue_id.$type` for example (588.bug)
+  * if you don't have an issue_id change it to the pr id after creating the pr
+  * ensure type is one of `removal`, `feature`, `bugfix`, `vendor`, `doc` or `trivial`
+  * Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files."
+- [ ] Target: for `bugfix`, `vendor`, `doc` or `trivial` fixes, target `master`; for removals or features target `features`;
+- [ ] Make sure to include reasonable tests for your change if necessary
+
+Unless your change is a trivial or a documentation fix (e.g.,  a typo or reword of a small section) please:
+
+- [ ] Add yourself to `AUTHORS`;
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..0a71e7d
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,51 @@
+sudo: false
+language: python
+python:
+    - '3.5'
+# command to install dependencies
+install: "pip install -U tox"
+# # command to run tests
+env:
+  matrix:
+    # coveralls is not listed in tox's envlist, but should run in travis
+    - TOXENV=coveralls
+    # note: please use "tox --listenvs" to populate the build matrix below
+    - TOXENV=linting
+    - TOXENV=py26
+    - TOXENV=py27
+    - TOXENV=py33
+    - TOXENV=py34
+    - TOXENV=py35
+    - TOXENV=pypy
+    - TOXENV=py27-pexpect
+    - TOXENV=py27-xdist
+    - TOXENV=py27-trial
+    - TOXENV=py35-pexpect
+    - TOXENV=py35-xdist
+    - TOXENV=py35-trial
+    - TOXENV=py27-nobyte
+    - TOXENV=doctesting
+    - TOXENV=freeze
+    - TOXENV=docs
+
+matrix:
+  include:
+    - env: TOXENV=py36
+      python: '3.6'
+    - env: TOXENV=py37
+      python: 'nightly'
+  allow_failures:
+    - env: TOXENV=py37
+      python: 'nightly'
+
+script: tox --recreate
+
+notifications:
+  irc:
+    channels:
+      - "chat.freenode.net#pytest"
+    on_success: change
+    on_failure: change
+    skip_join: true
+  email:
+    - pytest-commit at python.org
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 34fee33..1c1185d 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -8,6 +8,52 @@
 
 .. towncrier release notes start
 
+Pytest 3.1.3 (2017-07-03)
+=========================
+
+Bug Fixes
+---------
+
+- Fix decode error in Python 2 for doctests in docstrings. (`#2434
+  <https://github.com/pytest-dev/pytest/issues/2434>`_)
+
+- Exceptions raised during teardown by finalizers are now suppressed until all
+  finalizers are called, with the initial exception reraised. (`#2440
+  <https://github.com/pytest-dev/pytest/issues/2440>`_)
+
+- Fix incorrect "collected items" report when specifying tests on the command-
+  line. (`#2464 <https://github.com/pytest-dev/pytest/issues/2464>`_)
+
+- ``deprecated_call`` in context-manager form now captures deprecation warnings
+  even if the same warning has already been raised. Also, ``deprecated_call``
+  will always produce the same error message (previously it would produce
+  different messages in context-manager vs. function-call mode). (`#2469
+  <https://github.com/pytest-dev/pytest/issues/2469>`_)
+
+- Fix issue where paths collected by pytest could have triple leading ``/``
+  characters. (`#2475 <https://github.com/pytest-dev/pytest/issues/2475>`_)
+
+- Fix internal error when trying to detect the start of a recursive traceback.
+  (`#2486 <https://github.com/pytest-dev/pytest/issues/2486>`_)
+
+
+Improved Documentation
+----------------------
+
+- Explicitly state for which hooks the calls stop after the first non-None
+  result. (`#2493 <https://github.com/pytest-dev/pytest/issues/2493>`_)
+
+
+Trivial/Internal Changes
+------------------------
+
+- Create invoke tasks for updating the vendored packages. (`#2474
+  <https://github.com/pytest-dev/pytest/issues/2474>`_)
+
+- Update copyright dates in LICENSE, README.rst and in the documentation.
+  (`#2499 <https://github.com/pytest-dev/pytest/issues/2499>`_)
+
+
 Pytest 3.1.2 (2017-06-08)
 =========================
 
@@ -25,9 +71,9 @@ Bug Fixes
 - ``UnicodeWarning`` is issued from the internal pytest warnings plugin only
   when the message contains non-ascii unicode (Python 2 only). (#2463)
 
-- Added a workaround for Python 3.6 WindowsConsoleIO breaking due to Pytests's
-  FDCapture. Other code using console handles might still be affected by the
-  very same issue and might require further workarounds/fixes, i.e. colorama.
+- Added a workaround for Python 3.6 ``WindowsConsoleIO`` breaking due to Pytests's
+  ``FDCapture``. Other code using console handles might still be affected by the
+  very same issue and might require further workarounds/fixes, i.e. ``colorama``.
   (#2467)
 
 
diff --git a/LICENSE b/LICENSE
index 9e27bd7..629df45 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2004-2016 Holger Krekel and others
+Copyright (c) 2004-2017 Holger Krekel and others
 
 Permission is hereby granted, free of charge, to any person obtaining a copy of
 this software and associated documentation files (the "Software"), to deal in
diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index abf57fe..0000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,39 +0,0 @@
-include CHANGELOG.rst
-include LICENSE
-include AUTHORS
-include pyproject.toml
-
-include README.rst
-include CONTRIBUTING.rst
-include HOWTORELEASE.rst
-
-include tox.ini
-include setup.py
-
-recursive-include changelog *
-recursive-include scripts *.py
-recursive-include scripts *.bat
-
-include .coveragerc
-
-recursive-include bench *.py
-recursive-include extra *.py
-
-graft testing
-graft doc
-prune doc/en/_build
-graft tasks
-
-exclude _pytest/impl
-
-graft _pytest/vendored_packages
-
-recursive-exclude * *.pyc *.pyo
-recursive-exclude testing/.hypothesis *
-recursive-exclude testing/freeze/~ *
-recursive-exclude testing/freeze/build *
-recursive-exclude testing/freeze/dist *
-
-exclude appveyor.yml
-exclude .travis.yml
-prune .github
diff --git a/PKG-INFO b/PKG-INFO
index e033632..2b08b6c 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: pytest
-Version: 3.1.2
+Version: 3.1.3
 Summary: pytest: simple powerful testing with Python
 Home-page: http://pytest.org
 Author: Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others
@@ -110,7 +110,7 @@ Description: .. image:: http://docs.pytest.org/en/latest/_static/pytest1.png
         License
         -------
         
-        Copyright Holger Krekel and others, 2004-2016.
+        Copyright Holger Krekel and others, 2004-2017.
         
         Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
         
diff --git a/README.rst b/README.rst
index 3a0abc5..cf2304e 100644
--- a/README.rst
+++ b/README.rst
@@ -102,7 +102,7 @@ Consult the `Changelog <http://docs.pytest.org/en/latest/changelog.html>`__ page
 License
 -------
 
-Copyright Holger Krekel and others, 2004-2016.
+Copyright Holger Krekel and others, 2004-2017.
 
 Distributed under the terms of the `MIT`_ license, pytest is free and open source software.
 
diff --git a/_pytest/_code/code.py b/_pytest/_code/code.py
index 9b3408d..5b7cc41 100644
--- a/_pytest/_code/code.py
+++ b/_pytest/_code/code.py
@@ -640,8 +640,11 @@ class FormattedExcinfo(object):
             ).format(exc_type=type(e).__name__, exc_msg=safe_str(e), max_frames=max_frames, total=len(traceback))
             traceback = traceback[:max_frames] + traceback[-max_frames:]
         else:
-            extraline = "!!! Recursion detected (same locals & position)"
-            traceback = traceback[:recursionindex + 1]
+            if recursionindex is not None:
+                extraline = "!!! Recursion detected (same locals & position)"
+                traceback = traceback[:recursionindex + 1]
+            else:
+                extraline = None
             
         return traceback, extraline
 
diff --git a/_pytest/_version.py b/_pytest/_version.py
index 0a1abdd..e03109e 100644
--- a/_pytest/_version.py
+++ b/_pytest/_version.py
@@ -1,4 +1,4 @@
 # coding: utf-8
 # file generated by setuptools_scm
 # don't change, don't track in version control
-version = '3.1.2'
+version = '3.1.3'
diff --git a/_pytest/assertion/rewrite.py b/_pytest/assertion/rewrite.py
index 79943cc..6ec54d7 100644
--- a/_pytest/assertion/rewrite.py
+++ b/_pytest/assertion/rewrite.py
@@ -162,10 +162,6 @@ class AssertionRewritingHook(object):
         # modules not passed explicitly on the command line are only
         # rewritten if they match the naming convention for test files
         for pat in self.fnpats:
-            # use fnmatch instead of fn_pypath.fnmatch because the
-            # latter might trigger an import to fnmatch.fnmatch
-            # internally, which would cause this method to be
-            # called recursively
             if fn_pypath.fnmatch(pat):
                 state.trace("matched test file %r" % (fn,))
                 return True
diff --git a/_pytest/doctest.py b/_pytest/doctest.py
index 46b49d2..fde6dd7 100644
--- a/_pytest/doctest.py
+++ b/_pytest/doctest.py
@@ -177,7 +177,6 @@ class DoctestTextfile(pytest.Module):
         name = self.fspath.basename
         globs = {'__name__': '__main__'}
 
-
         optionflags = get_optionflags(self)
         runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
                                      checker=_get_checker())
@@ -218,9 +217,6 @@ class DoctestModule(pytest.Module):
         runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
                                      checker=_get_checker())
 
-        encoding = self.config.getini("doctest_encoding")
-        _fix_spoof_python2(runner, encoding)
-        
         for test in finder.find(module, module.__name__):
             if test.examples:  # skip empty doctests
                 yield DoctestItem(test.name, self, runner, test)
@@ -332,7 +328,10 @@ def _get_report_choice(key):
 
 def _fix_spoof_python2(runner, encoding):
     """
-    Installs a "SpoofOut" into the given DebugRunner so it properly deals with unicode output.
+    Installs a "SpoofOut" into the given DebugRunner so it properly deals with unicode output. This
+    should patch only doctests for text files because they don't have a way to declare their
+    encoding. Doctests in docstrings from Python modules don't have the same problem given that
+    Python already decoded the strings.
     
     This fixes the problem related in issue #2434.
     """
diff --git a/_pytest/fixtures.py b/_pytest/fixtures.py
index 1a6e245..64d21b9 100644
--- a/_pytest/fixtures.py
+++ b/_pytest/fixtures.py
@@ -733,10 +733,19 @@ class FixtureDef:
         self._finalizer.append(finalizer)
 
     def finish(self):
+        exceptions = []
         try:
             while self._finalizer:
-                func = self._finalizer.pop()
-                func()
+                try:
+                    func = self._finalizer.pop()
+                    func()
+                except:
+                    exceptions.append(sys.exc_info())
+            if exceptions:
+                e = exceptions[0]
+                del exceptions  # ensure we don't keep all frames alive because of the traceback
+                py.builtin._reraise(*e)
+
         finally:
             ihook = self._fixturemanager.session.ihook
             ihook.pytest_fixture_post_finalizer(fixturedef=self)
diff --git a/_pytest/hookspec.py b/_pytest/hookspec.py
index e96fe32..2c9a661 100644
--- a/_pytest/hookspec.py
+++ b/_pytest/hookspec.py
@@ -73,7 +73,9 @@ def pytest_configure(config):
 
 @hookspec(firstresult=True)
 def pytest_cmdline_parse(pluginmanager, args):
-    """return initialized config object, parsing the specified args. """
+    """return initialized config object, parsing the specified args.
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 def pytest_cmdline_preparse(config, args):
     """(deprecated) modify command line arguments before option parsing. """
@@ -81,7 +83,9 @@ def pytest_cmdline_preparse(config, args):
 @hookspec(firstresult=True)
 def pytest_cmdline_main(config):
     """ called for performing the main command line action. The default
-    implementation will invoke the configure hooks and runtest_mainloop. """
+    implementation will invoke the configure hooks and runtest_mainloop.
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 def pytest_load_initial_conftests(early_config, parser, args):
     """ implements the loading of initial conftest files ahead
@@ -94,7 +98,9 @@ def pytest_load_initial_conftests(early_config, parser, args):
 
 @hookspec(firstresult=True)
 def pytest_collection(session):
-    """ perform the collection protocol for the given session. """
+    """ perform the collection protocol for the given session.
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 def pytest_collection_modifyitems(session, config, items):
     """ called after collection has been performed, may filter or re-order
@@ -108,11 +114,15 @@ def pytest_ignore_collect(path, config):
     """ return True to prevent considering this path for collection.
     This hook is consulted for all files and directories prior to calling
     more specific hooks.
+
+    Stops at first non-None result, see :ref:`firstresult`
     """
 
 @hookspec(firstresult=True)
 def pytest_collect_directory(path, parent):
-    """ called before traversing a directory for collection files. """
+    """ called before traversing a directory for collection files.
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 def pytest_collect_file(path, parent):
     """ return collection Node or None for the given path. Any new node
@@ -133,7 +143,9 @@ def pytest_deselected(items):
 
 @hookspec(firstresult=True)
 def pytest_make_collect_report(collector):
-    """ perform ``collector.collect()`` and return a CollectReport. """
+    """ perform ``collector.collect()`` and return a CollectReport.
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 # -------------------------------------------------------------------------
 # Python test function related hooks
@@ -145,15 +157,20 @@ def pytest_pycollect_makemodule(path, parent):
     This hook will be called for each matching test module path.
     The pytest_collect_file hook needs to be used if you want to
     create test modules for files that do not match as a test module.
-    """
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 @hookspec(firstresult=True)
 def pytest_pycollect_makeitem(collector, name, obj):
-    """ return custom item/collector for a python object in a module, or None.  """
+    """ return custom item/collector for a python object in a module, or None.
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 @hookspec(firstresult=True)
 def pytest_pyfunc_call(pyfuncitem):
-    """ call underlying test function. """
+    """ call underlying test function.
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 def pytest_generate_tests(metafunc):
     """ generate (multiple) parametrized calls to a test function."""
@@ -163,7 +180,8 @@ def pytest_make_parametrize_id(config, val, argname):
     """Return a user-friendly string representation of the given ``val`` that will be used
     by @pytest.mark.parametrize calls. Return None if the hook doesn't know about ``val``.
     The parameter name is available as ``argname``, if required.
-    """
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 # -------------------------------------------------------------------------
 # generic runtest related hooks
@@ -172,7 +190,9 @@ def pytest_make_parametrize_id(config, val, argname):
 @hookspec(firstresult=True)
 def pytest_runtestloop(session):
     """ called for performing the main runtest loop
-    (after collection finished). """
+    (after collection finished).
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 def pytest_itemstart(item, node):
     """ (deprecated, use pytest_runtest_logstart). """
@@ -190,7 +210,9 @@ def pytest_runtest_protocol(item, nextitem):
                    :py:func:`pytest_runtest_teardown`.
 
     :return boolean: True if no further hook implementations should be invoked.
-    """
+
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 def pytest_runtest_logstart(nodeid, location):
     """ signal the start of running a single test item. """
@@ -215,7 +237,8 @@ def pytest_runtest_makereport(item, call):
     """ return a :py:class:`_pytest.runner.TestReport` object
     for the given :py:class:`pytest.Item <_pytest.main.Item>` and
     :py:class:`_pytest.runner.CallInfo`.
-    """
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 def pytest_runtest_logreport(report):
     """ process a test setup/call/teardown report relating to
@@ -227,7 +250,9 @@ def pytest_runtest_logreport(report):
 
 @hookspec(firstresult=True)
 def pytest_fixture_setup(fixturedef, request):
-    """ performs fixture setup execution. """
+    """ performs fixture setup execution.
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 def pytest_fixture_post_finalizer(fixturedef):
     """ called after fixture teardown, but before the cache is cleared so
@@ -277,7 +302,9 @@ def pytest_report_header(config, startdir):
 
 @hookspec(firstresult=True)
 def pytest_report_teststatus(report):
-    """ return result-category, shortletter and verbose word for reporting."""
+    """ return result-category, shortletter and verbose word for reporting.
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 def pytest_terminal_summary(terminalreporter, exitstatus):
     """ add additional section in terminal summary reporting.  """
@@ -295,7 +322,9 @@ def pytest_logwarning(message, code, nodeid, fslocation):
 
 @hookspec(firstresult=True)
 def pytest_doctest_prepare_content(content):
-    """ return processed content for a given doctest"""
+    """ return processed content for a given doctest
+
+    Stops at first non-None result, see :ref:`firstresult` """
 
 # -------------------------------------------------------------------------
 # error handling and internal debugging hooks
diff --git a/_pytest/impl b/_pytest/impl
new file mode 100644
index 0000000..889e37e
--- /dev/null
+++ b/_pytest/impl
@@ -0,0 +1,254 @@
+Sorting per-resource
+-----------------------------
+
+for any given set of items:
+
+- collect items per session-scoped parametrized funcarg
+- re-order until items no parametrizations are mixed
+
+  examples:
+
+        test()
+        test1(s1)
+        test1(s2)     
+        test2()
+        test3(s1)
+        test3(s2)
+
+  gets sorted to:
+
+        test()
+        test2()
+        test1(s1)
+        test3(s1)
+        test1(s2)
+        test3(s2)
+ 
+
+the new @setup functions 
+--------------------------------------
+
+Consider a given @setup-marked function::
+
+    @pytest.mark.setup(maxscope=SCOPE)
+    def mysetup(request, arg1, arg2, ...)
+        ...
+        request.addfinalizer(fin)
+        ...
+
+then FUNCARGSET denotes the set of (arg1, arg2, ...) funcargs and
+all of its dependent funcargs.  The mysetup function will execute
+for any matching test item once per scope.  
+
+The scope is determined as the minimum scope of all scopes of the args
+in FUNCARGSET and the given "maxscope". 
+
+If mysetup has been called and no finalizers have been called it is
+called "active".
+
+Furthermore the following rules apply:
+
+- if an arg value in FUNCARGSET is about to be torn down, the 
+  mysetup-registered finalizers will execute as well.
+
+- There will never be two active mysetup invocations.
+
+Example 1, session scope::
+
+    @pytest.mark.funcarg(scope="session", params=[1,2])
+    def db(request):
+        request.addfinalizer(db_finalize)
+
+    @pytest.mark.setup
+    def mysetup(request, db):
+        request.addfinalizer(mysetup_finalize)
+        ...
+
+And a given test module:
+
+    def test_something():
+        ...
+    def test_otherthing():
+        pass
+
+Here is what happens::
+
+    db(request) executes with request.param == 1
+        mysetup(request, db) executes
+            test_something() executes
+            test_otherthing() executes
+            mysetup_finalize() executes
+    db_finalize() executes
+    db(request) executes with request.param == 2
+        mysetup(request, db) executes
+            test_something() executes
+            test_otherthing() executes
+        mysetup_finalize() executes
+    db_finalize() executes
+
+Example 2, session/function scope::
+
+    @pytest.mark.funcarg(scope="session", params=[1,2])
+    def db(request):
+        request.addfinalizer(db_finalize)
+
+    @pytest.mark.setup(scope="function")
+    def mysetup(request, db):
+        ...
+        request.addfinalizer(mysetup_finalize)
+        ...
+
+And a given test module:
+
+    def test_something():
+        ...
+    def test_otherthing():
+        pass
+
+Here is what happens::
+
+    db(request) executes with request.param == 1
+        mysetup(request, db) executes
+            test_something() executes
+        mysetup_finalize() executes
+        mysetup(request, db) executes
+            test_otherthing() executes
+        mysetup_finalize() executes
+    db_finalize() executes
+    db(request) executes with request.param == 2
+        mysetup(request, db) executes
+            test_something() executes
+        mysetup_finalize() executes
+        mysetup(request, db) executes
+            test_otherthing() executes
+        mysetup_finalize() executes
+    db_finalize() executes
+
+
+Example 3 - funcargs session-mix
+----------------------------------------
+
+Similar with funcargs, an example::
+
+    @pytest.mark.funcarg(scope="session", params=[1,2])
+    def db(request):
+        request.addfinalizer(db_finalize)
+
+    @pytest.mark.funcarg(scope="function")
+    def table(request, db):
+        ...
+        request.addfinalizer(table_finalize)
+        ...
+
+And a given test module:
+
+    def test_something(table):
+        ...
+    def test_otherthing(table):
+        pass
+    def test_thirdthing():
+        pass
+
+Here is what happens::
+
+    db(request) executes with param == 1
+        table(request, db)
+            test_something(table)
+        table_finalize()
+        table(request, db)
+            test_otherthing(table)
+        table_finalize()
+    db_finalize
+    db(request) executes with param == 2
+        table(request, db)
+            test_something(table)
+        table_finalize()
+        table(request, db)
+            test_otherthing(table)
+        table_finalize()
+    db_finalize
+    test_thirdthing()
+    
+Data structures
+--------------------
+
+pytest internally maintains a dict of active funcargs with cache, param,
+finalizer, (scopeitem?) information:
+
+    active_funcargs = dict()
+
+if a parametrized "db" is activated:
+    
+    active_funcargs["db"] = FuncargInfo(dbvalue, paramindex, 
+                                        FuncargFinalize(...), scopeitem)
+
+if a test is torn down and the next test requires a differently 
+parametrized "db":
+
+    for argname in item.callspec.params:
+        if argname in active_funcargs:
+            funcarginfo = active_funcargs[argname]
+            if funcarginfo.param != item.callspec.params[argname]:
+                funcarginfo.callfinalizer()
+                del node2funcarg[funcarginfo.scopeitem]
+                del active_funcargs[argname]
+    nodes_to_be_torn_down = ...
+    for node in nodes_to_be_torn_down:
+        if node in node2funcarg:
+            argname = node2funcarg[node]
+            active_funcargs[argname].callfinalizer()
+            del node2funcarg[node]
+            del active_funcargs[argname]
+
+if a test is setup requiring a "db" funcarg:
+
+    if "db" in active_funcargs:
+        return active_funcargs["db"][0]
+    funcarginfo = setup_funcarg()
+    active_funcargs["db"] = funcarginfo
+    node2funcarg[funcarginfo.scopeitem] = "db"
+
+Implementation plan for resources
+------------------------------------------
+
+1. Revert FuncargRequest to the old form, unmerge item/request
+   (done)
+2. make funcarg factories be discovered at collection time
+3. Introduce funcarg marker
+4. Introduce funcarg scope parameter
+5. Introduce funcarg parametrize parameter
+6. make setup functions be discovered at collection time
+7. (Introduce a pytest_fixture_protocol/setup_funcargs hook)
+
+methods and data structures
+--------------------------------
+
+A FuncarcManager holds all information about funcarg definitions
+including parametrization and scope definitions.  It implements
+a pytest_generate_tests hook which performs parametrization as appropriate.
+
+as a simple example, let's consider a tree where a test function requires
+a "abc" funcarg and its factory defines it as parametrized and scoped
+for Modules.  When collections hits the function item, it creates
+the metafunc object, and calls funcargdb.pytest_generate_tests(metafunc)
+which looks up available funcarg factories and their scope and parametrization.
+This information is equivalent to what can be provided today directly
+at the function site and it should thus be relatively straight forward
+to implement the additional way of defining parametrization/scoping.
+
+conftest loading:
+    each funcarg-factory will populate the session.funcargmanager
+
+When a test item is collected, it grows a dictionary 
+(funcargname2factorycalllist).  A factory lookup is performed 
+for each required funcarg.  The resulting factory call is stored 
+with the item.  If a function is parametrized multiple items are 
+created with respective factory calls. Else if a factory is parametrized
+multiple items and calls to the factory function are created as well.
+
+At setup time, an item populates a funcargs mapping, mapping names
+to values.  If a value is funcarg factories are queried for a given item
+test functions and setup functions are put in a class
+which looks up required funcarg factories.
+
+
diff --git a/_pytest/main.py b/_pytest/main.py
index 480810c..e6f679a 100644
--- a/_pytest/main.py
+++ b/_pytest/main.py
@@ -168,14 +168,13 @@ def pytest_runtestloop(session):
 
 
 def pytest_ignore_collect(path, config):
-    p = path.dirpath()
-    ignore_paths = config._getconftest_pathlist("collect_ignore", path=p)
+    ignore_paths = config._getconftest_pathlist("collect_ignore", path=path.dirpath())
     ignore_paths = ignore_paths or []
     excludeopt = config.getoption("ignore")
     if excludeopt:
         ignore_paths.extend([py.path.local(x) for x in excludeopt])
 
-    if path in ignore_paths:
+    if py.path.local(path) in ignore_paths:
         return True
 
     # Skip duplicate paths.
@@ -763,7 +762,11 @@ class Session(FSCollector):
                 if not has_matched and len(rep.result) == 1 and x.name == "()":
                     nextnames.insert(0, name)
                     resultnodes.extend(self.matchnodes([x], nextnames))
-            node.ihook.pytest_collectreport(report=rep)
+            else:
+                # report collection failures here to avoid failing to run some test
+                # specified in the command line because the module could not be
+                # imported (#134)
+                node.ihook.pytest_collectreport(report=rep)
         return resultnodes
 
     def genitems(self, node):
diff --git a/_pytest/recwarn.py b/_pytest/recwarn.py
index 7ad6fef..9cc404a 100644
--- a/_pytest/recwarn.py
+++ b/_pytest/recwarn.py
@@ -27,10 +27,8 @@ def recwarn():
 
 
 def deprecated_call(func=None, *args, **kwargs):
-    """ assert that calling ``func(*args, **kwargs)`` triggers a
-    ``DeprecationWarning`` or ``PendingDeprecationWarning``.
-
-    This function can be used as a context manager::
+    """context manager that can be used to ensure a block of code triggers a
+    ``DeprecationWarning`` or ``PendingDeprecationWarning``::
 
         >>> import warnings
         >>> def api_call_v2():
@@ -40,38 +38,47 @@ def deprecated_call(func=None, *args, **kwargs):
         >>> with deprecated_call():
         ...    assert api_call_v2() == 200
 
-    Note: we cannot use WarningsRecorder here because it is still subject
-    to the mechanism that prevents warnings of the same type from being
-    triggered twice for the same module. See #1190.
+    ``deprecated_call`` can also be used by passing a function and ``*args`` and ``*kwargs``,
+    in which case it will ensure calling ``func(*args, **kwargs)`` produces one of the warnings
+    types above.
     """
     if not func:
-        return WarningsChecker(expected_warning=(DeprecationWarning, PendingDeprecationWarning))
+        return _DeprecatedCallContext()
+    else:
+        __tracebackhide__ = True
+        with _DeprecatedCallContext():
+            return func(*args, **kwargs)
 
-    categories = []
 
-    def warn_explicit(message, category, *args, **kwargs):
-        categories.append(category)
+class _DeprecatedCallContext(object):
+    """Implements the logic to capture deprecation warnings as a context manager."""
 
-    def warn(message, category=None, *args, **kwargs):
+    def __enter__(self):
+        self._captured_categories = []
+        self._old_warn = warnings.warn
+        self._old_warn_explicit = warnings.warn_explicit
+        warnings.warn_explicit = self._warn_explicit
+        warnings.warn = self._warn
+
+    def _warn_explicit(self, message, category, *args, **kwargs):
+        self._captured_categories.append(category)
+
+    def _warn(self, message, category=None, *args, **kwargs):
         if isinstance(message, Warning):
-            categories.append(message.__class__)
+            self._captured_categories.append(message.__class__)
         else:
-            categories.append(category)
-
-    old_warn = warnings.warn
-    old_warn_explicit = warnings.warn_explicit
-    warnings.warn_explicit = warn_explicit
-    warnings.warn = warn
-    try:
-        ret = func(*args, **kwargs)
-    finally:
-        warnings.warn_explicit = old_warn_explicit
-        warnings.warn = old_warn
-    deprecation_categories = (DeprecationWarning, PendingDeprecationWarning)
-    if not any(issubclass(c, deprecation_categories) for c in categories):
-        __tracebackhide__ = True
-        raise AssertionError("%r did not produce DeprecationWarning" % (func,))
-    return ret
+            self._captured_categories.append(category)
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        warnings.warn_explicit = self._old_warn_explicit
+        warnings.warn = self._old_warn
+
+        if exc_type is None:
+            deprecation_categories = (DeprecationWarning, PendingDeprecationWarning)
+            if not any(issubclass(c, deprecation_categories) for c in self._captured_categories):
+                __tracebackhide__ = True
+                msg = "Did not produce DeprecationWarning or PendingDeprecationWarning"
+                raise AssertionError(msg)
 
 
 def warns(expected_warning, *args, **kwargs):
diff --git a/_pytest/terminal.py b/_pytest/terminal.py
index e226d60..af89d0f 100644
--- a/_pytest/terminal.py
+++ b/_pytest/terminal.py
@@ -282,7 +282,7 @@ class TerminalReporter:
             line = "collected "
         else:
             line = "collecting "
-        line += str(self._numcollected) + " items"
+        line += str(self._numcollected) + " item" + ('' if self._numcollected == 1 else 's')
         if errors:
             line += " / %d errors" % errors
         if skipped:
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..cc72b4b
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,42 @@
+environment:
+  COVERALLS_REPO_TOKEN:
+    secure: 2NJ5Ct55cHJ9WEg3xbSqCuv0rdgzzb6pnzOIG5OkMbTndw3wOBrXntWFoQrXiMFi
+    # this is pytest's token in coveralls.io, encrypted
+    # using pytestbot account as detailed here:
+    # https://www.appveyor.com/docs/build-configuration#secure-variables
+
+  matrix:
+  # coveralls is not in the default env list
+  - TOXENV: "coveralls"
+  # note: please use "tox --listenvs" to populate the build matrix below
+  - TOXENV: "linting"
+  - TOXENV: "py26"
+  - TOXENV: "py27"
+  - TOXENV: "py33"
+  - TOXENV: "py34"
+  - TOXENV: "py35"
+  - TOXENV: "py36"
+  - TOXENV: "pypy"
+  - TOXENV: "py27-pexpect"
... 999 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/pytest.git



More information about the Python-modules-commits mailing list