2 new commits in pytest:

https://bitbucket.org/hpk42/pytest/commits/05ea7094569f/
Changeset:   05ea7094569f
User:        hpk42
Date:        2013-11-21 12:21:52
Summary:     refactor internal FixtureRequest handling to avoid monkeypatching.
One of the positive user-facing effects is that the "request" object
can now be used in closures.
Affected #:  3 files

diff -r dcbe5c70239930fafdc762d0a427f8814c1d4799 -r 
05ea7094569fe3f3098fa8e965014d236bc3c907 CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -65,6 +65,10 @@
 - fix issue221 - handle importing of namespace-package with no 
   __init__.py properly.
 
+- refactor internal FixtureRequest handling to avoid monkeypatching.
+  One of the positive user-facing effects is that the "request" object 
+  can now be used in closures.
+
 Changes between 2.4.1 and 2.4.2
 -----------------------------------
 

diff -r dcbe5c70239930fafdc762d0a427f8814c1d4799 -r 
05ea7094569fe3f3098fa8e965014d236bc3c907 _pytest/python.py
--- a/_pytest/python.py
+++ b/_pytest/python.py
@@ -4,7 +4,6 @@
 import sys
 import pytest
 from _pytest.mark import MarkDecorator
-from _pytest.monkeypatch import monkeypatch
 from py._code.code import TerminalRepr
 
 import _pytest
@@ -1083,14 +1082,12 @@
         self.fixturename = None
         #: Scope string, one of "function", "cls", "module", "session"
         self.scope = "function"
-        self.getparent = pyfuncitem.getparent
         self._funcargs  = self._pyfuncitem.funcargs.copy()
-        self._fixtureinfo = fi = pyfuncitem._fixtureinfo
-        self._arg2fixturedefs = fi.name2fixturedefs
+        fixtureinfo = pyfuncitem._fixtureinfo
+        self._arg2fixturedefs = fixtureinfo.name2fixturedefs
         self._arg2index = {}
-        self.fixturenames = self._fixtureinfo.names_closure
+        self.fixturenames = fixtureinfo.names_closure
         self._fixturemanager = pyfuncitem.session._fixturemanager
-        self._parentid = pyfuncitem.parent.nodeid
         self._fixturestack = []
 
     @property
@@ -1104,7 +1101,7 @@
             # we arrive here because of a getfuncargvalue(argname) usage which
             # was naturally not knowable at parsing/collection time
             fixturedefs = self._fixturemanager.getfixturedefs(
-                            argname, self._parentid)
+                            argname, self._pyfuncitem.parent.nodeid)
             self._arg2fixturedefs[argname] = fixturedefs
         # fixturedefs is immutable so we maintain a decreasing index
         index = self._arg2index.get(argname, 0) - 1
@@ -1270,29 +1267,21 @@
             return fixturedef.cached_result # set by fixturedef.execute()
         except AttributeError:
             pass
-
-        # prepare request fixturename and param attributes before
-        # calling into fixture function
+        # prepare a subrequest object before calling fixture function
+        # (latter managed by fixturedef)
         argname = fixturedef.argname
         node = self._pyfuncitem
-        mp = monkeypatch()
-        mp.setattr(self, 'fixturename', argname)
+        scope = fixturedef.scope
         try:
             param = node.callspec.getparam(argname)
         except (AttributeError, ValueError):
-            pass
+            param = notset
         else:
-            mp.setattr(self, 'param', param, raising=False)
-
-        # if a parametrize invocation set a scope it will override
-        # the static scope defined with the fixture function
-        scope = fixturedef.scope
-        try:
-            paramscopenum = node.callspec._arg2scopenum[argname]
-        except (KeyError, AttributeError):
-            pass
-        else:
-            if paramscopenum != scopenum_subfunction:
+            # if a parametrize invocation set a scope it will override
+            # the static scope defined with the fixture function
+            paramscopenum = node.callspec._arg2scopenum.get(argname)
+            if paramscopenum is not None and \
+               paramscopenum != scopenum_subfunction:
                 scope = scopes[paramscopenum]
 
         # check if a higher-level scoped fixture accesses a lower level one
@@ -1302,31 +1291,29 @@
                 # try to report something helpful
                 lines = self._factorytraceback()
                 raise ScopeMismatchError("You tried to access the %r scoped "
-                    "funcarg %r with a %r scoped request object, "
+                    "fixture %r with a %r scoped request object, "
                     "involved factories\n%s" %(
                     (scope, argname, self.scope, "\n".join(lines))))
             __tracebackhide__ = False
-            mp.setattr(self, "scope", scope)
+        else:
+            scope = self.scope
 
-        # route request.addfinalizer to fixturedef
-        mp.setattr(self, "addfinalizer", fixturedef.addfinalizer)
-
+        subrequest = SubRequest(self, argname, scope, param,
+                                fixturedef.addfinalizer)
         try:
             # perform the fixture call
-            val = fixturedef.execute(request=self)
+            val = fixturedef.execute(request=subrequest)
         finally:
             # if the fixture function failed it might still have
             # registered finalizers so we can register
-
             # prepare finalization according to scope
             # (XXX analyse exact finalizing mechanics / cleanup)
             self.session._setupstate.addfinalizer(fixturedef.finish,
-                                                  self.node)
+                                                  subrequest.node)
             self._fixturemanager.addargfinalizer(fixturedef.finish, argname)
             for subargname in fixturedef.argnames: # XXX all deps?
                 self._fixturemanager.addargfinalizer(fixturedef.finish,
                                                      subargname)
-        mp.undo()
         return val
 
     def _factorytraceback(self):
@@ -1358,6 +1345,28 @@
     def __repr__(self):
         return "<FixtureRequest for %r>" %(self.node)
 
+notset = object()
+class SubRequest(FixtureRequest):
+    """ a sub request for handling getting a fixture from a
+    test function/fixture. """
+    def __init__(self, request, argname, scope, param, addfinalizer):
+        self._parent_request = request
+        self.fixturename = argname
+        if param is not notset:
+            self.param = param
+        self.scope = scope
+        self.addfinalizer = addfinalizer
+        self._pyfuncitem = request._pyfuncitem
+        self._funcargs  = request._funcargs
+        self._arg2fixturedefs = request._arg2fixturedefs
+        self._arg2index = request._arg2index
+        self.fixturenames = request.fixturenames
+        self._fixturemanager = request._fixturemanager
+        self._fixturestack = request._fixturestack
+
+    def __repr__(self):
+        return "<SubRequest %r for %r>" % (self.fixturename, self.node)
+
 class ScopeMismatchError(Exception):
     """ A fixture function tries to use a different fixture function which
     which has a lower scope (e.g. a Session one calls a function one)
@@ -1399,8 +1408,8 @@
             fm = self.request._fixturemanager
             available = []
             for name, fixturedef in fm._arg2fixturedefs.items():
-                faclist = list(fm._matchfactories(fixturedef,
-                                                  self.request._parentid))
+                parentid = self.request._pyfuncitem.parent.nodeid
+                faclist = list(fm._matchfactories(fixturedef, parentid))
                 if faclist:
                     available.append(name)
             msg = "fixture %r not found" % (self.argname,)
@@ -1744,7 +1753,6 @@
             func()
         # check neccesity of next commented call
         self._fixturemanager.removefinalizer(self.finish)
-        #print "finished", self
         try:
             del self.cached_result
         except AttributeError:

diff -r dcbe5c70239930fafdc762d0a427f8814c1d4799 -r 
05ea7094569fe3f3098fa8e965014d236bc3c907 testing/python/fixture.py
--- a/testing/python/fixture.py
+++ b/testing/python/fixture.py
@@ -473,7 +473,7 @@
                 assert l == ["module", "function", "class",
                              "function", "method", "function"]
         """)
-        reprec = testdir.inline_run()
+        reprec = testdir.inline_run("-v")
         reprec.assertoutcome(passed=3)
 
     def test_fixtures_sub_subdir_normalize_sep(self, testdir):
@@ -1792,6 +1792,20 @@
         for test in ['test_a', 'test_b', 'test_c']:
             assert reprec.matchreport(test).passed
 
+    def test_request_is_clean(self, testdir):
+        testdir.makepyfile("""
+            import pytest
+            l = []
+            @pytest.fixture(params=[1, 2])
+            def fix(request):
+                request.addfinalizer(lambda: l.append(request.param))
+            def test_fix(fix):
+                pass
+        """)
+        reprec = testdir.inline_run("-s")
+        l = reprec.getcalls("pytest_runtest_call")[0].item.module.l
+        assert l == [1,2]
+
     def test_parametrize_separated_lifecycle(self, testdir):
         testdir.makepyfile("""
             import pytest


https://bitbucket.org/hpk42/pytest/commits/7c1e760976b2/
Changeset:   7c1e760976b2
User:        hpk42
Date:        2013-11-21 12:31:22
Summary:     fix py25 compat
Affected #:  1 file

diff -r 05ea7094569fe3f3098fa8e965014d236bc3c907 -r 
7c1e760976b2e7097588da6d7d9cdc892330722f testing/test_runner.py
--- a/testing/test_runner.py
+++ b/testing/test_runner.py
@@ -1,3 +1,5 @@
+from __future__ import with_statement
+
 import pytest, py, sys, os
 from _pytest import runner, main

Repository URL: https://bitbucket.org/hpk42/pytest/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
_______________________________________________
pytest-commit mailing list
pytest-commit@python.org
https://mail.python.org/mailman/listinfo/pytest-commit

Reply via email to