A first pass at simple paste.script style templates.
Committing my work before this code hoses my filesystem.
This commit is contained in:
@@ -9,11 +9,27 @@ from pecan import load_app
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HelpfulArgumentParser(argparse.ArgumentParser):
|
||||
|
||||
def error(self, message):
|
||||
"""error(message: string)
|
||||
|
||||
Prints a usage message incorporating the message to stderr and
|
||||
exits.
|
||||
|
||||
If you override this in a subclass, it should not return -- it
|
||||
should either exit or raise an exception.
|
||||
"""
|
||||
self.print_help(sys.stderr)
|
||||
self._print_message('\n')
|
||||
self.exit(2, '%s: %s\n' % (self.prog, message))
|
||||
|
||||
|
||||
class CommandManager(object):
|
||||
""" Used to discover `pecan.command` entry points. """
|
||||
|
||||
def __init__(self):
|
||||
self.commands_ = {}
|
||||
self.commands = {}
|
||||
self.load_commands()
|
||||
|
||||
def load_commands(self):
|
||||
@@ -28,11 +44,7 @@ class CommandManager(object):
|
||||
self.add({ep.name: cmd})
|
||||
|
||||
def add(self, cmd):
|
||||
self.commands_.update(cmd)
|
||||
|
||||
@property
|
||||
def commands(self):
|
||||
return self.commands_
|
||||
self.commands.update(cmd)
|
||||
|
||||
|
||||
class CommandRunner(object):
|
||||
@@ -40,13 +52,13 @@ class CommandRunner(object):
|
||||
|
||||
def __init__(self):
|
||||
self.manager = CommandManager()
|
||||
self.parser = argparse.ArgumentParser(
|
||||
self.parser = HelpfulArgumentParser(
|
||||
version='Pecan %s' % self.version,
|
||||
add_help=True
|
||||
)
|
||||
self.parse_commands()
|
||||
self.parse_sub_commands()
|
||||
|
||||
def parse_commands(self):
|
||||
def parse_sub_commands(self):
|
||||
subparsers = self.parser.add_subparsers(
|
||||
dest='command_name',
|
||||
metavar='command'
|
||||
@@ -67,8 +79,7 @@ class CommandRunner(object):
|
||||
@classmethod
|
||||
def handle_command_line(cls):
|
||||
runner = CommandRunner()
|
||||
exit_code = runner.run(sys.argv[1:])
|
||||
sys.exit(exit_code)
|
||||
runner.run(sys.argv[1:])
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
@@ -83,7 +94,7 @@ class CommandRunner(object):
|
||||
|
||||
@property
|
||||
def commands(self):
|
||||
return self.manager.commands_
|
||||
return self.manager.commands
|
||||
|
||||
|
||||
class BaseCommand(object):
|
||||
|
||||
@@ -2,29 +2,23 @@
|
||||
Create command for Pecan
|
||||
"""
|
||||
from pecan.commands import BaseCommand
|
||||
from pecan.templates import DEFAULT_TEMPLATE
|
||||
|
||||
import copy
|
||||
import sys
|
||||
from pecan.scaffolds import DEFAULT_SCAFFOLD, BaseScaffold
|
||||
|
||||
|
||||
class CreateCommand(BaseCommand):
|
||||
"""
|
||||
Creates the file layout for a new Pecan distribution.
|
||||
|
||||
For a template to show up when using this command, its name must begin
|
||||
with "pecan-". Although not required, it should also include the "Pecan"
|
||||
egg plugin for user convenience.
|
||||
Creates the file layout for a new Pecan scaffolded project.
|
||||
"""
|
||||
|
||||
arguments = ({
|
||||
'command': 'template_name',
|
||||
'help': 'a registered Pecan template',
|
||||
'nargs': '?',
|
||||
'default': DEFAULT_TEMPLATE
|
||||
'default': DEFAULT_SCAFFOLD
|
||||
},)
|
||||
|
||||
def run(self, args):
|
||||
super(CreateCommand, self).run(args)
|
||||
print "NOT IMPLEMENTED"
|
||||
print args.template_name
|
||||
BaseScaffold().copy_to(args.template_name)
|
||||
|
||||
40
pecan/compat.py
Normal file
40
pecan/compat.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import sys
|
||||
|
||||
# True if we are running on Python 3.
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
if PY3: # pragma: no cover
|
||||
text_type = str
|
||||
else:
|
||||
text_type = unicode
|
||||
|
||||
|
||||
def bytes_(s, encoding='latin-1', errors='strict'):
|
||||
""" If ``s`` is an instance of ``text_type``, return
|
||||
``s.encode(encoding, errors)``, otherwise return ``s``"""
|
||||
if isinstance(s, text_type): # pragma: no cover
|
||||
return s.encode(encoding, errors)
|
||||
return s
|
||||
|
||||
if PY3: # pragma: no cover
|
||||
def native_(s, encoding='latin-1', errors='strict'):
|
||||
""" If ``s`` is an instance of ``text_type``, return
|
||||
``s``, otherwise return ``str(s, encoding, errors)``"""
|
||||
if isinstance(s, text_type):
|
||||
return s
|
||||
return str(s, encoding, errors)
|
||||
else:
|
||||
def native_(s, encoding='latin-1', errors='strict'): # noqa
|
||||
""" If ``s`` is an instance of ``text_type``, return
|
||||
``s.encode(encoding, errors)``, otherwise return ``str(s)``"""
|
||||
if isinstance(s, text_type):
|
||||
return s.encode(encoding, errors)
|
||||
return str(s)
|
||||
|
||||
native_.__doc__ = """
|
||||
Python 3: If ``s`` is an instance of ``text_type``, return ``s``, otherwise
|
||||
return ``str(s, encoding, errors)``
|
||||
|
||||
Python 2: If ``s`` is an instance of ``text_type``, return
|
||||
``s.encode(encoding, errors)``, otherwise return ``str(s)``
|
||||
"""
|
||||
133
pecan/scaffolds/__init__.py
Normal file
133
pecan/scaffolds/__init__.py
Normal file
@@ -0,0 +1,133 @@
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import pkg_resources
|
||||
from pecan.compat import native_, bytes_
|
||||
|
||||
DEFAULT_SCAFFOLD = 'base'
|
||||
|
||||
|
||||
class PecanScaffold(object):
|
||||
|
||||
@property
|
||||
def template_dir(self):
|
||||
if isinstance(self._scaffold_dir, tuple):
|
||||
return self._scaffold_dir
|
||||
else:
|
||||
return os.path.join(self.module_dir, self._scaffold_dir)
|
||||
|
||||
@property
|
||||
def module_dir(self):
|
||||
mod = sys.modules[self.__class__.__module__]
|
||||
return os.path.dirname(mod.__file__)
|
||||
|
||||
@property
|
||||
def variables(self):
|
||||
return {}
|
||||
|
||||
def copy_to(self, dest, **kwargs):
|
||||
copy_dir(self.template_dir, dest, self.variables)
|
||||
|
||||
|
||||
class BaseScaffold(PecanScaffold):
|
||||
_scaffold_dir = 'base'
|
||||
|
||||
|
||||
def copy_dir(source, dest, variables, out_=sys.stdout):
|
||||
"""
|
||||
Copies the ``source`` directory to the ``dest`` directory.
|
||||
|
||||
``variables``: A dictionary of variables to use in any substitutions.
|
||||
|
||||
``out_``: File object to write to
|
||||
"""
|
||||
def out(msg):
|
||||
out_.write(msg)
|
||||
out_.write('\n')
|
||||
out_.flush()
|
||||
|
||||
use_pkg_resources = isinstance(source, tuple)
|
||||
|
||||
if use_pkg_resources:
|
||||
names = sorted(pkg_resources.resource_listdir(source[0], source[1]))
|
||||
else:
|
||||
names = sorted(os.listdir(source))
|
||||
if not os.path.exists(dest):
|
||||
out('Creating %s' % dest)
|
||||
makedirs(dest)
|
||||
else:
|
||||
out('Directory %s already exists' % dest)
|
||||
return
|
||||
|
||||
for name in names:
|
||||
|
||||
if use_pkg_resources:
|
||||
full = '/'.join([source[1], name])
|
||||
else:
|
||||
full = os.path.join(source, name)
|
||||
|
||||
dest_full = os.path.join(dest, substitute_filename(name, variables))
|
||||
|
||||
sub_file = False
|
||||
if dest_full.endswith('_tmpl'):
|
||||
dest_full = dest_full[:-5]
|
||||
sub_file = True
|
||||
|
||||
if use_pkg_resources and pkg_resources.resource_isdir(source[0], full):
|
||||
out('Recursing into %s' % os.path.basename(full))
|
||||
copy_dir((source[0], full), dest_full, variables, out_)
|
||||
continue
|
||||
elif not use_pkg_resources and os.path.isdir(full):
|
||||
out('Recursing into %s' % os.path.basename(full))
|
||||
copy_dir(full, dest_full, variables, out_)
|
||||
continue
|
||||
elif use_pkg_resources:
|
||||
content = pkg_resources.resource_string(source[0], full)
|
||||
else:
|
||||
f = open(full, 'rb')
|
||||
content = f.read()
|
||||
f.close()
|
||||
|
||||
if sub_file:
|
||||
content = render_template(content, variables)
|
||||
if content is None:
|
||||
continue # pragma: no cover
|
||||
|
||||
if use_pkg_resources:
|
||||
out('Copying %s to %s' % (full, dest_full))
|
||||
else:
|
||||
out('Copying %s to %s' % (
|
||||
os.path.basename(full),
|
||||
dest_full)
|
||||
)
|
||||
|
||||
|
||||
def makedirs(directory):
|
||||
parent = os.path.dirname(os.path.abspath(directory))
|
||||
if not os.path.exists(parent):
|
||||
makedirs(parent)
|
||||
os.mkdir(directory)
|
||||
|
||||
|
||||
def substitute_filename(fn, variables):
|
||||
for var, value in variables.items():
|
||||
fn = fn.replace('+%s+' % var, str(value))
|
||||
return fn
|
||||
|
||||
|
||||
def render_template(self, content, variables):
|
||||
""" Return a bytestring representing a templated file based on the
|
||||
input (content) and the variable names defined (vars)."""
|
||||
fsenc = sys.getfilesystemencoding()
|
||||
content = native_(content, fsenc)
|
||||
return bytes_(
|
||||
substitute_double_braces(content, variables), fsenc)
|
||||
|
||||
|
||||
def substitute_double_braces(content, values):
|
||||
double_brace_pattern = re.compile(r'{{(?P<braced>.*?)}}')
|
||||
|
||||
def double_bracerepl(match):
|
||||
value = match.group('braced').strip()
|
||||
return values[value]
|
||||
return double_brace_pattern.sub(double_bracerepl, content)
|
||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
@@ -1,9 +0,0 @@
|
||||
from paste.script.templates import Template
|
||||
|
||||
DEFAULT_TEMPLATE = 'base'
|
||||
|
||||
|
||||
class BaseTemplate(Template):
|
||||
summary = 'Template for creating a basic Pecan project'
|
||||
_template_dir = 'project'
|
||||
egg_plugins = ['Pecan']
|
||||
Reference in New Issue
Block a user