more fixes regarding marking, in particular plugins should use add_marker/get_marker now.
This commit is contained in:
		
							parent
							
								
									9fdfa155fb
								
							
						
					
					
						commit
						2248a31a44
					
				
							
								
								
									
										10
									
								
								CHANGELOG
								
								
								
								
							
							
						
						
									
										10
									
								
								CHANGELOG
								
								
								
								
							|  | @ -11,6 +11,16 @@ Changes between 2.4.1 and 2.4.2 | ||||||
| - avoid tmpdir fixture to create too long filenames especially | - avoid tmpdir fixture to create too long filenames especially | ||||||
|   when parametrization is used (issue354) |   when parametrization is used (issue354) | ||||||
| 
 | 
 | ||||||
|  | - fix pytest-pep8 and pytest-flakes / pytest interactions | ||||||
|  |   (collection names in mark plugin was assuming an item always | ||||||
|  |   has a function which is not true for those plugins etc.) | ||||||
|  |   Thanks Andi Zeidler. | ||||||
|  | 
 | ||||||
|  | - introduce node.get_marker/node.add_marker API for plugins | ||||||
|  |   like pytest-pep8 and pytest-flakes to avoid the messy | ||||||
|  |   details of the node.keywords  pseudo-dicts.  Adapated | ||||||
|  |   docs. | ||||||
|  | 
 | ||||||
| Changes between 2.4.0 and 2.4.1 | Changes between 2.4.0 and 2.4.1 | ||||||
| ----------------------------------- | ----------------------------------- | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -321,6 +321,27 @@ class Node(object): | ||||||
|         chain.reverse() |         chain.reverse() | ||||||
|         return chain |         return chain | ||||||
| 
 | 
 | ||||||
|  |     def add_marker(self, marker): | ||||||
|  |         """ dynamically add a marker object to the node. | ||||||
|  | 
 | ||||||
|  |         ``marker`` can be a string or pytest.mark.* instance. | ||||||
|  |         """ | ||||||
|  |         from _pytest.mark import MarkDecorator | ||||||
|  |         if isinstance(marker, py.builtin._basestring): | ||||||
|  |             marker = MarkDecorator(marker) | ||||||
|  |         elif not isinstance(marker, MarkDecorator): | ||||||
|  |             raise ValueError("is not a string or pytest.mark.* Marker") | ||||||
|  |         self.keywords[marker.name] = marker | ||||||
|  | 
 | ||||||
|  |     def get_marker(self, name): | ||||||
|  |         """ get a marker object from this node or None if | ||||||
|  |         the node doesn't have a marker with that name. """ | ||||||
|  |         val = self.keywords.get(name, None) | ||||||
|  |         if val is not None: | ||||||
|  |             from _pytest.mark import MarkInfo, MarkDecorator | ||||||
|  |             if isinstance(val, (MarkDecorator, MarkInfo)): | ||||||
|  |                 return val | ||||||
|  | 
 | ||||||
|     def listextrakeywords(self): |     def listextrakeywords(self): | ||||||
|         """ Return a set of all extra keywords in self and any parents.""" |         """ Return a set of all extra keywords in self and any parents.""" | ||||||
|         extra_keywords = set() |         extra_keywords = set() | ||||||
|  |  | ||||||
|  | @ -81,11 +81,8 @@ def pytest_collection_modifyitems(items, config): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class MarkMapping: | class MarkMapping: | ||||||
|     """Provides a local mapping for markers. |     """Provides a local mapping for markers where item access | ||||||
|     Only the marker names from the given :class:`NodeKeywords` will be mapped, |     resolves to True if the marker is present. """ | ||||||
|     so the names are taken only from :class:`MarkInfo` or |  | ||||||
|     :class:`MarkDecorator` items. |  | ||||||
|     """ |  | ||||||
|     def __init__(self, keywords): |     def __init__(self, keywords): | ||||||
|         mymarks = set() |         mymarks = set() | ||||||
|         for key, value in keywords.items(): |         for key, value in keywords.items(): | ||||||
|  | @ -93,8 +90,8 @@ class MarkMapping: | ||||||
|                 mymarks.add(key) |                 mymarks.add(key) | ||||||
|         self._mymarks = mymarks |         self._mymarks = mymarks | ||||||
| 
 | 
 | ||||||
|     def __getitem__(self, markname): |     def __getitem__(self, name): | ||||||
|         return markname in self._mymarks |         return name in self._mymarks | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class KeywordMapping: | class KeywordMapping: | ||||||
|  | @ -202,13 +199,17 @@ class MarkDecorator: | ||||||
|             pass |             pass | ||||||
|     """ |     """ | ||||||
|     def __init__(self, name, args=None, kwargs=None): |     def __init__(self, name, args=None, kwargs=None): | ||||||
|         self.markname = name |         self.name = name | ||||||
|         self.args = args or () |         self.args = args or () | ||||||
|         self.kwargs = kwargs or {} |         self.kwargs = kwargs or {} | ||||||
| 
 | 
 | ||||||
|  |     @property | ||||||
|  |     def markname(self): | ||||||
|  |         return self.name # for backward-compat (2.4.1 had this attr) | ||||||
|  | 
 | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         d = self.__dict__.copy() |         d = self.__dict__.copy() | ||||||
|         name = d.pop('markname') |         name = d.pop('name') | ||||||
|         return "<MarkDecorator %r %r>" % (name, d) |         return "<MarkDecorator %r %r>" % (name, d) | ||||||
| 
 | 
 | ||||||
|     def __call__(self, *args, **kwargs): |     def __call__(self, *args, **kwargs): | ||||||
|  | @ -228,19 +229,19 @@ class MarkDecorator: | ||||||
|                     else: |                     else: | ||||||
|                         func.pytestmark = [self] |                         func.pytestmark = [self] | ||||||
|                 else: |                 else: | ||||||
|                     holder = getattr(func, self.markname, None) |                     holder = getattr(func, self.name, None) | ||||||
|                     if holder is None: |                     if holder is None: | ||||||
|                         holder = MarkInfo( |                         holder = MarkInfo( | ||||||
|                             self.markname, self.args, self.kwargs |                             self.name, self.args, self.kwargs | ||||||
|                         ) |                         ) | ||||||
|                         setattr(func, self.markname, holder) |                         setattr(func, self.name, holder) | ||||||
|                     else: |                     else: | ||||||
|                         holder.add(self.args, self.kwargs) |                         holder.add(self.args, self.kwargs) | ||||||
|                 return func |                 return func | ||||||
|         kw = self.kwargs.copy() |         kw = self.kwargs.copy() | ||||||
|         kw.update(kwargs) |         kw.update(kwargs) | ||||||
|         args = self.args + args |         args = self.args + args | ||||||
|         return self.__class__(self.markname, args=args, kwargs=kw) |         return self.__class__(self.name, args=args, kwargs=kw) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class MarkInfo: | class MarkInfo: | ||||||
|  |  | ||||||
|  | @ -235,7 +235,7 @@ specifies via named environments:: | ||||||
|             "env(name): mark test to run only on named environment") |             "env(name): mark test to run only on named environment") | ||||||
| 
 | 
 | ||||||
|     def pytest_runtest_setup(item): |     def pytest_runtest_setup(item): | ||||||
|         envmarker = item.keywords.get("env", None) |         envmarker = item.get_marker("env") | ||||||
|         if envmarker is not None: |         if envmarker is not None: | ||||||
|             envname = envmarker.args[0] |             envname = envmarker.args[0] | ||||||
|             if envname != item.config.getoption("-E"): |             if envname != item.config.getoption("-E"): | ||||||
|  | @ -318,7 +318,7 @@ test function.  From a conftest file we can read it like this:: | ||||||
|     import sys |     import sys | ||||||
| 
 | 
 | ||||||
|     def pytest_runtest_setup(item): |     def pytest_runtest_setup(item): | ||||||
|         g = item.keywords.get("glob", None) |         g = item.get_marker("glob") | ||||||
|         if g is not None: |         if g is not None: | ||||||
|             for info in g: |             for info in g: | ||||||
|                 print ("glob args=%s kwargs=%s" %(info.args, info.kwargs)) |                 print ("glob args=%s kwargs=%s" %(info.args, info.kwargs)) | ||||||
|  | @ -353,7 +353,7 @@ for your particular platform, you could use the following plugin:: | ||||||
|     def pytest_runtest_setup(item): |     def pytest_runtest_setup(item): | ||||||
|         if isinstance(item, item.Function): |         if isinstance(item, item.Function): | ||||||
|             plat = sys.platform |             plat = sys.platform | ||||||
|             if plat not in item.keywords: |             if not item.get_marker(plat): | ||||||
|                 if ALL.intersection(item.keywords): |                 if ALL.intersection(item.keywords): | ||||||
|                     pytest.skip("cannot run on platform %s" %(plat)) |                     pytest.skip("cannot run on platform %s" %(plat)) | ||||||
| 
 | 
 | ||||||
|  | @ -439,9 +439,9 @@ We want to dynamically define two markers and can do it in a | ||||||
|     def pytest_collection_modifyitems(items): |     def pytest_collection_modifyitems(items): | ||||||
|         for item in items: |         for item in items: | ||||||
|             if "interface" in item.nodeid: |             if "interface" in item.nodeid: | ||||||
|                 item.keywords["interface"] = pytest.mark.interface |                 item.add_marker(pytest.mark.interface) | ||||||
|             elif "event" in item.nodeid: |             elif "event" in item.nodeid: | ||||||
|                 item.keywords["event"] = pytest.mark.event |                 item.add_marker(pytest.mark.event) | ||||||
| 
 | 
 | ||||||
| We can now use the ``-m option`` to select one set:: | We can now use the ``-m option`` to select one set:: | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,16 @@ | ||||||
|  | #!/bin/bash | ||||||
|  | 
 | ||||||
|  | # this assumes plugins are installed as sister directories | ||||||
|  | 
 | ||||||
|  | set -e | ||||||
|  | cd ../pytest-pep8 | ||||||
|  | py.test | ||||||
|  | cd ../pytest-instafail | ||||||
|  | py.test  | ||||||
|  | cd ../pytest-cache | ||||||
|  | py.test | ||||||
|  | #cd ../pytest-cov | ||||||
|  | #py.test | ||||||
|  | cd ../pytest-xdist | ||||||
|  | py.test | ||||||
|  | 
 | ||||||
|  | @ -180,6 +180,7 @@ def test_keyword_option_custom(spec, testdir): | ||||||
|     assert len(passed) == len(passed_result) |     assert len(passed) == len(passed_result) | ||||||
|     assert list(passed) == list(passed_result) |     assert list(passed) == list(passed_result) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class TestFunctional: | class TestFunctional: | ||||||
| 
 | 
 | ||||||
|     def test_mark_per_function(self, testdir): |     def test_mark_per_function(self, testdir): | ||||||
|  | @ -362,7 +363,6 @@ class TestFunctional: | ||||||
|         deselected_tests = dlist[0].items |         deselected_tests = dlist[0].items | ||||||
|         assert len(deselected_tests) == 2 |         assert len(deselected_tests) == 2 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def test_keywords_at_node_level(self, testdir): |     def test_keywords_at_node_level(self, testdir): | ||||||
|         p = testdir.makepyfile(""" |         p = testdir.makepyfile(""" | ||||||
|             import pytest |             import pytest | ||||||
|  | @ -383,6 +383,30 @@ class TestFunctional: | ||||||
|         reprec = testdir.inline_run() |         reprec = testdir.inline_run() | ||||||
|         reprec.assertoutcome(passed=1) |         reprec.assertoutcome(passed=1) | ||||||
| 
 | 
 | ||||||
|  |     def test_keyword_added_for_session(self, testdir): | ||||||
|  |         testdir.makeconftest(""" | ||||||
|  |             import pytest | ||||||
|  |             def pytest_collection_modifyitems(session): | ||||||
|  |                 session.add_marker("mark1") | ||||||
|  |                 session.add_marker(pytest.mark.mark2) | ||||||
|  |                 session.add_marker(pytest.mark.mark3) | ||||||
|  |                 pytest.raises(ValueError, lambda: | ||||||
|  |                         session.add_marker(10)) | ||||||
|  |         """) | ||||||
|  |         testdir.makepyfile(""" | ||||||
|  |             def test_some(request): | ||||||
|  |                 assert "mark1" in request.keywords | ||||||
|  |                 assert "mark2" in request.keywords | ||||||
|  |                 assert "mark3" in request.keywords | ||||||
|  |                 assert 10 not in request.keywords | ||||||
|  |                 marker = request.node.get_marker("mark1") | ||||||
|  |                 assert marker.name == "mark1" | ||||||
|  |                 assert marker.args == () | ||||||
|  |                 assert marker.kwargs == {} | ||||||
|  |         """) | ||||||
|  |         reprec = testdir.inline_run("-m", "mark1") | ||||||
|  |         reprec.assertoutcome(passed=1) | ||||||
|  | 
 | ||||||
| class TestKeywordSelection: | class TestKeywordSelection: | ||||||
|     def test_select_simple(self, testdir): |     def test_select_simple(self, testdir): | ||||||
|         file_test = testdir.makepyfile(""" |         file_test = testdir.makepyfile(""" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue