Skip to content

Commit

Permalink
Merge pull request #2005 from feliperuhland/master
Browse files Browse the repository at this point in the history
[MRG+1] Included new optional parameter in startproject command line
  • Loading branch information
redapple committed Jul 19, 2016
2 parents a86562b + fe08892 commit ec1c615
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 16 deletions.
12 changes: 7 additions & 5 deletions docs/topics/commands.rst
Expand Up @@ -103,13 +103,14 @@ Creating projects
The first thing you typically do with the ``scrapy`` tool is create your Scrapy
project::

scrapy startproject myproject
scrapy startproject myproject [project_dir]

That will create a Scrapy project under the ``myproject`` directory.
That will create a Scrapy project under the ``project_dir`` directory.
If ``project_dir`` wasn't specified, ``project_dir`` will be the same as ``myproject``.

Next, you go inside the new project directory::

cd myproject
cd project_dir

And you're ready to use the ``scrapy`` command to manage and control your
project from there.
Expand Down Expand Up @@ -181,11 +182,12 @@ Project-only commands:
startproject
------------

* Syntax: ``scrapy startproject <project_name>``
* Syntax: ``scrapy startproject <project_name> [project_dir]``
* Requires project: *no*

Creates a new Scrapy project named ``project_name``, under the ``project_name``
Creates a new Scrapy project named ``project_name``, under the ``project_dir``
directory.
If ``project_dir`` wasn't specified, ``project_dir`` will be the same as ``myproject``.

Usage example::

Expand Down
59 changes: 48 additions & 11 deletions scrapy/commands/startproject.py
@@ -1,9 +1,10 @@
from __future__ import print_function
import re
import os
import string
from importlib import import_module
from os.path import join, exists, abspath
from shutil import copytree, ignore_patterns, move
from shutil import ignore_patterns, move, copy2, copystat

import scrapy
from scrapy.commands import ScrapyCommand
Expand All @@ -27,7 +28,7 @@ class Command(ScrapyCommand):
default_settings = {'LOG_ENABLED': False}

def syntax(self):
return "<project_name>"
return "<project_name> [project_dir]"

def short_desc(self):
return "Create new project"
Expand All @@ -43,41 +44,77 @@ def _module_exists(module_name):
if not re.search(r'^[_a-zA-Z]\w*$', project_name):
print('Error: Project names must begin with a letter and contain'\
' only\nletters, numbers and underscores')
elif exists(project_name):
print('Error: Directory %r already exists' % project_name)
elif _module_exists(project_name):
print('Error: Module %r already exists' % project_name)
else:
return True
return False

def _copytree(self, src, dst):
"""
Since the original function always creates the directory, to resolve
the issue a new function had to be created. It's a simple copy and
was reduced for this case.
More info at:
https://github.com/scrapy/scrapy/pull/2005
"""
ignore = IGNORE
names = os.listdir(src)
ignored_names = ignore(src, names)

if not os.path.exists(dst):
os.makedirs(dst)

for name in names:
if name in ignored_names:
continue

srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
if os.path.isdir(srcname):
self._copytree(srcname, dstname)
else:
copy2(srcname, dstname)
copystat(src, dst)

def run(self, args, opts):
if len(args) != 1:
if len(args) not in (1, 2):
raise UsageError()

project_name = args[0]
project_dir = args[0]

if len(args) == 2:
project_dir = args[1]

if exists(join(project_dir, 'scrapy.cfg')):
self.exitcode = 1
print('Error: scrapy.cfg already exists in %s' % abspath(project_dir))
return

if not self._is_valid_name(project_name):
self.exitcode = 1
return

copytree(self.templates_dir, project_name, ignore=IGNORE)
move(join(project_name, 'module'), join(project_name, project_name))
self._copytree(self.templates_dir, abspath(project_dir))
move(join(project_dir, 'module'), join(project_dir, project_name))
for paths in TEMPLATES_TO_RENDER:
path = join(*paths)
tplfile = join(project_name,
tplfile = join(project_dir,
string.Template(path).substitute(project_name=project_name))
render_templatefile(tplfile, project_name=project_name,
ProjectName=string_camelcase(project_name))
print("New Scrapy project %r, using template directory %r, created in:" % \
(project_name, self.templates_dir))
print(" %s\n" % abspath(project_name))
print(" %s\n" % abspath(project_dir))
print("You can start your first spider with:")
print(" cd %s" % project_name)
print(" cd %s" % project_dir)
print(" scrapy genspider example example.com")

@property
def templates_dir(self):
_templates_base_dir = self.settings['TEMPLATES_DIR'] or \
join(scrapy.__path__[0], 'templates')
return join(_templates_base_dir, 'project')


21 changes: 21 additions & 0 deletions tests/test_commands.py
Expand Up @@ -73,6 +73,27 @@ def test_startproject(self):
self.assertEqual(1, self.call('startproject', 'wrong---project---name'))
self.assertEqual(1, self.call('startproject', 'sys'))

def test_startproject_with_project_dir(self):
project_dir = mkdtemp()
self.assertEqual(0, self.call('startproject', self.project_name, project_dir))

assert exists(join(abspath(project_dir), 'scrapy.cfg'))
assert exists(join(abspath(project_dir), 'testproject'))
assert exists(join(join(abspath(project_dir), self.project_name), '__init__.py'))
assert exists(join(join(abspath(project_dir), self.project_name), 'items.py'))
assert exists(join(join(abspath(project_dir), self.project_name), 'pipelines.py'))
assert exists(join(join(abspath(project_dir), self.project_name), 'settings.py'))
assert exists(join(join(abspath(project_dir), self.project_name), 'spiders', '__init__.py'))

self.assertEqual(0, self.call('startproject', self.project_name, project_dir + '2'))

self.assertEqual(1, self.call('startproject', self.project_name, project_dir))
self.assertEqual(1, self.call('startproject', self.project_name + '2', project_dir))
self.assertEqual(1, self.call('startproject', 'wrong---project---name'))
self.assertEqual(1, self.call('startproject', 'sys'))
self.assertEqual(2, self.call('startproject'))
self.assertEqual(2, self.call('startproject', self.project_name, project_dir, 'another_params'))


class StartprojectTemplatesTest(ProjectTest):

Expand Down

0 comments on commit ec1c615

Please sign in to comment.