diff --git a/scrapy/spider.py b/scrapy/spider.py index c377b4c3a..f58840f8d 100644 --- a/scrapy/spider.py +++ b/scrapy/spider.py @@ -67,9 +67,8 @@ class Spider(object_ref): class BaseSpider(Spider): __metaclass__ = warn_when_subclassed( - 4, # == len([object, object_ref, Spider, BaseSpider]) - "scrapy.spider.BaseSpider was deprecated. " - "Please inherit from scrapy.spider.Spider." + superclass=Spider, + message="scrapy.spider.BaseSpider was deprecated. Please inherit from scrapy.spider.Spider." ) diff --git a/scrapy/tests/test_spider.py b/scrapy/tests/test_spider.py index c587d8f3a..bd565a0ec 100644 --- a/scrapy/tests/test_spider.py +++ b/scrapy/tests/test_spider.py @@ -5,10 +5,11 @@ from cStringIO import StringIO from twisted.trial import unittest -from scrapy.spider import Spider +from scrapy.spider import Spider, BaseSpider from scrapy.http import Response, TextResponse, XmlResponse, HtmlResponse from scrapy.contrib.spiders.init import InitSpider from scrapy.contrib.spiders import CrawlSpider, XMLFeedSpider, CSVFeedSpider, SitemapSpider +from scrapy.exceptions import ScrapyDeprecationWarning class SpiderTest(unittest.TestCase): @@ -134,5 +135,19 @@ class SitemapSpiderTest(SpiderTest): r = Response(url="http://www.example.com/sitemap.xml.gz", body=self.GZBODY) self.assertEqual(spider._get_sitemap_body(r), self.BODY) + +class BaseSpiderDeprecationTest(unittest.TestCase): + + def test_basespider_is_deprecated(self): + with warnings.catch_warnings(record=True) as w: + + class MySpider(BaseSpider): + pass + + self.assertEqual(len(w), 1) + self.assertEqual(w[0].category, ScrapyDeprecationWarning) + self.assertEqual(w[0].lineno, inspect.getsourcelines(MySpider)[1]) + + if __name__ == '__main__': unittest.main() diff --git a/scrapy/tests/test_utils_deprecate.py b/scrapy/tests/test_utils_deprecate.py new file mode 100644 index 000000000..068c93e29 --- /dev/null +++ b/scrapy/tests/test_utils_deprecate.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import +import inspect +import unittest +import warnings +from scrapy.utils.deprecate import warn_when_subclassed + +class MyWarning(UserWarning): + pass + +class SomeBaseClass(object): + pass + +class NewName(SomeBaseClass): + pass + + +class WarnWhenSubclassedTest(unittest.TestCase): + + def test_no_warning_on_definition(self): + with warnings.catch_warnings(record=True) as w: + + class Deprecated(NewName): + __metaclass__ = warn_when_subclassed(NewName, "message") + + self.assertEqual(w, []) + + def test_warning_on_subclassing(self): + with warnings.catch_warnings(record=True) as w: + + class Deprecated(NewName): + __metaclass__ = warn_when_subclassed(NewName, "message", MyWarning) + + class UserClass(Deprecated): + pass + + self.assertEqual(len(w), 1) + msg = w[0] + assert issubclass(msg.category, MyWarning) + self.assertEqual(str(msg.message), "message") + self.assertEqual(msg.lineno, inspect.getsourcelines(UserClass)[1]) diff --git a/scrapy/utils/deprecate.py b/scrapy/utils/deprecate.py index 3b67a3d7d..df882792c 100644 --- a/scrapy/utils/deprecate.py +++ b/scrapy/utils/deprecate.py @@ -11,14 +11,14 @@ def attribute(obj, oldattr, newattr, version='0.12'): (cname, oldattr, version, cname, newattr), ScrapyDeprecationWarning, stacklevel=3) -def warn_when_subclassed(mro_len, message, category=ScrapyDeprecationWarning): +def warn_when_subclassed(superclass, message, category=ScrapyDeprecationWarning): """ Return a metaclass that causes classes to issue a warning when they are subclassed. """ class Metaclass(type): def __init__(cls, name, bases, clsdict): - if len(cls.mro()) > mro_len: + if len(cls.mro()) > len(superclass.mro()) + 1: warnings.warn(message, category, stacklevel=2) super(Metaclass, cls).__init__(name, bases, clsdict) return Metaclass