Floris Bruynooghe avatar Floris Bruynooghe committed 1be8fc7

Look up the pytest_assertrepr_compare hook for each test item

Before this was only done at the time the assertion plugin was loaded.
This lead to counter-intuitive behaviour where two subdirectories with
a pytest_assertrepr_compare hook in their conftest.py would not work,
only one would ever be used.

This defers assiging the _pytest.assertion.util._reprcompare function
until the item is loaded (pytest_runtest_setup) so that it can use the
hookrelay of the test item to find the appropriate
pytest_assertrepr_compare hook for the item.

This fixes issue #77.

Comments (0)

Files changed (2)

_pytest/assertion/__init__.py

                 mode = "reinterp"
     if mode != "plain":
         _load_modules(mode)
-        def callbinrepr(op, left, right):
-            hook_result = config.hook.pytest_assertrepr_compare(
-                config=config, op=op, left=left, right=right)
-            for new_expl in hook_result:
-                if new_expl:
-                    res = '\n~'.join(new_expl)
-                    if mode == "rewrite":
-                        # The result will be fed back a python % formatting
-                        # operation, which will fail if there are extraneous
-                        # '%'s in the string. Escape them here.
-                        res = res.replace("%", "%%")
-                    return res
         m = monkeypatch()
         config._cleanup.append(m.undo)
         m.setattr(py.builtin.builtins, 'AssertionError',
                   reinterpret.AssertionError)
-        m.setattr(util, '_reprcompare', callbinrepr)
     hook = None
     if mode == "rewrite":
         hook = rewrite.AssertionRewritingHook()
     if hook is not None:
         hook.set_session(session)
 
+def pytest_runtest_setup(item):
+    def callbinrepr(op, left, right):
+        hook_result = item.ihook.pytest_assertrepr_compare(
+            config=item.config, op=op, left=left, right=right)
+        for new_expl in hook_result:
+            if new_expl:
+                res = '\n~'.join(new_expl)
+                if item.config.getvalue("assertmode") == "rewrite":
+                    # The result will be fed back a python % formatting
+                    # operation, which will fail if there are extraneous
+                    # '%'s in the string. Escape them here.
+                    res = res.replace("%", "%%")
+                return res
+    util._reprcompare = callbinrepr
+
+def pytest_runtest_teardown(item):
+    util._reprcompare = None
+
 def pytest_sessionfinish(session):
     hook = session.config._assertstate.hook
     if hook is not None:

testing/test_assertion.py

         assert hook.left == [0, 1]
         assert hook.right == [0, 2]
 
-    def test_configure_unconfigure(self, testdir, hook):
-        assert hook == util._reprcompare
-        config = testdir.parseconfig()
-        plugin.pytest_configure(config)
-        assert hook != util._reprcompare
-        from _pytest.config import pytest_unconfigure
-        pytest_unconfigure(config)
-        assert hook == util._reprcompare
-
 def callequal(left, right):
     return plugin.pytest_assertrepr_compare('==', left, right)
 
         "*E*'y'*",
     ])
 
+@needsnewassert
+def test_assertrepr_loaded_per_dir(testdir):
+    testdir.makepyfile(test_base=['def test_base(): assert 1 == 2'])
+    a = testdir.mkdir('a')
+    a_test = a.join('test_a.py')
+    a_test.write('def test_a(): assert 1 == 2')
+    a_conftest = a.join('conftest.py')
+    a_conftest.write('def pytest_assertrepr_compare(): return ["summary a"]')
+    b = testdir.mkdir('b')
+    b_test = b.join('test_b.py')
+    b_test.write('def test_b(): assert 1 == 2')
+    b_conftest = b.join('conftest.py')
+    b_conftest.write('def pytest_assertrepr_compare(): return ["summary b"]')
+    result = testdir.runpytest()
+    result.stdout.fnmatch_lines([
+            '*def test_base():*',
+            '*E*assert 1 == 2*',
+            '*def test_a():*',
+            '*E*assert summary a*',
+            '*def test_b():*',
+            '*E*assert summary b*'])
+
 
 def test_assertion_options(testdir):
     testdir.makepyfile("""
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.