From 9c285dfc1d1c79f71a532e8618a8eb9b443f9df8 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 6 Dec 2016 09:13:25 +0100 Subject: [PATCH] fix #2118 - rework Node._getcustomclass and Node compat properties --- CHANGELOG.rst | 4 +++- _pytest/main.py | 47 ++++++++++++++++++++++++++++++----------------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d49214e3f..ff6efb7ef 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,7 +1,9 @@ 3.0.6.dev0 ========== -* +* fix issue #2118 - protect against internal deprecationerrors by changing the code path of Node._getcustomclass. + This also turns a internal deprecation into a real deprecation. + Thanks to `@nicoddemus`_ for the report and `@RonnyPfannschmidt`_ for the PR. * diff --git a/_pytest/main.py b/_pytest/main.py index 52876c12a..905b35d83 100644 --- a/_pytest/main.py +++ b/_pytest/main.py @@ -3,6 +3,8 @@ import functools import os import sys +import warnings + import _pytest import _pytest._code import py @@ -190,14 +192,21 @@ class FSHookProxy: self.__dict__[name] = x return x -def compatproperty(name): - def fget(self): - import warnings - warnings.warn("This usage is deprecated, please use pytest.{0} instead".format(name), - PendingDeprecationWarning, stacklevel=2) - return getattr(pytest, name) +class _CompatProperty(object): + def __init__(self, name): + self.name = name + + def __get__(self, obj, owner): + if obj is None: + return self + + warnings.warn( + "usage of {owner!r}.{name} is deprecated, please use pytest.{name} instead".format( + name=self.name, owner=type(owner).__name__), + PendingDeprecationWarning, stacklevel=2) + return getattr(pytest, self.name) + - return property(fget) class NodeKeywords(MappingMixin): def __init__(self, node): @@ -269,19 +278,23 @@ class Node(object): """ fspath sensitive hook proxy used to call pytest hooks""" return self.session.gethookproxy(self.fspath) - Module = compatproperty("Module") - Class = compatproperty("Class") - Instance = compatproperty("Instance") - Function = compatproperty("Function") - File = compatproperty("File") - Item = compatproperty("Item") + Module = _CompatProperty("Module") + Class = _CompatProperty("Class") + Instance = _CompatProperty("Instance") + Function = _CompatProperty("Function") + File = _CompatProperty("File") + Item = _CompatProperty("Item") def _getcustomclass(self, name): - cls = getattr(self, name) - if cls != getattr(pytest, name): - py.log._apiwarn("2.0", "use of node.%s is deprecated, " + maybe_compatprop = getattr(type(self), name) + if isinstance(maybe_compatprop, _CompatProperty): + return getattr(pytest, name) + else: + cls = getattr(self, name) + + warnings.warn("use of node.%s is deprecated, " "use pytest_pycollect_makeitem(...) to create custom " - "collection nodes" % name) + "collection nodes" % name, category=DeprecationWarning) return cls def __repr__(self):