From 667e70f5551da23b86dd6ee29b5f77d5c604d003 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 13 Sep 2017 12:17:08 +0200 Subject: [PATCH 1/7] switch out the placeholder MarkEvaluator in unittest plugin --- _pytest/unittest.py | 3 +-- changelog/2767.trivial | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 changelog/2767.trivial diff --git a/_pytest/unittest.py b/_pytest/unittest.py index 585f81472..5c7f38f48 100644 --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -134,8 +134,7 @@ class TestCaseFunction(Function): try: skip(reason) except skip.Exception: - self._evalskip = MarkEvaluator(self, 'SkipTest') - self._evalskip.result = True + self._evalskip = True self._addexcinfo(sys.exc_info()) def addExpectedFailure(self, testcase, rawexcinfo, reason=""): diff --git a/changelog/2767.trivial b/changelog/2767.trivial new file mode 100644 index 000000000..c42a06e07 --- /dev/null +++ b/changelog/2767.trivial @@ -0,0 +1 @@ +* remove unnecessary mark evaluator in unittest plugin \ No newline at end of file From a33650953a4838cd742f547ea14dcd8fc7731adb Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 13 Sep 2017 12:20:29 +0200 Subject: [PATCH 2/7] remove unused import --- _pytest/unittest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/_pytest/unittest.py b/_pytest/unittest.py index 5c7f38f48..1ea1b0121 100644 --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -9,7 +9,6 @@ import _pytest._code from _pytest.config import hookimpl from _pytest.outcomes import fail, skip, xfail from _pytest.python import transfer_markers, Class, Module, Function -from _pytest.skipping import MarkEvaluator def pytest_pycollect_makeitem(collector, name, obj): From 9ad2b75038204ab44125219797df3b9dabb99889 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Wed, 13 Sep 2017 12:49:11 +0200 Subject: [PATCH 3/7] skipping: replace _evalskip with a more consistent _skipped_by_mark --- _pytest/skipping.py | 9 ++++----- _pytest/unittest.py | 2 +- changelog/2767.removal | 2 ++ 3 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 changelog/2767.removal diff --git a/_pytest/skipping.py b/_pytest/skipping.py index ef9f601ca..e2565f8ae 100644 --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -155,17 +155,17 @@ class MarkEvaluator: @hookimpl(tryfirst=True) def pytest_runtest_setup(item): # Check if skip or skipif are specified as pytest marks - + item._skipped_by_mark = False skipif_info = item.keywords.get('skipif') if isinstance(skipif_info, (MarkInfo, MarkDecorator)): eval_skipif = MarkEvaluator(item, 'skipif') if eval_skipif.istrue(): - item._evalskip = eval_skipif + item._skipped_by_mark = True skip(eval_skipif.getexplanation()) skip_info = item.keywords.get('skip') if isinstance(skip_info, (MarkInfo, MarkDecorator)): - item._evalskip = True + item._skipped_by_mark = True if 'reason' in skip_info.kwargs: skip(skip_info.kwargs['reason']) elif skip_info.args: @@ -212,7 +212,6 @@ def pytest_runtest_makereport(item, call): outcome = yield rep = outcome.get_result() evalxfail = getattr(item, '_evalxfail', None) - evalskip = getattr(item, '_evalskip', None) # unitttest special case, see setting of _unexpectedsuccess if hasattr(item, '_unexpectedsuccess') and rep.when == "call": from _pytest.compat import _is_unittest_unexpected_success_a_failure @@ -248,7 +247,7 @@ def pytest_runtest_makereport(item, call): else: rep.outcome = "passed" rep.wasxfail = explanation - elif evalskip is not None and rep.skipped and type(rep.longrepr) is tuple: + elif item._skipped_by_mark and rep.skipped and type(rep.longrepr) is tuple: # skipped by mark.skipif; change the location of the failure # to point to the item definition, otherwise it will display # the location of where the skip exception was raised within pytest diff --git a/_pytest/unittest.py b/_pytest/unittest.py index 1ea1b0121..7842d1658 100644 --- a/_pytest/unittest.py +++ b/_pytest/unittest.py @@ -133,7 +133,7 @@ class TestCaseFunction(Function): try: skip(reason) except skip.Exception: - self._evalskip = True + self._skipped_by_mark = True self._addexcinfo(sys.exc_info()) def addExpectedFailure(self, testcase, rawexcinfo, reason=""): diff --git a/changelog/2767.removal b/changelog/2767.removal new file mode 100644 index 000000000..702a0a36c --- /dev/null +++ b/changelog/2767.removal @@ -0,0 +1,2 @@ +* remove the internal optional multi-typed attribute _evalskip + and replacce it with the boolean _skipped_by_mark \ No newline at end of file From 8480075f01af2f35e97ad99bb6eeb39b90b0a24a Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 14 Sep 2017 09:31:07 +0200 Subject: [PATCH 4/7] resuffle markevaluator internal structure --- _pytest/skipping.py | 98 +++++++++++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 39 deletions(-) diff --git a/_pytest/skipping.py b/_pytest/skipping.py index e2565f8ae..838cee563 100644 --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -60,22 +60,31 @@ def pytest_configure(config): ) -class MarkEvaluator: +class MarkEvaluator(object): def __init__(self, item, name): self.item = item - self.name = name - - @property - def holder(self): - return self.item.keywords.get(self.name) + self._marks = None + self._mark = None + self._repr_name = name def __bool__(self): - return bool(self.holder) + self._marks = self._get_marks() + return bool(self._marks) __nonzero__ = __bool__ def wasvalid(self): return not hasattr(self, 'exc') + def _get_marks(self): + + keyword = self.item.keywords.get(self._repr_name) + if isinstance(keyword, MarkDecorator): + return [keyword.mark] + elif isinstance(keyword, MarkInfo): + return [x.combined for x in keyword] + else: + return [] + def invalidraise(self, exc): raises = self.get('raises') if not raises: @@ -95,7 +104,7 @@ class MarkEvaluator: fail("Error evaluating %r expression\n" " %s\n" "%s" - % (self.name, self.expr, "\n".join(msg)), + % (self._repr_name, self.expr, "\n".join(msg)), pytrace=False) def _getglobals(self): @@ -107,40 +116,51 @@ class MarkEvaluator: def _istrue(self): if hasattr(self, 'result'): return self.result - if self.holder: - if self.holder.args or 'condition' in self.holder.kwargs: - self.result = False - # "holder" might be a MarkInfo or a MarkDecorator; only - # MarkInfo keeps track of all parameters it received in an - # _arglist attribute - marks = getattr(self.holder, '_marks', None) \ - or [self.holder.mark] - for _, args, kwargs in marks: - if 'condition' in kwargs: - args = (kwargs['condition'],) - for expr in args: + self._marks = self._get_marks() + + def needs_eval(mark): + return mark.args or 'condition' in mark.kwargs + + if self._marks: + self.result = False + # "holder" might be a MarkInfo or a MarkDecorator; only + # MarkInfo keeps track of all parameters it received in an + # _arglist attribute + for mark in self._marks: + self._mark = mark + if 'condition' in mark.kwargs: + args = (mark.kwargs['condition'],) + else: + args = mark.args + + for expr in args: + self.expr = expr + if isinstance(expr, six.string_types): + d = self._getglobals() + result = cached_eval(self.item.config, expr, d) + else: + if "reason" not in mark.kwargs: + # XXX better be checked at collection time + msg = "you need to specify reason=STRING " \ + "when using booleans as conditions." + fail(msg) + result = bool(expr) + if result: + self.result = True + self.reason = mark.kwargs.get('reason', None) self.expr = expr - if isinstance(expr, six.string_types): - d = self._getglobals() - result = cached_eval(self.item.config, expr, d) - else: - if "reason" not in kwargs: - # XXX better be checked at collection time - msg = "you need to specify reason=STRING " \ - "when using booleans as conditions." - fail(msg) - result = bool(expr) - if result: - self.result = True - self.reason = kwargs.get('reason', None) - self.expr = expr - return self.result - else: - self.result = True - return getattr(self, 'result', False) + return self.result + + if not args: + self.result = True + self.reason = mark.kwargs.get('reason', None) + return self.result + return False def get(self, attr, default=None): - return self.holder.kwargs.get(attr, default) + if self._mark is None: + return default + return self._mark.kwargs.get(attr, default) def getexplanation(self): expl = getattr(self, 'reason', None) or self.get('reason', None) From e3b73682b229fc52db2124981bd6f21ad1c2dc9d Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Fri, 15 Sep 2017 08:47:20 +0200 Subject: [PATCH 5/7] flake8 fix --- _pytest/skipping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_pytest/skipping.py b/_pytest/skipping.py index 838cee563..e439105d4 100644 --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -151,7 +151,7 @@ class MarkEvaluator(object): self.expr = expr return self.result - if not args: + if not args: self.result = True self.reason = mark.kwargs.get('reason', None) return self.result From 459cc401929db9f6c8b9970a015ab251b221ffec Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 16 Sep 2017 21:08:48 +0200 Subject: [PATCH 6/7] skipping: cleanup remove dead comments fix naming remove dead code --- _pytest/skipping.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/_pytest/skipping.py b/_pytest/skipping.py index e439105d4..11ae2d03d 100644 --- a/_pytest/skipping.py +++ b/_pytest/skipping.py @@ -65,7 +65,7 @@ class MarkEvaluator(object): self.item = item self._marks = None self._mark = None - self._repr_name = name + self._mark_name = name def __bool__(self): self._marks = self._get_marks() @@ -77,7 +77,7 @@ class MarkEvaluator(object): def _get_marks(self): - keyword = self.item.keywords.get(self._repr_name) + keyword = self.item.keywords.get(self._mark_name) if isinstance(keyword, MarkDecorator): return [keyword.mark] elif isinstance(keyword, MarkInfo): @@ -104,7 +104,7 @@ class MarkEvaluator(object): fail("Error evaluating %r expression\n" " %s\n" "%s" - % (self._repr_name, self.expr, "\n".join(msg)), + % (self._mark_name, self.expr, "\n".join(msg)), pytrace=False) def _getglobals(self): @@ -118,14 +118,8 @@ class MarkEvaluator(object): return self.result self._marks = self._get_marks() - def needs_eval(mark): - return mark.args or 'condition' in mark.kwargs - if self._marks: self.result = False - # "holder" might be a MarkInfo or a MarkDecorator; only - # MarkInfo keeps track of all parameters it received in an - # _arglist attribute for mark in self._marks: self._mark = mark if 'condition' in mark.kwargs: From 8a6bdb282f2ddaa72f5010e5fbe88726b4b8d022 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 9 Oct 2017 15:21:06 +0200 Subject: [PATCH 7/7] fix changelog entry --- changelog/2767.removal | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/changelog/2767.removal b/changelog/2767.removal index 702a0a36c..b9c3984cd 100644 --- a/changelog/2767.removal +++ b/changelog/2767.removal @@ -1,2 +1 @@ -* remove the internal optional multi-typed attribute _evalskip - and replacce it with the boolean _skipped_by_mark \ No newline at end of file +Remove the internal multi-typed attribute ``Node._evalskip`` and replace it with the boolean ``Node._skipped_by_mark``. \ No newline at end of file