279 lines
9.8 KiB
Python
Executable File
279 lines
9.8 KiB
Python
Executable File
# (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 sys
|
|
import os
|
|
import inspect
|
|
import copydir
|
|
import command
|
|
|
|
from paste.util.template import paste_script_template_renderer
|
|
|
|
class Template(object):
|
|
|
|
# Subclasses must define:
|
|
# _template_dir (or template_dir())
|
|
# summary
|
|
|
|
# Variables this template uses (mostly for documentation now)
|
|
# a list of instances of var()
|
|
vars = []
|
|
|
|
# Eggs that should be added as plugins:
|
|
egg_plugins = []
|
|
|
|
# Templates that must be applied first:
|
|
required_templates = []
|
|
|
|
# Use Cheetah for substituting templates:
|
|
use_cheetah = False
|
|
# If true, then read all the templates to find the variables:
|
|
read_vars_from_templates = False
|
|
|
|
# You can also give this function/method to use something other
|
|
# than Cheetah or string.Template. The function should be of the
|
|
# signature template_renderer(content, vars, filename=filename).
|
|
# Careful you don't turn this into a method by putting a function
|
|
# here (without staticmethod)!
|
|
template_renderer = None
|
|
|
|
def __init__(self, name):
|
|
self.name = name
|
|
self._read_vars = None
|
|
|
|
def module_dir(self):
|
|
"""Returns the module directory of this template."""
|
|
mod = sys.modules[self.__class__.__module__]
|
|
return os.path.dirname(mod.__file__)
|
|
|
|
def template_dir(self):
|
|
assert self._template_dir is not None, (
|
|
"Template %r didn't set _template_dir" % self)
|
|
if isinstance( self._template_dir, tuple):
|
|
return self._template_dir
|
|
else:
|
|
return os.path.join(self.module_dir(), self._template_dir)
|
|
|
|
def run(self, command, output_dir, vars):
|
|
self.pre(command, output_dir, vars)
|
|
self.write_files(command, output_dir, vars)
|
|
self.post(command, output_dir, vars)
|
|
|
|
def check_vars(self, vars, cmd):
|
|
expect_vars = self.read_vars(cmd)
|
|
if not expect_vars:
|
|
# Assume that variables aren't defined
|
|
return vars
|
|
converted_vars = {}
|
|
unused_vars = vars.copy()
|
|
errors = []
|
|
for var in expect_vars:
|
|
if var.name not in unused_vars:
|
|
if cmd.interactive:
|
|
prompt = 'Enter %s' % var.full_description()
|
|
response = cmd.challenge(prompt, var.default, var.should_echo)
|
|
converted_vars[var.name] = response
|
|
elif var.default is command.NoDefault:
|
|
errors.append('Required variable missing: %s'
|
|
% var.full_description())
|
|
else:
|
|
converted_vars[var.name] = var.default
|
|
else:
|
|
converted_vars[var.name] = unused_vars.pop(var.name)
|
|
if errors:
|
|
raise command.BadCommand(
|
|
'Errors in variables:\n%s' % '\n'.join(errors))
|
|
converted_vars.update(unused_vars)
|
|
vars.update(converted_vars)
|
|
return converted_vars
|
|
|
|
def read_vars(self, command=None):
|
|
if self._read_vars is not None:
|
|
return self._read_vars
|
|
assert (not self.read_vars_from_templates
|
|
or self.use_cheetah), (
|
|
"You can only read variables from templates if using Cheetah")
|
|
if not self.read_vars_from_templates:
|
|
self._read_vars = self.vars
|
|
return self.vars
|
|
|
|
vars = self.vars[:]
|
|
var_names = [var.name for var in self.vars]
|
|
read_vars = find_args_in_dir(
|
|
self.template_dir(),
|
|
verbose=command and command.verbose > 1).items()
|
|
read_vars.sort()
|
|
for var_name, var in read_vars:
|
|
if var_name not in var_names:
|
|
vars.append(var)
|
|
self._read_vars = vars
|
|
return vars
|
|
|
|
def write_files(self, command, output_dir, vars):
|
|
template_dir = self.template_dir()
|
|
if not os.path.exists(output_dir):
|
|
print "Creating directory %s" % output_dir
|
|
if not command.simulate:
|
|
# Don't let copydir create this top-level directory,
|
|
# since copydir will svn add it sometimes:
|
|
os.makedirs(output_dir)
|
|
copydir.copy_dir(template_dir, output_dir,
|
|
vars,
|
|
verbosity=command.verbose,
|
|
simulate=command.options.simulate,
|
|
interactive=command.interactive,
|
|
overwrite=command.options.overwrite,
|
|
indent=1,
|
|
use_cheetah=self.use_cheetah,
|
|
template_renderer=self.template_renderer)
|
|
|
|
def print_vars(self, indent=0):
|
|
vars = self.read_vars()
|
|
var.print_vars(vars)
|
|
|
|
def pre(self, command, output_dir, vars):
|
|
"""
|
|
Called before template is applied.
|
|
"""
|
|
pass
|
|
|
|
def post(self, command, output_dir, vars):
|
|
"""
|
|
Called after template is applied.
|
|
"""
|
|
pass
|
|
|
|
NoDefault = command.NoDefault
|
|
|
|
class var(object):
|
|
|
|
def __init__(self, name, description,
|
|
default='', should_echo=True):
|
|
self.name = name
|
|
self.description = description
|
|
self.default = default
|
|
self.should_echo = should_echo
|
|
|
|
def __repr__(self):
|
|
return '<%s %s default=%r should_echo=%s>' % (
|
|
self.__class__.__name__,
|
|
self.name, self.default, self.should_echo)
|
|
|
|
def full_description(self):
|
|
if self.description:
|
|
return '%s (%s)' % (self.name, self.description)
|
|
else:
|
|
return self.name
|
|
|
|
def print_vars(cls, vars, indent=0):
|
|
max_name = max([len(v.name) for v in vars])
|
|
for var in vars:
|
|
if var.description:
|
|
print '%s%s%s %s' % (
|
|
' '*indent,
|
|
var.name,
|
|
' '*(max_name-len(var.name)),
|
|
var.description)
|
|
else:
|
|
print ' %s' % var.name
|
|
if var.default is not command.NoDefault:
|
|
print ' default: %r' % var.default
|
|
if var.should_echo is True:
|
|
print ' should_echo: %s' % var.should_echo
|
|
print
|
|
|
|
print_vars = classmethod(print_vars)
|
|
|
|
class BasicPackage(Template):
|
|
|
|
_template_dir = 'paster-templates/basic_package'
|
|
summary = "A basic setuptools-enabled package"
|
|
vars = [
|
|
var('version', 'Version (like 0.1)'),
|
|
var('description', 'One-line description of the package'),
|
|
var('long_description', 'Multi-line description (in reST)'),
|
|
var('keywords', 'Space-separated keywords/tags'),
|
|
var('author', 'Author name'),
|
|
var('author_email', 'Author email'),
|
|
var('url', 'URL of homepage'),
|
|
var('license_name', 'License name'),
|
|
var('zip_safe', 'True/False: if the package can be distributed as a .zip file', default=False),
|
|
]
|
|
|
|
template_renderer = staticmethod(paste_script_template_renderer)
|
|
|
|
_skip_variables = ['VFN', 'currentTime', 'self', 'VFFSL', 'dummyTrans',
|
|
'getmtime', 'trans']
|
|
|
|
def find_args_in_template(template):
|
|
if isinstance(template, (str, unicode)):
|
|
# Treat as filename:
|
|
import Cheetah.Template
|
|
template = Cheetah.Template.Template(file=template)
|
|
if not hasattr(template, 'body'):
|
|
# Don't know...
|
|
return None
|
|
method = template.body
|
|
args, varargs, varkw, defaults = inspect.getargspec(method)
|
|
defaults=list(defaults or [])
|
|
vars = []
|
|
while args:
|
|
if len(args) == len(defaults):
|
|
default = defaults.pop(0)
|
|
else:
|
|
default = command.NoDefault
|
|
arg = args.pop(0)
|
|
if arg in _skip_variables:
|
|
continue
|
|
# @@: No way to get description yet
|
|
vars.append(
|
|
var(arg, description=None,
|
|
default=default))
|
|
return vars
|
|
|
|
def find_args_in_dir(dir, verbose=False):
|
|
all_vars = {}
|
|
for fn in os.listdir(dir):
|
|
if fn.startswith('.') or fn == 'CVS' or fn == '_darcs':
|
|
continue
|
|
full = os.path.join(dir, fn)
|
|
if os.path.isdir(full):
|
|
inner_vars = find_args_in_dir(full)
|
|
elif full.endswith('_tmpl'):
|
|
inner_vars = {}
|
|
found = find_args_in_template(full)
|
|
if found is None:
|
|
# Couldn't read variables
|
|
if verbose:
|
|
print 'Template %s has no parseable variables' % full
|
|
continue
|
|
for var in found:
|
|
inner_vars[var.name] = var
|
|
else:
|
|
# Not a template, don't read it
|
|
continue
|
|
if verbose:
|
|
print 'Found variable(s) %s in Template %s' % (
|
|
', '.join(inner_vars.keys()), full)
|
|
for var_name, var in inner_vars.items():
|
|
# Easy case:
|
|
if var_name not in all_vars:
|
|
all_vars[var_name] = var
|
|
continue
|
|
# Emit warnings if the variables don't match well:
|
|
cur_var = all_vars[var_name]
|
|
if not cur_var.description:
|
|
cur_var.description = var.description
|
|
elif (cur_var.description and var.description
|
|
and var.description != cur_var.description):
|
|
print >> sys.stderr, (
|
|
"Variable descriptions do not match: %s: %s and %s"
|
|
% (var_name, cur_var.description, var.description))
|
|
if (cur_var.default is not command.NoDefault
|
|
and var.default is not command.NoDefault
|
|
and cur_var.default != var.default):
|
|
print >> sys.stderr, (
|
|
"Variable defaults do not match: %s: %r and %r"
|
|
% (var_name, cur_var.default, var.default))
|
|
return all_vars
|
|
|