prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  String value for order of Scrapy component
  If Scrapy component order is defined as a string, it leads to undefined behaviour on Python 2 and to the following errors on Python 3:
  ```
  File "/usr/local/lib/python3.5/site-packages/scrapy/middleware.py", line 58, in from_crawler
   return cls.from_settings(crawler.settings, crawler)
  File "/usr/local/lib/python3.5/site-packages/scrapy/middleware.py", line 29, in from_settings
   mwlist = cls._get_mwlist_from_settings(settings)
  File "/usr/local/lib/python3.5/site-packages/scrapy/core/spidermw.py", line 21, in _get_mwlist_from_settings
   return build_component_list(settings.getwithbase('SPIDER_MIDDLEWARES'))
  File "/usr/local/lib/python3.5/site-packages/scrapy/utils/conf.py", line 47, in build_component_list
   return [k for k, v in sorted(six.iteritems(compdict), key=itemgetter(1))]
  builtins.TypeError: unorderable types: str() < int()
  ```
  
  My guess that 1) order of a Scrapy component should be stated as of integer type (or `None`) and there should be a check somewhere, 2) or the sorting logic should be fixed.
  
  --- 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/conf.py`
  Content:
  ```
  1 import os
  2 import sys
  3 from operator import itemgetter
  4 
  5 import six
  6 from six.moves.configparser import SafeConfigParser
  7 
  8 from scrapy.settings import BaseSettings
  9 from scrapy.utils.deprecate import update_classpath
  10 from scrapy.utils.python import without_none_values
  11 
  12 
  13 def build_component_list(compdict, custom=None, convert=update_classpath):
  14     """Compose a component list from a { class: order } dictionary."""
  15 
  16     def _check_components(complist):
  17         if len({convert(c) for c in complist}) != len(complist):
  18             raise ValueError('Some paths in {!r} convert to the same object, '
  19                              'please update your settings'.format(complist))
  20 
  21     def _map_keys(compdict):
  22         if isinstance(compdict, BaseSettings):
  23             compbs = BaseSettings()
  24             for k, v in six.iteritems(compdict):
  25                 prio = compdict.getpriority(k)
  26                 if compbs.getpriority(convert(k)) == prio:
  27                     raise ValueError('Some paths in {!r} convert to the same '
  28                                      'object, please update your settings'
  29                                      ''.format(list(compdict.keys())))
  30                 else:
  31                     compbs.set(convert(k), v, priority=prio)
  32             return compbs
  33         else:
  34             _check_components(compdict)
  35             return {convert(k): v for k, v in six.iteritems(compdict)}
  36 
  37     # BEGIN Backwards compatibility for old (base, custom) call signature
  38     if isinstance(custom, (list, tuple)):
  39         _check_components(custom)
  40         return type(custom)(convert(c) for c in custom)
  41 
  42     if custom is not None:
  43         compdict.update(custom)
  44     # END Backwards compatibility
  45 
  46     compdict = without_none_values(_map_keys(compdict))
  47     return [k for k, v in sorted(six.iteritems(compdict), key=itemgetter(1))]
  48 
  49 
  50 def arglist_to_dict(arglist):
  51     """Convert a list of arguments like ['arg1=val1', 'arg2=val2', ...] to a
  52     dict
  53     """
  54     return dict(x.split('=', 1) for x in arglist)
  55 
  56 
  57 def closest_scrapy_cfg(path='.', prevpath=None):
  58     """Return the path to the closest scrapy.cfg file by traversing the current
  59     directory and its parents
  60     """
  61     if path == prevpath:
  62         return ''
  63     path = os.path.abspath(path)
  64     cfgfile = os.path.join(path, 'scrapy.cfg')
  65     if os.path.exists(cfgfile):
  66         return cfgfile
  67     return closest_scrapy_cfg(os.path.dirname(path), path)
  68 
  69 
  70 def init_env(project='default', set_syspath=True):
  71     """Initialize environment to use command-line tool from inside a project
  72     dir. This sets the Scrapy settings module and modifies the Python path to
  73     be able to locate the project module.
  74     """
  75     cfg = get_config()
  76     if cfg.has_option('settings', project):
  77         os.environ['SCRAPY_SETTINGS_MODULE'] = cfg.get('settings', project)
  78     closest = closest_scrapy_cfg()
  79     if closest:
  80         projdir = os.path.dirname(closest)
  81         if set_syspath and projdir not in sys.path:
  82             sys.path.append(projdir)
  83 
  84 
  85 def get_config(use_closest=True):
  86     """Get Scrapy config file as a SafeConfigParser"""
  87     sources = get_sources(use_closest)
  88     cfg = SafeConfigParser()
  89     cfg.read(sources)
  90     return cfg
  91 
  92 
  93 def get_sources(use_closest=True):
  94     xdg_config_home = os.environ.get('XDG_CONFIG_HOME') or \
  95         os.path.expanduser('~/.config')
  96     sources = ['/etc/scrapy.cfg', r'c:\scrapy\scrapy.cfg',
  97                xdg_config_home + '/scrapy.cfg',
  98                os.path.expanduser('~/.scrapy.cfg')]
  99     if use_closest:
  100         sources.append(closest_scrapy_cfg())
  101     return sources
  102
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/scrapy/utils/conf.py b/scrapy/utils/conf.py
  --- a/scrapy/utils/conf.py
  +++ b/scrapy/utils/conf.py
  @@ -1,5 +1,6 @@
   import os
   import sys
  +import numbers
   from operator import itemgetter
   
   import six
  @@ -34,6 +35,13 @@
               _check_components(compdict)
               return {convert(k): v for k, v in six.iteritems(compdict)}
   
  +    def _validate_values(compdict):
  +        """Fail if a value in the components dict is not a real number or None."""
  +        for name, value in six.iteritems(compdict):
  +            if value is not None and not isinstance(value, numbers.Real):
  +                raise ValueError('Invalid value {} for component {}, please provide ' \
  +                                 'a real number or None instead'.format(value, name))
  +
       # BEGIN Backwards compatibility for old (base, custom) call signature
       if isinstance(custom, (list, tuple)):
           _check_components(custom)
  @@ -43,6 +51,7 @@
           compdict.update(custom)
       # END Backwards compatibility
   
  +    _validate_values(compdict)
       compdict = without_none_values(_map_keys(compdict))
       return [k for k, v in sorted(six.iteritems(compdict), key=itemgetter(1))]
  
verification_info: {"golden_diff": "diff --git a/scrapy/utils/conf.py b/scrapy/utils/conf.py\n--- a/scrapy/utils/conf.py\n+++ b/scrapy/utils/conf.py\n@@ -1,5 +1,6 @@\n import os\n import sys\n+import numbers\n from operator import itemgetter\n \n import six\n@@ -34,6 +35,13 @@\n             _check_components(compdict)\n             return {convert(k): v for k, v in six.iteritems(compdict)}\n \n+    def _validate_values(compdict):\n+        \"\"\"Fail if a value in the components dict is not a real number or None.\"\"\"\n+        for name, value in six.iteritems(compdict):\n+            if value is not None and not isinstance(value, numbers.Real):\n+                raise ValueError('Invalid value {} for component {}, please provide ' \\\n+                                 'a real number or None instead'.format(value, name))\n+\n     # BEGIN Backwards compatibility for old (base, custom) call signature\n     if isinstance(custom, (list, tuple)):\n         _check_components(custom)\n@@ -43,6 +51,7 @@\n         compdict.update(custom)\n     # END Backwards compatibility\n \n+    _validate_values(compdict)\n     compdict = without_none_values(_map_keys(compdict))\n     return [k for k, v in sorted(six.iteritems(compdict), key=itemgetter(1))]\n", "issue": "String value for order of Scrapy component\nIf Scrapy component order is defined as a string, it leads to undefined behaviour on Python 2 and to the following errors on Python 3:\r\n```\r\nFile \"/usr/local/lib/python3.5/site-packages/scrapy/middleware.py\", line 58, in from_crawler\r\n return cls.from_settings(crawler.settings, crawler)\r\nFile \"/usr/local/lib/python3.5/site-packages/scrapy/middleware.py\", line 29, in from_settings\r\n mwlist = cls._get_mwlist_from_settings(settings)\r\nFile \"/usr/local/lib/python3.5/site-packages/scrapy/core/spidermw.py\", line 21, in _get_mwlist_from_settings\r\n return build_component_list(settings.getwithbase('SPIDER_MIDDLEWARES'))\r\nFile \"/usr/local/lib/python3.5/site-packages/scrapy/utils/conf.py\", line 47, in build_component_list\r\n return [k for k, v in sorted(six.iteritems(compdict), key=itemgetter(1))]\r\nbuiltins.TypeError: unorderable types: str() < int()\r\n```\r\n\r\nMy guess that 1) order of a Scrapy component should be stated as of integer type (or `None`) and there should be a check somewhere, 2) or the sorting logic should be fixed.\n", "before_files": [{"content": "import os\nimport sys\nfrom operator import itemgetter\n\nimport six\nfrom six.moves.configparser import SafeConfigParser\n\nfrom scrapy.settings import BaseSettings\nfrom scrapy.utils.deprecate import update_classpath\nfrom scrapy.utils.python import without_none_values\n\n\ndef build_component_list(compdict, custom=None, convert=update_classpath):\n    \"\"\"Compose a component list from a { class: order } dictionary.\"\"\"\n\n    def _check_components(complist):\n        if len({convert(c) for c in complist}) != len(complist):\n            raise ValueError('Some paths in {!r} convert to the same object, '\n                             'please update your settings'.format(complist))\n\n    def _map_keys(compdict):\n        if isinstance(compdict, BaseSettings):\n            compbs = BaseSettings()\n            for k, v in six.iteritems(compdict):\n                prio = compdict.getpriority(k)\n                if compbs.getpriority(convert(k)) == prio:\n                    raise ValueError('Some paths in {!r} convert to the same '\n                                     'object, please update your settings'\n                                     ''.format(list(compdict.keys())))\n                else:\n                    compbs.set(convert(k), v, priority=prio)\n            return compbs\n        else:\n            _check_components(compdict)\n            return {convert(k): v for k, v in six.iteritems(compdict)}\n\n    # BEGIN Backwards compatibility for old (base, custom) call signature\n    if isinstance(custom, (list, tuple)):\n        _check_components(custom)\n        return type(custom)(convert(c) for c in custom)\n\n    if custom is not None:\n        compdict.update(custom)\n    # END Backwards compatibility\n\n    compdict = without_none_values(_map_keys(compdict))\n    return [k for k, v in sorted(six.iteritems(compdict), key=itemgetter(1))]\n\n\ndef arglist_to_dict(arglist):\n    \"\"\"Convert a list of arguments like ['arg1=val1', 'arg2=val2', ...] to a\n    dict\n    \"\"\"\n    return dict(x.split('=', 1) for x in arglist)\n\n\ndef closest_scrapy_cfg(path='.', prevpath=None):\n    \"\"\"Return the path to the closest scrapy.cfg file by traversing the current\n    directory and its parents\n    \"\"\"\n    if path == prevpath:\n        return ''\n    path = os.path.abspath(path)\n    cfgfile = os.path.join(path, 'scrapy.cfg')\n    if os.path.exists(cfgfile):\n        return cfgfile\n    return closest_scrapy_cfg(os.path.dirname(path), path)\n\n\ndef init_env(project='default', set_syspath=True):\n    \"\"\"Initialize environment to use command-line tool from inside a project\n    dir. This sets the Scrapy settings module and modifies the Python path to\n    be able to locate the project module.\n    \"\"\"\n    cfg = get_config()\n    if cfg.has_option('settings', project):\n        os.environ['SCRAPY_SETTINGS_MODULE'] = cfg.get('settings', project)\n    closest = closest_scrapy_cfg()\n    if closest:\n        projdir = os.path.dirname(closest)\n        if set_syspath and projdir not in sys.path:\n            sys.path.append(projdir)\n\n\ndef get_config(use_closest=True):\n    \"\"\"Get Scrapy config file as a SafeConfigParser\"\"\"\n    sources = get_sources(use_closest)\n    cfg = SafeConfigParser()\n    cfg.read(sources)\n    return cfg\n\n\ndef get_sources(use_closest=True):\n    xdg_config_home = os.environ.get('XDG_CONFIG_HOME') or \\\n        os.path.expanduser('~/.config')\n    sources = ['/etc/scrapy.cfg', r'c:\\scrapy\\scrapy.cfg',\n               xdg_config_home + '/scrapy.cfg',\n               os.path.expanduser('~/.scrapy.cfg')]\n    if use_closest:\n        sources.append(closest_scrapy_cfg())\n    return sources\n", "path": "scrapy/utils/conf.py"}], "after_files": [{"content": "import os\nimport sys\nimport numbers\nfrom operator import itemgetter\n\nimport six\nfrom six.moves.configparser import SafeConfigParser\n\nfrom scrapy.settings import BaseSettings\nfrom scrapy.utils.deprecate import update_classpath\nfrom scrapy.utils.python import without_none_values\n\n\ndef build_component_list(compdict, custom=None, convert=update_classpath):\n    \"\"\"Compose a component list from a { class: order } dictionary.\"\"\"\n\n    def _check_components(complist):\n        if len({convert(c) for c in complist}) != len(complist):\n            raise ValueError('Some paths in {!r} convert to the same object, '\n                             'please update your settings'.format(complist))\n\n    def _map_keys(compdict):\n        if isinstance(compdict, BaseSettings):\n            compbs = BaseSettings()\n            for k, v in six.iteritems(compdict):\n                prio = compdict.getpriority(k)\n                if compbs.getpriority(convert(k)) == prio:\n                    raise ValueError('Some paths in {!r} convert to the same '\n                                     'object, please update your settings'\n                                     ''.format(list(compdict.keys())))\n                else:\n                    compbs.set(convert(k), v, priority=prio)\n            return compbs\n        else:\n            _check_components(compdict)\n            return {convert(k): v for k, v in six.iteritems(compdict)}\n\n    def _validate_values(compdict):\n        \"\"\"Fail if a value in the components dict is not a real number or None.\"\"\"\n        for name, value in six.iteritems(compdict):\n            if value is not None and not isinstance(value, numbers.Real):\n                raise ValueError('Invalid value {} for component {}, please provide ' \\\n                                 'a real number or None instead'.format(value, name))\n\n    # BEGIN Backwards compatibility for old (base, custom) call signature\n    if isinstance(custom, (list, tuple)):\n        _check_components(custom)\n        return type(custom)(convert(c) for c in custom)\n\n    if custom is not None:\n        compdict.update(custom)\n    # END Backwards compatibility\n\n    _validate_values(compdict)\n    compdict = without_none_values(_map_keys(compdict))\n    return [k for k, v in sorted(six.iteritems(compdict), key=itemgetter(1))]\n\n\ndef arglist_to_dict(arglist):\n    \"\"\"Convert a list of arguments like ['arg1=val1', 'arg2=val2', ...] to a\n    dict\n    \"\"\"\n    return dict(x.split('=', 1) for x in arglist)\n\n\ndef closest_scrapy_cfg(path='.', prevpath=None):\n    \"\"\"Return the path to the closest scrapy.cfg file by traversing the current\n    directory and its parents\n    \"\"\"\n    if path == prevpath:\n        return ''\n    path = os.path.abspath(path)\n    cfgfile = os.path.join(path, 'scrapy.cfg')\n    if os.path.exists(cfgfile):\n        return cfgfile\n    return closest_scrapy_cfg(os.path.dirname(path), path)\n\n\ndef init_env(project='default', set_syspath=True):\n    \"\"\"Initialize environment to use command-line tool from inside a project\n    dir. This sets the Scrapy settings module and modifies the Python path to\n    be able to locate the project module.\n    \"\"\"\n    cfg = get_config()\n    if cfg.has_option('settings', project):\n        os.environ['SCRAPY_SETTINGS_MODULE'] = cfg.get('settings', project)\n    closest = closest_scrapy_cfg()\n    if closest:\n        projdir = os.path.dirname(closest)\n        if set_syspath and projdir not in sys.path:\n            sys.path.append(projdir)\n\n\ndef get_config(use_closest=True):\n    \"\"\"Get Scrapy config file as a SafeConfigParser\"\"\"\n    sources = get_sources(use_closest)\n    cfg = SafeConfigParser()\n    cfg.read(sources)\n    return cfg\n\n\ndef get_sources(use_closest=True):\n    xdg_config_home = os.environ.get('XDG_CONFIG_HOME') or \\\n        os.path.expanduser('~/.config')\n    sources = ['/etc/scrapy.cfg', r'c:\\scrapy\\scrapy.cfg',\n               xdg_config_home + '/scrapy.cfg',\n               os.path.expanduser('~/.scrapy.cfg')]\n    if use_closest:\n        sources.append(closest_scrapy_cfg())\n    return sources\n", "path": "scrapy/utils/conf.py"}]}