We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Should `scrapy.utils.python.WeakKeyCache` be deprecated?
## Summary
I can't find any reference to `scrapy.utils.python.WeakKeyCache` (except in tests) and I think that it should be deprecated.

## Motivation
Keeping non-used and non-documented code makes the codebase harder to maintain.

## Describe alternatives you've considered
As it's tested we could keep it, but I don't think it's a good idea.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `scrapy/utils/python.py`
Content:
```
1 """
2 This module contains essential stuff that should've come with Python itself ;)
3 """
4 import errno
5 import gc
6 import inspect
7 import re
8 import sys
9 import weakref
10 from functools import partial, wraps
11 from itertools import chain
12 
13 from scrapy.utils.decorators import deprecated
14 
15 
16 def flatten(x):
17     """flatten(sequence) -> list
18 
19     Returns a single, flat list which contains all elements retrieved
20     from the sequence and all recursively contained sub-sequences
21     (iterables).
22 
23     Examples:
24     >>> [1, 2, [3,4], (5,6)]
25     [1, 2, [3, 4], (5, 6)]
26     >>> flatten([[[1,2,3], (42,None)], [4,5], [6], 7, (8,9,10)])
27     [1, 2, 3, 42, None, 4, 5, 6, 7, 8, 9, 10]
28     >>> flatten(["foo", "bar"])
29     ['foo', 'bar']
30     >>> flatten(["foo", ["baz", 42], "bar"])
31     ['foo', 'baz', 42, 'bar']
32     """
33     return list(iflatten(x))
34 
35 
36 def iflatten(x):
37     """iflatten(sequence) -> iterator
38 
39     Similar to ``.flatten()``, but returns iterator instead"""
40     for el in x:
41         if is_listlike(el):
42             for el_ in iflatten(el):
43                 yield el_
44         else:
45             yield el
46 
47 
48 def is_listlike(x):
49     """
50     >>> is_listlike("foo")
51     False
52     >>> is_listlike(5)
53     False
54     >>> is_listlike(b"foo")
55     False
56     >>> is_listlike([b"foo"])
57     True
58     >>> is_listlike((b"foo",))
59     True
60     >>> is_listlike({})
61     True
62     >>> is_listlike(set())
63     True
64     >>> is_listlike((x for x in range(3)))
65     True
66     >>> is_listlike(range(5))
67     True
68     """
69     return hasattr(x, "__iter__") and not isinstance(x, (str, bytes))
70 
71 
72 def unique(list_, key=lambda x: x):
73     """efficient function to uniquify a list preserving item order"""
74     seen = set()
75     result = []
76     for item in list_:
77         seenkey = key(item)
78         if seenkey in seen:
79             continue
80         seen.add(seenkey)
81         result.append(item)
82     return result
83 
84 
85 def to_unicode(text, encoding=None, errors='strict'):
86     """Return the unicode representation of a bytes object ``text``. If
87     ``text`` is already an unicode object, return it as-is."""
88     if isinstance(text, str):
89         return text
90     if not isinstance(text, (bytes, str)):
91         raise TypeError('to_unicode must receive a bytes or str '
92                         'object, got %s' % type(text).__name__)
93     if encoding is None:
94         encoding = 'utf-8'
95     return text.decode(encoding, errors)
96 
97 
98 def to_bytes(text, encoding=None, errors='strict'):
99     """Return the binary representation of ``text``. If ``text``
100     is already a bytes object, return it as-is."""
101     if isinstance(text, bytes):
102         return text
103     if not isinstance(text, str):
104         raise TypeError('to_bytes must receive a str or bytes '
105                         'object, got %s' % type(text).__name__)
106     if encoding is None:
107         encoding = 'utf-8'
108     return text.encode(encoding, errors)
109 
110 
111 @deprecated('to_unicode')
112 def to_native_str(text, encoding=None, errors='strict'):
113     """ Return str representation of ``text``. """
114     return to_unicode(text, encoding, errors)
115 
116 
117 def re_rsearch(pattern, text, chunk_size=1024):
118     """
119     This function does a reverse search in a text using a regular expression
120     given in the attribute 'pattern'.
121     Since the re module does not provide this functionality, we have to find for
122     the expression into chunks of text extracted from the end (for the sake of efficiency).
123     At first, a chunk of 'chunk_size' kilobytes is extracted from the end, and searched for
124     the pattern. If the pattern is not found, another chunk is extracted, and another
125     search is performed.
126     This process continues until a match is found, or until the whole file is read.
127     In case the pattern wasn't found, None is returned, otherwise it returns a tuple containing
128     the start position of the match, and the ending (regarding the entire text).
129     """
130     def _chunk_iter():
131         offset = len(text)
132         while True:
133             offset -= (chunk_size * 1024)
134             if offset <= 0:
135                 break
136             yield (text[offset:], offset)
137         yield (text, 0)
138 
139     if isinstance(pattern, str):
140         pattern = re.compile(pattern)
141 
142     for chunk, offset in _chunk_iter():
143         matches = [match for match in pattern.finditer(chunk)]
144         if matches:
145             start, end = matches[-1].span()
146             return offset + start, offset + end
147     return None
148 
149 
150 def memoizemethod_noargs(method):
151     """Decorator to cache the result of a method (without arguments) using a
152     weak reference to its object
153     """
154     cache = weakref.WeakKeyDictionary()
155 
156     @wraps(method)
157     def new_method(self, *args, **kwargs):
158         if self not in cache:
159             cache[self] = method(self, *args, **kwargs)
160         return cache[self]
161     return new_method
162 
163 
164 _BINARYCHARS = {to_bytes(chr(i)) for i in range(32)} - {b"\0", b"\t", b"\n", b"\r"}
165 _BINARYCHARS |= {ord(ch) for ch in _BINARYCHARS}
166 
167 
168 def binary_is_text(data):
169     """ Returns ``True`` if the given ``data`` argument (a ``bytes`` object)
170     does not contain unprintable control characters.
171     """
172     if not isinstance(data, bytes):
173         raise TypeError("data must be bytes, got '%s'" % type(data).__name__)
174     return all(c not in _BINARYCHARS for c in data)
175 
176 
177 def _getargspec_py23(func):
178     """_getargspec_py23(function) -> named tuple ArgSpec(args, varargs, keywords,
179                                                         defaults)
180 
181     Was identical to inspect.getargspec() in python2, but uses
182     inspect.getfullargspec() for python3 behind the scenes to avoid
183     DeprecationWarning.
184 
185     >>> def f(a, b=2, *ar, **kw):
186     ...     pass
187 
188     >>> _getargspec_py23(f)
189     ArgSpec(args=['a', 'b'], varargs='ar', keywords='kw', defaults=(2,))
190     """
191     return inspect.ArgSpec(*inspect.getfullargspec(func)[:4])
192 
193 
194 def get_func_args(func, stripself=False):
195     """Return the argument name list of a callable"""
196     if inspect.isfunction(func):
197         func_args, _, _, _ = _getargspec_py23(func)
198     elif inspect.isclass(func):
199         return get_func_args(func.__init__, True)
200     elif inspect.ismethod(func):
201         return get_func_args(func.__func__, True)
202     elif inspect.ismethoddescriptor(func):
203         return []
204     elif isinstance(func, partial):
205         return [x for x in get_func_args(func.func)[len(func.args):]
206                 if not (func.keywords and x in func.keywords)]
207     elif hasattr(func, '__call__'):
208         if inspect.isroutine(func):
209             return []
210         elif getattr(func, '__name__', None) == '__call__':
211             return []
212         else:
213             return get_func_args(func.__call__, True)
214     else:
215         raise TypeError('%s is not callable' % type(func))
216     if stripself:
217         func_args.pop(0)
218     return func_args
219 
220 
221 def get_spec(func):
222     """Returns (args, kwargs) tuple for a function
223     >>> import re
224     >>> get_spec(re.match)
225     (['pattern', 'string'], {'flags': 0})
226 
227     >>> class Test:
228     ...     def __call__(self, val):
229     ...         pass
230     ...     def method(self, val, flags=0):
231     ...         pass
232 
233     >>> get_spec(Test)
234     (['self', 'val'], {})
235 
236     >>> get_spec(Test.method)
237     (['self', 'val'], {'flags': 0})
238 
239     >>> get_spec(Test().method)
240     (['self', 'val'], {'flags': 0})
241     """
242 
243     if inspect.isfunction(func) or inspect.ismethod(func):
244         spec = _getargspec_py23(func)
245     elif hasattr(func, '__call__'):
246         spec = _getargspec_py23(func.__call__)
247     else:
248         raise TypeError('%s is not callable' % type(func))
249 
250     defaults = spec.defaults or []
251 
252     firstdefault = len(spec.args) - len(defaults)
253     args = spec.args[:firstdefault]
254     kwargs = dict(zip(spec.args[firstdefault:], defaults))
255     return args, kwargs
256 
257 
258 def equal_attributes(obj1, obj2, attributes):
259     """Compare two objects attributes"""
260     # not attributes given return False by default
261     if not attributes:
262         return False
263 
264     temp1, temp2 = object(), object()
265     for attr in attributes:
266         # support callables like itemgetter
267         if callable(attr):
268             if attr(obj1) != attr(obj2):
269                 return False
270         elif getattr(obj1, attr, temp1) != getattr(obj2, attr, temp2):
271             return False
272     # all attributes equal
273     return True
274 
275 
276 class WeakKeyCache:
277 
278     def __init__(self, default_factory):
279         self.default_factory = default_factory
280         self._weakdict = weakref.WeakKeyDictionary()
281 
282     def __getitem__(self, key):
283         if key not in self._weakdict:
284             self._weakdict[key] = self.default_factory(key)
285         return self._weakdict[key]
286 
287 
288 @deprecated
289 def retry_on_eintr(function, *args, **kw):
290     """Run a function and retry it while getting EINTR errors"""
291     while True:
292         try:
293             return function(*args, **kw)
294         except IOError as e:
295             if e.errno != errno.EINTR:
296                 raise
297 
298 
299 def without_none_values(iterable):
300     """Return a copy of ``iterable`` with all ``None`` entries removed.
301 
302     If ``iterable`` is a mapping, return a dictionary where all pairs that have
303     value ``None`` have been removed.
304     """
305     try:
306         return {k: v for k, v in iterable.items() if v is not None}
307     except AttributeError:
308         return type(iterable)((v for v in iterable if v is not None))
309 
310 
311 def global_object_name(obj):
312     """
313     Return full name of a global object.
314 
315     >>> from scrapy import Request
316     >>> global_object_name(Request)
317     'scrapy.http.request.Request'
318     """
319     return "%s.%s" % (obj.__module__, obj.__name__)
320 
321 
322 if hasattr(sys, "pypy_version_info"):
323     def garbage_collect():
324         # Collecting weakreferences can take two collections on PyPy.
325         gc.collect()
326         gc.collect()
327 else:
328     def garbage_collect():
329         gc.collect()
330 
331 
332 class MutableChain:
333     """
334     Thin wrapper around itertools.chain, allowing to add iterables "in-place"
335     """
336 
337     def __init__(self, *args):
338         self.data = chain.from_iterable(args)
339 
340     def extend(self, *iterables):
341         self.data = chain(self.data, chain.from_iterable(iterables))
342 
343     def __iter__(self):
344         return self
345 
346     def __next__(self):
347         return next(self.data)
348 
349     @deprecated("scrapy.utils.python.MutableChain.__next__")
350     def next(self):
351         return self.__next__()
352
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/scrapy/utils/python.py b/scrapy/utils/python.py
--- a/scrapy/utils/python.py
+++ b/scrapy/utils/python.py
@@ -6,10 +6,12 @@
 import inspect
 import re
 import sys
+import warnings
 import weakref
 from functools import partial, wraps
 from itertools import chain
 
+from scrapy.exceptions import ScrapyDeprecationWarning
 from scrapy.utils.decorators import deprecated
 
 
@@ -127,6 +129,7 @@
     In case the pattern wasn't found, None is returned, otherwise it returns a tuple containing
     the start position of the match, and the ending (regarding the entire text).
     """
+
     def _chunk_iter():
         offset = len(text)
         while True:
@@ -158,6 +161,7 @@
         if self not in cache:
             cache[self] = method(self, *args, **kwargs)
         return cache[self]
+
     return new_method
 
 
@@ -276,6 +280,7 @@
 class WeakKeyCache:
 
     def __init__(self, default_factory):
+        warnings.warn("The WeakKeyCache class is deprecated", category=ScrapyDeprecationWarning, stacklevel=2)
         self.default_factory = default_factory
         self._weakdict = weakref.WeakKeyDictionary()

{"golden_diff": "diff --git a/scrapy/utils/python.py b/scrapy/utils/python.py\n--- a/scrapy/utils/python.py\n+++ b/scrapy/utils/python.py\n@@ -6,10 +6,12 @@\n import inspect\n import re\n import sys\n+import warnings\n import weakref\n from functools import partial, wraps\n from itertools import chain\n \n+from scrapy.exceptions import ScrapyDeprecationWarning\n from scrapy.utils.decorators import deprecated\n \n \n@@ -127,6 +129,7 @@\n     In case the pattern wasn't found, None is returned, otherwise it returns a tuple containing\n     the start position of the match, and the ending (regarding the entire text).\n     \"\"\"\n+\n     def _chunk_iter():\n         offset = len(text)\n         while True:\n@@ -158,6 +161,7 @@\n         if self not in cache:\n             cache[self] = method(self, *args, **kwargs)\n         return cache[self]\n+\n     return new_method\n \n \n@@ -276,6 +280,7 @@\n class WeakKeyCache:\n \n     def __init__(self, default_factory):\n+        warnings.warn(\"The WeakKeyCache class is deprecated\", category=ScrapyDeprecationWarning, stacklevel=2)\n         self.default_factory = default_factory\n         self._weakdict = weakref.WeakKeyDictionary()\n", "issue": "Should `scrapy.utils.python.WeakKeyCache` be deprecated?\n## Summary\r\nI can't find any reference to `scrapy.utils.python.WeakKeyCache` (except in tests) and I think that it should be deprecated.\r\n\r\n## Motivation\r\nKeeping non-used and non-documented code makes the codebase harder to maintain.\r\n\r\n## Describe alternatives you've considered\r\nAs it's tested we could keep it, but I don't think it's a good idea.\r\n\n", "before_files": [{"content": "\"\"\"\nThis module contains essential stuff that should've come with Python itself ;)\n\"\"\"\nimport errno\nimport gc\nimport inspect\nimport re\nimport sys\nimport weakref\nfrom functools import partial, wraps\nfrom itertools import chain\n\nfrom scrapy.utils.decorators import deprecated\n\n\ndef flatten(x):\n    \"\"\"flatten(sequence) -> list\n\n    Returns a single, flat list which contains all elements retrieved\n    from the sequence and all recursively contained sub-sequences\n    (iterables).\n\n    Examples:\n    >>> [1, 2, [3,4], (5,6)]\n    [1, 2, [3, 4], (5, 6)]\n    >>> flatten([[[1,2,3], (42,None)], [4,5], [6], 7, (8,9,10)])\n    [1, 2, 3, 42, None, 4, 5, 6, 7, 8, 9, 10]\n    >>> flatten([\"foo\", \"bar\"])\n    ['foo', 'bar']\n    >>> flatten([\"foo\", [\"baz\", 42], \"bar\"])\n    ['foo', 'baz', 42, 'bar']\n    \"\"\"\n    return list(iflatten(x))\n\n\ndef iflatten(x):\n    \"\"\"iflatten(sequence) -> iterator\n\n    Similar to ``.flatten()``, but returns iterator instead\"\"\"\n    for el in x:\n        if is_listlike(el):\n            for el_ in iflatten(el):\n                yield el_\n        else:\n            yield el\n\n\ndef is_listlike(x):\n    \"\"\"\n    >>> is_listlike(\"foo\")\n    False\n    >>> is_listlike(5)\n    False\n    >>> is_listlike(b\"foo\")\n    False\n    >>> is_listlike([b\"foo\"])\n    True\n    >>> is_listlike((b\"foo\",))\n    True\n    >>> is_listlike({})\n    True\n    >>> is_listlike(set())\n    True\n    >>> is_listlike((x for x in range(3)))\n    True\n    >>> is_listlike(range(5))\n    True\n    \"\"\"\n    return hasattr(x, \"__iter__\") and not isinstance(x, (str, bytes))\n\n\ndef unique(list_, key=lambda x: x):\n    \"\"\"efficient function to uniquify a list preserving item order\"\"\"\n    seen = set()\n    result = []\n    for item in list_:\n        seenkey = key(item)\n        if seenkey in seen:\n            continue\n        seen.add(seenkey)\n        result.append(item)\n    return result\n\n\ndef to_unicode(text, encoding=None, errors='strict'):\n    \"\"\"Return the unicode representation of a bytes object ``text``. If\n    ``text`` is already an unicode object, return it as-is.\"\"\"\n    if isinstance(text, str):\n        return text\n    if not isinstance(text, (bytes, str)):\n        raise TypeError('to_unicode must receive a bytes or str '\n                        'object, got %s' % type(text).__name__)\n    if encoding is None:\n        encoding = 'utf-8'\n    return text.decode(encoding, errors)\n\n\ndef to_bytes(text, encoding=None, errors='strict'):\n    \"\"\"Return the binary representation of ``text``. If ``text``\n    is already a bytes object, return it as-is.\"\"\"\n    if isinstance(text, bytes):\n        return text\n    if not isinstance(text, str):\n        raise TypeError('to_bytes must receive a str or bytes '\n                        'object, got %s' % type(text).__name__)\n    if encoding is None:\n        encoding = 'utf-8'\n    return text.encode(encoding, errors)\n\n\n@deprecated('to_unicode')\ndef to_native_str(text, encoding=None, errors='strict'):\n    \"\"\" Return str representation of ``text``. \"\"\"\n    return to_unicode(text, encoding, errors)\n\n\ndef re_rsearch(pattern, text, chunk_size=1024):\n    \"\"\"\n    This function does a reverse search in a text using a regular expression\n    given in the attribute 'pattern'.\n    Since the re module does not provide this functionality, we have to find for\n    the expression into chunks of text extracted from the end (for the sake of efficiency).\n    At first, a chunk of 'chunk_size' kilobytes is extracted from the end, and searched for\n    the pattern. If the pattern is not found, another chunk is extracted, and another\n    search is performed.\n    This process continues until a match is found, or until the whole file is read.\n    In case the pattern wasn't found, None is returned, otherwise it returns a tuple containing\n    the start position of the match, and the ending (regarding the entire text).\n    \"\"\"\n    def _chunk_iter():\n        offset = len(text)\n        while True:\n            offset -= (chunk_size * 1024)\n            if offset <= 0:\n                break\n            yield (text[offset:], offset)\n        yield (text, 0)\n\n    if isinstance(pattern, str):\n        pattern = re.compile(pattern)\n\n    for chunk, offset in _chunk_iter():\n        matches = [match for match in pattern.finditer(chunk)]\n        if matches:\n            start, end = matches[-1].span()\n            return offset + start, offset + end\n    return None\n\n\ndef memoizemethod_noargs(method):\n    \"\"\"Decorator to cache the result of a method (without arguments) using a\n    weak reference to its object\n    \"\"\"\n    cache = weakref.WeakKeyDictionary()\n\n    @wraps(method)\n    def new_method(self, *args, **kwargs):\n        if self not in cache:\n            cache[self] = method(self, *args, **kwargs)\n        return cache[self]\n    return new_method\n\n\n_BINARYCHARS = {to_bytes(chr(i)) for i in range(32)} - {b\"\\0\", b\"\\t\", b\"\\n\", b\"\\r\"}\n_BINARYCHARS |= {ord(ch) for ch in _BINARYCHARS}\n\n\ndef binary_is_text(data):\n    \"\"\" Returns ``True`` if the given ``data`` argument (a ``bytes`` object)\n    does not contain unprintable control characters.\n    \"\"\"\n    if not isinstance(data, bytes):\n        raise TypeError(\"data must be bytes, got '%s'\" % type(data).__name__)\n    return all(c not in _BINARYCHARS for c in data)\n\n\ndef _getargspec_py23(func):\n    \"\"\"_getargspec_py23(function) -> named tuple ArgSpec(args, varargs, keywords,\n                                                        defaults)\n\n    Was identical to inspect.getargspec() in python2, but uses\n    inspect.getfullargspec() for python3 behind the scenes to avoid\n    DeprecationWarning.\n\n    >>> def f(a, b=2, *ar, **kw):\n    ...     pass\n\n    >>> _getargspec_py23(f)\n    ArgSpec(args=['a', 'b'], varargs='ar', keywords='kw', defaults=(2,))\n    \"\"\"\n    return inspect.ArgSpec(*inspect.getfullargspec(func)[:4])\n\n\ndef get_func_args(func, stripself=False):\n    \"\"\"Return the argument name list of a callable\"\"\"\n    if inspect.isfunction(func):\n        func_args, _, _, _ = _getargspec_py23(func)\n    elif inspect.isclass(func):\n        return get_func_args(func.__init__, True)\n    elif inspect.ismethod(func):\n        return get_func_args(func.__func__, True)\n    elif inspect.ismethoddescriptor(func):\n        return []\n    elif isinstance(func, partial):\n        return [x for x in get_func_args(func.func)[len(func.args):]\n                if not (func.keywords and x in func.keywords)]\n    elif hasattr(func, '__call__'):\n        if inspect.isroutine(func):\n            return []\n        elif getattr(func, '__name__', None) == '__call__':\n            return []\n        else:\n            return get_func_args(func.__call__, True)\n    else:\n        raise TypeError('%s is not callable' % type(func))\n    if stripself:\n        func_args.pop(0)\n    return func_args\n\n\ndef get_spec(func):\n    \"\"\"Returns (args, kwargs) tuple for a function\n    >>> import re\n    >>> get_spec(re.match)\n    (['pattern', 'string'], {'flags': 0})\n\n    >>> class Test:\n    ...     def __call__(self, val):\n    ...         pass\n    ...     def method(self, val, flags=0):\n    ...         pass\n\n    >>> get_spec(Test)\n    (['self', 'val'], {})\n\n    >>> get_spec(Test.method)\n    (['self', 'val'], {'flags': 0})\n\n    >>> get_spec(Test().method)\n    (['self', 'val'], {'flags': 0})\n    \"\"\"\n\n    if inspect.isfunction(func) or inspect.ismethod(func):\n        spec = _getargspec_py23(func)\n    elif hasattr(func, '__call__'):\n        spec = _getargspec_py23(func.__call__)\n    else:\n        raise TypeError('%s is not callable' % type(func))\n\n    defaults = spec.defaults or []\n\n    firstdefault = len(spec.args) - len(defaults)\n    args = spec.args[:firstdefault]\n    kwargs = dict(zip(spec.args[firstdefault:], defaults))\n    return args, kwargs\n\n\ndef equal_attributes(obj1, obj2, attributes):\n    \"\"\"Compare two objects attributes\"\"\"\n    # not attributes given return False by default\n    if not attributes:\n        return False\n\n    temp1, temp2 = object(), object()\n    for attr in attributes:\n        # support callables like itemgetter\n        if callable(attr):\n            if attr(obj1) != attr(obj2):\n                return False\n        elif getattr(obj1, attr, temp1) != getattr(obj2, attr, temp2):\n            return False\n    # all attributes equal\n    return True\n\n\nclass WeakKeyCache:\n\n    def __init__(self, default_factory):\n        self.default_factory = default_factory\n        self._weakdict = weakref.WeakKeyDictionary()\n\n    def __getitem__(self, key):\n        if key not in self._weakdict:\n            self._weakdict[key] = self.default_factory(key)\n        return self._weakdict[key]\n\n\n@deprecated\ndef retry_on_eintr(function, *args, **kw):\n    \"\"\"Run a function and retry it while getting EINTR errors\"\"\"\n    while True:\n        try:\n            return function(*args, **kw)\n        except IOError as e:\n            if e.errno != errno.EINTR:\n                raise\n\n\ndef without_none_values(iterable):\n    \"\"\"Return a copy of ``iterable`` with all ``None`` entries removed.\n\n    If ``iterable`` is a mapping, return a dictionary where all pairs that have\n    value ``None`` have been removed.\n    \"\"\"\n    try:\n        return {k: v for k, v in iterable.items() if v is not None}\n    except AttributeError:\n        return type(iterable)((v for v in iterable if v is not None))\n\n\ndef global_object_name(obj):\n    \"\"\"\n    Return full name of a global object.\n\n    >>> from scrapy import Request\n    >>> global_object_name(Request)\n    'scrapy.http.request.Request'\n    \"\"\"\n    return \"%s.%s\" % (obj.__module__, obj.__name__)\n\n\nif hasattr(sys, \"pypy_version_info\"):\n    def garbage_collect():\n        # Collecting weakreferences can take two collections on PyPy.\n        gc.collect()\n        gc.collect()\nelse:\n    def garbage_collect():\n        gc.collect()\n\n\nclass MutableChain:\n    \"\"\"\n    Thin wrapper around itertools.chain, allowing to add iterables \"in-place\"\n    \"\"\"\n\n    def __init__(self, *args):\n        self.data = chain.from_iterable(args)\n\n    def extend(self, *iterables):\n        self.data = chain(self.data, chain.from_iterable(iterables))\n\n    def __iter__(self):\n        return self\n\n    def __next__(self):\n        return next(self.data)\n\n    @deprecated(\"scrapy.utils.python.MutableChain.__next__\")\n    def next(self):\n        return self.__next__()\n", "path": "scrapy/utils/python.py"}], "after_files": [{"content": "\"\"\"\nThis module contains essential stuff that should've come with Python itself ;)\n\"\"\"\nimport errno\nimport gc\nimport inspect\nimport re\nimport sys\nimport warnings\nimport weakref\nfrom functools import partial, wraps\nfrom itertools import chain\n\nfrom scrapy.exceptions import ScrapyDeprecationWarning\nfrom scrapy.utils.decorators import deprecated\n\n\ndef flatten(x):\n    \"\"\"flatten(sequence) -> list\n\n    Returns a single, flat list which contains all elements retrieved\n    from the sequence and all recursively contained sub-sequences\n    (iterables).\n\n    Examples:\n    >>> [1, 2, [3,4], (5,6)]\n    [1, 2, [3, 4], (5, 6)]\n    >>> flatten([[[1,2,3], (42,None)], [4,5], [6], 7, (8,9,10)])\n    [1, 2, 3, 42, None, 4, 5, 6, 7, 8, 9, 10]\n    >>> flatten([\"foo\", \"bar\"])\n    ['foo', 'bar']\n    >>> flatten([\"foo\", [\"baz\", 42], \"bar\"])\n    ['foo', 'baz', 42, 'bar']\n    \"\"\"\n    return list(iflatten(x))\n\n\ndef iflatten(x):\n    \"\"\"iflatten(sequence) -> iterator\n\n    Similar to ``.flatten()``, but returns iterator instead\"\"\"\n    for el in x:\n        if is_listlike(el):\n            for el_ in iflatten(el):\n                yield el_\n        else:\n            yield el\n\n\ndef is_listlike(x):\n    \"\"\"\n    >>> is_listlike(\"foo\")\n    False\n    >>> is_listlike(5)\n    False\n    >>> is_listlike(b\"foo\")\n    False\n    >>> is_listlike([b\"foo\"])\n    True\n    >>> is_listlike((b\"foo\",))\n    True\n    >>> is_listlike({})\n    True\n    >>> is_listlike(set())\n    True\n    >>> is_listlike((x for x in range(3)))\n    True\n    >>> is_listlike(range(5))\n    True\n    \"\"\"\n    return hasattr(x, \"__iter__\") and not isinstance(x, (str, bytes))\n\n\ndef unique(list_, key=lambda x: x):\n    \"\"\"efficient function to uniquify a list preserving item order\"\"\"\n    seen = set()\n    result = []\n    for item in list_:\n        seenkey = key(item)\n        if seenkey in seen:\n            continue\n        seen.add(seenkey)\n        result.append(item)\n    return result\n\n\ndef to_unicode(text, encoding=None, errors='strict'):\n    \"\"\"Return the unicode representation of a bytes object ``text``. If\n    ``text`` is already an unicode object, return it as-is.\"\"\"\n    if isinstance(text, str):\n        return text\n    if not isinstance(text, (bytes, str)):\n        raise TypeError('to_unicode must receive a bytes or str '\n                        'object, got %s' % type(text).__name__)\n    if encoding is None:\n        encoding = 'utf-8'\n    return text.decode(encoding, errors)\n\n\ndef to_bytes(text, encoding=None, errors='strict'):\n    \"\"\"Return the binary representation of ``text``. If ``text``\n    is already a bytes object, return it as-is.\"\"\"\n    if isinstance(text, bytes):\n        return text\n    if not isinstance(text, str):\n        raise TypeError('to_bytes must receive a str or bytes '\n                        'object, got %s' % type(text).__name__)\n    if encoding is None:\n        encoding = 'utf-8'\n    return text.encode(encoding, errors)\n\n\n@deprecated('to_unicode')\ndef to_native_str(text, encoding=None, errors='strict'):\n    \"\"\" Return str representation of ``text``. \"\"\"\n    return to_unicode(text, encoding, errors)\n\n\ndef re_rsearch(pattern, text, chunk_size=1024):\n    \"\"\"\n    This function does a reverse search in a text using a regular expression\n    given in the attribute 'pattern'.\n    Since the re module does not provide this functionality, we have to find for\n    the expression into chunks of text extracted from the end (for the sake of efficiency).\n    At first, a chunk of 'chunk_size' kilobytes is extracted from the end, and searched for\n    the pattern. If the pattern is not found, another chunk is extracted, and another\n    search is performed.\n    This process continues until a match is found, or until the whole file is read.\n    In case the pattern wasn't found, None is returned, otherwise it returns a tuple containing\n    the start position of the match, and the ending (regarding the entire text).\n    \"\"\"\n\n    def _chunk_iter():\n        offset = len(text)\n        while True:\n            offset -= (chunk_size * 1024)\n            if offset <= 0:\n                break\n            yield (text[offset:], offset)\n        yield (text, 0)\n\n    if isinstance(pattern, str):\n        pattern = re.compile(pattern)\n\n    for chunk, offset in _chunk_iter():\n        matches = [match for match in pattern.finditer(chunk)]\n        if matches:\n            start, end = matches[-1].span()\n            return offset + start, offset + end\n    return None\n\n\ndef memoizemethod_noargs(method):\n    \"\"\"Decorator to cache the result of a method (without arguments) using a\n    weak reference to its object\n    \"\"\"\n    cache = weakref.WeakKeyDictionary()\n\n    @wraps(method)\n    def new_method(self, *args, **kwargs):\n        if self not in cache:\n            cache[self] = method(self, *args, **kwargs)\n        return cache[self]\n\n    return new_method\n\n\n_BINARYCHARS = {to_bytes(chr(i)) for i in range(32)} - {b\"\\0\", b\"\\t\", b\"\\n\", b\"\\r\"}\n_BINARYCHARS |= {ord(ch) for ch in _BINARYCHARS}\n\n\ndef binary_is_text(data):\n    \"\"\" Returns ``True`` if the given ``data`` argument (a ``bytes`` object)\n    does not contain unprintable control characters.\n    \"\"\"\n    if not isinstance(data, bytes):\n        raise TypeError(\"data must be bytes, got '%s'\" % type(data).__name__)\n    return all(c not in _BINARYCHARS for c in data)\n\n\ndef _getargspec_py23(func):\n    \"\"\"_getargspec_py23(function) -> named tuple ArgSpec(args, varargs, keywords,\n                                                        defaults)\n\n    Was identical to inspect.getargspec() in python2, but uses\n    inspect.getfullargspec() for python3 behind the scenes to avoid\n    DeprecationWarning.\n\n    >>> def f(a, b=2, *ar, **kw):\n    ...     pass\n\n    >>> _getargspec_py23(f)\n    ArgSpec(args=['a', 'b'], varargs='ar', keywords='kw', defaults=(2,))\n    \"\"\"\n    return inspect.ArgSpec(*inspect.getfullargspec(func)[:4])\n\n\ndef get_func_args(func, stripself=False):\n    \"\"\"Return the argument name list of a callable\"\"\"\n    if inspect.isfunction(func):\n        func_args, _, _, _ = _getargspec_py23(func)\n    elif inspect.isclass(func):\n        return get_func_args(func.__init__, True)\n    elif inspect.ismethod(func):\n        return get_func_args(func.__func__, True)\n    elif inspect.ismethoddescriptor(func):\n        return []\n    elif isinstance(func, partial):\n        return [x for x in get_func_args(func.func)[len(func.args):]\n                if not (func.keywords and x in func.keywords)]\n    elif hasattr(func, '__call__'):\n        if inspect.isroutine(func):\n            return []\n        elif getattr(func, '__name__', None) == '__call__':\n            return []\n        else:\n            return get_func_args(func.__call__, True)\n    else:\n        raise TypeError('%s is not callable' % type(func))\n    if stripself:\n        func_args.pop(0)\n    return func_args\n\n\ndef get_spec(func):\n    \"\"\"Returns (args, kwargs) tuple for a function\n    >>> import re\n    >>> get_spec(re.match)\n    (['pattern', 'string'], {'flags': 0})\n\n    >>> class Test:\n    ...     def __call__(self, val):\n    ...         pass\n    ...     def method(self, val, flags=0):\n    ...         pass\n\n    >>> get_spec(Test)\n    (['self', 'val'], {})\n\n    >>> get_spec(Test.method)\n    (['self', 'val'], {'flags': 0})\n\n    >>> get_spec(Test().method)\n    (['self', 'val'], {'flags': 0})\n    \"\"\"\n\n    if inspect.isfunction(func) or inspect.ismethod(func):\n        spec = _getargspec_py23(func)\n    elif hasattr(func, '__call__'):\n        spec = _getargspec_py23(func.__call__)\n    else:\n        raise TypeError('%s is not callable' % type(func))\n\n    defaults = spec.defaults or []\n\n    firstdefault = len(spec.args) - len(defaults)\n    args = spec.args[:firstdefault]\n    kwargs = dict(zip(spec.args[firstdefault:], defaults))\n    return args, kwargs\n\n\ndef equal_attributes(obj1, obj2, attributes):\n    \"\"\"Compare two objects attributes\"\"\"\n    # not attributes given return False by default\n    if not attributes:\n        return False\n\n    temp1, temp2 = object(), object()\n    for attr in attributes:\n        # support callables like itemgetter\n        if callable(attr):\n            if attr(obj1) != attr(obj2):\n                return False\n        elif getattr(obj1, attr, temp1) != getattr(obj2, attr, temp2):\n            return False\n    # all attributes equal\n    return True\n\n\nclass WeakKeyCache:\n\n    def __init__(self, default_factory):\n        warnings.warn(\"The WeakKeyCache class is deprecated\", category=ScrapyDeprecationWarning, stacklevel=2)\n        self.default_factory = default_factory\n        self._weakdict = weakref.WeakKeyDictionary()\n\n    def __getitem__(self, key):\n        if key not in self._weakdict:\n            self._weakdict[key] = self.default_factory(key)\n        return self._weakdict[key]\n\n\n@deprecated\ndef retry_on_eintr(function, *args, **kw):\n    \"\"\"Run a function and retry it while getting EINTR errors\"\"\"\n    while True:\n        try:\n            return function(*args, **kw)\n        except IOError as e:\n            if e.errno != errno.EINTR:\n                raise\n\n\ndef without_none_values(iterable):\n    \"\"\"Return a copy of ``iterable`` with all ``None`` entries removed.\n\n    If ``iterable`` is a mapping, return a dictionary where all pairs that have\n    value ``None`` have been removed.\n    \"\"\"\n    try:\n        return {k: v for k, v in iterable.items() if v is not None}\n    except AttributeError:\n        return type(iterable)((v for v in iterable if v is not None))\n\n\ndef global_object_name(obj):\n    \"\"\"\n    Return full name of a global object.\n\n    >>> from scrapy import Request\n    >>> global_object_name(Request)\n    'scrapy.http.request.Request'\n    \"\"\"\n    return \"%s.%s\" % (obj.__module__, obj.__name__)\n\n\nif hasattr(sys, \"pypy_version_info\"):\n    def garbage_collect():\n        # Collecting weakreferences can take two collections on PyPy.\n        gc.collect()\n        gc.collect()\nelse:\n    def garbage_collect():\n        gc.collect()\n\n\nclass MutableChain:\n    \"\"\"\n    Thin wrapper around itertools.chain, allowing to add iterables \"in-place\"\n    \"\"\"\n\n    def __init__(self, *args):\n        self.data = chain.from_iterable(args)\n\n    def extend(self, *iterables):\n        self.data = chain(self.data, chain.from_iterable(iterables))\n\n    def __iter__(self):\n        return self\n\n    def __next__(self):\n        return next(self.data)\n\n    @deprecated(\"scrapy.utils.python.MutableChain.__next__\")\n    def next(self):\n        return self.__next__()\n", "path": "scrapy/utils/python.py"}]}