# (c) 2005 Ian Bicking and contributors; written for Paste
|
# (http://pythonpaste.org) Licensed under the MIT license:
|
# http://www.opensource.org/licenses/mit-license.php
|
|
import argparse
|
import os
|
import os.path
|
import pkg_resources
|
import re
|
import sys
|
from pyramid.compat import input_
|
|
_bad_chars_re = re.compile('[^a-zA-Z0-9_]')
|
|
|
def main(argv=sys.argv, quiet=False):
|
command = PCreateCommand(argv, quiet)
|
try:
|
return command.run()
|
except KeyboardInterrupt: # pragma: no cover
|
return 1
|
|
|
class PCreateCommand(object):
|
verbosity = 1 # required
|
parser = argparse.ArgumentParser(
|
description="""\
|
Render Pyramid scaffolding to an output directory.
|
|
Note: As of Pyramid 1.8, this command is deprecated. Use
|
pyramid-cookiecutter-starter instead:
|
https://github.com/Pylons/pyramid-cookiecutter-starter
|
""",
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
)
|
parser.add_argument(
|
'-s',
|
'--scaffold',
|
dest='scaffold_name',
|
action='append',
|
help=(
|
"Add a scaffold to the create process "
|
"(multiple -s args accepted)"
|
),
|
)
|
parser.add_argument(
|
'-t',
|
'--template',
|
dest='scaffold_name',
|
action='append',
|
help=(
|
'A backwards compatibility alias for '
|
'-s/--scaffold. Add a scaffold to the '
|
'create process (multiple -t args accepted)'
|
),
|
)
|
parser.add_argument(
|
'-l',
|
'--list',
|
dest='list',
|
action='store_true',
|
help="List all available scaffold names",
|
)
|
parser.add_argument(
|
'--list-templates',
|
dest='list',
|
action='store_true',
|
help=(
|
"A backwards compatibility alias for -l/--list. "
|
"List all available scaffold names."
|
),
|
)
|
parser.add_argument(
|
'--package-name',
|
dest='package_name',
|
action='store',
|
help='Package name to use. The name provided is '
|
'assumed to be a valid Python package name, and '
|
'will not be validated. By default the package '
|
'name is derived from the value of '
|
'output_directory.',
|
)
|
parser.add_argument(
|
'--simulate',
|
dest='simulate',
|
action='store_true',
|
help='Simulate but do no work',
|
)
|
parser.add_argument(
|
'--overwrite',
|
dest='overwrite',
|
action='store_true',
|
help='Always overwrite',
|
)
|
parser.add_argument(
|
'--interactive',
|
dest='interactive',
|
action='store_true',
|
help='When a file would be overwritten, interrogate '
|
'(this is the default, but you may specify it to '
|
'override --overwrite)',
|
)
|
parser.add_argument(
|
'--ignore-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.',
|
)
|
parser.add_argument(
|
'output_directory',
|
nargs='?',
|
default=None,
|
help='The directory where the project will be ' 'created.',
|
)
|
|
pyramid_dist = pkg_resources.get_distribution("pyramid")
|
|
def __init__(self, argv, quiet=False):
|
self.quiet = quiet
|
self.args = self.parser.parse_args(argv[1:])
|
if not self.args.interactive and not self.args.overwrite:
|
self.args.interactive = True
|
self.scaffolds = self.all_scaffolds()
|
|
def run(self):
|
if self.args.list:
|
return self.show_scaffolds()
|
if not self.args.scaffold_name and not self.args.output_directory:
|
if not self.quiet: # pragma: no cover
|
self.parser.print_help()
|
self.out('')
|
self.show_scaffolds()
|
return 2
|
|
if not self.validate_input():
|
return 2
|
self._warn_pcreate_deprecated()
|
|
return self.render_scaffolds()
|
|
@property
|
def output_path(self):
|
return os.path.abspath(os.path.normpath(self.args.output_directory))
|
|
@property
|
def project_vars(self):
|
output_dir = self.output_path
|
project_name = os.path.basename(os.path.split(output_dir)[1])
|
if self.args.package_name is None:
|
pkg_name = _bad_chars_re.sub(
|
'', project_name.lower().replace('-', '_')
|
)
|
safe_name = pkg_resources.safe_name(project_name)
|
else:
|
pkg_name = self.args.package_name
|
safe_name = pkg_name
|
egg_name = pkg_resources.to_filename(safe_name)
|
|
# get pyramid package version
|
pyramid_version = self.pyramid_dist.version
|
|
# map pyramid package version of the documentation branch ##
|
# if version ends with 'dev' then docs version is 'master'
|
if self.pyramid_dist.version[-3:] == 'dev':
|
pyramid_docs_branch = 'master'
|
else:
|
# if not version is not 'dev' find the version.major_version string
|
# and combine it with '-branch'
|
version_match = re.match(r'(\d+\.\d+)', self.pyramid_dist.version)
|
if version_match is not None:
|
pyramid_docs_branch = "%s-branch" % version_match.group()
|
# if can not parse the version then default to 'latest'
|
else:
|
pyramid_docs_branch = 'latest'
|
|
return {
|
'project': project_name,
|
'package': pkg_name,
|
'egg': egg_name,
|
'pyramid_version': pyramid_version,
|
'pyramid_docs_branch': pyramid_docs_branch,
|
}
|
|
def render_scaffolds(self):
|
props = self.project_vars
|
output_dir = self.output_path
|
for scaffold_name in self.args.scaffold_name:
|
for scaffold in self.scaffolds:
|
if scaffold.name == scaffold_name:
|
scaffold.run(self, output_dir, props)
|
return 0
|
|
def show_scaffolds(self):
|
scaffolds = sorted(self.scaffolds, key=lambda x: x.name)
|
if scaffolds:
|
max_name = max([len(t.name) for t in scaffolds])
|
self.out('Available scaffolds:')
|
for scaffold in scaffolds:
|
self.out(
|
' %s:%s %s'
|
% (
|
scaffold.name,
|
' ' * (max_name - len(scaffold.name)),
|
scaffold.summary,
|
)
|
)
|
else:
|
self.out('No scaffolds available')
|
return 0
|
|
def all_scaffolds(self):
|
scaffolds = []
|
eps = list(pkg_resources.iter_entry_points('pyramid.scaffold'))
|
for entry in eps:
|
try:
|
scaffold_class = entry.load()
|
scaffold = scaffold_class(entry.name)
|
scaffolds.append(scaffold)
|
except Exception as e: # pragma: no cover
|
self.out(
|
'Warning: could not load entry point %s (%s: %s)'
|
% (entry.name, e.__class__.__name__, e)
|
)
|
return scaffolds
|
|
def out(self, msg): # pragma: no cover
|
if not self.quiet:
|
print(msg)
|
|
def validate_input(self):
|
if not self.args.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.output_directory:
|
self.out('You must provide a project name')
|
return False
|
available = [x.name for x in self.scaffolds]
|
diff = set(self.args.scaffold_name).difference(available)
|
if diff:
|
self.out('Unavailable scaffolds: %s' % ", ".join(sorted(diff)))
|
return False
|
|
pkg_name = self.project_vars['package']
|
|
if pkg_name == 'site' and not self.args.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 "{0}"?: '.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:
|
# use absolute imports
|
__import__(pkg_name, globals(), locals(), [], 0)
|
except ImportError as error:
|
pkg_exists = False
|
if not pkg_exists:
|
return True
|
|
if self.args.force_bad_name:
|
return True
|
self.out(
|
'A package named "{0}" 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 "{0}"?: '.format(pkg_name))
|
|
def confirm_bad_name(self, prompt): # pragma: no cover
|
answer = input_('{0} [y|N]: '.format(prompt))
|
return answer.strip().lower() == 'y'
|
|
def _warn_pcreate_deprecated(self):
|
self.out(
|
'''\
|
Note: As of Pyramid 1.8, this command is deprecated. Use a specific
|
cookiecutter instead:
|
https://github.com/pylons/?query=cookiecutter
|
'''
|
)
|
|
|
if __name__ == '__main__': # pragma: no cover
|
sys.exit(main() or 0)
|