Skip to content

Commit

Permalink
Handle template checking
Browse files Browse the repository at this point in the history
Extend jinja generator to enable basic template checking
  • Loading branch information
aremazeilles committed Jan 20, 2020
1 parent 3442f50 commit fbccc56
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 61 deletions.
1 change: 1 addition & 0 deletions package_generator/src/package_generator/code_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ def check_template_file(self, file_template):
self.do_generate_ = False
if not self.process_file(file_template):
return False
# self.log("Processed file: \n {}".format(self.rendered_))
return True

def get_all_tags(self, line):
Expand Down
160 changes: 100 additions & 60 deletions package_generator/src/package_generator/generate_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ def __init__(self, name="PackageGenerator"):
self.spec_ = None
# generic file generator
self.file_generator_ = None
# jinja-based generator
self.jinja_generator_ = None
# if the package already existed, location of the package backup
self.path_pkg_backup_ = None

Expand Down Expand Up @@ -209,6 +211,7 @@ def generate_package(self, package_desc, output_path):

# self.xml_parser_.print_xml_parsed()

# todo why only the custom generator is configured?
if not self.file_generator_.configure(self.xml_parser_, self.spec_):
return False

Expand Down Expand Up @@ -278,7 +281,6 @@ def generate_one_file(self, template_file, result_file, force_write):
Returns:
Bool: True on success
"""
self.spec_.generators_

generator = dict()
generator["custom"] = self.file_generator_
Expand All @@ -301,6 +303,45 @@ def generate_one_file(self, template_file, result_file, force_write):
result_file,
force_write)

def check_template_file(self, template_file):
"""Generate a template file, depending on the generators to be used
Args:
template_file (str): template filename
result_file (str): filename to store the result (unless is None)
force_write (str): force the writting of empty files (if not, files is not written)
Returns:
Bool: True on success
"""

generator = dict()
generator["custom"] = self.file_generator_
generator["jinja"] = self.jinja_generator_

if len(self.spec_.generators_) == 1:
# self.log("Check with Generator {}".format(self.spec_.generators_[0]))
return generator[self.spec_.generators_[0]].check_template_file(template_file)

# two generators are to be used
gen_one = generator[self.spec_.generators_[0]]
gen_two = generator[self.spec_.generators_[1]]

# self.log("Check with Generator {}".format(self.spec_.generators_[0]))

is_ok = gen_one.check_template_file(template_file)

if not is_ok:
return False

# self.log("Check with Generator {}".format(self.spec_.generators_[1]))

if self.spec_.generators_[1] == "jinja":
return gen_two.check_template_file(gen_one.rendered_, is_filename=False)
else:
return gen_two.check_template_file(template_file)


def write_generated_file(self, result_file):
"""Write a generated file
Expand Down Expand Up @@ -616,19 +657,48 @@ def generate_content(self):
return False
return True

def template_sanity_check(self):
def template_sanity_check(self, template):
"""Perform the package sanity check
Returns:
Bool: True on success
"""

# Locate template location
try:
[all_template_path, template_names] = self.get_template_info()
except rospkg.common.ResourceNotFound as error:
msg = "Package package_generator_templates not found in rospack"
self.log_error(msg)
self.log_error(error)
return False
except OSError as error:
msg = "No template dounf in package_generator_templates"
self.log_error(msg)
self.log_error(error)
return False

if template not in template_names:
msg = "Template requested: {} unknown".format(template)
self.log_error(msg)
msg = "Available templates: {}".format(template_names)
self.log_error(msg)
return False

template_path = all_template_path + "/" + template

# confirm this is a template...
if not self.check_template_structure(template_path):
msg = "Please revise template structure"
self.log_error(msg)
return False

# TODO list number of files in template
# Extracting all components from the template
file_list = list()
dir_list = list()

path_root_template = self.template_path_ + "/template"
path_root_template = template_path + "/template"

for (root, dirs, files) in os.walk(path_root_template):
# print "check {}: dir {}, files: {}".format(root, dirs, files)
Expand All @@ -654,8 +724,9 @@ def template_sanity_check(self):
self.spec_ = TemplateSpec()
self.xml_parser_ = PackageXMLParser()
self.file_generator_ = CodeGenerator()
self.jinja_generator_ = JinjaGenerator()

dir_template_spec = self.template_path_ + "/config/"
dir_template_spec = template_path + "/config/"
if not self.spec_.load_spec(dir_template_spec):
self.log_error("Could not load the template spec")
return False
Expand All @@ -673,13 +744,17 @@ def template_sanity_check(self):
if not self.file_generator_.configure(self.xml_parser_, self.spec_):
return False

if not self.jinja_generator_.configure(self.xml_parser_, self.spec_):
return False

is_ok = True

for item in file_list:
self.log("Checking file: {}".format(item))
item_abs = path_root_template + "/" + item
is_ok = self.file_generator_.check_template_file(item_abs)

is_ok = self.check_template_file(item_abs)
if not is_ok:
break
if is_ok:
self.log("No error detected")
else:
Expand Down Expand Up @@ -719,7 +794,7 @@ def main():
print colored(error, 'red')
return -1
except OSError as error:
msg = "No template dounf in package_generator_templates"
msg = "No template found in package_generator_templates"
print colored(msg, 'red')
print colored(error, 'red')
return -1
Expand Down Expand Up @@ -756,67 +831,32 @@ def main_check():
Returns:
int: negative value on error
"""

# checking available templates
rospack = rospkg.RosPack()
try:
default_templates_path = rospack.get_path('package_generator_templates')
default_templates_path += "/templates/"
except rospkg.common.ResourceNotFound as error:
msg = "Package package_generator_templates not found in rospack"
print colored(msg, "yellow")
print colored("{}".format(error), "yellow")
default_templates_path = None

available_templates = None
# look for the templates available
if default_templates_path is not None:
available_templates = os.listdir(default_templates_path)
gen = PackageGenerator()

if len(sys.argv) != 2:
print colored("Wrong input parameters !", "red")
print colored(USAGE_CHECK, "yellow")
if available_templates is not None:
msg = "Available templates are: {}"
print colored(msg.format(available_templates), 'yellow')
print "Bye bye"
return -1

path_template = sys.argv[1]

# searching for the template location
if os.path.isabs(path_template):
print "Loading model from absolute path {}".format(path_template)
else:
# relative path.
# either from the current path, or from the template package
path_current = os.getcwd()
path_attempt = path_current + "/" + path_template

if os.path.isdir(path_attempt):
path_template = path_attempt
print "Loading template from path {}".format(path_template)
else:
if path_template in available_templates:
path_template = default_templates_path + path_template
msg = "Loading template from template package: {}"
print msg.format(path_template)
else:
msg = "Template name not found in package_generator_templates"
print colored(msg, "red")
print colored("Please verify your setup", "red")
return -1

gen = PackageGenerator()
try:
[_, template_names] = gen.get_template_info()
except rospkg.common.ResourceNotFound as error:
msg = "Package package_generator_templates not found in rospack"
print colored(msg, 'red')
print colored(error, 'red')
return -1
except OSError as error:
msg = "No template found in package_generator_templates"
print colored(msg, 'red')
print colored(error, 'red')
return -1

# Todo: method is not existing anymore
if not gen.set_package_template(path_template):
print colored("Not able to load the template at:", "red")
print colored(path_template, "red")
print "Bye"
msg = "Available templates are: {}"
print colored(msg.format(template_names), 'yellow')
print "Bye bye"
return -1

if not gen.template_sanity_check():
template_name = sys.argv[1]
if not gen.template_sanity_check(template_name):
print colored("Issue detected in template", "red")
return -1
else:
Expand Down
22 changes: 22 additions & 0 deletions package_generator/src/package_generator/jinja_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,28 @@ def write_rendered_file(self, filename):
return False
return True

def check_template_file(self, filename, is_filename=True):
env = jinja2.Environment()
# self.log("Checking file: {}".format(filename))

if is_filename:
try:
with open(filename) as template:
env.parse(template.read())
except jinja2.exceptions.TemplateSyntaxError as err:
self.log_warn("Syntax error: line {}: {}".format(err.lineno, err))
return False
return True

# file provided is not a filename, but the outcome of another generator
str_template = "\n".join(filename)
try:
env.parse(str_template)
except jinja2.exceptions.TemplateSyntaxError as err:
self.log_warn("Syntax error: line {}: {}".format(err.lineno, err))
return False
return True

# todo:
# * Define appropriate context structure
# * Handle error situations
Expand Down
4 changes: 3 additions & 1 deletion package_generator_templates/README_designer.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,6 @@ The following context is made available:
* `attributes`: attributes of the current component (dictionary)
* `interface`: interface of the current component (list of dictionaries)

To avoid useless warning, it is better to write simple variable access inserting a space in between the brackets and the variable name, i.e. write `{{ package.name }}` instead of `{{package.name}}`.
To avoid useless warning, it is better to write simple variable access inserting a space in between the brackets and the variable name, i.e. write `{{ package.name }}` instead of `{{package.name}}`.

_The template checking using command `check_template` is still experimental when jinja generator is used._

0 comments on commit fbccc56

Please sign in to comment.