Update dependencies and update conditions

Add elif/else if/else for template

Add SysDep class for template settings

Update Param class. Now, supports callable vtype
This commit is contained in:
Dmitriy Pleshevskiy 2018-06-16 19:01:23 +03:00
parent 642ba66405
commit 95a6bec274
7 changed files with 122 additions and 37 deletions

View file

@ -1,6 +1,7 @@
from .param import Param
from .dependency import SysDep
__all__ = ('Param', )
__all__ = ('Param', 'SysDep')
if __name__ == '__main__':
from .config import Config

View file

@ -3,8 +3,8 @@
__title__ = 'ictmpl'
__description__ = 'Generate projects from templates'
__url__ = 'https://github.com/ideascup/ictmpl/'
__version__ = '1.1.2'
__build__ = 0x010102
__version__ = '1.2.0'
__build__ = 0x010200
__author__ = 'Dmitriy Pleshevskiy'
__author_email__ = 'dmitriy@ideascup.me'
__license__ = 'MIT'

25
ictmpl/dependency.py Normal file
View file

@ -0,0 +1,25 @@
from subprocess import Popen, PIPE
class SysDep:
def __init__(self, packages, checkcmd=None, checkparams=None):
self.packages = packages
self.checkcmd = checkcmd
self.checkparams = checkparams
def check(self, app):
if self.checkcmd:
proc = Popen('command -v {}'.format(self.checkcmd), shell=True, stdout=PIPE)
stdout = proc.communicate()[0]
if not stdout:
return True
elif self.checkparams:
if callable(self.checkparams):
return bool(self.checkparams(app.params))
elif isinstance(self.checkparams, str):
return bool(app.params.get(self.checkparams))
else:
return True
return False

View file

@ -1,16 +1,22 @@
import re
import os
import json
from re import compile, sub, _pattern_type
from os.path import abspath, isdir, join
from shutil import copytree, rmtree
__all__ = ('parse_ignore_file', 'copytree_with_ignore', 'treewalk')
RE_DOUBLESTAR = compile(r'\*\*')
RE_STAR = compile(r'\*')
RE_DOUBLESTAR = re.compile(r'\*\*')
RE_STAR = re.compile(r'\*')
RE_IF_CONDITION = compile(r'\s*%\s*(endif|if\s+(.+))\s*%')
RE_IF_CONDITION = re.compile(r"""
(?:
^\s*%\s*(else|(?:el(?:se )?)?if\s+(.+))\s*%
|
\s*%\s*(endif)\s*%
)
""", re.VERBOSE|re.MULTILINE)
def parse_ignore_file(filepath):
@ -30,7 +36,7 @@ def parse_ignore_file(filepath):
if line.startswith('/'):
line = '^'+line
regexes.append(compile(line))
regexes.append(re.compile(line))
return regexes
@ -108,34 +114,62 @@ def replace_template_file(filepath, app):
break
start, end = [start_index+v for v in match.span()]
statement = match.group(1)
statement = match.group(1) or match.group(3)
if statement.startswith('if '):
cond_deep += 1
if cut_info is not None:
continue
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:
else:
cut_info = (start, cond_deep)
start_index = end
else:
elif statement.startswith('elif ') \
or statement.startswith('else if '):
if cut_info is not None:
start = cut_info[0]
cut_info = None
local = params.copy()
exec('result__=bool({})'.format(match.group(2)), local)
if local['result__']:
filedata = filedata[:start] + filedata[end:]
start_index = start
continue
cut_info = (start, cond_deep)
start_index = end
elif statement.startswith('else') \
and not match.group(2):
if cut_info is not None:
start = cut_info[0]
cut_info = None
filedata = filedata[:start] + filedata[end:]
start_index = start
elif statement.startswith('endif'):
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
cond_deep -= 1
# replace params
for key, value in params.items():
if isinstance(value, _pattern_type):
if isinstance(value, re._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)
filedata = re.sub(r'%%{}%%'.format(key), value, filedata)
with open(filepath, 'w') as file:
file.write(filedata)

View file

@ -7,7 +7,7 @@ from uuid import uuid4
from textwrap import dedent
from shutil import rmtree
from ictmpl import Param
from ictmpl import Param, SysDep
from ictmpl.helpers.git import Git
from ictmpl.helpers.fsutil import (
copytree_with_ignore, rmtree_without_ignore, replace_template_file)
@ -74,29 +74,19 @@ def create_project(args, app):
print(e)
return
dependencies = rc.get('sysDependencies', None)
if dependencies and isinstance(dependencies, dict):
commands = []
for key, packages in dependencies.items():
pipes = Popen('command -v %s' % key, shell=True, stdout=PIPE)
stdout, _ = pipes.communicate()
if not stdout:
commands.append(packages)
if commands:
# TODO: Need check platform
command = 'sudo apt-get install %s' % ' '.join(commands)
Popen(command, shell=True)
for key in ('name', 'version', 'description', 'author', 'author_email',
'author_website', 'licence'):
app.params['__{}__'.format(key.upper())] = rc.get(key, '')
params = rc.get('params', [])
if params:
if params and isinstance(params, (list, tuple)):
print('Configure template:')
ask_params(app, params)
print(app.params)
try:
ask_params(app, params)
except KeyboardInterrupt:
rmtree(project_path)
exit()
print('------------------\n')
print('Walking files and replace params')
@ -111,6 +101,29 @@ def create_project(args, app):
for filename in files:
replace_template_file(join(root, filename), app)
try:
dependencies = rc.get('sys_dependencies', [])
if dependencies and isinstance(dependencies, (list, tuple)):
packages = ' '.join(
sysdep.packages
for sysdep in dependencies
if isinstance(sysdep, SysDep) and sysdep.check(app)
)
if packages:
# TODO: Need check platform
try:
proc = Popen('sudo apt-get install {}'.format(packages),
shell=True)
proc.communicate()
except KeyboardInterrupt:
proc.kill()
print(os.linesep)
exit()
except Exception as e:
import traceback
traceback.print_exc()
setup_command = rc.get('commands', {}).get('setup', None)
if setup_command:
if isinstance(setup_command, (list, tuple)):

View file

@ -1,4 +1,6 @@
class auto(str): pass
class Param:
def __init__(self, name, default, vtype=None, question=None, children=None):
@ -13,20 +15,30 @@ class Param:
except:
pass
self.question = question + ' '
self.question = question.rstrip() + ' '
self.children = children
def strparse(self, value):
if self.vtype is not str and type(value) is self.vtype:
if not isinstance(self.vtype, type) and callable(self.vtype):
vtype = self.vtype(value)
else:
vtype = self.vtype
if value is 'None':
return None
if type(value) is vtype:
return value
if self.vtype in (list, tuple) \
or (self.vtype is str and ',' in value):
lvalue = value.lower()
if vtype in (list, tuple) \
or (vtype is auto and ',' in value):
return [v.strip() for v in value.split(',') if v.strip()]
elif self.vtype is bool:
return value[:1].lower() == 'y'
elif vtype is bool \
or (vtype is auto and lvalue in ('true', 'false')):
return lvalue == 'y' or lvalue == 'true'
return self.vtype(value)
return vtype(value)
def format(self, value):
type_ = type(value)