diff --git a/ictmpl/__init__.py b/ictmpl/__init__.py index d1300dc..de73617 100644 --- a/ictmpl/__init__.py +++ b/ictmpl/__init__.py @@ -1,10 +1,11 @@ -from .config import Config -from .ictmpl import Ictmpl +from .param import Param - -ictmpl = Ictmpl(Config()) +__all__ = ('Param', ) if __name__ == '__main__': + from .config import Config + from .ictmpl import Ictmpl + ictmpl = Ictmpl(Config()) ictmpl.run() diff --git a/ictmpl/__version__.py b/ictmpl/__version__.py index e609533..935b54e 100644 --- a/ictmpl/__version__.py +++ b/ictmpl/__version__.py @@ -3,8 +3,8 @@ __title__ = 'ictmpl' __description__ = 'Generate projects from templates' __url__ = 'https://github.com/ideascup/ictmpl/' -__version__ = '1.0.0' -__build__ = 0x010000 +__version__ = '1.1.0' +__build__ = 0x010100 __author__ = 'Dmitriy Pleshevskiy' __author_email__ = 'dmitriy@ideascup.me' __license__ = 'MIT' diff --git a/ictmpl/helpers/fsutil.py b/ictmpl/helpers/fsutil.py index 6a5d7a0..5008cf9 100644 --- a/ictmpl/helpers/fsutil.py +++ b/ictmpl/helpers/fsutil.py @@ -1,5 +1,6 @@ import os -from re import compile, sub +import json +from re import compile, sub, _pattern_type from os.path import abspath, isdir, join from shutil import copytree, rmtree @@ -9,6 +10,8 @@ __all__ = ('parse_ignore_file', 'copytree_with_ignore', 'treewalk') RE_DOUBLESTAR = compile(r'\*\*') RE_STAR = compile(r'\*') +RE_IF_CONDITION = compile(r'\s*%\s*(endif|if\s+(.+))\s*%') + def parse_ignore_file(filepath): regexes = [] @@ -94,8 +97,45 @@ def replace_template_file(filepath, app): params = app.params with open(filepath, 'r') as file: filedata = file.read() - for key, value in params.items(): - filedata = sub(r'%%{}%%'.format(key), value, filedata) + + # replace conditions + cut_info = None + cond_deep = 0 + start_index = 0 + while True: + match = RE_IF_CONDITION.search(filedata[start_index:]) + if not match: + break + + start, end = [start_index+v for v in match.span()] + statement = match.group(1) + if statement.startswith('if '): + cond_deep += 1 + local = params.copy() + exec('result__=bool({})'.format(match.group(2)), local) + if local['result__']: + filedata = filedata[:start] + filedata[end:] + start_index = start + elif cut_info is None: + cut_info = (start, cond_deep) + start_index = end + else: + if cut_info is not None \ + and cut_info[1] is cond_deep: + start = cut_info[0] + cut_info = None + filedata = filedata[:start] + filedata[end:] + start_index = start + + # replace params + for key, value in params.items(): + if isinstance(value, _pattern_type): + continue + elif value is None or isinstance(value, bool): + value = str(value) + else: + value = json.dumps(value) + filedata = sub(r'%%{}%%'.format(key), value, filedata) with open(filepath, 'w') as file: file.write(filedata) diff --git a/ictmpl/methods/create.py b/ictmpl/methods/create.py index 6cecf65..d1cd4f7 100644 --- a/ictmpl/methods/create.py +++ b/ictmpl/methods/create.py @@ -7,6 +7,7 @@ from uuid import uuid4 from textwrap import dedent from shutil import rmtree +from ictmpl import Param from ictmpl.helpers.git import Git from ictmpl.helpers.fsutil import ( copytree_with_ignore, rmtree_without_ignore, replace_template_file) @@ -21,6 +22,28 @@ RE_GITLINK = compile(r'^(?:git@|https?://)') RE_COMMAND_ENV = compile(r'#!(.+)') +def ask_params(app, params=None, deep=0): + if not params: + return + + for param in params: + if not isinstance(param, Param): + if isinstance(param, (list, tuple)): + app.params[param[0]] = param[1] + continue + + while True: + value = input(' '*deep*4 + param.question) or param.default + try: + param_value = param.strparse(value) + app.params[param.name] = param_value + if param.children and param_value: + ask_params(app, param.children, deep=deep+1) + break + except Exception as e: + print(e) + + def create_project(args, app): project_path = abspath(args.path) @@ -47,7 +70,8 @@ def create_project(args, app): try: with open('.ictmplrc', 'r') as file: exec(file.read(), rc) - except: + except Exception as e: + print(e) return dependencies = rc.get('sysDependencies', None) @@ -68,12 +92,11 @@ def create_project(args, app): 'author_website', 'licence'): app.params['__{}__'.format(key.upper())] = rc.get(key, '') - params = rc.get('params', {}) + params = rc.get('params', []) if params: print('Configure template:') - for param, default in params.items(): - question = 'SET {}: ({}) '.format(param, default) - app.params[param] = input(question) or default + ask_params(app, params) + print(app.params) print('------------------\n') print('Walking files and replace params') @@ -81,6 +104,7 @@ def create_project(args, app): if templates: templates = map(lambda p: join(project_path, p), templates) for filepath in templates: + print(filepath) replace_template_file(filepath, app) else: for root, dirs, files in os.walk(project_path): @@ -108,6 +132,6 @@ def create_project(args, app): proc.communicate() except KeyboardInterrupt: proc.kill() - rmtree(project_path) + # rmtree(project_path) finally: os.remove(tmpfilename) diff --git a/ictmpl/param.py b/ictmpl/param.py new file mode 100644 index 0000000..ecc27a0 --- /dev/null +++ b/ictmpl/param.py @@ -0,0 +1,41 @@ + + +class Param: + def __init__(self, name, default, vtype=None, question=None, children=None): + self.name = name + self.default = default + self.vtype = vtype or type(default) + + if not question: + question = 'SET {0}: ({1})' + try: + question = question.format(name, self.format(default)) + except: + pass + + self.question = question + ' ' + self.children = children + + def strparse(self, value): + if self.vtype is not str and type(value) is self.vtype: + return value + + if self.vtype in (list, tuple) \ + or (self.vtype is str and ',' in value): + return [v.strip() for v in value.split(',') if v.strip()] + elif self.vtype is bool: + return value[:1].lower() == 'y' + + return self.vtype(value) + + def format(self, value): + type_ = type(value) + if type_ in (list, tuple): + value = ', '.join(value) + elif type_ is bool: + nvindex = int(not value) + value = '/'.join( + v.upper() if nvindex is i else v + for i, v in enumerate('yn')) + + return value \ No newline at end of file