prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Deprecate Reppy support
  https://github.com/scrapy/scrapy/issues/5226#issuecomment-897621867
  
  </issue>
  <code>
  [start of scrapy/robotstxt.py]
  1 import logging
  2 import sys
  3 from abc import ABCMeta, abstractmethod
  4 
  5 from scrapy.utils.python import to_unicode
  6 
  7 logger = logging.getLogger(__name__)
  8 
  9 
  10 def decode_robotstxt(robotstxt_body, spider, to_native_str_type=False):
  11     try:
  12         if to_native_str_type:
  13             robotstxt_body = to_unicode(robotstxt_body)
  14         else:
  15             robotstxt_body = robotstxt_body.decode("utf-8")
  16     except UnicodeDecodeError:
  17         # If we found garbage or robots.txt in an encoding other than UTF-8, disregard it.
  18         # Switch to 'allow all' state.
  19         logger.warning(
  20             "Failure while parsing robots.txt. File either contains garbage or "
  21             "is in an encoding other than UTF-8, treating it as an empty file.",
  22             exc_info=sys.exc_info(),
  23             extra={"spider": spider},
  24         )
  25         robotstxt_body = ""
  26     return robotstxt_body
  27 
  28 
  29 class RobotParser(metaclass=ABCMeta):
  30     @classmethod
  31     @abstractmethod
  32     def from_crawler(cls, crawler, robotstxt_body):
  33         """Parse the content of a robots.txt_ file as bytes. This must be a class method.
  34         It must return a new instance of the parser backend.
  35 
  36         :param crawler: crawler which made the request
  37         :type crawler: :class:`~scrapy.crawler.Crawler` instance
  38 
  39         :param robotstxt_body: content of a robots.txt_ file.
  40         :type robotstxt_body: bytes
  41         """
  42         pass
  43 
  44     @abstractmethod
  45     def allowed(self, url, user_agent):
  46         """Return ``True`` if  ``user_agent`` is allowed to crawl ``url``, otherwise return ``False``.
  47 
  48         :param url: Absolute URL
  49         :type url: str
  50 
  51         :param user_agent: User agent
  52         :type user_agent: str
  53         """
  54         pass
  55 
  56 
  57 class PythonRobotParser(RobotParser):
  58     def __init__(self, robotstxt_body, spider):
  59         from urllib.robotparser import RobotFileParser
  60 
  61         self.spider = spider
  62         robotstxt_body = decode_robotstxt(
  63             robotstxt_body, spider, to_native_str_type=True
  64         )
  65         self.rp = RobotFileParser()
  66         self.rp.parse(robotstxt_body.splitlines())
  67 
  68     @classmethod
  69     def from_crawler(cls, crawler, robotstxt_body):
  70         spider = None if not crawler else crawler.spider
  71         o = cls(robotstxt_body, spider)
  72         return o
  73 
  74     def allowed(self, url, user_agent):
  75         user_agent = to_unicode(user_agent)
  76         url = to_unicode(url)
  77         return self.rp.can_fetch(user_agent, url)
  78 
  79 
  80 class ReppyRobotParser(RobotParser):
  81     def __init__(self, robotstxt_body, spider):
  82         from reppy.robots import Robots
  83 
  84         self.spider = spider
  85         self.rp = Robots.parse("", robotstxt_body)
  86 
  87     @classmethod
  88     def from_crawler(cls, crawler, robotstxt_body):
  89         spider = None if not crawler else crawler.spider
  90         o = cls(robotstxt_body, spider)
  91         return o
  92 
  93     def allowed(self, url, user_agent):
  94         return self.rp.allowed(url, user_agent)
  95 
  96 
  97 class RerpRobotParser(RobotParser):
  98     def __init__(self, robotstxt_body, spider):
  99         from robotexclusionrulesparser import RobotExclusionRulesParser
  100 
  101         self.spider = spider
  102         self.rp = RobotExclusionRulesParser()
  103         robotstxt_body = decode_robotstxt(robotstxt_body, spider)
  104         self.rp.parse(robotstxt_body)
  105 
  106     @classmethod
  107     def from_crawler(cls, crawler, robotstxt_body):
  108         spider = None if not crawler else crawler.spider
  109         o = cls(robotstxt_body, spider)
  110         return o
  111 
  112     def allowed(self, url, user_agent):
  113         user_agent = to_unicode(user_agent)
  114         url = to_unicode(url)
  115         return self.rp.is_allowed(user_agent, url)
  116 
  117 
  118 class ProtegoRobotParser(RobotParser):
  119     def __init__(self, robotstxt_body, spider):
  120         from protego import Protego
  121 
  122         self.spider = spider
  123         robotstxt_body = decode_robotstxt(robotstxt_body, spider)
  124         self.rp = Protego.parse(robotstxt_body)
  125 
  126     @classmethod
  127     def from_crawler(cls, crawler, robotstxt_body):
  128         spider = None if not crawler else crawler.spider
  129         o = cls(robotstxt_body, spider)
  130         return o
  131 
  132     def allowed(self, url, user_agent):
  133         user_agent = to_unicode(user_agent)
  134         url = to_unicode(url)
  135         return self.rp.can_fetch(url, user_agent)
  136
  [end of scrapy/robotstxt.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/scrapy/robotstxt.py b/scrapy/robotstxt.py
  --- a/scrapy/robotstxt.py
  +++ b/scrapy/robotstxt.py
  @@ -1,7 +1,9 @@
   import logging
   import sys
   from abc import ABCMeta, abstractmethod
  +from warnings import warn
   
  +from scrapy.exceptions import ScrapyDeprecationWarning
   from scrapy.utils.python import to_unicode
   
   logger = logging.getLogger(__name__)
  @@ -79,6 +81,7 @@
   
   class ReppyRobotParser(RobotParser):
       def __init__(self, robotstxt_body, spider):
  +        warn("ReppyRobotParser is deprecated.", ScrapyDeprecationWarning, stacklevel=2)
           from reppy.robots import Robots
   
           self.spider = spider
  
verification_info: {"golden_diff": "diff --git a/scrapy/robotstxt.py b/scrapy/robotstxt.py\n--- a/scrapy/robotstxt.py\n+++ b/scrapy/robotstxt.py\n@@ -1,7 +1,9 @@\n import logging\n import sys\n from abc import ABCMeta, abstractmethod\n+from warnings import warn\n \n+from scrapy.exceptions import ScrapyDeprecationWarning\n from scrapy.utils.python import to_unicode\n \n logger = logging.getLogger(__name__)\n@@ -79,6 +81,7 @@\n \n class ReppyRobotParser(RobotParser):\n     def __init__(self, robotstxt_body, spider):\n+        warn(\"ReppyRobotParser is deprecated.\", ScrapyDeprecationWarning, stacklevel=2)\n         from reppy.robots import Robots\n \n         self.spider = spider\n", "issue": "Deprecate Reppy support\nhttps://github.com/scrapy/scrapy/issues/5226#issuecomment-897621867\n", "before_files": [{"content": "import logging\nimport sys\nfrom abc import ABCMeta, abstractmethod\n\nfrom scrapy.utils.python import to_unicode\n\nlogger = logging.getLogger(__name__)\n\n\ndef decode_robotstxt(robotstxt_body, spider, to_native_str_type=False):\n    try:\n        if to_native_str_type:\n            robotstxt_body = to_unicode(robotstxt_body)\n        else:\n            robotstxt_body = robotstxt_body.decode(\"utf-8\")\n    except UnicodeDecodeError:\n        # If we found garbage or robots.txt in an encoding other than UTF-8, disregard it.\n        # Switch to 'allow all' state.\n        logger.warning(\n            \"Failure while parsing robots.txt. File either contains garbage or \"\n            \"is in an encoding other than UTF-8, treating it as an empty file.\",\n            exc_info=sys.exc_info(),\n            extra={\"spider\": spider},\n        )\n        robotstxt_body = \"\"\n    return robotstxt_body\n\n\nclass RobotParser(metaclass=ABCMeta):\n    @classmethod\n    @abstractmethod\n    def from_crawler(cls, crawler, robotstxt_body):\n        \"\"\"Parse the content of a robots.txt_ file as bytes. This must be a class method.\n        It must return a new instance of the parser backend.\n\n        :param crawler: crawler which made the request\n        :type crawler: :class:`~scrapy.crawler.Crawler` instance\n\n        :param robotstxt_body: content of a robots.txt_ file.\n        :type robotstxt_body: bytes\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def allowed(self, url, user_agent):\n        \"\"\"Return ``True`` if  ``user_agent`` is allowed to crawl ``url``, otherwise return ``False``.\n\n        :param url: Absolute URL\n        :type url: str\n\n        :param user_agent: User agent\n        :type user_agent: str\n        \"\"\"\n        pass\n\n\nclass PythonRobotParser(RobotParser):\n    def __init__(self, robotstxt_body, spider):\n        from urllib.robotparser import RobotFileParser\n\n        self.spider = spider\n        robotstxt_body = decode_robotstxt(\n            robotstxt_body, spider, to_native_str_type=True\n        )\n        self.rp = RobotFileParser()\n        self.rp.parse(robotstxt_body.splitlines())\n\n    @classmethod\n    def from_crawler(cls, crawler, robotstxt_body):\n        spider = None if not crawler else crawler.spider\n        o = cls(robotstxt_body, spider)\n        return o\n\n    def allowed(self, url, user_agent):\n        user_agent = to_unicode(user_agent)\n        url = to_unicode(url)\n        return self.rp.can_fetch(user_agent, url)\n\n\nclass ReppyRobotParser(RobotParser):\n    def __init__(self, robotstxt_body, spider):\n        from reppy.robots import Robots\n\n        self.spider = spider\n        self.rp = Robots.parse(\"\", robotstxt_body)\n\n    @classmethod\n    def from_crawler(cls, crawler, robotstxt_body):\n        spider = None if not crawler else crawler.spider\n        o = cls(robotstxt_body, spider)\n        return o\n\n    def allowed(self, url, user_agent):\n        return self.rp.allowed(url, user_agent)\n\n\nclass RerpRobotParser(RobotParser):\n    def __init__(self, robotstxt_body, spider):\n        from robotexclusionrulesparser import RobotExclusionRulesParser\n\n        self.spider = spider\n        self.rp = RobotExclusionRulesParser()\n        robotstxt_body = decode_robotstxt(robotstxt_body, spider)\n        self.rp.parse(robotstxt_body)\n\n    @classmethod\n    def from_crawler(cls, crawler, robotstxt_body):\n        spider = None if not crawler else crawler.spider\n        o = cls(robotstxt_body, spider)\n        return o\n\n    def allowed(self, url, user_agent):\n        user_agent = to_unicode(user_agent)\n        url = to_unicode(url)\n        return self.rp.is_allowed(user_agent, url)\n\n\nclass ProtegoRobotParser(RobotParser):\n    def __init__(self, robotstxt_body, spider):\n        from protego import Protego\n\n        self.spider = spider\n        robotstxt_body = decode_robotstxt(robotstxt_body, spider)\n        self.rp = Protego.parse(robotstxt_body)\n\n    @classmethod\n    def from_crawler(cls, crawler, robotstxt_body):\n        spider = None if not crawler else crawler.spider\n        o = cls(robotstxt_body, spider)\n        return o\n\n    def allowed(self, url, user_agent):\n        user_agent = to_unicode(user_agent)\n        url = to_unicode(url)\n        return self.rp.can_fetch(url, user_agent)\n", "path": "scrapy/robotstxt.py"}]}