prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  startproject only allows creating the project on an existing folder if the path is specified with an invalid module name
  ```
  (venv) [adrian@afonsox temporal]$ mkdir existing_folder
  (venv) [adrian@afonsox temporal]$ scrapy startproject existing_folder
  Error: Module 'existing_folder' already exists
  (venv) [adrian@afonsox temporal]$ cd existing_folder && scrapy startproject existing_folder .
  New Scrapy project 'existing_folder', using template directory '/home/adrian/temporal/venv/lib/python3.8/site-packages/scrapy/templates/project', created in:
      /home/adrian/temporal/existing_folder
  
  You can start your first spider with:
      cd .
      scrapy genspider example example.com
  ```
  
  --- 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/commands/startproject.py`
  Content:
  ```
  1 import re
  2 import os
  3 import string
  4 from importlib import import_module
  5 from os.path import join, exists, abspath
  6 from shutil import ignore_patterns, move, copy2, copystat
  7 from stat import S_IWUSR as OWNER_WRITE_PERMISSION
  8 
  9 import scrapy
  10 from scrapy.commands import ScrapyCommand
  11 from scrapy.utils.template import render_templatefile, string_camelcase
  12 from scrapy.exceptions import UsageError
  13 
  14 
  15 TEMPLATES_TO_RENDER = (
  16     ('scrapy.cfg',),
  17     ('${project_name}', 'settings.py.tmpl'),
  18     ('${project_name}', 'items.py.tmpl'),
  19     ('${project_name}', 'pipelines.py.tmpl'),
  20     ('${project_name}', 'middlewares.py.tmpl'),
  21 )
  22 
  23 IGNORE = ignore_patterns('*.pyc', '__pycache__', '.svn')
  24 
  25 
  26 def _make_writable(path):
  27     current_permissions = os.stat(path).st_mode
  28     os.chmod(path, current_permissions | OWNER_WRITE_PERMISSION)
  29 
  30 
  31 class Command(ScrapyCommand):
  32 
  33     requires_project = False
  34     default_settings = {'LOG_ENABLED': False,
  35                         'SPIDER_LOADER_WARN_ONLY': True}
  36 
  37     def syntax(self):
  38         return "<project_name> [project_dir]"
  39 
  40     def short_desc(self):
  41         return "Create new project"
  42 
  43     def _is_valid_name(self, project_name):
  44         def _module_exists(module_name):
  45             try:
  46                 import_module(module_name)
  47                 return True
  48             except ImportError:
  49                 return False
  50 
  51         if not re.search(r'^[_a-zA-Z]\w*$', project_name):
  52             print('Error: Project names must begin with a letter and contain'
  53                   ' only\nletters, numbers and underscores')
  54         elif _module_exists(project_name):
  55             print(f'Error: Module {project_name!r} already exists')
  56         else:
  57             return True
  58         return False
  59 
  60     def _copytree(self, src, dst):
  61         """
  62         Since the original function always creates the directory, to resolve
  63         the issue a new function had to be created. It's a simple copy and
  64         was reduced for this case.
  65 
  66         More info at:
  67         https://github.com/scrapy/scrapy/pull/2005
  68         """
  69         ignore = IGNORE
  70         names = os.listdir(src)
  71         ignored_names = ignore(src, names)
  72 
  73         if not os.path.exists(dst):
  74             os.makedirs(dst)
  75 
  76         for name in names:
  77             if name in ignored_names:
  78                 continue
  79 
  80             srcname = os.path.join(src, name)
  81             dstname = os.path.join(dst, name)
  82             if os.path.isdir(srcname):
  83                 self._copytree(srcname, dstname)
  84             else:
  85                 copy2(srcname, dstname)
  86                 _make_writable(dstname)
  87 
  88         copystat(src, dst)
  89         _make_writable(dst)
  90 
  91     def run(self, args, opts):
  92         if len(args) not in (1, 2):
  93             raise UsageError()
  94 
  95         project_name = args[0]
  96         project_dir = args[0]
  97 
  98         if len(args) == 2:
  99             project_dir = args[1]
  100 
  101         if exists(join(project_dir, 'scrapy.cfg')):
  102             self.exitcode = 1
  103             print(f'Error: scrapy.cfg already exists in {abspath(project_dir)}')
  104             return
  105 
  106         if not self._is_valid_name(project_name):
  107             self.exitcode = 1
  108             return
  109 
  110         self._copytree(self.templates_dir, abspath(project_dir))
  111         move(join(project_dir, 'module'), join(project_dir, project_name))
  112         for paths in TEMPLATES_TO_RENDER:
  113             path = join(*paths)
  114             tplfile = join(project_dir, string.Template(path).substitute(project_name=project_name))
  115             render_templatefile(tplfile, project_name=project_name, ProjectName=string_camelcase(project_name))
  116         print(f"New Scrapy project '{project_name}', using template directory "
  117               f"'{self.templates_dir}', created in:")
  118         print(f"    {abspath(project_dir)}\n")
  119         print("You can start your first spider with:")
  120         print(f"    cd {project_dir}")
  121         print("    scrapy genspider example example.com")
  122 
  123     @property
  124     def templates_dir(self):
  125         return join(
  126             self.settings['TEMPLATES_DIR'] or join(scrapy.__path__[0], 'templates'),
  127             'project'
  128         )
  129
  ```
  --- 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/commands/startproject.py b/scrapy/commands/startproject.py
  --- a/scrapy/commands/startproject.py
  +++ b/scrapy/commands/startproject.py
  @@ -1,7 +1,7 @@
   import re
   import os
   import string
  -from importlib import import_module
  +from importlib.util import find_spec
   from os.path import join, exists, abspath
   from shutil import ignore_patterns, move, copy2, copystat
   from stat import S_IWUSR as OWNER_WRITE_PERMISSION
  @@ -42,11 +42,8 @@
   
       def _is_valid_name(self, project_name):
           def _module_exists(module_name):
  -            try:
  -                import_module(module_name)
  -                return True
  -            except ImportError:
  -                return False
  +            spec = find_spec(module_name)
  +            return spec is not None and spec.loader is not None
   
           if not re.search(r'^[_a-zA-Z]\w*$', project_name):
               print('Error: Project names must begin with a letter and contain'
  
verification_info: {"golden_diff": "diff --git a/scrapy/commands/startproject.py b/scrapy/commands/startproject.py\n--- a/scrapy/commands/startproject.py\n+++ b/scrapy/commands/startproject.py\n@@ -1,7 +1,7 @@\n import re\n import os\n import string\n-from importlib import import_module\n+from importlib.util import find_spec\n from os.path import join, exists, abspath\n from shutil import ignore_patterns, move, copy2, copystat\n from stat import S_IWUSR as OWNER_WRITE_PERMISSION\n@@ -42,11 +42,8 @@\n \n     def _is_valid_name(self, project_name):\n         def _module_exists(module_name):\n-            try:\n-                import_module(module_name)\n-                return True\n-            except ImportError:\n-                return False\n+            spec = find_spec(module_name)\n+            return spec is not None and spec.loader is not None\n \n         if not re.search(r'^[_a-zA-Z]\\w*$', project_name):\n             print('Error: Project names must begin with a letter and contain'\n", "issue": "startproject only allows creating the project on an existing folder if the path is specified with an invalid module name\n```\r\n(venv) [adrian@afonsox temporal]$ mkdir existing_folder\r\n(venv) [adrian@afonsox temporal]$ scrapy startproject existing_folder\r\nError: Module 'existing_folder' already exists\r\n(venv) [adrian@afonsox temporal]$ cd existing_folder && scrapy startproject existing_folder .\r\nNew Scrapy project 'existing_folder', using template directory '/home/adrian/temporal/venv/lib/python3.8/site-packages/scrapy/templates/project', created in:\r\n    /home/adrian/temporal/existing_folder\r\n\r\nYou can start your first spider with:\r\n    cd .\r\n    scrapy genspider example example.com\r\n```\n", "before_files": [{"content": "import re\nimport os\nimport string\nfrom importlib import import_module\nfrom os.path import join, exists, abspath\nfrom shutil import ignore_patterns, move, copy2, copystat\nfrom stat import S_IWUSR as OWNER_WRITE_PERMISSION\n\nimport scrapy\nfrom scrapy.commands import ScrapyCommand\nfrom scrapy.utils.template import render_templatefile, string_camelcase\nfrom scrapy.exceptions import UsageError\n\n\nTEMPLATES_TO_RENDER = (\n    ('scrapy.cfg',),\n    ('${project_name}', 'settings.py.tmpl'),\n    ('${project_name}', 'items.py.tmpl'),\n    ('${project_name}', 'pipelines.py.tmpl'),\n    ('${project_name}', 'middlewares.py.tmpl'),\n)\n\nIGNORE = ignore_patterns('*.pyc', '__pycache__', '.svn')\n\n\ndef _make_writable(path):\n    current_permissions = os.stat(path).st_mode\n    os.chmod(path, current_permissions | OWNER_WRITE_PERMISSION)\n\n\nclass Command(ScrapyCommand):\n\n    requires_project = False\n    default_settings = {'LOG_ENABLED': False,\n                        'SPIDER_LOADER_WARN_ONLY': True}\n\n    def syntax(self):\n        return \"<project_name> [project_dir]\"\n\n    def short_desc(self):\n        return \"Create new project\"\n\n    def _is_valid_name(self, project_name):\n        def _module_exists(module_name):\n            try:\n                import_module(module_name)\n                return True\n            except ImportError:\n                return False\n\n        if not re.search(r'^[_a-zA-Z]\\w*$', project_name):\n            print('Error: Project names must begin with a letter and contain'\n                  ' only\\nletters, numbers and underscores')\n        elif _module_exists(project_name):\n            print(f'Error: Module {project_name!r} already exists')\n        else:\n            return True\n        return False\n\n    def _copytree(self, src, dst):\n        \"\"\"\n        Since the original function always creates the directory, to resolve\n        the issue a new function had to be created. It's a simple copy and\n        was reduced for this case.\n\n        More info at:\n        https://github.com/scrapy/scrapy/pull/2005\n        \"\"\"\n        ignore = IGNORE\n        names = os.listdir(src)\n        ignored_names = ignore(src, names)\n\n        if not os.path.exists(dst):\n            os.makedirs(dst)\n\n        for name in names:\n            if name in ignored_names:\n                continue\n\n            srcname = os.path.join(src, name)\n            dstname = os.path.join(dst, name)\n            if os.path.isdir(srcname):\n                self._copytree(srcname, dstname)\n            else:\n                copy2(srcname, dstname)\n                _make_writable(dstname)\n\n        copystat(src, dst)\n        _make_writable(dst)\n\n    def run(self, args, opts):\n        if len(args) not in (1, 2):\n            raise UsageError()\n\n        project_name = args[0]\n        project_dir = args[0]\n\n        if len(args) == 2:\n            project_dir = args[1]\n\n        if exists(join(project_dir, 'scrapy.cfg')):\n            self.exitcode = 1\n            print(f'Error: scrapy.cfg already exists in {abspath(project_dir)}')\n            return\n\n        if not self._is_valid_name(project_name):\n            self.exitcode = 1\n            return\n\n        self._copytree(self.templates_dir, abspath(project_dir))\n        move(join(project_dir, 'module'), join(project_dir, project_name))\n        for paths in TEMPLATES_TO_RENDER:\n            path = join(*paths)\n            tplfile = join(project_dir, string.Template(path).substitute(project_name=project_name))\n            render_templatefile(tplfile, project_name=project_name, ProjectName=string_camelcase(project_name))\n        print(f\"New Scrapy project '{project_name}', using template directory \"\n              f\"'{self.templates_dir}', created in:\")\n        print(f\"    {abspath(project_dir)}\\n\")\n        print(\"You can start your first spider with:\")\n        print(f\"    cd {project_dir}\")\n        print(\"    scrapy genspider example example.com\")\n\n    @property\n    def templates_dir(self):\n        return join(\n            self.settings['TEMPLATES_DIR'] or join(scrapy.__path__[0], 'templates'),\n            'project'\n        )\n", "path": "scrapy/commands/startproject.py"}], "after_files": [{"content": "import re\nimport os\nimport string\nfrom importlib.util import find_spec\nfrom os.path import join, exists, abspath\nfrom shutil import ignore_patterns, move, copy2, copystat\nfrom stat import S_IWUSR as OWNER_WRITE_PERMISSION\n\nimport scrapy\nfrom scrapy.commands import ScrapyCommand\nfrom scrapy.utils.template import render_templatefile, string_camelcase\nfrom scrapy.exceptions import UsageError\n\n\nTEMPLATES_TO_RENDER = (\n    ('scrapy.cfg',),\n    ('${project_name}', 'settings.py.tmpl'),\n    ('${project_name}', 'items.py.tmpl'),\n    ('${project_name}', 'pipelines.py.tmpl'),\n    ('${project_name}', 'middlewares.py.tmpl'),\n)\n\nIGNORE = ignore_patterns('*.pyc', '__pycache__', '.svn')\n\n\ndef _make_writable(path):\n    current_permissions = os.stat(path).st_mode\n    os.chmod(path, current_permissions | OWNER_WRITE_PERMISSION)\n\n\nclass Command(ScrapyCommand):\n\n    requires_project = False\n    default_settings = {'LOG_ENABLED': False,\n                        'SPIDER_LOADER_WARN_ONLY': True}\n\n    def syntax(self):\n        return \"<project_name> [project_dir]\"\n\n    def short_desc(self):\n        return \"Create new project\"\n\n    def _is_valid_name(self, project_name):\n        def _module_exists(module_name):\n            spec = find_spec(module_name)\n            return spec is not None and spec.loader is not None\n\n        if not re.search(r'^[_a-zA-Z]\\w*$', project_name):\n            print('Error: Project names must begin with a letter and contain'\n                  ' only\\nletters, numbers and underscores')\n        elif _module_exists(project_name):\n            print('Error: Module %r already exists' % project_name)\n        else:\n            return True\n        return False\n\n    def _copytree(self, src, dst):\n        \"\"\"\n        Since the original function always creates the directory, to resolve\n        the issue a new function had to be created. It's a simple copy and\n        was reduced for this case.\n\n        More info at:\n        https://github.com/scrapy/scrapy/pull/2005\n        \"\"\"\n        ignore = IGNORE\n        names = os.listdir(src)\n        ignored_names = ignore(src, names)\n\n        if not os.path.exists(dst):\n            os.makedirs(dst)\n\n        for name in names:\n            if name in ignored_names:\n                continue\n\n            srcname = os.path.join(src, name)\n            dstname = os.path.join(dst, name)\n            if os.path.isdir(srcname):\n                self._copytree(srcname, dstname)\n            else:\n                copy2(srcname, dstname)\n                _make_writable(dstname)\n\n        copystat(src, dst)\n        _make_writable(dst)\n\n    def run(self, args, opts):\n        if len(args) not in (1, 2):\n            raise UsageError()\n\n        project_name = args[0]\n        project_dir = args[0]\n\n        if len(args) == 2:\n            project_dir = args[1]\n\n        if exists(join(project_dir, 'scrapy.cfg')):\n            self.exitcode = 1\n            print('Error: scrapy.cfg already exists in %s' % abspath(project_dir))\n            return\n\n        if not self._is_valid_name(project_name):\n            self.exitcode = 1\n            return\n\n        self._copytree(self.templates_dir, abspath(project_dir))\n        move(join(project_dir, 'module'), join(project_dir, project_name))\n        for paths in TEMPLATES_TO_RENDER:\n            path = join(*paths)\n            tplfile = join(project_dir, string.Template(path).substitute(project_name=project_name))\n            render_templatefile(tplfile, project_name=project_name, ProjectName=string_camelcase(project_name))\n        print(\"New Scrapy project '%s', using template directory '%s', \"\n              \"created in:\" % (project_name, self.templates_dir))\n        print(\"    %s\\n\" % abspath(project_dir))\n        print(\"You can start your first spider with:\")\n        print(\"    cd %s\" % project_dir)\n        print(\"    scrapy genspider example example.com\")\n\n    @property\n    def templates_dir(self):\n        return join(\n            self.settings['TEMPLATES_DIR'] or join(scrapy.__path__[0], 'templates'),\n            'project'\n        )\n", "path": "scrapy/commands/startproject.py"}]}