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 .param import Param
from .dependency import SysDep
__all__ = ('Param', ) __all__ = ('Param', 'SysDep')
if __name__ == '__main__': if __name__ == '__main__':
from .config import Config from .config import Config

View file

@ -3,8 +3,8 @@
__title__ = 'ictmpl' __title__ = 'ictmpl'
__description__ = 'Generate projects from templates' __description__ = 'Generate projects from templates'
__url__ = 'https://github.com/ideascup/ictmpl/' __url__ = 'https://github.com/ideascup/ictmpl/'
__version__ = '1.1.2' __version__ = '1.2.0'
__build__ = 0x010102 __build__ = 0x010200
__author__ = 'Dmitriy Pleshevskiy' __author__ = 'Dmitriy Pleshevskiy'
__author_email__ = 'dmitriy@ideascup.me' __author_email__ = 'dmitriy@ideascup.me'
__license__ = 'MIT' __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 os
import json import json
from re import compile, sub, _pattern_type
from os.path import abspath, isdir, join from os.path import abspath, isdir, join
from shutil import copytree, rmtree from shutil import copytree, rmtree
__all__ = ('parse_ignore_file', 'copytree_with_ignore', 'treewalk') __all__ = ('parse_ignore_file', 'copytree_with_ignore', 'treewalk')
RE_DOUBLESTAR = compile(r'\*\*') RE_DOUBLESTAR = re.compile(r'\*\*')
RE_STAR = 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): def parse_ignore_file(filepath):
@ -30,7 +36,7 @@ def parse_ignore_file(filepath):
if line.startswith('/'): if line.startswith('/'):
line = '^'+line line = '^'+line
regexes.append(compile(line)) regexes.append(re.compile(line))
return regexes return regexes
@ -108,34 +114,62 @@ def replace_template_file(filepath, app):
break break
start, end = [start_index+v for v in match.span()] 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 '): if statement.startswith('if '):
cond_deep += 1 cond_deep += 1
if cut_info is not None:
continue
local = params.copy() local = params.copy()
exec('result__=bool({})'.format(match.group(2)), local) exec('result__=bool({})'.format(match.group(2)), local)
if local['result__']: if local['result__']:
filedata = filedata[:start] + filedata[end:] filedata = filedata[:start] + filedata[end:]
start_index = start start_index = start
elif cut_info is None: else:
cut_info = (start, cond_deep) cut_info = (start, cond_deep)
start_index = end 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 \ if cut_info is not None \
and cut_info[1] is cond_deep: and cut_info[1] is cond_deep:
start = cut_info[0] start = cut_info[0]
cut_info = None cut_info = None
filedata = filedata[:start] + filedata[end:] filedata = filedata[:start] + filedata[end:]
start_index = start start_index = start
cond_deep -= 1
# replace params # replace params
for key, value in params.items(): for key, value in params.items():
if isinstance(value, _pattern_type): if isinstance(value, re._pattern_type):
continue continue
elif value is None or isinstance(value, bool): elif value is None or isinstance(value, bool):
value = str(value) value = str(value)
else: else:
value = json.dumps(value) 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: with open(filepath, 'w') as file:
file.write(filedata) file.write(filedata)

View file

@ -7,7 +7,7 @@ from uuid import uuid4
from textwrap import dedent from textwrap import dedent
from shutil import rmtree from shutil import rmtree
from ictmpl import Param from ictmpl import Param, SysDep
from ictmpl.helpers.git import Git from ictmpl.helpers.git import Git
from ictmpl.helpers.fsutil import ( from ictmpl.helpers.fsutil import (
copytree_with_ignore, rmtree_without_ignore, replace_template_file) copytree_with_ignore, rmtree_without_ignore, replace_template_file)
@ -74,29 +74,19 @@ def create_project(args, app):
print(e) print(e)
return 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', for key in ('name', 'version', 'description', 'author', 'author_email',
'author_website', 'licence'): 'author_website', 'licence'):
app.params['__{}__'.format(key.upper())] = rc.get(key, '') app.params['__{}__'.format(key.upper())] = rc.get(key, '')
params = rc.get('params', []) params = rc.get('params', [])
if params: if params and isinstance(params, (list, tuple)):
print('Configure template:') print('Configure template:')
ask_params(app, params) try:
print(app.params) ask_params(app, params)
except KeyboardInterrupt:
rmtree(project_path)
exit()
print('------------------\n') print('------------------\n')
print('Walking files and replace params') print('Walking files and replace params')
@ -111,6 +101,29 @@ def create_project(args, app):
for filename in files: for filename in files:
replace_template_file(join(root, filename), app) 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) setup_command = rc.get('commands', {}).get('setup', None)
if setup_command: if setup_command:
if isinstance(setup_command, (list, tuple)): if isinstance(setup_command, (list, tuple)):

View file

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