diff options
| author | Igor Stroh <rennpferd@gmail.com> | 2015-06-10 22:13:54 +0200 |
|---|---|---|
| committer | Igor Stroh <rennpferd@gmail.com> | 2015-06-10 22:13:54 +0200 |
| commit | 589a398c2a538f7f58775d1a841249540ac7134a (patch) | |
| tree | 1b08881f5721a1af0e9a64996c1ae044fd18eab4 | |
| parent | 2c3d653e0982eca137e58fe9ba0138469d862bf7 (diff) | |
| download | pyramid-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.py | 103 | ||||
| -rw-r--r-- | pyramid/tests/test_scripts/test_pcreate.py | 46 |
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): |
