diff --git a/scrapy/tests/test_utils_deprecate.py b/scrapy/tests/test_utils_deprecate.py index fd6bc90c3..2a042ba85 100644 --- a/scrapy/tests/test_utils_deprecate.py +++ b/scrapy/tests/test_utils_deprecate.py @@ -3,14 +3,18 @@ from __future__ import absolute_import import inspect import unittest import warnings +import mock from scrapy.utils.deprecate import create_deprecated_class + class MyWarning(UserWarning): pass + class SomeBaseClass(object): pass + class NewName(SomeBaseClass): pass @@ -234,3 +238,12 @@ class WarnWhenSubclassedTest(unittest.TestCase): self.assertIn('foo.Bar', str(w[0].message)) self.assertIn('AlsoDeprecated', str(w[1].message)) self.assertIn('foo.Bar', str(w[1].message)) + + def test_inspect_stack(self): + with mock.patch('inspect.stack', side_effect=IndexError): + with warnings.catch_warnings(record=True) as w: + DeprecatedName = create_deprecated_class('DeprecatedName', NewName) + class SubClass(DeprecatedName): + pass + + self.assertIn("Error detecting parent module", str(w[0].message)) diff --git a/scrapy/utils/deprecate.py b/scrapy/utils/deprecate.py index 66f0217a0..6f1940611 100644 --- a/scrapy/utils/deprecate.py +++ b/scrapy/utils/deprecate.py @@ -101,10 +101,18 @@ def create_deprecated_class(name, new_class, clsdict=None, return super(DeprecatedClass, cls).__call__(*args, **kwargs) deprecated_cls = DeprecatedClass(name, (new_class,), clsdict or {}) - frm = inspect.stack()[1] - parent_module = inspect.getmodule(frm[0]) - if parent_module is not None: - deprecated_cls.__module__ = parent_module.__name__ + + try: + frm = inspect.stack()[1] + parent_module = inspect.getmodule(frm[0]) + if parent_module is not None: + deprecated_cls.__module__ = parent_module.__name__ + except Exception as e: + # Sometimes inspect.stack() fails (e.g. when the first import of + # deprecated class is in jinja2 template). __module__ attribute is not + # important enough to raise an exception as users may be unable + # to fix inspect.stack() errors. + warnings.warn("Error detecting parent module: %r" % e) return deprecated_cls