summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIgor Stroh <rennpferd@gmail.com>2015-06-10 22:13:54 +0200
committerIgor Stroh <rennpferd@gmail.com>2015-06-10 22:13:54 +0200
commit589a398c2a538f7f58775d1a841249540ac7134a (patch)
tree1b08881f5721a1af0e9a64996c1ae044fd18eab4
parent2c3d653e0982eca137e58fe9ba0138469d862bf7 (diff)
downloadpyramid-589a398c2a538f7f58775d1a841249540ac7134a.tar.gz
pyramid-589a398c2a538f7f58775d1a841249540ac7134a.tar.bz2
pyramid-589a398c2a538f7f58775d1a841249540ac7134a.zip
handle conflicting project names gracefully, fixes: #1357
- Check for importable packages/modules with the same name as the package to be created - Added --force-conflicting-name option to enforce the use of a conflicting package - Added appropriate tests
-rw-r--r--pyramid/scripts/pcreate.py103
-rw-r--r--pyramid/tests/test_scripts/test_pcreate.py46
2 files changed, 129 insertions, 20 deletions
diff --git a/pyramid/scripts/pcreate.py b/pyramid/scripts/pcreate.py
index f6376f575..5ef0ac2d5 100644
--- a/pyramid/scripts/pcreate.py
+++ b/pyramid/scripts/pcreate.py
@@ -13,7 +13,21 @@ _bad_chars_re = re.compile('[^a-zA-Z0-9_]')
def main(argv=sys.argv, quiet=False):
command = PCreateCommand(argv, quiet)
- return command.run()
+ try:
+ return command.run()
+ except KeyboardInterrupt: # pragma: no cover
+ return 1
+
+
+class InvalidInputError(Exception):
+ """ This is raised in case a project name is
+ missing or a scaffold name was omitted.
+ """
+ pass
+
+class ExistingProjectNameError(InvalidInputError):
+ pass
+
class PCreateCommand(object):
verbosity = 1 # required
@@ -52,6 +66,13 @@ class PCreateCommand(object):
dest='interactive',
action='store_true',
help='When a file would be overwritten, interrogate')
+ parser.add_option('--force-conflicting-name',
+ dest='force_bad_name',
+ action='store_true',
+ default=False,
+ help='Do create a project even if the chosen name '
+ 'is the name of an already existing / importable '
+ 'package.')
pyramid_dist = pkg_resources.get_distribution("pyramid")
@@ -69,25 +90,21 @@ class PCreateCommand(object):
self.out('')
self.show_scaffolds()
return 2
- if not self.options.scaffold_name:
- self.out('You must provide at least one scaffold name: -s <scaffold name>')
- self.out('')
- self.show_scaffolds()
- return 2
- if not self.args:
- self.out('You must provide a project name')
- return 2
- available = [x.name for x in self.scaffolds]
- diff = set(self.options.scaffold_name).difference(available)
- if diff:
- self.out('Unavailable scaffolds: %s' % list(diff))
+
+ if not self.validate_input():
return 2
+
return self.render_scaffolds()
- def render_scaffolds(self):
+ @property
+ def output_path(self):
+ return os.path.abspath(os.path.normpath(self.args[0]))
+
+ @property
+ def project_vars(self):
options = self.options
args = self.args
- output_dir = os.path.abspath(os.path.normpath(args[0]))
+ output_dir = self.output_path
project_name = os.path.basename(os.path.split(output_dir)[1])
pkg_name = _bad_chars_re.sub(
'', project_name.lower().replace('-', '_'))
@@ -111,17 +128,22 @@ class PCreateCommand(object):
else:
pyramid_docs_branch = 'latest'
- vars = {
+ return {
'project': project_name,
'package': pkg_name,
'egg': egg_name,
'pyramid_version': pyramid_version,
'pyramid_docs_branch': pyramid_docs_branch,
- }
- for scaffold_name in options.scaffold_name:
+ }
+
+
+ def render_scaffolds(self):
+ props = self.project_vars
+ output_dir = self.output_path
+ for scaffold_name in self.options.scaffold_name:
for scaffold in self.scaffolds:
if scaffold.name == scaffold_name:
- scaffold.run(self, output_dir, vars)
+ scaffold.run(self, output_dir, props)
return 0
def show_scaffolds(self):
@@ -154,5 +176,48 @@ class PCreateCommand(object):
if not self.quiet:
print(msg)
+ def validate_input(self):
+ if not self.options.scaffold_name:
+ self.out('You must provide at least one scaffold name: -s <scaffold name>')
+ self.out('')
+ self.show_scaffolds()
+ return False
+ if not self.args:
+ self.out('You must provide a project name')
+ return False
+ available = [x.name for x in self.scaffolds]
+ diff = set(self.options.scaffold_name).difference(available)
+ if diff:
+ self.out('Unavailable scaffolds: %s' % ", ".join(list(diff)))
+ return False
+
+ pkg_name = self.project_vars['package']
+
+ if pkg_name == 'site' and not self.options.force_bad_name:
+ self.out('The package name "site" has a special meaning in '
+ 'Python. Are you sure you want to use it as your '
+ 'project\'s name?')
+ return self.confirm_bad_name('Really use "{}"?: '.format(pkg_name))
+
+ # check if pkg_name can be imported (i.e. already exists in current
+ # $PYTHON_PATH, if so - let the user confirm
+ pkg_exists = True
+ try:
+ __import__(pkg_name, globals(), locals(), [], 0) # use absolute imports
+ except ImportError as error:
+ pkg_exists = False
+ if not pkg_exists:
+ return True
+
+ if self.options.force_bad_name:
+ return True
+ self.out('Package "{}" already exists, are you sure you want '
+ 'to use it as your project\'s name?'.format(pkg_name))
+ return self.confirm_bad_name('Really use "{}"?: '.format(pkg_name))
+
+ def confirm_bad_name(self, prompt): # pragma: no cover
+ answer = raw_input('{} [y|N]: '.format(prompt))
+ return answer.strip().lower() == 'y'
+
if __name__ == '__main__': # pragma: no cover
sys.exit(main() or 0)
diff --git a/pyramid/tests/test_scripts/test_pcreate.py b/pyramid/tests/test_scripts/test_pcreate.py
index 63e5e6368..8fc9c1267 100644
--- a/pyramid/tests/test_scripts/test_pcreate.py
+++ b/pyramid/tests/test_scripts/test_pcreate.py
@@ -1,5 +1,6 @@
import unittest
+
class TestPCreateCommand(unittest.TestCase):
def setUp(self):
from pyramid.compat import NativeIO
@@ -15,7 +16,8 @@ class TestPCreateCommand(unittest.TestCase):
def _makeOne(self, *args, **kw):
effargs = ['pcreate']
effargs.extend(args)
- cmd = self._getTargetClass()(effargs, **kw)
+ tgt_class = kw.pop('target_class', self._getTargetClass())
+ cmd = tgt_class(effargs, **kw)
cmd.out = self.out
return cmd
@@ -220,6 +222,48 @@ class TestPCreateCommand(unittest.TestCase):
'pyramid_version': '0.10.1dev',
'pyramid_docs_branch': 'master'})
+ def test_confirm_override_conflicting_name(self):
+ from pyramid.scripts.pcreate import PCreateCommand
+ class YahInputPCreateCommand(PCreateCommand):
+ def confirm_bad_name(self, pkg_name):
+ return True
+ cmd = self._makeOne('-s', 'dummy', 'Unittest', target_class=YahInputPCreateCommand)
+ scaffold = DummyScaffold('dummy')
+ cmd.scaffolds = [scaffold]
+ cmd.pyramid_dist = DummyDist("0.10.1dev")
+ result = cmd.run()
+ self.assertEqual(result, 0)
+ self.assertEqual(
+ scaffold.vars,
+ {'project': 'Unittest', 'egg': 'Unittest', 'package': 'unittest',
+ 'pyramid_version': '0.10.1dev',
+ 'pyramid_docs_branch': 'master'})
+
+ def test_force_override_conflicting_name(self):
+ cmd = self._makeOne('-s', 'dummy', 'Unittest', '--force-conflicting-name')
+ scaffold = DummyScaffold('dummy')
+ cmd.scaffolds = [scaffold]
+ cmd.pyramid_dist = DummyDist("0.10.1dev")
+ result = cmd.run()
+ self.assertEqual(result, 0)
+ self.assertEqual(
+ scaffold.vars,
+ {'project': 'Unittest', 'egg': 'Unittest', 'package': 'unittest',
+ 'pyramid_version': '0.10.1dev',
+ 'pyramid_docs_branch': 'master'})
+
+ def test_force_override_site_name(self):
+ from pyramid.scripts.pcreate import PCreateCommand
+ class NayInputPCreateCommand(PCreateCommand):
+ def confirm_bad_name(self, pkg_name):
+ return False
+ cmd = self._makeOne('-s', 'dummy', 'Site', target_class=NayInputPCreateCommand)
+ scaffold = DummyScaffold('dummy')
+ cmd.scaffolds = [scaffold]
+ cmd.pyramid_dist = DummyDist("0.10.1dev")
+ result = cmd.run()
+ self.assertEqual(result, 2)
+
class Test_main(unittest.TestCase):
def _callFUT(self, argv):