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__)
|
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):
|
class CommandManager(object):
|
||||||
""" Used to discover `pecan.command` entry points. """
|
""" Used to discover `pecan.command` entry points. """
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.commands_ = {}
|
self.commands = {}
|
||||||
self.load_commands()
|
self.load_commands()
|
||||||
|
|
||||||
def load_commands(self):
|
def load_commands(self):
|
||||||
@@ -28,11 +44,7 @@ class CommandManager(object):
|
|||||||
self.add({ep.name: cmd})
|
self.add({ep.name: cmd})
|
||||||
|
|
||||||
def add(self, cmd):
|
def add(self, cmd):
|
||||||
self.commands_.update(cmd)
|
self.commands.update(cmd)
|
||||||
|
|
||||||
@property
|
|
||||||
def commands(self):
|
|
||||||
return self.commands_
|
|
||||||
|
|
||||||
|
|
||||||
class CommandRunner(object):
|
class CommandRunner(object):
|
||||||
@@ -40,13 +52,13 @@ class CommandRunner(object):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.manager = CommandManager()
|
self.manager = CommandManager()
|
||||||
self.parser = argparse.ArgumentParser(
|
self.parser = HelpfulArgumentParser(
|
||||||
version='Pecan %s' % self.version,
|
version='Pecan %s' % self.version,
|
||||||
add_help=True
|
add_help=True
|
||||||
)
|
)
|
||||||
self.parse_commands()
|
self.parse_sub_commands()
|
||||||
|
|
||||||
def parse_commands(self):
|
def parse_sub_commands(self):
|
||||||
subparsers = self.parser.add_subparsers(
|
subparsers = self.parser.add_subparsers(
|
||||||
dest='command_name',
|
dest='command_name',
|
||||||
metavar='command'
|
metavar='command'
|
||||||
@@ -67,8 +79,7 @@ class CommandRunner(object):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def handle_command_line(cls):
|
def handle_command_line(cls):
|
||||||
runner = CommandRunner()
|
runner = CommandRunner()
|
||||||
exit_code = runner.run(sys.argv[1:])
|
runner.run(sys.argv[1:])
|
||||||
sys.exit(exit_code)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version(self):
|
def version(self):
|
||||||
@@ -83,7 +94,7 @@ class CommandRunner(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def commands(self):
|
def commands(self):
|
||||||
return self.manager.commands_
|
return self.manager.commands
|
||||||
|
|
||||||
|
|
||||||
class BaseCommand(object):
|
class BaseCommand(object):
|
||||||
|
|||||||
@@ -2,29 +2,23 @@
|
|||||||
Create command for Pecan
|
Create command for Pecan
|
||||||
"""
|
"""
|
||||||
from pecan.commands import BaseCommand
|
from pecan.commands import BaseCommand
|
||||||
from pecan.templates import DEFAULT_TEMPLATE
|
from pecan.scaffolds import DEFAULT_SCAFFOLD, BaseScaffold
|
||||||
|
|
||||||
import copy
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
class CreateCommand(BaseCommand):
|
class CreateCommand(BaseCommand):
|
||||||
"""
|
"""
|
||||||
Creates the file layout for a new Pecan distribution.
|
Creates the file layout for a new Pecan scaffolded project.
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
arguments = ({
|
arguments = ({
|
||||||
'command': 'template_name',
|
'command': 'template_name',
|
||||||
'help': 'a registered Pecan template',
|
'help': 'a registered Pecan template',
|
||||||
'nargs': '?',
|
'nargs': '?',
|
||||||
'default': DEFAULT_TEMPLATE
|
'default': DEFAULT_SCAFFOLD
|
||||||
},)
|
},)
|
||||||
|
|
||||||
def run(self, args):
|
def run(self, args):
|
||||||
super(CreateCommand, self).run(args)
|
super(CreateCommand, self).run(args)
|
||||||
print "NOT IMPLEMENTED"
|
print "NOT IMPLEMENTED"
|
||||||
print args.template_name
|
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