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 "<MarkDecorator %r %r>" %(name, d)
+        return "<MarkDecorator %r %r>" % (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 "<MarkInfo %r args=%r kwargs=%r>" % (
-                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 "<MarkDecorator %r %r>" %(name, d)
+        return "<MarkDecorator %r %r>" % (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 "<MarkInfo %r args=%r kwargs=%r>" % (
-                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.
_______________________________________________
pytest-commit mailing list
pytest-commit@python.org
http://mail.python.org/mailman/listinfo/pytest-commit

Reply via email to