We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
get_func_args maximum recursion
https://github.com/scrapy/scrapy/blob/master/scrapy/utils/python.py#L149

Today I was working on a project were I have to skip the first item of a list, and then join the rest. Instead of writing the typical slice I tried something much more good looking `Compose(itemgetter(slice(1, None)), Join())` but I found out this maximum recursion. I did some research and ask @dangra about it, but nothing came up.
I think the main problem is that `inspect` isn't able recognize `itemgetter` as `something`.

``` python
>>> inspect.getmembers(itemgetter(2))
[('__call__',
  <method-wrapper '__call__' of operator.itemgetter object at 0x7f79aeffb990>),
 ('__class__', <type 'operator.itemgetter'>),
 ('__delattr__',
  <method-wrapper '__delattr__' of operator.itemgetter object at 0x7f79aeffb990>),
 ('__doc__',
  'itemgetter(item, ...) --> itemgetter object\n\nReturn a callable object that fetches the given item(s) from its operand.\nAfter, f=itemgetter(2), the call f(r) returns r[2].\nAfter, g=itemgetter(2,5,3), the call g(r) returns (r[2], r[5], r[3])'),
 ('__format__',
  <built-in method __format__ of operator.itemgetter object at 0x7f79aeffb990>),
 ('__getattribute__',
  <method-wrapper '__getattribute__' of operator.itemgetter object at 0x7f79aeffb990>),
 ('__hash__',
  <method-wrapper '__hash__' of operator.itemgetter object at 0x7f79aeffb990>),
 ('__init__',
  <method-wrapper '__init__' of operator.itemgetter object at 0x7f79aeffb990>),
 ('__new__', <built-in method __new__ of type object at 0x8c1ec0>),
 ('__reduce__',
  <built-in method __reduce__ of operator.itemgetter object at 0x7f79aeffb990>),
 ('__reduce_ex__',
  <built-in method __reduce_ex__ of operator.itemgetter object at 0x7f79aeffb990>),
 ('__repr__',
  <method-wrapper '__repr__' of operator.itemgetter object at 0x7f79aeffb990>),
 ('__setattr__',
  <method-wrapper '__setattr__' of operator.itemgetter object at 0x7f79aeffb990>),
 ('__sizeof__',
  <built-in method __sizeof__ of operator.itemgetter object at 0x7f79aeffb990>),
 ('__str__',
  <method-wrapper '__str__' of operator.itemgetter object at 0x7f79aeffb990>),
 ('__subclasshook__',
  <built-in method __subclasshook__ of type object at 0x8c1ec0>)]
>>> inspect.getargspec(itemgetter(2).__call__)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/lib/python2.7/inspect.py", line 815, in getargspec
    raise TypeError('{!r} is not a Python function'.format(func))
TypeError: <method-wrapper '__call__' of operator.itemgetter object at 0xb3ddd0> is not a Python function
>>> inspect.getargspec(itemgetter(slice(None, 2)).__init__)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/lib/python2.7/inspect.py", line 815, in getargspec
    raise TypeError('{!r} is not a Python function'.format(func))
TypeError: <method-wrapper '__init__' of operator.itemgetter object at 0xb3de10> is not a Python function
```

EDIT: Looks like the reason was C functions weren't covered by inspect module until Python 3.4 (http://bugs.python.org/issue17481)


--- 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 It also contains functions (or functionality) which is in Python versions
5 higher than 2.5 which used to be the lowest version supported by Scrapy.
6 
7 """
8 import os
9 import re
10 import inspect
11 import weakref
12 import errno
13 import six
14 from functools import partial, wraps
15 
16 
17 def flatten(x):
18     """flatten(sequence) -> list
19 
20     Returns a single, flat list which contains all elements retrieved
21     from the sequence and all recursively contained sub-sequences
22     (iterables).
23 
24     Examples:
25     >>> [1, 2, [3,4], (5,6)]
26     [1, 2, [3, 4], (5, 6)]
27     >>> flatten([[[1,2,3], (42,None)], [4,5], [6], 7, (8,9,10)])
28     [1, 2, 3, 42, None, 4, 5, 6, 7, 8, 9, 10]"""
29 
30     result = []
31     for el in x:
32         if hasattr(el, "__iter__"):
33             result.extend(flatten(el))
34         else:
35             result.append(el)
36     return result
37 
38 
39 def unique(list_, key=lambda x: x):
40     """efficient function to uniquify a list preserving item order"""
41     seen = set()
42     result = []
43     for item in list_:
44         seenkey = key(item)
45         if seenkey in seen:
46             continue
47         seen.add(seenkey)
48         result.append(item)
49     return result
50 
51 
52 def str_to_unicode(text, encoding=None, errors='strict'):
53     """Return the unicode representation of text in the given encoding. Unlike
54     .encode(encoding) this function can be applied directly to a unicode
55     object without the risk of double-decoding problems (which can happen if
56     you don't use the default 'ascii' encoding)
57     """
58 
59     if encoding is None:
60         encoding = 'utf-8'
61     if isinstance(text, str):
62         return text.decode(encoding, errors)
63     elif isinstance(text, unicode):
64         return text
65     else:
66         raise TypeError('str_to_unicode must receive a str or unicode object, got %s' % type(text).__name__)
67 
68 def unicode_to_str(text, encoding=None, errors='strict'):
69     """Return the str representation of text in the given encoding. Unlike
70     .encode(encoding) this function can be applied directly to a str
71     object without the risk of double-decoding problems (which can happen if
72     you don't use the default 'ascii' encoding)
73     """
74 
75     if encoding is None:
76         encoding = 'utf-8'
77     if isinstance(text, unicode):
78         return text.encode(encoding, errors)
79     elif isinstance(text, str):
80         return text
81     else:
82         raise TypeError('unicode_to_str must receive a unicode or str object, got %s' % type(text).__name__)
83 
84 def re_rsearch(pattern, text, chunk_size=1024):
85     """
86     This function does a reverse search in a text using a regular expression
87     given in the attribute 'pattern'.
88     Since the re module does not provide this functionality, we have to find for
89     the expression into chunks of text extracted from the end (for the sake of efficiency).
90     At first, a chunk of 'chunk_size' kilobytes is extracted from the end, and searched for
91     the pattern. If the pattern is not found, another chunk is extracted, and another
92     search is performed.
93     This process continues until a match is found, or until the whole file is read.
94     In case the pattern wasn't found, None is returned, otherwise it returns a tuple containing
95     the start position of the match, and the ending (regarding the entire text).
96     """
97     def _chunk_iter():
98         offset = len(text)
99         while True:
100             offset -= (chunk_size * 1024)
101             if offset <= 0:
102                 break
103             yield (text[offset:], offset)
104         yield (text, 0)
105 
106     pattern = re.compile(pattern) if isinstance(pattern, basestring) else pattern
107     for chunk, offset in _chunk_iter():
108         matches = [match for match in pattern.finditer(chunk)]
109         if matches:
110             return (offset + matches[-1].span()[0], offset + matches[-1].span()[1])
111     return None
112 
113 def memoizemethod_noargs(method):
114     """Decorator to cache the result of a method (without arguments) using a
115     weak reference to its object
116     """
117     cache = weakref.WeakKeyDictionary()
118     @wraps(method)
119     def new_method(self, *args, **kwargs):
120         if self not in cache:
121             cache[self] = method(self, *args, **kwargs)
122         return cache[self]
123     return new_method
124 
125 _BINARYCHARS = set(map(chr, range(32))) - set(["\0", "\t", "\n", "\r"])
126 
127 def isbinarytext(text):
128     """Return True if the given text is considered binary, or false
129     otherwise, by looking for binary bytes at their chars
130     """
131     assert isinstance(text, str), "text must be str, got '%s'" % type(text).__name__
132     return any(c in _BINARYCHARS for c in text)
133 
134 def get_func_args(func, stripself=False):
135     """Return the argument name list of a callable"""
136     if inspect.isfunction(func):
137         func_args, _, _, _ = inspect.getargspec(func)
138     elif inspect.isclass(func):
139         return get_func_args(func.__init__, True)
140     elif inspect.ismethod(func):
141         return get_func_args(func.__func__, True)
142     elif inspect.ismethoddescriptor(func):
143         return []
144     elif isinstance(func, partial):
145         return [x for x in get_func_args(func.func)[len(func.args):]
146                 if not (func.keywords and x in func.keywords)]
147     elif hasattr(func, '__call__'):
148         if inspect.isroutine(func):
149             return []
150         else:
151             return get_func_args(func.__call__, True)
152     else:
153         raise TypeError('%s is not callable' % type(func))
154     if stripself:
155         func_args.pop(0)
156     return func_args
157 
158 def get_spec(func):
159     """Returns (args, kwargs) tuple for a function
160     >>> import re
161     >>> get_spec(re.match)
162     (['pattern', 'string'], {'flags': 0})
163 
164     >>> class Test(object):
165     ...     def __call__(self, val):
166     ...         pass
167     ...     def method(self, val, flags=0):
168     ...         pass
169 
170     >>> get_spec(Test)
171     (['self', 'val'], {})
172 
173     >>> get_spec(Test.method)
174     (['self', 'val'], {'flags': 0})
175 
176     >>> get_spec(Test().method)
177     (['self', 'val'], {'flags': 0})
178     """
179 
180     if inspect.isfunction(func) or inspect.ismethod(func):
181         spec = inspect.getargspec(func)
182     elif hasattr(func, '__call__'):
183         spec = inspect.getargspec(func.__call__)
184     else:
185         raise TypeError('%s is not callable' % type(func))
186 
187     defaults = spec.defaults or []
188 
189     firstdefault = len(spec.args) - len(defaults)
190     args = spec.args[:firstdefault]
191     kwargs = dict(zip(spec.args[firstdefault:], defaults))
192     return args, kwargs
193 
194 def equal_attributes(obj1, obj2, attributes):
195     """Compare two objects attributes"""
196     # not attributes given return False by default
197     if not attributes:
198         return False
199 
200     for attr in attributes:
201         # support callables like itemgetter
202         if callable(attr):
203             if not attr(obj1) == attr(obj2):
204                 return False
205         else:
206             # check that objects has attribute
207             if not hasattr(obj1, attr):
208                 return False
209             if not hasattr(obj2, attr):
210                 return False
211             # compare object attributes
212             if not getattr(obj1, attr) == getattr(obj2, attr):
213                 return False
214     # all attributes equal
215     return True
216 
217 
218 class WeakKeyCache(object):
219 
220     def __init__(self, default_factory):
221         self.default_factory = default_factory
222         self._weakdict = weakref.WeakKeyDictionary()
223 
224     def __getitem__(self, key):
225         if key not in self._weakdict:
226             self._weakdict[key] = self.default_factory(key)
227         return self._weakdict[key]
228 
229 
230 def stringify_dict(dct_or_tuples, encoding='utf-8', keys_only=True):
231     """Return a (new) dict with the unicode keys (and values if, keys_only is
232     False) of the given dict converted to strings. `dct_or_tuples` can be a
233     dict or a list of tuples, like any dict constructor supports.
234     """
235     d = {}
236     for k, v in six.iteritems(dict(dct_or_tuples)):
237         k = k.encode(encoding) if isinstance(k, unicode) else k
238         if not keys_only:
239             v = v.encode(encoding) if isinstance(v, unicode) else v
240         d[k] = v
241     return d
242 
243 def is_writable(path):
244     """Return True if the given path can be written (if it exists) or created
245     (if it doesn't exist)
246     """
247     if os.path.exists(path):
248         return os.access(path, os.W_OK)
249     else:
250         return os.access(os.path.dirname(path), os.W_OK)
251 
252 def setattr_default(obj, name, value):
253     """Set attribute value, but only if it's not already set. Similar to
254     setdefault() for dicts.
255     """
256     if not hasattr(obj, name):
257         setattr(obj, name, value)
258 
259 
260 def retry_on_eintr(function, *args, **kw):
261     """Run a function and retry it while getting EINTR errors"""
262     while True:
263         try:
264             return function(*args, **kw)
265         except IOError as e:
266             if e.errno != errno.EINTR:
267                 raise
268
```
--- 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
@@ -147,6 +147,8 @@
     elif hasattr(func, '__call__'):
         if inspect.isroutine(func):
             return []
+        elif getattr(func, '__name__', None) == '__call__':
+            return []
         else:
             return get_func_args(func.__call__, True)
     else:

{"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@@ -147,6 +147,8 @@\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", "issue": "get_func_args maximum recursion\nhttps://github.com/scrapy/scrapy/blob/master/scrapy/utils/python.py#L149\n\nToday I was working on a project were I have to skip the first item of a list, and then join the rest. Instead of writing the typical slice I tried something much more good looking `Compose(itemgetter(slice(1, None)), Join())` but I found out this maximum recursion. I did some research and ask @dangra about it, but nothing came up.\nI think the main problem is that `inspect` isn't able recognize `itemgetter` as `something`.\n\n``` python\n>>> inspect.getmembers(itemgetter(2))\n[('__call__',\n  <method-wrapper '__call__' of operator.itemgetter object at 0x7f79aeffb990>),\n ('__class__', <type 'operator.itemgetter'>),\n ('__delattr__',\n  <method-wrapper '__delattr__' of operator.itemgetter object at 0x7f79aeffb990>),\n ('__doc__',\n  'itemgetter(item, ...) --> itemgetter object\\n\\nReturn a callable object that fetches the given item(s) from its operand.\\nAfter, f=itemgetter(2), the call f(r) returns r[2].\\nAfter, g=itemgetter(2,5,3), the call g(r) returns (r[2], r[5], r[3])'),\n ('__format__',\n  <built-in method __format__ of operator.itemgetter object at 0x7f79aeffb990>),\n ('__getattribute__',\n  <method-wrapper '__getattribute__' of operator.itemgetter object at 0x7f79aeffb990>),\n ('__hash__',\n  <method-wrapper '__hash__' of operator.itemgetter object at 0x7f79aeffb990>),\n ('__init__',\n  <method-wrapper '__init__' of operator.itemgetter object at 0x7f79aeffb990>),\n ('__new__', <built-in method __new__ of type object at 0x8c1ec0>),\n ('__reduce__',\n  <built-in method __reduce__ of operator.itemgetter object at 0x7f79aeffb990>),\n ('__reduce_ex__',\n  <built-in method __reduce_ex__ of operator.itemgetter object at 0x7f79aeffb990>),\n ('__repr__',\n  <method-wrapper '__repr__' of operator.itemgetter object at 0x7f79aeffb990>),\n ('__setattr__',\n  <method-wrapper '__setattr__' of operator.itemgetter object at 0x7f79aeffb990>),\n ('__sizeof__',\n  <built-in method __sizeof__ of operator.itemgetter object at 0x7f79aeffb990>),\n ('__str__',\n  <method-wrapper '__str__' of operator.itemgetter object at 0x7f79aeffb990>),\n ('__subclasshook__',\n  <built-in method __subclasshook__ of type object at 0x8c1ec0>)]\n>>> inspect.getargspec(itemgetter(2).__call__)\nTraceback (most recent call last):\n  File \"<console>\", line 1, in <module>\n  File \"/usr/lib/python2.7/inspect.py\", line 815, in getargspec\n    raise TypeError('{!r} is not a Python function'.format(func))\nTypeError: <method-wrapper '__call__' of operator.itemgetter object at 0xb3ddd0> is not a Python function\n>>> inspect.getargspec(itemgetter(slice(None, 2)).__init__)\nTraceback (most recent call last):\n  File \"<console>\", line 1, in <module>\n  File \"/usr/lib/python2.7/inspect.py\", line 815, in getargspec\n    raise TypeError('{!r} is not a Python function'.format(func))\nTypeError: <method-wrapper '__init__' of operator.itemgetter object at 0xb3de10> is not a Python function\n```\n\nEDIT: Looks like the reason was C functions weren't covered by inspect module until Python 3.4 (http://bugs.python.org/issue17481)\n\n", "before_files": [{"content": "\"\"\"\nThis module contains essential stuff that should've come with Python itself ;)\n\nIt also contains functions (or functionality) which is in Python versions\nhigher than 2.5 which used to be the lowest version supported by Scrapy.\n\n\"\"\"\nimport os\nimport re\nimport inspect\nimport weakref\nimport errno\nimport six\nfrom functools import partial, wraps\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\n    result = []\n    for el in x:\n        if hasattr(el, \"__iter__\"):\n            result.extend(flatten(el))\n        else:\n            result.append(el)\n    return result\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 str_to_unicode(text, encoding=None, errors='strict'):\n    \"\"\"Return the unicode representation of text in the given encoding. Unlike\n    .encode(encoding) this function can be applied directly to a unicode\n    object without the risk of double-decoding problems (which can happen if\n    you don't use the default 'ascii' encoding)\n    \"\"\"\n\n    if encoding is None:\n        encoding = 'utf-8'\n    if isinstance(text, str):\n        return text.decode(encoding, errors)\n    elif isinstance(text, unicode):\n        return text\n    else:\n        raise TypeError('str_to_unicode must receive a str or unicode object, got %s' % type(text).__name__)\n\ndef unicode_to_str(text, encoding=None, errors='strict'):\n    \"\"\"Return the str representation of text in the given encoding. Unlike\n    .encode(encoding) this function can be applied directly to a str\n    object without the risk of double-decoding problems (which can happen if\n    you don't use the default 'ascii' encoding)\n    \"\"\"\n\n    if encoding is None:\n        encoding = 'utf-8'\n    if isinstance(text, unicode):\n        return text.encode(encoding, errors)\n    elif isinstance(text, str):\n        return text\n    else:\n        raise TypeError('unicode_to_str must receive a unicode or str object, got %s' % type(text).__name__)\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    pattern = re.compile(pattern) if isinstance(pattern, basestring) else pattern\n    for chunk, offset in _chunk_iter():\n        matches = [match for match in pattern.finditer(chunk)]\n        if matches:\n            return (offset + matches[-1].span()[0], offset + matches[-1].span()[1])\n    return None\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    @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_BINARYCHARS = set(map(chr, range(32))) - set([\"\\0\", \"\\t\", \"\\n\", \"\\r\"])\n\ndef isbinarytext(text):\n    \"\"\"Return True if the given text is considered binary, or false\n    otherwise, by looking for binary bytes at their chars\n    \"\"\"\n    assert isinstance(text, str), \"text must be str, got '%s'\" % type(text).__name__\n    return any(c in _BINARYCHARS for c in text)\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, _, _, _ = inspect.getargspec(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        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\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(object):\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 = inspect.getargspec(func)\n    elif hasattr(func, '__call__'):\n        spec = inspect.getargspec(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\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    for attr in attributes:\n        # support callables like itemgetter\n        if callable(attr):\n            if not attr(obj1) == attr(obj2):\n                return False\n        else:\n            # check that objects has attribute\n            if not hasattr(obj1, attr):\n                return False\n            if not hasattr(obj2, attr):\n                return False\n            # compare object attributes\n            if not getattr(obj1, attr) == getattr(obj2, attr):\n                return False\n    # all attributes equal\n    return True\n\n\nclass WeakKeyCache(object):\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\ndef stringify_dict(dct_or_tuples, encoding='utf-8', keys_only=True):\n    \"\"\"Return a (new) dict with the unicode keys (and values if, keys_only is\n    False) of the given dict converted to strings. `dct_or_tuples` can be a\n    dict or a list of tuples, like any dict constructor supports.\n    \"\"\"\n    d = {}\n    for k, v in six.iteritems(dict(dct_or_tuples)):\n        k = k.encode(encoding) if isinstance(k, unicode) else k\n        if not keys_only:\n            v = v.encode(encoding) if isinstance(v, unicode) else v\n        d[k] = v\n    return d\n\ndef is_writable(path):\n    \"\"\"Return True if the given path can be written (if it exists) or created\n    (if it doesn't exist)\n    \"\"\"\n    if os.path.exists(path):\n        return os.access(path, os.W_OK)\n    else:\n        return os.access(os.path.dirname(path), os.W_OK)\n\ndef setattr_default(obj, name, value):\n    \"\"\"Set attribute value, but only if it's not already set. Similar to\n    setdefault() for dicts.\n    \"\"\"\n    if not hasattr(obj, name):\n        setattr(obj, name, value)\n\n\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", "path": "scrapy/utils/python.py"}], "after_files": [{"content": "\"\"\"\nThis module contains essential stuff that should've come with Python itself ;)\n\nIt also contains functions (or functionality) which is in Python versions\nhigher than 2.5 which used to be the lowest version supported by Scrapy.\n\n\"\"\"\nimport os\nimport re\nimport inspect\nimport weakref\nimport errno\nimport six\nfrom functools import partial, wraps\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\n    result = []\n    for el in x:\n        if hasattr(el, \"__iter__\"):\n            result.extend(flatten(el))\n        else:\n            result.append(el)\n    return result\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 str_to_unicode(text, encoding=None, errors='strict'):\n    \"\"\"Return the unicode representation of text in the given encoding. Unlike\n    .encode(encoding) this function can be applied directly to a unicode\n    object without the risk of double-decoding problems (which can happen if\n    you don't use the default 'ascii' encoding)\n    \"\"\"\n\n    if encoding is None:\n        encoding = 'utf-8'\n    if isinstance(text, str):\n        return text.decode(encoding, errors)\n    elif isinstance(text, unicode):\n        return text\n    else:\n        raise TypeError('str_to_unicode must receive a str or unicode object, got %s' % type(text).__name__)\n\ndef unicode_to_str(text, encoding=None, errors='strict'):\n    \"\"\"Return the str representation of text in the given encoding. Unlike\n    .encode(encoding) this function can be applied directly to a str\n    object without the risk of double-decoding problems (which can happen if\n    you don't use the default 'ascii' encoding)\n    \"\"\"\n\n    if encoding is None:\n        encoding = 'utf-8'\n    if isinstance(text, unicode):\n        return text.encode(encoding, errors)\n    elif isinstance(text, str):\n        return text\n    else:\n        raise TypeError('unicode_to_str must receive a unicode or str object, got %s' % type(text).__name__)\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    pattern = re.compile(pattern) if isinstance(pattern, basestring) else pattern\n    for chunk, offset in _chunk_iter():\n        matches = [match for match in pattern.finditer(chunk)]\n        if matches:\n            return (offset + matches[-1].span()[0], offset + matches[-1].span()[1])\n    return None\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    @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_BINARYCHARS = set(map(chr, range(32))) - set([\"\\0\", \"\\t\", \"\\n\", \"\\r\"])\n\ndef isbinarytext(text):\n    \"\"\"Return True if the given text is considered binary, or false\n    otherwise, by looking for binary bytes at their chars\n    \"\"\"\n    assert isinstance(text, str), \"text must be str, got '%s'\" % type(text).__name__\n    return any(c in _BINARYCHARS for c in text)\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, _, _, _ = inspect.getargspec(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\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(object):\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 = inspect.getargspec(func)\n    elif hasattr(func, '__call__'):\n        spec = inspect.getargspec(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\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    for attr in attributes:\n        # support callables like itemgetter\n        if callable(attr):\n            if not attr(obj1) == attr(obj2):\n                return False\n        else:\n            # check that objects has attribute\n            if not hasattr(obj1, attr):\n                return False\n            if not hasattr(obj2, attr):\n                return False\n            # compare object attributes\n            if not getattr(obj1, attr) == getattr(obj2, attr):\n                return False\n    # all attributes equal\n    return True\n\n\nclass WeakKeyCache(object):\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\ndef stringify_dict(dct_or_tuples, encoding='utf-8', keys_only=True):\n    \"\"\"Return a (new) dict with the unicode keys (and values if, keys_only is\n    False) of the given dict converted to strings. `dct_or_tuples` can be a\n    dict or a list of tuples, like any dict constructor supports.\n    \"\"\"\n    d = {}\n    for k, v in six.iteritems(dict(dct_or_tuples)):\n        k = k.encode(encoding) if isinstance(k, unicode) else k\n        if not keys_only:\n            v = v.encode(encoding) if isinstance(v, unicode) else v\n        d[k] = v\n    return d\n\ndef is_writable(path):\n    \"\"\"Return True if the given path can be written (if it exists) or created\n    (if it doesn't exist)\n    \"\"\"\n    if os.path.exists(path):\n        return os.access(path, os.W_OK)\n    else:\n        return os.access(os.path.dirname(path), os.W_OK)\n\ndef setattr_default(obj, name, value):\n    \"\"\"Set attribute value, but only if it's not already set. Similar to\n    setdefault() for dicts.\n    \"\"\"\n    if not hasattr(obj, name):\n        setattr(obj, name, value)\n\n\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", "path": "scrapy/utils/python.py"}]}