mirror of
https://github.com/datafolklabs/cement.git
synced 2026-02-06 11:56:51 +00:00
Minimalize Project Template
This commit is contained in:
parent
5ac3379e73
commit
a1b3d44333
@ -4,3 +4,5 @@ WORKDIR /app
|
||||
COPY . /app
|
||||
RUN python setup.py install \
|
||||
&& rm -rf /app
|
||||
WORKDIR /
|
||||
ENTRYPOINT ["/usr/local/bin/cement"]
|
||||
|
||||
@ -2,3 +2,4 @@ recursive-include *.py
|
||||
include setup.cfg
|
||||
include README.md CHANGELOG.md LICENSE.md CONTRIBUTORS.md
|
||||
include *.txt
|
||||
recursive-include cement/cli/templates/generate *
|
||||
|
||||
4
Makefile
4
Makefile
@ -1,6 +1,4 @@
|
||||
.PHONY: all dev test comply docs clean dist dist-upload
|
||||
|
||||
all: test comply comply-test api-docs clean
|
||||
.PHONY: dev test test-core comply-fix docs clean dist dist-upload
|
||||
|
||||
dev:
|
||||
docker-compose up -d
|
||||
|
||||
305
cement/cli/contrib/markupsafe/__init__.py
Normal file
305
cement/cli/contrib/markupsafe/__init__.py
Normal file
@ -0,0 +1,305 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
markupsafe
|
||||
~~~~~~~~~~
|
||||
|
||||
Implements a Markup string.
|
||||
|
||||
:copyright: (c) 2010 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import re
|
||||
import string
|
||||
from collections import Mapping
|
||||
from markupsafe._compat import text_type, string_types, int_types, \
|
||||
unichr, iteritems, PY2
|
||||
|
||||
__version__ = "1.0"
|
||||
|
||||
__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent']
|
||||
|
||||
|
||||
_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
|
||||
_entity_re = re.compile(r'&([^& ;]+);')
|
||||
|
||||
|
||||
class Markup(text_type):
|
||||
r"""Marks a string as being safe for inclusion in HTML/XML output without
|
||||
needing to be escaped. This implements the `__html__` interface a couple
|
||||
of frameworks and web applications use. :class:`Markup` is a direct
|
||||
subclass of `unicode` and provides all the methods of `unicode` just that
|
||||
it escapes arguments passed and always returns `Markup`.
|
||||
|
||||
The `escape` function returns markup objects so that double escaping can't
|
||||
happen.
|
||||
|
||||
The constructor of the :class:`Markup` class can be used for three
|
||||
different things: When passed an unicode object it's assumed to be safe,
|
||||
when passed an object with an HTML representation (has an `__html__`
|
||||
method) that representation is used, otherwise the object passed is
|
||||
converted into a unicode string and then assumed to be safe:
|
||||
|
||||
>>> Markup("Hello <em>World</em>!")
|
||||
Markup(u'Hello <em>World</em>!')
|
||||
>>> class Foo(object):
|
||||
... def __html__(self):
|
||||
... return '<a href="#">foo</a>'
|
||||
...
|
||||
>>> Markup(Foo())
|
||||
Markup(u'<a href="#">foo</a>')
|
||||
|
||||
If you want object passed being always treated as unsafe you can use the
|
||||
:meth:`escape` classmethod to create a :class:`Markup` object:
|
||||
|
||||
>>> Markup.escape("Hello <em>World</em>!")
|
||||
Markup(u'Hello <em>World</em>!')
|
||||
|
||||
Operations on a markup string are markup aware which means that all
|
||||
arguments are passed through the :func:`escape` function:
|
||||
|
||||
>>> em = Markup("<em>%s</em>")
|
||||
>>> em % "foo & bar"
|
||||
Markup(u'<em>foo & bar</em>')
|
||||
>>> strong = Markup("<strong>%(text)s</strong>")
|
||||
>>> strong % {'text': '<blink>hacker here</blink>'}
|
||||
Markup(u'<strong><blink>hacker here</blink></strong>')
|
||||
>>> Markup("<em>Hello</em> ") + "<foo>"
|
||||
Markup(u'<em>Hello</em> <foo>')
|
||||
"""
|
||||
__slots__ = ()
|
||||
|
||||
def __new__(cls, base=u'', encoding=None, errors='strict'):
|
||||
if hasattr(base, '__html__'):
|
||||
base = base.__html__()
|
||||
if encoding is None:
|
||||
return text_type.__new__(cls, base)
|
||||
return text_type.__new__(cls, base, encoding, errors)
|
||||
|
||||
def __html__(self):
|
||||
return self
|
||||
|
||||
def __add__(self, other):
|
||||
if isinstance(other, string_types) or hasattr(other, '__html__'):
|
||||
return self.__class__(super(Markup, self).__add__(self.escape(other)))
|
||||
return NotImplemented
|
||||
|
||||
def __radd__(self, other):
|
||||
if hasattr(other, '__html__') or isinstance(other, string_types):
|
||||
return self.escape(other).__add__(self)
|
||||
return NotImplemented
|
||||
|
||||
def __mul__(self, num):
|
||||
if isinstance(num, int_types):
|
||||
return self.__class__(text_type.__mul__(self, num))
|
||||
return NotImplemented
|
||||
__rmul__ = __mul__
|
||||
|
||||
def __mod__(self, arg):
|
||||
if isinstance(arg, tuple):
|
||||
arg = tuple(_MarkupEscapeHelper(x, self.escape) for x in arg)
|
||||
else:
|
||||
arg = _MarkupEscapeHelper(arg, self.escape)
|
||||
return self.__class__(text_type.__mod__(self, arg))
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%s)' % (
|
||||
self.__class__.__name__,
|
||||
text_type.__repr__(self)
|
||||
)
|
||||
|
||||
def join(self, seq):
|
||||
return self.__class__(text_type.join(self, map(self.escape, seq)))
|
||||
join.__doc__ = text_type.join.__doc__
|
||||
|
||||
def split(self, *args, **kwargs):
|
||||
return list(map(self.__class__, text_type.split(self, *args, **kwargs)))
|
||||
split.__doc__ = text_type.split.__doc__
|
||||
|
||||
def rsplit(self, *args, **kwargs):
|
||||
return list(map(self.__class__, text_type.rsplit(self, *args, **kwargs)))
|
||||
rsplit.__doc__ = text_type.rsplit.__doc__
|
||||
|
||||
def splitlines(self, *args, **kwargs):
|
||||
return list(map(self.__class__, text_type.splitlines(
|
||||
self, *args, **kwargs)))
|
||||
splitlines.__doc__ = text_type.splitlines.__doc__
|
||||
|
||||
def unescape(self):
|
||||
r"""Unescape markup again into an text_type string. This also resolves
|
||||
known HTML4 and XHTML entities:
|
||||
|
||||
>>> Markup("Main » <em>About</em>").unescape()
|
||||
u'Main \xbb <em>About</em>'
|
||||
"""
|
||||
from markupsafe._constants import HTML_ENTITIES
|
||||
def handle_match(m):
|
||||
name = m.group(1)
|
||||
if name in HTML_ENTITIES:
|
||||
return unichr(HTML_ENTITIES[name])
|
||||
try:
|
||||
if name[:2] in ('#x', '#X'):
|
||||
return unichr(int(name[2:], 16))
|
||||
elif name.startswith('#'):
|
||||
return unichr(int(name[1:]))
|
||||
except ValueError:
|
||||
pass
|
||||
# Don't modify unexpected input.
|
||||
return m.group()
|
||||
return _entity_re.sub(handle_match, text_type(self))
|
||||
|
||||
def striptags(self):
|
||||
r"""Unescape markup into an text_type string and strip all tags. This
|
||||
also resolves known HTML4 and XHTML entities. Whitespace is
|
||||
normalized to one:
|
||||
|
||||
>>> Markup("Main » <em>About</em>").striptags()
|
||||
u'Main \xbb About'
|
||||
"""
|
||||
stripped = u' '.join(_striptags_re.sub('', self).split())
|
||||
return Markup(stripped).unescape()
|
||||
|
||||
@classmethod
|
||||
def escape(cls, s):
|
||||
"""Escape the string. Works like :func:`escape` with the difference
|
||||
that for subclasses of :class:`Markup` this function would return the
|
||||
correct subclass.
|
||||
"""
|
||||
rv = escape(s)
|
||||
if rv.__class__ is not cls:
|
||||
return cls(rv)
|
||||
return rv
|
||||
|
||||
def make_simple_escaping_wrapper(name):
|
||||
orig = getattr(text_type, name)
|
||||
def func(self, *args, **kwargs):
|
||||
args = _escape_argspec(list(args), enumerate(args), self.escape)
|
||||
_escape_argspec(kwargs, iteritems(kwargs), self.escape)
|
||||
return self.__class__(orig(self, *args, **kwargs))
|
||||
func.__name__ = orig.__name__
|
||||
func.__doc__ = orig.__doc__
|
||||
return func
|
||||
|
||||
for method in '__getitem__', 'capitalize', \
|
||||
'title', 'lower', 'upper', 'replace', 'ljust', \
|
||||
'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
|
||||
'translate', 'expandtabs', 'swapcase', 'zfill':
|
||||
locals()[method] = make_simple_escaping_wrapper(method)
|
||||
|
||||
# new in python 2.5
|
||||
if hasattr(text_type, 'partition'):
|
||||
def partition(self, sep):
|
||||
return tuple(map(self.__class__,
|
||||
text_type.partition(self, self.escape(sep))))
|
||||
def rpartition(self, sep):
|
||||
return tuple(map(self.__class__,
|
||||
text_type.rpartition(self, self.escape(sep))))
|
||||
|
||||
# new in python 2.6
|
||||
if hasattr(text_type, 'format'):
|
||||
def format(*args, **kwargs):
|
||||
self, args = args[0], args[1:]
|
||||
formatter = EscapeFormatter(self.escape)
|
||||
kwargs = _MagicFormatMapping(args, kwargs)
|
||||
return self.__class__(formatter.vformat(self, args, kwargs))
|
||||
|
||||
def __html_format__(self, format_spec):
|
||||
if format_spec:
|
||||
raise ValueError('Unsupported format specification '
|
||||
'for Markup.')
|
||||
return self
|
||||
|
||||
# not in python 3
|
||||
if hasattr(text_type, '__getslice__'):
|
||||
__getslice__ = make_simple_escaping_wrapper('__getslice__')
|
||||
|
||||
del method, make_simple_escaping_wrapper
|
||||
|
||||
|
||||
class _MagicFormatMapping(Mapping):
|
||||
"""This class implements a dummy wrapper to fix a bug in the Python
|
||||
standard library for string formatting.
|
||||
|
||||
See http://bugs.python.org/issue13598 for information about why
|
||||
this is necessary.
|
||||
"""
|
||||
|
||||
def __init__(self, args, kwargs):
|
||||
self._args = args
|
||||
self._kwargs = kwargs
|
||||
self._last_index = 0
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == '':
|
||||
idx = self._last_index
|
||||
self._last_index += 1
|
||||
try:
|
||||
return self._args[idx]
|
||||
except LookupError:
|
||||
pass
|
||||
key = str(idx)
|
||||
return self._kwargs[key]
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._kwargs)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._kwargs)
|
||||
|
||||
|
||||
if hasattr(text_type, 'format'):
|
||||
class EscapeFormatter(string.Formatter):
|
||||
|
||||
def __init__(self, escape):
|
||||
self.escape = escape
|
||||
|
||||
def format_field(self, value, format_spec):
|
||||
if hasattr(value, '__html_format__'):
|
||||
rv = value.__html_format__(format_spec)
|
||||
elif hasattr(value, '__html__'):
|
||||
if format_spec:
|
||||
raise ValueError('No format specification allowed '
|
||||
'when formatting an object with '
|
||||
'its __html__ method.')
|
||||
rv = value.__html__()
|
||||
else:
|
||||
# We need to make sure the format spec is unicode here as
|
||||
# otherwise the wrong callback methods are invoked. For
|
||||
# instance a byte string there would invoke __str__ and
|
||||
# not __unicode__.
|
||||
rv = string.Formatter.format_field(
|
||||
self, value, text_type(format_spec))
|
||||
return text_type(self.escape(rv))
|
||||
|
||||
|
||||
def _escape_argspec(obj, iterable, escape):
|
||||
"""Helper for various string-wrapped functions."""
|
||||
for key, value in iterable:
|
||||
if hasattr(value, '__html__') or isinstance(value, string_types):
|
||||
obj[key] = escape(value)
|
||||
return obj
|
||||
|
||||
|
||||
class _MarkupEscapeHelper(object):
|
||||
"""Helper for Markup.__mod__"""
|
||||
|
||||
def __init__(self, obj, escape):
|
||||
self.obj = obj
|
||||
self.escape = escape
|
||||
|
||||
__getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x], s.escape)
|
||||
__unicode__ = __str__ = lambda s: text_type(s.escape(s.obj))
|
||||
__repr__ = lambda s: str(s.escape(repr(s.obj)))
|
||||
__int__ = lambda s: int(s.obj)
|
||||
__float__ = lambda s: float(s.obj)
|
||||
|
||||
|
||||
# we have to import it down here as the speedups and native
|
||||
# modules imports the markup type which is define above.
|
||||
try:
|
||||
from markupsafe._speedups import escape, escape_silent, soft_unicode
|
||||
except ImportError:
|
||||
from markupsafe._native import escape, escape_silent, soft_unicode
|
||||
|
||||
if not PY2:
|
||||
soft_str = soft_unicode
|
||||
__all__.append('soft_str')
|
||||
26
cement/cli/contrib/markupsafe/_compat.py
Normal file
26
cement/cli/contrib/markupsafe/_compat.py
Normal file
@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
markupsafe._compat
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Compatibility module for different Python versions.
|
||||
|
||||
:copyright: (c) 2013 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import sys
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
|
||||
if not PY2:
|
||||
text_type = str
|
||||
string_types = (str,)
|
||||
unichr = chr
|
||||
int_types = (int,)
|
||||
iteritems = lambda x: iter(x.items())
|
||||
else:
|
||||
text_type = unicode
|
||||
string_types = (str, unicode)
|
||||
unichr = unichr
|
||||
int_types = (int, long)
|
||||
iteritems = lambda x: x.iteritems()
|
||||
267
cement/cli/contrib/markupsafe/_constants.py
Normal file
267
cement/cli/contrib/markupsafe/_constants.py
Normal file
@ -0,0 +1,267 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
markupsafe._constants
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Highlevel implementation of the Markup string.
|
||||
|
||||
:copyright: (c) 2010 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
|
||||
HTML_ENTITIES = {
|
||||
'AElig': 198,
|
||||
'Aacute': 193,
|
||||
'Acirc': 194,
|
||||
'Agrave': 192,
|
||||
'Alpha': 913,
|
||||
'Aring': 197,
|
||||
'Atilde': 195,
|
||||
'Auml': 196,
|
||||
'Beta': 914,
|
||||
'Ccedil': 199,
|
||||
'Chi': 935,
|
||||
'Dagger': 8225,
|
||||
'Delta': 916,
|
||||
'ETH': 208,
|
||||
'Eacute': 201,
|
||||
'Ecirc': 202,
|
||||
'Egrave': 200,
|
||||
'Epsilon': 917,
|
||||
'Eta': 919,
|
||||
'Euml': 203,
|
||||
'Gamma': 915,
|
||||
'Iacute': 205,
|
||||
'Icirc': 206,
|
||||
'Igrave': 204,
|
||||
'Iota': 921,
|
||||
'Iuml': 207,
|
||||
'Kappa': 922,
|
||||
'Lambda': 923,
|
||||
'Mu': 924,
|
||||
'Ntilde': 209,
|
||||
'Nu': 925,
|
||||
'OElig': 338,
|
||||
'Oacute': 211,
|
||||
'Ocirc': 212,
|
||||
'Ograve': 210,
|
||||
'Omega': 937,
|
||||
'Omicron': 927,
|
||||
'Oslash': 216,
|
||||
'Otilde': 213,
|
||||
'Ouml': 214,
|
||||
'Phi': 934,
|
||||
'Pi': 928,
|
||||
'Prime': 8243,
|
||||
'Psi': 936,
|
||||
'Rho': 929,
|
||||
'Scaron': 352,
|
||||
'Sigma': 931,
|
||||
'THORN': 222,
|
||||
'Tau': 932,
|
||||
'Theta': 920,
|
||||
'Uacute': 218,
|
||||
'Ucirc': 219,
|
||||
'Ugrave': 217,
|
||||
'Upsilon': 933,
|
||||
'Uuml': 220,
|
||||
'Xi': 926,
|
||||
'Yacute': 221,
|
||||
'Yuml': 376,
|
||||
'Zeta': 918,
|
||||
'aacute': 225,
|
||||
'acirc': 226,
|
||||
'acute': 180,
|
||||
'aelig': 230,
|
||||
'agrave': 224,
|
||||
'alefsym': 8501,
|
||||
'alpha': 945,
|
||||
'amp': 38,
|
||||
'and': 8743,
|
||||
'ang': 8736,
|
||||
'apos': 39,
|
||||
'aring': 229,
|
||||
'asymp': 8776,
|
||||
'atilde': 227,
|
||||
'auml': 228,
|
||||
'bdquo': 8222,
|
||||
'beta': 946,
|
||||
'brvbar': 166,
|
||||
'bull': 8226,
|
||||
'cap': 8745,
|
||||
'ccedil': 231,
|
||||
'cedil': 184,
|
||||
'cent': 162,
|
||||
'chi': 967,
|
||||
'circ': 710,
|
||||
'clubs': 9827,
|
||||
'cong': 8773,
|
||||
'copy': 169,
|
||||
'crarr': 8629,
|
||||
'cup': 8746,
|
||||
'curren': 164,
|
||||
'dArr': 8659,
|
||||
'dagger': 8224,
|
||||
'darr': 8595,
|
||||
'deg': 176,
|
||||
'delta': 948,
|
||||
'diams': 9830,
|
||||
'divide': 247,
|
||||
'eacute': 233,
|
||||
'ecirc': 234,
|
||||
'egrave': 232,
|
||||
'empty': 8709,
|
||||
'emsp': 8195,
|
||||
'ensp': 8194,
|
||||
'epsilon': 949,
|
||||
'equiv': 8801,
|
||||
'eta': 951,
|
||||
'eth': 240,
|
||||
'euml': 235,
|
||||
'euro': 8364,
|
||||
'exist': 8707,
|
||||
'fnof': 402,
|
||||
'forall': 8704,
|
||||
'frac12': 189,
|
||||
'frac14': 188,
|
||||
'frac34': 190,
|
||||
'frasl': 8260,
|
||||
'gamma': 947,
|
||||
'ge': 8805,
|
||||
'gt': 62,
|
||||
'hArr': 8660,
|
||||
'harr': 8596,
|
||||
'hearts': 9829,
|
||||
'hellip': 8230,
|
||||
'iacute': 237,
|
||||
'icirc': 238,
|
||||
'iexcl': 161,
|
||||
'igrave': 236,
|
||||
'image': 8465,
|
||||
'infin': 8734,
|
||||
'int': 8747,
|
||||
'iota': 953,
|
||||
'iquest': 191,
|
||||
'isin': 8712,
|
||||
'iuml': 239,
|
||||
'kappa': 954,
|
||||
'lArr': 8656,
|
||||
'lambda': 955,
|
||||
'lang': 9001,
|
||||
'laquo': 171,
|
||||
'larr': 8592,
|
||||
'lceil': 8968,
|
||||
'ldquo': 8220,
|
||||
'le': 8804,
|
||||
'lfloor': 8970,
|
||||
'lowast': 8727,
|
||||
'loz': 9674,
|
||||
'lrm': 8206,
|
||||
'lsaquo': 8249,
|
||||
'lsquo': 8216,
|
||||
'lt': 60,
|
||||
'macr': 175,
|
||||
'mdash': 8212,
|
||||
'micro': 181,
|
||||
'middot': 183,
|
||||
'minus': 8722,
|
||||
'mu': 956,
|
||||
'nabla': 8711,
|
||||
'nbsp': 160,
|
||||
'ndash': 8211,
|
||||
'ne': 8800,
|
||||
'ni': 8715,
|
||||
'not': 172,
|
||||
'notin': 8713,
|
||||
'nsub': 8836,
|
||||
'ntilde': 241,
|
||||
'nu': 957,
|
||||
'oacute': 243,
|
||||
'ocirc': 244,
|
||||
'oelig': 339,
|
||||
'ograve': 242,
|
||||
'oline': 8254,
|
||||
'omega': 969,
|
||||
'omicron': 959,
|
||||
'oplus': 8853,
|
||||
'or': 8744,
|
||||
'ordf': 170,
|
||||
'ordm': 186,
|
||||
'oslash': 248,
|
||||
'otilde': 245,
|
||||
'otimes': 8855,
|
||||
'ouml': 246,
|
||||
'para': 182,
|
||||
'part': 8706,
|
||||
'permil': 8240,
|
||||
'perp': 8869,
|
||||
'phi': 966,
|
||||
'pi': 960,
|
||||
'piv': 982,
|
||||
'plusmn': 177,
|
||||
'pound': 163,
|
||||
'prime': 8242,
|
||||
'prod': 8719,
|
||||
'prop': 8733,
|
||||
'psi': 968,
|
||||
'quot': 34,
|
||||
'rArr': 8658,
|
||||
'radic': 8730,
|
||||
'rang': 9002,
|
||||
'raquo': 187,
|
||||
'rarr': 8594,
|
||||
'rceil': 8969,
|
||||
'rdquo': 8221,
|
||||
'real': 8476,
|
||||
'reg': 174,
|
||||
'rfloor': 8971,
|
||||
'rho': 961,
|
||||
'rlm': 8207,
|
||||
'rsaquo': 8250,
|
||||
'rsquo': 8217,
|
||||
'sbquo': 8218,
|
||||
'scaron': 353,
|
||||
'sdot': 8901,
|
||||
'sect': 167,
|
||||
'shy': 173,
|
||||
'sigma': 963,
|
||||
'sigmaf': 962,
|
||||
'sim': 8764,
|
||||
'spades': 9824,
|
||||
'sub': 8834,
|
||||
'sube': 8838,
|
||||
'sum': 8721,
|
||||
'sup': 8835,
|
||||
'sup1': 185,
|
||||
'sup2': 178,
|
||||
'sup3': 179,
|
||||
'supe': 8839,
|
||||
'szlig': 223,
|
||||
'tau': 964,
|
||||
'there4': 8756,
|
||||
'theta': 952,
|
||||
'thetasym': 977,
|
||||
'thinsp': 8201,
|
||||
'thorn': 254,
|
||||
'tilde': 732,
|
||||
'times': 215,
|
||||
'trade': 8482,
|
||||
'uArr': 8657,
|
||||
'uacute': 250,
|
||||
'uarr': 8593,
|
||||
'ucirc': 251,
|
||||
'ugrave': 249,
|
||||
'uml': 168,
|
||||
'upsih': 978,
|
||||
'upsilon': 965,
|
||||
'uuml': 252,
|
||||
'weierp': 8472,
|
||||
'xi': 958,
|
||||
'yacute': 253,
|
||||
'yen': 165,
|
||||
'yuml': 255,
|
||||
'zeta': 950,
|
||||
'zwj': 8205,
|
||||
'zwnj': 8204
|
||||
}
|
||||
46
cement/cli/contrib/markupsafe/_native.py
Normal file
46
cement/cli/contrib/markupsafe/_native.py
Normal file
@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
markupsafe._native
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Native Python implementation the C module is not compiled.
|
||||
|
||||
:copyright: (c) 2010 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from markupsafe import Markup
|
||||
from markupsafe._compat import text_type
|
||||
|
||||
|
||||
def escape(s):
|
||||
"""Convert the characters &, <, >, ' and " in string s to HTML-safe
|
||||
sequences. Use this if you need to display text that might contain
|
||||
such characters in HTML. Marks return value as markup string.
|
||||
"""
|
||||
if hasattr(s, '__html__'):
|
||||
return s.__html__()
|
||||
return Markup(text_type(s)
|
||||
.replace('&', '&')
|
||||
.replace('>', '>')
|
||||
.replace('<', '<')
|
||||
.replace("'", ''')
|
||||
.replace('"', '"')
|
||||
)
|
||||
|
||||
|
||||
def escape_silent(s):
|
||||
"""Like :func:`escape` but converts `None` into an empty
|
||||
markup string.
|
||||
"""
|
||||
if s is None:
|
||||
return Markup()
|
||||
return escape(s)
|
||||
|
||||
|
||||
def soft_unicode(s):
|
||||
"""Make a string unicode if it isn't already. That way a markup
|
||||
string is not converted back to unicode.
|
||||
"""
|
||||
if not isinstance(s, text_type):
|
||||
s = text_type(s)
|
||||
return s
|
||||
239
cement/cli/contrib/markupsafe/_speedups.c
Normal file
239
cement/cli/contrib/markupsafe/_speedups.c
Normal file
@ -0,0 +1,239 @@
|
||||
/**
|
||||
* markupsafe._speedups
|
||||
* ~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* This module implements functions for automatic escaping in C for better
|
||||
* performance.
|
||||
*
|
||||
* :copyright: (c) 2010 by Armin Ronacher.
|
||||
* :license: BSD.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#define ESCAPED_CHARS_TABLE_SIZE 63
|
||||
#define UNICHR(x) (PyUnicode_AS_UNICODE((PyUnicodeObject*)PyUnicode_DecodeASCII(x, strlen(x), NULL)));
|
||||
|
||||
#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
|
||||
typedef int Py_ssize_t;
|
||||
#define PY_SSIZE_T_MAX INT_MAX
|
||||
#define PY_SSIZE_T_MIN INT_MIN
|
||||
#endif
|
||||
|
||||
|
||||
static PyObject* markup;
|
||||
static Py_ssize_t escaped_chars_delta_len[ESCAPED_CHARS_TABLE_SIZE];
|
||||
static Py_UNICODE *escaped_chars_repl[ESCAPED_CHARS_TABLE_SIZE];
|
||||
|
||||
static int
|
||||
init_constants(void)
|
||||
{
|
||||
PyObject *module;
|
||||
/* mapping of characters to replace */
|
||||
escaped_chars_repl['"'] = UNICHR(""");
|
||||
escaped_chars_repl['\''] = UNICHR("'");
|
||||
escaped_chars_repl['&'] = UNICHR("&");
|
||||
escaped_chars_repl['<'] = UNICHR("<");
|
||||
escaped_chars_repl['>'] = UNICHR(">");
|
||||
|
||||
/* lengths of those characters when replaced - 1 */
|
||||
memset(escaped_chars_delta_len, 0, sizeof (escaped_chars_delta_len));
|
||||
escaped_chars_delta_len['"'] = escaped_chars_delta_len['\''] = \
|
||||
escaped_chars_delta_len['&'] = 4;
|
||||
escaped_chars_delta_len['<'] = escaped_chars_delta_len['>'] = 3;
|
||||
|
||||
/* import markup type so that we can mark the return value */
|
||||
module = PyImport_ImportModule("markupsafe");
|
||||
if (!module)
|
||||
return 0;
|
||||
markup = PyObject_GetAttrString(module, "Markup");
|
||||
Py_DECREF(module);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
escape_unicode(PyUnicodeObject *in)
|
||||
{
|
||||
PyUnicodeObject *out;
|
||||
Py_UNICODE *inp = PyUnicode_AS_UNICODE(in);
|
||||
const Py_UNICODE *inp_end = PyUnicode_AS_UNICODE(in) + PyUnicode_GET_SIZE(in);
|
||||
Py_UNICODE *next_escp;
|
||||
Py_UNICODE *outp;
|
||||
Py_ssize_t delta=0, erepl=0, delta_len=0;
|
||||
|
||||
/* First we need to figure out how long the escaped string will be */
|
||||
while (*(inp) || inp < inp_end) {
|
||||
if (*inp < ESCAPED_CHARS_TABLE_SIZE) {
|
||||
delta += escaped_chars_delta_len[*inp];
|
||||
erepl += !!escaped_chars_delta_len[*inp];
|
||||
}
|
||||
++inp;
|
||||
}
|
||||
|
||||
/* Do we need to escape anything at all? */
|
||||
if (!erepl) {
|
||||
Py_INCREF(in);
|
||||
return (PyObject*)in;
|
||||
}
|
||||
|
||||
out = (PyUnicodeObject*)PyUnicode_FromUnicode(NULL, PyUnicode_GET_SIZE(in) + delta);
|
||||
if (!out)
|
||||
return NULL;
|
||||
|
||||
outp = PyUnicode_AS_UNICODE(out);
|
||||
inp = PyUnicode_AS_UNICODE(in);
|
||||
while (erepl-- > 0) {
|
||||
/* look for the next substitution */
|
||||
next_escp = inp;
|
||||
while (next_escp < inp_end) {
|
||||
if (*next_escp < ESCAPED_CHARS_TABLE_SIZE &&
|
||||
(delta_len = escaped_chars_delta_len[*next_escp])) {
|
||||
++delta_len;
|
||||
break;
|
||||
}
|
||||
++next_escp;
|
||||
}
|
||||
|
||||
if (next_escp > inp) {
|
||||
/* copy unescaped chars between inp and next_escp */
|
||||
Py_UNICODE_COPY(outp, inp, next_escp-inp);
|
||||
outp += next_escp - inp;
|
||||
}
|
||||
|
||||
/* escape 'next_escp' */
|
||||
Py_UNICODE_COPY(outp, escaped_chars_repl[*next_escp], delta_len);
|
||||
outp += delta_len;
|
||||
|
||||
inp = next_escp + 1;
|
||||
}
|
||||
if (inp < inp_end)
|
||||
Py_UNICODE_COPY(outp, inp, PyUnicode_GET_SIZE(in) - (inp - PyUnicode_AS_UNICODE(in)));
|
||||
|
||||
return (PyObject*)out;
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
escape(PyObject *self, PyObject *text)
|
||||
{
|
||||
PyObject *s = NULL, *rv = NULL, *html;
|
||||
|
||||
/* we don't have to escape integers, bools or floats */
|
||||
if (PyLong_CheckExact(text) ||
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
PyInt_CheckExact(text) ||
|
||||
#endif
|
||||
PyFloat_CheckExact(text) || PyBool_Check(text) ||
|
||||
text == Py_None)
|
||||
return PyObject_CallFunctionObjArgs(markup, text, NULL);
|
||||
|
||||
/* if the object has an __html__ method that performs the escaping */
|
||||
html = PyObject_GetAttrString(text, "__html__");
|
||||
if (html) {
|
||||
rv = PyObject_CallObject(html, NULL);
|
||||
Py_DECREF(html);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* otherwise make the object unicode if it isn't, then escape */
|
||||
PyErr_Clear();
|
||||
if (!PyUnicode_Check(text)) {
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
PyObject *unicode = PyObject_Unicode(text);
|
||||
#else
|
||||
PyObject *unicode = PyObject_Str(text);
|
||||
#endif
|
||||
if (!unicode)
|
||||
return NULL;
|
||||
s = escape_unicode((PyUnicodeObject*)unicode);
|
||||
Py_DECREF(unicode);
|
||||
}
|
||||
else
|
||||
s = escape_unicode((PyUnicodeObject*)text);
|
||||
|
||||
/* convert the unicode string into a markup object. */
|
||||
rv = PyObject_CallFunctionObjArgs(markup, (PyObject*)s, NULL);
|
||||
Py_DECREF(s);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
escape_silent(PyObject *self, PyObject *text)
|
||||
{
|
||||
if (text != Py_None)
|
||||
return escape(self, text);
|
||||
return PyObject_CallFunctionObjArgs(markup, NULL);
|
||||
}
|
||||
|
||||
|
||||
static PyObject*
|
||||
soft_unicode(PyObject *self, PyObject *s)
|
||||
{
|
||||
if (!PyUnicode_Check(s))
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
return PyObject_Unicode(s);
|
||||
#else
|
||||
return PyObject_Str(s);
|
||||
#endif
|
||||
Py_INCREF(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
{"escape", (PyCFunction)escape, METH_O,
|
||||
"escape(s) -> markup\n\n"
|
||||
"Convert the characters &, <, >, ', and \" in string s to HTML-safe\n"
|
||||
"sequences. Use this if you need to display text that might contain\n"
|
||||
"such characters in HTML. Marks return value as markup string."},
|
||||
{"escape_silent", (PyCFunction)escape_silent, METH_O,
|
||||
"escape_silent(s) -> markup\n\n"
|
||||
"Like escape but converts None to an empty string."},
|
||||
{"soft_unicode", (PyCFunction)soft_unicode, METH_O,
|
||||
"soft_unicode(object) -> string\n\n"
|
||||
"Make a string unicode if it isn't already. That way a markup\n"
|
||||
"string is not converted back to unicode."},
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
|
||||
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
|
||||
#define PyMODINIT_FUNC void
|
||||
#endif
|
||||
PyMODINIT_FUNC
|
||||
init_speedups(void)
|
||||
{
|
||||
if (!init_constants())
|
||||
return;
|
||||
|
||||
Py_InitModule3("markupsafe._speedups", module_methods, "");
|
||||
}
|
||||
|
||||
#else /* Python 3.x module initialization */
|
||||
|
||||
static struct PyModuleDef module_definition = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"markupsafe._speedups",
|
||||
NULL,
|
||||
-1,
|
||||
module_methods,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit__speedups(void)
|
||||
{
|
||||
if (!init_constants())
|
||||
return NULL;
|
||||
|
||||
return PyModule_Create(&module_definition);
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -1,18 +1,8 @@
|
||||
|
||||
import sys
|
||||
import platform
|
||||
from cement import Controller
|
||||
from cement.utils.version import get_version
|
||||
from cement.utils.version import get_version_banner
|
||||
|
||||
VERSION = get_version()
|
||||
PYTHON_VERSION = '.'.join([str(x) for x in sys.version_info[0:3]])
|
||||
PLATFORM = platform.platform()
|
||||
|
||||
BANNER = """
|
||||
Cement Framework %s
|
||||
Python %s
|
||||
Platform %s
|
||||
""" % (VERSION, PYTHON_VERSION, PLATFORM)
|
||||
BANNER = get_version_banner()
|
||||
|
||||
|
||||
class Base(Controller):
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
---
|
||||
|
||||
exclude:
|
||||
- '^(.*)[\/\\\\]extension[\/\\\\]{{ label }}[\/\\\\]templates[\/\\\\](.*)$'
|
||||
|
||||
ignore:
|
||||
- '^(.*)pyc(.*)$'
|
||||
- '^(.*)pyo(.*)$'
|
||||
- '^(.*)__pycache__(.*)$'
|
||||
|
||||
variables:
|
||||
- name: label
|
||||
prompt: "Extension Label"
|
||||
|
||||
@ -3,6 +3,11 @@
|
||||
exclude:
|
||||
- '^(.*)[\/\\\\]plugin[\/\\\\]{{ label }}[\/\\\\]templates[\/\\\\](.*)$'
|
||||
|
||||
ignore:
|
||||
- '^(.*)pyc(.*)$'
|
||||
- '^(.*)pyo(.*)$'
|
||||
- '^(.*)__pycache__(.*)$'
|
||||
|
||||
variables:
|
||||
- name: label
|
||||
prompt: "Plugin Label"
|
||||
|
||||
39
cement/cli/templates/generate/project-loaded/.generate.yml
Normal file
39
cement/cli/templates/generate/project-loaded/.generate.yml
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
|
||||
exclude:
|
||||
- '^(.*)[\/\\\\]project[\/\\\\]{{ label }}[\/\\\\]templates[\/\\\\](.*)$'
|
||||
|
||||
variables:
|
||||
- name: label
|
||||
prompt: "App Label"
|
||||
case: "lower"
|
||||
default: "myapp"
|
||||
|
||||
- name: name
|
||||
prompt: "App Name"
|
||||
default: "My Application"
|
||||
|
||||
- name: class_name
|
||||
prompt: "App Class Name"
|
||||
validate: "^[a-zA-Z0-9]+$"
|
||||
default: "MyApp"
|
||||
|
||||
- name: description
|
||||
prompt: "App Description"
|
||||
default: "MyApp Does Amazing Things!"
|
||||
|
||||
- name: creator
|
||||
prompt: "Creator Name"
|
||||
default: "John Doe"
|
||||
|
||||
- name: creator_email
|
||||
prompt: "Creator Email"
|
||||
default: "john.doe@example.com"
|
||||
|
||||
- name: url
|
||||
prompt: "Project URL"
|
||||
default: "https://github.com/johndoe/myapp/"
|
||||
|
||||
- name: license
|
||||
prompt: "License"
|
||||
default: "unlicensed"
|
||||
105
cement/cli/templates/generate/project-loaded/.gitignore
vendored
Normal file
105
cement/cli/templates/generate/project-loaded/.gitignore
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
coverage-report/
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
15
cement/cli/templates/generate/project-loaded/Dockerfile
Normal file
15
cement/cli/templates/generate/project-loaded/Dockerfile
Normal file
@ -0,0 +1,15 @@
|
||||
FROM python:3.6-alpine
|
||||
MAINTAINER {{ creator }} <{{ creator_email }}>
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
RUN apk update \
|
||||
&& apk add git \
|
||||
&& pip install --no-cache-dir -r requirements.txt \
|
||||
&& rm -f /usr/local/lib/python3.6/site-packages/cement.egg-link \
|
||||
&& cd src/cement \
|
||||
&& python setup.py install \
|
||||
&& cd /app \
|
||||
&& python setup.py install \
|
||||
&& rm -rf /app
|
||||
WORKDIR /
|
||||
ENTRYPOINT ["{{ label }}"]
|
||||
1
cement/cli/templates/generate/project-loaded/LICENSE.md
Normal file
1
cement/cli/templates/generate/project-loaded/LICENSE.md
Normal file
@ -0,0 +1 @@
|
||||
|
||||
4
cement/cli/templates/generate/project-loaded/MANIFEST.in
Normal file
4
cement/cli/templates/generate/project-loaded/MANIFEST.in
Normal file
@ -0,0 +1,4 @@
|
||||
recursive-include *.py
|
||||
include setup.cfg
|
||||
include README.md CHANGELOG.md LICENSE.md
|
||||
include *.txt
|
||||
85
cement/cli/templates/generate/project-loaded/README.md
Normal file
85
cement/cli/templates/generate/project-loaded/README.md
Normal file
@ -0,0 +1,85 @@
|
||||
# {{ description }}
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ pip install -r requirements.txt
|
||||
|
||||
$ pip install setup.py
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Environment Setup
|
||||
|
||||
This project includes a basic Docker Compose configuration that will setup a local development environment with all dependencies, and services required for development and testing.
|
||||
|
||||
```
|
||||
$ make dev
|
||||
[...]
|
||||
|> {{ label }} <| app #
|
||||
```
|
||||
|
||||
The `{{ label }}` command line application is installed in `develop` mode, therefore all changes will be live and can be tested immediately as code is modified.
|
||||
|
||||
```
|
||||
|> {{ label }} <| app # {{ label }} --help
|
||||
```
|
||||
|
||||
### Running Tests
|
||||
|
||||
Execute tests from within the development environment:
|
||||
|
||||
```
|
||||
|> {{ label }} <| app # make test
|
||||
```
|
||||
|
||||
|
||||
### Releasing to PyPi
|
||||
|
||||
Before releasing to PyPi, you must configure your login credentials:
|
||||
|
||||
**~/.pypirc**:
|
||||
|
||||
```
|
||||
[pypi]
|
||||
username = YOUR_USERNAME
|
||||
password = YOUR_PASSWORD
|
||||
```
|
||||
|
||||
Then use the included helper function via the `Makefile`:
|
||||
|
||||
```
|
||||
$ make dist
|
||||
|
||||
$ make dist-upload
|
||||
```
|
||||
|
||||
## Deployments
|
||||
|
||||
### Docker
|
||||
|
||||
Included is a basic `Dockerfile` for building and distributing `{{ name }}`,
|
||||
and can be built with the included `make` helper:
|
||||
|
||||
```
|
||||
$ make docker
|
||||
|
||||
$ docker run -it {{ label }} --help
|
||||
usage: {{ label }} [-h] [--debug] [--quiet] [-o {json,yaml}] [-v] {command1} ...
|
||||
|
||||
{{ description }}
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
--debug toggle debug output
|
||||
--quiet suppress all output
|
||||
-o {json,yaml} output handler
|
||||
-v, --version show program's version number and exit
|
||||
|
||||
sub-commands:
|
||||
{command1}
|
||||
command1 example sub command1
|
||||
|
||||
Usage: {{ title }} command1 --foo bar
|
||||
```
|
||||
@ -0,0 +1,12 @@
|
||||
version: "3"
|
||||
services:
|
||||
{{ label }}:
|
||||
image: "{{ label }}:dev"
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile.dev
|
||||
hostname: cement
|
||||
stdin_open: true
|
||||
tty: true
|
||||
volumes:
|
||||
- ".:/app"
|
||||
@ -0,0 +1,6 @@
|
||||
### FIXME: Replace with 'cement==3.0.0' once it is stable
|
||||
-e git+https://github.com/datafolklabs/cement.git@portland#egg=cement
|
||||
|
||||
jinja2
|
||||
pyyaml
|
||||
colorlog
|
||||
28
cement/cli/templates/generate/project-loaded/setup.py
Normal file
28
cement/cli/templates/generate/project-loaded/setup.py
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
from {{ label }}.core.version import get_version
|
||||
|
||||
VERSION = get_version()
|
||||
|
||||
f = open('README.md', 'r')
|
||||
LONG_DESCRIPTION = f.read()
|
||||
f.close()
|
||||
|
||||
setup(
|
||||
name='{{ label }}',
|
||||
version=VERSION,
|
||||
description='{{ description }}',
|
||||
long_description=LONG_DESCRIPTION,
|
||||
long_description_content_type='text/markdown',
|
||||
author='{{ creator }}',
|
||||
author_email='{{ creator_email }}',
|
||||
url='{{ url }}',
|
||||
license='{{ license }}',
|
||||
packages=find_packages(exclude=['ez_setup', 'tests*']),
|
||||
package_data={'{{ label }}': ['templates/*']},
|
||||
include_package_data=True,
|
||||
entry_points="""
|
||||
[console_scripts]
|
||||
{{ label }} = {{ label }}.main:main
|
||||
""",
|
||||
)
|
||||
@ -0,0 +1,27 @@
|
||||
"""
|
||||
PyTest Fixtures.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import pytest
|
||||
from tempfile import mkstemp, mkdtemp
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def tmp(request):
|
||||
"""
|
||||
Create a `tmp` object that geneates a unique temporary directory, and file
|
||||
for each test function that requires it.
|
||||
"""
|
||||
|
||||
class Tmp(object):
|
||||
def __init__(self):
|
||||
self.dir = mkdtemp()
|
||||
_, self.file = mkstemp(dir=self.dir)
|
||||
t = Tmp()
|
||||
yield t
|
||||
|
||||
# cleanup
|
||||
if os.path.exists(t.dir):
|
||||
shutil.rmtree(t.dir)
|
||||
@ -0,0 +1,13 @@
|
||||
|
||||
from {{ label }}.main import {{ class_name}}Test
|
||||
|
||||
def test_{{ label }}(tmp):
|
||||
with {{ class_name }}Test() as app:
|
||||
res = app.run()
|
||||
print(res)
|
||||
raise Exception
|
||||
|
||||
def test_command1(tmp):
|
||||
argv = ['command1']
|
||||
with {{ class_name }}Test(argv=argv) as app:
|
||||
app.run()
|
||||
@ -0,0 +1,5 @@
|
||||
|
||||
from .controllers.base import Base
|
||||
|
||||
def load(app):
|
||||
app.handler.register(Base)
|
||||
@ -0,0 +1,61 @@
|
||||
|
||||
from cement import Controller, ex
|
||||
from ..core.version import get_version
|
||||
|
||||
VERSION_BANNER = """
|
||||
{{ description }}
|
||||
Version: %s
|
||||
Created by: {{ creator }}
|
||||
URL: {{ url }}
|
||||
""" % get_version()
|
||||
|
||||
|
||||
class Base(Controller):
|
||||
class Meta:
|
||||
label = 'base'
|
||||
|
||||
# text displayed at the top of --help output
|
||||
description = '{{ description }}'
|
||||
|
||||
# text displayed at the bottom of --help output
|
||||
epilog = 'Usage: {{ label }} command1 --foo bar'
|
||||
|
||||
# controller level arguments. ex: '{{ label }} --version'
|
||||
arguments = [
|
||||
### add a version banner
|
||||
( [ '-v', '--version' ],
|
||||
{ 'action' : 'version',
|
||||
'version' : VERSION_BANNER } ),
|
||||
]
|
||||
|
||||
|
||||
def _default(self):
|
||||
"""Default action if no sub-command is passed."""
|
||||
|
||||
self.app.args.print_help()
|
||||
|
||||
|
||||
@ex(
|
||||
help='example sub command1',
|
||||
|
||||
# sub-command level arguments. ex: '{{ label }} command1 --foo bar'
|
||||
arguments=[
|
||||
### add a sample foo option under subcommand namespace
|
||||
( [ '-f', '--foo' ],
|
||||
{ 'help' : 'notorious foo option',
|
||||
'action' : 'store',
|
||||
'dest' : 'foo' } ),
|
||||
],
|
||||
)
|
||||
def command1(self):
|
||||
"""Example sub-command."""
|
||||
|
||||
data = {
|
||||
'foo' : 'bar',
|
||||
}
|
||||
|
||||
### do something with arguments
|
||||
if self.app.pargs.foo is not None:
|
||||
data['foo'] = self.app.pargs.foo
|
||||
|
||||
self.app.render(data, 'command1.jinja2')
|
||||
@ -0,0 +1,13 @@
|
||||
|
||||
class {{ class_name }}Error(Exception):
|
||||
"""Generic errors."""
|
||||
|
||||
def __init__(self, msg):
|
||||
Exception.__init__(self)
|
||||
self.msg = msg
|
||||
|
||||
def __str__(self):
|
||||
return self.msg
|
||||
|
||||
def __repr__(self):
|
||||
return "<{{ class_name }}Error - %s>" % self.msg
|
||||
@ -0,0 +1,7 @@
|
||||
|
||||
from cement.utils.version import get_version as cement_get_version
|
||||
|
||||
VERSION = (0, 0, 1, 'alpha', 0)
|
||||
|
||||
def get_version(version=VERSION):
|
||||
return cement_get_version(version)
|
||||
@ -0,0 +1,90 @@
|
||||
|
||||
from cement import App, init_defaults
|
||||
from cement.core.exc import CaughtSignal
|
||||
from .core.exc import {{ class_name }}Error
|
||||
|
||||
|
||||
# configuration defaults
|
||||
DEFAULTS = init_defaults('{{ label }}')
|
||||
DEFAULTS['{{ label }}']['{{ foo }}'] = 'bar'
|
||||
|
||||
|
||||
class {{ class_name }}(App):
|
||||
"""{{ name }} primary application."""
|
||||
|
||||
class Meta:
|
||||
label = '{{ label }}'
|
||||
|
||||
# offload handler/hook registration to a separate module
|
||||
bootstrap = '{{ label }}.bootstrap'
|
||||
|
||||
# configuration defaults
|
||||
config_defaults = DEFAULTS
|
||||
|
||||
# load additional framework extensions
|
||||
extensions = [
|
||||
'json',
|
||||
'yaml',
|
||||
'colorlog',
|
||||
'jinja2',
|
||||
]
|
||||
|
||||
# configuration handler
|
||||
config_handler = 'yaml'
|
||||
|
||||
# configuration file suffix
|
||||
config_file_suffix = '.yml'
|
||||
|
||||
# set the log handler
|
||||
log_handler = 'colorlog'
|
||||
|
||||
# set the output handler
|
||||
output_handler = 'jinja2'
|
||||
|
||||
# call sys.exit() on close
|
||||
close_on_exit = True
|
||||
|
||||
|
||||
class {{ class_name }}Test({{ class_name}}):
|
||||
"""A test app that is better suited for testing."""
|
||||
|
||||
class Meta:
|
||||
# default argv to empty (don't use sys.argv)
|
||||
argv = []
|
||||
|
||||
# don't look for config files (could break tests)
|
||||
config_files = []
|
||||
|
||||
# don't call sys.exit() when app.close() is called in tests
|
||||
exit_on_close = False
|
||||
|
||||
|
||||
def main():
|
||||
with {{ class_name }}() as app:
|
||||
try:
|
||||
app.run()
|
||||
|
||||
except AssertionError as e:
|
||||
print('AssertionError > %s' % e.args[0])
|
||||
app.exit_code = 1
|
||||
|
||||
if app.debug is True:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
except {{ class_name}}Error:
|
||||
print('{{ class_name }}Error > %s' % e.args[0])
|
||||
app.exit_code = 1
|
||||
|
||||
if app.debug is True:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
except CaughtSignal as e:
|
||||
# Default Cement signals are SIGINT and SIGTERM, exit 0 (non-error)
|
||||
print('\n%s' % e)
|
||||
app.exit_code = 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -3,6 +3,11 @@
|
||||
exclude:
|
||||
- '^(.*)[\/\\\\]project[\/\\\\]{{ label }}[\/\\\\]templates[\/\\\\](.*)$'
|
||||
|
||||
ignore:
|
||||
- '^(.*)pyc(.*)$'
|
||||
- '^(.*)pyo(.*)$'
|
||||
- '^(.*)__pycache__(.*)$'
|
||||
|
||||
variables:
|
||||
- name: label
|
||||
prompt: "App Label"
|
||||
|
||||
@ -2,14 +2,15 @@ FROM python:3.6-alpine
|
||||
MAINTAINER {{ creator }} <{{ creator_email }}>
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
RUN apk update \
|
||||
&& apk add git \
|
||||
&& pip install --no-cache-dir -r requirements.txt \
|
||||
RUN apk update && \
|
||||
apk add git && \
|
||||
pip install --no-cache-dir -r requirements.txt \
|
||||
&& rm -f /usr/local/lib/python3.6/site-packages/cement.egg-link \
|
||||
&& cd src/cement \
|
||||
&& python setup.py install \
|
||||
&& cd /app \
|
||||
&& python setup.py install \
|
||||
&& rm -rf /app
|
||||
&& rm -rf src/cement \
|
||||
&& python setup.py install
|
||||
RUN rm -rf /app
|
||||
WORKDIR /
|
||||
ENTRYPOINT ["{{ label }}"]
|
||||
|
||||
@ -2,3 +2,4 @@ recursive-include *.py
|
||||
include setup.cfg
|
||||
include README.md CHANGELOG.md LICENSE.md
|
||||
include *.txt
|
||||
recursive-include {{ label }}/templates *
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
### {{ name }} Configuration Settings
|
||||
|
||||
[{{ label }}]
|
||||
|
||||
### Toggle application level debug (does not toggle framework debugging)
|
||||
# debug = false
|
||||
|
||||
### Where external (third-party) plugins are loaded from
|
||||
# plugin_dir = /var/lib/{{ label }}/plugins/
|
||||
|
||||
### Where all plugin configurations are loaded from
|
||||
# plugin_config_dir = /etc/{{ label }}/plugins.d/
|
||||
|
||||
### sample foo option
|
||||
# foo = bar
|
||||
|
||||
|
||||
[log.logging]
|
||||
|
||||
### Where the log file lives (no log file by default)
|
||||
# file =
|
||||
|
||||
### The level for which to log. One of: info, warning, error, fatal, debug
|
||||
# level = info
|
||||
|
||||
### Whether or not to log to console
|
||||
# to_console = true
|
||||
|
||||
### Whether or not to rotate the log file when it reaches `max_bytes`
|
||||
# rotate = false
|
||||
|
||||
### Max size in bytes that a log file can grow until it is rotated.
|
||||
# max_bytes = 512000
|
||||
|
||||
### The maximun number of log files to maintain when rotating
|
||||
# max_files = 4
|
||||
@ -4,9 +4,11 @@ services:
|
||||
image: "{{ label }}:dev"
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile.dev
|
||||
hostname: cement
|
||||
dockerfile: Dockerfile
|
||||
hostname: {{ label }}
|
||||
stdin_open: true
|
||||
tty: true
|
||||
working_dir: '/{{ label }}'
|
||||
entrypoint: '/bin/ash'
|
||||
volumes:
|
||||
- ".:/app"
|
||||
- ".:/{{ label }}"
|
||||
|
||||
@ -1,6 +1,2 @@
|
||||
### FIXME: Replace with 'cement==3.0.0' once it is stable
|
||||
-e git+https://github.com/datafolklabs/cement.git@portland#egg=cement
|
||||
|
||||
jinja2
|
||||
pyyaml
|
||||
colorlog
|
||||
|
||||
@ -16,6 +16,8 @@ def tmp(request):
|
||||
"""
|
||||
|
||||
class Tmp(object):
|
||||
cleanup = True
|
||||
|
||||
def __init__(self):
|
||||
self.dir = mkdtemp()
|
||||
_, self.file = mkstemp(dir=self.dir)
|
||||
@ -23,5 +25,5 @@ def tmp(request):
|
||||
yield t
|
||||
|
||||
# cleanup
|
||||
if os.path.exists(t.dir):
|
||||
if os.path.exists(t.dir) and cleanup is True:
|
||||
shutil.rmtree(t.dir)
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
|
||||
from cement import Controller, ex
|
||||
from cement.utils.version import get_version_banner
|
||||
from ..core.version import get_version
|
||||
|
||||
VERSION_BANNER = """
|
||||
{{ description }}
|
||||
Version: %s
|
||||
Created by: {{ creator }}
|
||||
URL: {{ url }}
|
||||
""" % get_version()
|
||||
{{ description }} %s
|
||||
%s
|
||||
""" % (get_version(), get_version_banner())
|
||||
|
||||
|
||||
class Base(Controller):
|
||||
@ -54,8 +53,8 @@ class Base(Controller):
|
||||
'foo' : 'bar',
|
||||
}
|
||||
|
||||
self.app.log.info('Inside Base.command1()')
|
||||
|
||||
### do something with arguments
|
||||
if self.app.pargs.foo is not None:
|
||||
data['foo'] = self.app.pargs.foo
|
||||
|
||||
self.app.render(data, 'command1.jinja2')
|
||||
print('Foo => %s' % self.app.pargs.foo)
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
from cement import App, init_defaults
|
||||
from cement.core.exc import CaughtSignal
|
||||
from .core.exc import {{ class_name }}Error
|
||||
|
||||
from .controllers.base import Base
|
||||
|
||||
# configuration defaults
|
||||
DEFAULTS = init_defaults('{{ label }}')
|
||||
@ -15,38 +15,20 @@ class {{ class_name }}(App):
|
||||
class Meta:
|
||||
label = '{{ label }}'
|
||||
|
||||
# offload handler/hook registration to a separate module
|
||||
bootstrap = '{{ label }}.bootstrap'
|
||||
|
||||
# configuration defaults
|
||||
config_defaults = DEFAULTS
|
||||
|
||||
# load additional framework extensions
|
||||
extensions = [
|
||||
'json',
|
||||
'yaml',
|
||||
'colorlog',
|
||||
'jinja2',
|
||||
]
|
||||
|
||||
# configuration handler
|
||||
config_handler = 'yaml'
|
||||
|
||||
# configuration file suffix
|
||||
config_file_suffix = '.yml'
|
||||
|
||||
# set the log handler
|
||||
log_handler = 'colorlog'
|
||||
|
||||
# set the output handler
|
||||
output_handler = 'jinja2'
|
||||
|
||||
# call sys.exit() on close
|
||||
close_on_exit = True
|
||||
|
||||
# register handlers
|
||||
handlers = [
|
||||
Base
|
||||
]
|
||||
|
||||
|
||||
class {{ class_name }}Test({{ class_name}}):
|
||||
"""A test app that is better suited for testing."""
|
||||
"""A sub-class of {{ class_name}} that is better suited for testing."""
|
||||
|
||||
class Meta:
|
||||
# default argv to empty (don't use sys.argv)
|
||||
|
||||
@ -1,5 +1,14 @@
|
||||
---
|
||||
|
||||
exclude:
|
||||
- '^(.*)[\/\\\\]script[\/\\\\]{{ label }}[\/\\\\]templates[\/\\\\](.*)$'
|
||||
|
||||
ignore:
|
||||
- '^(.*)pyc(.*)$'
|
||||
- '^(.*)pyo(.*)$'
|
||||
- '^(.*)__pycache__(.*)$'
|
||||
|
||||
|
||||
variables:
|
||||
- name: label
|
||||
prompt: "Script Name"
|
||||
|
||||
@ -126,6 +126,12 @@ class TemplateHandler(TemplateInterface, Handler):
|
||||
# must be provided by a subclass
|
||||
raise NotImplemented # pragma: nocover
|
||||
|
||||
def _match_patterns(self, item, patterns):
|
||||
for pattern in patterns:
|
||||
if re.match(pattern, item):
|
||||
return True
|
||||
return False
|
||||
|
||||
def copy(self, src, dest, data, force=False, exclude=None, ignore=None):
|
||||
"""
|
||||
Render ``src`` directory as template, including directory and file
|
||||
@ -152,89 +158,109 @@ class TemplateHandler(TemplateInterface, Handler):
|
||||
|
||||
dest = fs.abspath(dest)
|
||||
src = fs.abspath(src)
|
||||
|
||||
if exclude is None:
|
||||
exclude = []
|
||||
if ignore is None:
|
||||
ignore = []
|
||||
ignore_patterns = self._meta.ignore + ignore
|
||||
exclude_patterns = self._meta.exclude + exclude
|
||||
|
||||
assert os.path.exists(src), "Source path %s does not exist!" % src
|
||||
|
||||
if not os.path.exists(dest):
|
||||
os.makedirs(dest)
|
||||
|
||||
self.app.log.debug('Copying source template %s -> %s' % (src, dest))
|
||||
LOG.debug('copying source template %s -> %s' % (src, dest))
|
||||
|
||||
# here's the fun
|
||||
for cur_dir, sub_dirs, files in os.walk(src):
|
||||
if cur_dir == '.':
|
||||
continue # pragma: nocover
|
||||
|
||||
# don't render the source base dir (because we are telling it
|
||||
# where to go as `dest`)
|
||||
if cur_dir == src:
|
||||
elif cur_dir == src:
|
||||
# don't render the source base dir (because we are telling it
|
||||
# where to go as `dest`)
|
||||
cur_dir_dest = dest
|
||||
elif self._match_patterns(cur_dir, ignore_patterns):
|
||||
LOG.debug(
|
||||
'not copying ignored directory: %s' % cur_dir)
|
||||
continue
|
||||
elif self._match_patterns(cur_dir, exclude_patterns):
|
||||
LOG.debug(
|
||||
'not rendering excluded directory as template: ' +
|
||||
'%s' % cur_dir)
|
||||
cur_dir_stub = re.sub(src, '', cur_dir)
|
||||
cur_dir_stub = cur_dir_stub.lstrip('/')
|
||||
cur_dir_stub = cur_dir_stub.lstrip('\\')
|
||||
cur_dir_dest = os.path.join(dest, cur_dir_stub)
|
||||
else:
|
||||
# render the cur dir
|
||||
self.app.log.debug('rendering template %s' % cur_dir)
|
||||
LOG.debug(
|
||||
'rendering directory as template: %s' % cur_dir)
|
||||
cur_dir_stub = re.sub(src,
|
||||
'',
|
||||
self.render(cur_dir, data))
|
||||
|
||||
cur_dir_stub = cur_dir_stub.lstrip('/')
|
||||
cur_dir_stub = cur_dir_stub.lstrip('\\')
|
||||
cur_dir_dest = os.path.join(dest, cur_dir_stub)
|
||||
|
||||
# render sub-dirs
|
||||
for sub_dir in sub_dirs:
|
||||
self.app.log.debug('rendering template %s' % sub_dir)
|
||||
new_sub_dir = re.sub(src,
|
||||
'',
|
||||
self.render(sub_dir, data))
|
||||
sub_dir_dest = os.path.join(cur_dir_dest, new_sub_dir)
|
||||
full_path = os.path.join(cur_dir, sub_dir)
|
||||
|
||||
if self._match_patterns(full_path, ignore_patterns):
|
||||
LOG.debug(
|
||||
'not copying ignored sub-directory: ' +
|
||||
'%s' % full_path)
|
||||
continue
|
||||
elif self._match_patterns(full_path, exclude_patterns):
|
||||
LOG.debug(
|
||||
'not rendering excluded sub-directory as template: ' +
|
||||
'%s' % full_path)
|
||||
sub_dir_dest = os.path.join(cur_dir_dest, sub_dir)
|
||||
else:
|
||||
LOG.debug(
|
||||
'rendering sub-directory as template: %s' % full_path)
|
||||
new_sub_dir = re.sub(src,
|
||||
'',
|
||||
self.render(sub_dir, data))
|
||||
sub_dir_dest = os.path.join(cur_dir_dest, new_sub_dir)
|
||||
|
||||
if not os.path.exists(sub_dir_dest):
|
||||
self.app.log.debug('Creating sub-directory %s' %
|
||||
sub_dir_dest)
|
||||
LOG.debug('creating sub-directory %s' % sub_dir_dest)
|
||||
os.makedirs(sub_dir_dest)
|
||||
|
||||
for _file in files:
|
||||
self.app.log.debug('rendering template %s' % _file)
|
||||
new_file = re.sub(src, '', self.render(_file, data))
|
||||
_file = fs.abspath(os.path.join(cur_dir, _file))
|
||||
_file_dest = fs.abspath(os.path.join(cur_dir_dest, new_file))
|
||||
|
||||
if force is True:
|
||||
LOG.debug('Overwriting existing file: %s ' % _file_dest)
|
||||
else:
|
||||
assert not os.path.exists(_file_dest), \
|
||||
'Destination file already exists: %s ' % _file_dest
|
||||
# handle if destination path already exists
|
||||
|
||||
ignore_it = False
|
||||
all_patterns = self._meta.ignore + ignore
|
||||
for pattern in all_patterns:
|
||||
if re.match(pattern, _file):
|
||||
ignore_it = True
|
||||
break
|
||||
if os.path.exists(_file_dest):
|
||||
if force is True:
|
||||
LOG.debug(
|
||||
'overwriting existing file: %s ' % _file_dest)
|
||||
else:
|
||||
assert False, \
|
||||
'Destination file already exists: %s ' % _file_dest
|
||||
|
||||
if ignore_it is True:
|
||||
self.app.log.debug(
|
||||
'Not copying ignored file: ' +
|
||||
if self._match_patterns(_file, ignore_patterns):
|
||||
LOG.debug(
|
||||
'not copying ignored file: ' +
|
||||
'%s' % _file)
|
||||
continue
|
||||
|
||||
exclude_it = False
|
||||
all_patterns = self._meta.exclude + exclude
|
||||
for pattern in all_patterns:
|
||||
if re.match(pattern, _file):
|
||||
exclude_it = True
|
||||
break
|
||||
|
||||
if exclude_it is True:
|
||||
self.app.log.debug(
|
||||
'Not rendering excluded file as template: ' +
|
||||
elif self._match_patterns(_file, exclude_patterns):
|
||||
LOG.debug(
|
||||
'not rendering excluded file: ' +
|
||||
'%s' % _file)
|
||||
shutil.copy(_file, _file_dest)
|
||||
|
||||
else:
|
||||
f = open(os.path.join(cur_dir, _file), 'r')
|
||||
LOG.debug('rendering file as template: %s' % _file)
|
||||
f = open(_file, 'r')
|
||||
content = f.read()
|
||||
f.close()
|
||||
|
||||
@ -251,8 +277,8 @@ class TemplateHandler(TemplateInterface, Handler):
|
||||
template_path = template_path.lstrip('/')
|
||||
full_path = fs.abspath(os.path.join(template_prefix,
|
||||
template_path))
|
||||
LOG.debug("attemping to load output template from file %s" %
|
||||
full_path)
|
||||
LOG.debug(
|
||||
"attemping to load output template from file %s" % full_path)
|
||||
if os.path.exists(full_path):
|
||||
content = open(full_path, 'r').read()
|
||||
LOG.debug("loaded output template from file %s" %
|
||||
|
||||
@ -123,6 +123,7 @@ import os
|
||||
import inspect
|
||||
import yaml
|
||||
from .. import Controller, minimal_logger, shell, FrameworkError
|
||||
from ..utils.version import VERSION, get_version
|
||||
|
||||
LOG = minimal_logger(__name__)
|
||||
|
||||
@ -140,6 +141,14 @@ class GenerateTemplateAbstractBase(Controller):
|
||||
self.app.log.info(msg)
|
||||
data = {}
|
||||
|
||||
# builtin vars
|
||||
maj_min = float('%s.%s' % (VERSION[0], VERSION[1]))
|
||||
data['cement'] = {}
|
||||
data['cement']['version'] = get_version()
|
||||
data['cement']['major_version'] = VERSION[0]
|
||||
data['cement']['minor_version'] = VERSION[1]
|
||||
data['cement']['major_minor_version'] = maj_min
|
||||
|
||||
f = open(os.path.join(source, '.generate.yml'))
|
||||
g_config = yaml.load(f)
|
||||
f.close()
|
||||
|
||||
@ -38,8 +38,9 @@
|
||||
|
||||
import datetime # pragma: nocover
|
||||
import os # pragma: nocover
|
||||
import sys # pragma: nocover
|
||||
import subprocess # pragma: nocover
|
||||
|
||||
import platform # pragma: nocover
|
||||
from ..core.backend import VERSION # pragma: nocover
|
||||
|
||||
|
||||
@ -71,6 +72,18 @@ def get_version(version=VERSION): # pragma: nocover
|
||||
return main + sub
|
||||
|
||||
|
||||
def get_version_banner():
|
||||
cement_ver = get_version()
|
||||
python_ver = '.'.join([str(x) for x in sys.version_info[0:3]])
|
||||
plat = platform.platform()
|
||||
|
||||
banner = 'Cement Framework %s\n' % cement_ver + \
|
||||
'Python %s\n' % python_ver + \
|
||||
'Platform %s' % plat
|
||||
|
||||
return banner
|
||||
|
||||
|
||||
def get_git_changeset(): # pragma: nocover
|
||||
"""Returns a numeric identifier of the latest git changeset.
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ RUN apk update \
|
||||
make \
|
||||
vim \
|
||||
bash \
|
||||
git \
|
||||
&& ln -sf /usr/bin/vim /usr/bin/vi \
|
||||
&& pip install --no-cache-dir -r requirements-dev.txt
|
||||
COPY . /app
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
FROM alpine:3.5
|
||||
MAINTAINER BJ Dierkes <derks@datafolklabs.com>
|
||||
EXPOSE 8000
|
||||
WORKDIR /app
|
||||
RUN apk update && apk add hugo py-pygments
|
||||
COPY doc-new/ /app/
|
||||
COPY docker/bin/run-docs.sh /usr/bin/run-docs.sh
|
||||
CMD ["/usr/bin/run-docs.sh"]
|
||||
4
setup.py
4
setup.py
@ -11,16 +11,18 @@ f.close()
|
||||
|
||||
setup(name='cement',
|
||||
version=VERSION,
|
||||
description='CLI Application Framework for Python',
|
||||
description='CLI Framework for Python',
|
||||
long_description=LONG,
|
||||
long_description_content_type='text/markdown',
|
||||
classifiers=[],
|
||||
install_requires=[],
|
||||
keywords='cli framework',
|
||||
author='Data Folk Labs, LLC',
|
||||
author_email='derks@datafolklabs.com',
|
||||
url='http://builtoncement.org',
|
||||
license='BSD',
|
||||
packages=find_packages(exclude=['ez_setup', 'tests*']),
|
||||
package_data={'cement': ['cement/cli/templates/generate/*']},
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
test_suite='nose.collector',
|
||||
|
||||
@ -19,7 +19,7 @@ def test_app():
|
||||
|
||||
|
||||
def test_generate(tmp):
|
||||
argv = ['generate', 'app', tmp.dir, '--defaults']
|
||||
argv = ['generate', 'project', tmp.dir, '--defaults']
|
||||
|
||||
with App(argv=argv) as app:
|
||||
app.run()
|
||||
|
||||
@ -9,6 +9,8 @@ from tempfile import mkstemp, mkdtemp
|
||||
@pytest.fixture(scope="function")
|
||||
def tmp(request):
|
||||
class Tmp(object):
|
||||
cleanup = True
|
||||
|
||||
def __init__(self):
|
||||
self.dir = mkdtemp()
|
||||
_, self.file = mkstemp(dir=self.dir)
|
||||
@ -16,7 +18,7 @@ def tmp(request):
|
||||
yield t
|
||||
|
||||
# cleanup
|
||||
if os.path.exists(t.dir):
|
||||
if os.path.exists(t.dir) and t.cleanup is True:
|
||||
shutil.rmtree(t.dir)
|
||||
|
||||
|
||||
|
||||
8
tests/data/templates/generate/test4/.generate.yml
Normal file
8
tests/data/templates/generate/test4/.generate.yml
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
|
||||
ignore:
|
||||
- '.*ignore-me.*'
|
||||
|
||||
exclude:
|
||||
- '.*exclude-me.*'
|
||||
|
||||
0
tests/data/templates/generate/test4/take-me/take-me
Normal file
0
tests/data/templates/generate/test4/take-me/take-me
Normal file
@ -108,3 +108,20 @@ def test_generate_default_command(tmp):
|
||||
argv = ['generate']
|
||||
with GenerateApp(argv=argv) as app:
|
||||
app.run()
|
||||
|
||||
|
||||
def test_filtered_sub_dirs(tmp):
|
||||
tmp.cleanup = False
|
||||
argv = ['generate', 'test4', tmp.dir, '--defaults']
|
||||
|
||||
with GenerateApp(argv=argv) as app:
|
||||
app.run()
|
||||
|
||||
assert exists_join(tmp.dir, 'take-me')
|
||||
assert exists_join(tmp.dir, 'take-me', 'take-me')
|
||||
assert exists_join(tmp.dir, 'take-me', 'exclude-me')
|
||||
assert not exists_join(tmp.dir, 'take-me', 'ignore-me')
|
||||
assert exists_join(tmp.dir, 'exclude-me')
|
||||
assert exists_join(tmp.dir, 'exclude-me', 'take-me')
|
||||
assert not exists_join(tmp.dir, 'ignore-me')
|
||||
assert not exists_join(tmp.dir, 'ignore-me', 'take-me')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user