Files
Eric F d398a6ced2 Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
2026-06-08 00:38:27 -04:00

193 lines
6.3 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# SPDX-License-Identifier: Apache-2.0
# Copyright Pionix GmbH and Contributors to EVerest
#
"""
author: andreas.heinrich@pionix.de
This script processes a template file with Jinja2 and YAML data.
"""
import argparse
import jinja2
import yaml
from pathlib import Path
def rst_indent(input):
lines = input.splitlines()
lines = [f"| {line}\r\n" for line in lines]
return "".join(lines)
def make_rst_ref(input):
output = input.replace("/", "")
output = output.replace("#", "-")
return output
def literal_rst_filter(value):
"""Wraps a string in double backticks to treat it as a literal in RST."""
str_value = str(value)
has_trailing_whitespace = (str_value != str_value.rstrip())
cleaned_value = str_value.rstrip()
parts = cleaned_value.split(':', 1)
if len(parts) == 2 and parts[0] == "pattern":
formatted_value = f"{parts[0]}:``{parts[1].lstrip()}``"
else:
formatted_value = cleaned_value
if has_trailing_whitespace:
return formatted_value + '\r\n'
else:
return formatted_value
def main():
parser = argparse.ArgumentParser(description='Processes a template file with Jinja2 and YAML data.')
parser.add_argument(
'--template-dir',
type=Path,
dest='template_dir',
action='store',
required=True,
help='Directory containing the Jinja2 template files'
)
parser.add_argument(
'--template-file',
type=Path,
dest='template_file',
action='store',
required=True,
help='Jinja2 template file to process'
)
parser.add_argument(
'--name',
type=str,
dest='name',
action='store',
required=True,
help='Name to be used in the template rendering'
)
parser.add_argument(
'--data-file',
type=Path,
dest='data_file',
action='store',
required=True,
help='YAML file containing data for the template'
)
parser.add_argument(
'--module-handwritten-doc',
type=Path,
dest='module_handwritten_doc',
action='store',
help='Path to the handwritten module documentation if it exists'
)
parser.add_argument(
'--errors-yaml-path',
type=Path,
dest='errors_path',
action='store',
help='Path to the error definition yaml files'
)
parser.add_argument(
'--target-file',
type=Path,
dest='target_file',
action='store',
required=True,
help='Output file for the processed template'
)
args = parser.parse_args()
if not args.template_dir.is_absolute():
raise ValueError("Template directory path must be absolute")
if not args.template_dir.exists():
raise ValueError("Template directory does not exist")
if not args.template_dir.is_dir():
raise ValueError("Template directory path is not a directory")
if not args.template_file.is_absolute():
raise ValueError("Template file path must be absolute")
if not args.template_file.exists():
raise ValueError("Template file does not exist")
if not args.template_file.is_file():
raise ValueError("Template file path is not a file")
if not args.template_file.is_relative_to(args.template_dir):
raise ValueError("Template file path is not relative to template directory")
if not args.data_file.is_absolute():
raise ValueError("Data file path must be absolute")
if not args.data_file.exists():
raise ValueError("Data file does not exist")
if not args.data_file.is_file():
raise ValueError("Data file path is not a file")
if args.data_file.suffix not in ['.yml', '.yaml']:
raise ValueError("Data file must have a .yml or .yaml extension")
if not args.target_file.is_absolute():
raise ValueError("Target file path must be absolute")
if args.target_file.suffix != '.rst':
raise ValueError("Target file must have a .rst extension")
if args.errors_path:
if not args.errors_path.is_absolute():
raise ValueError("Errors yaml directory path must be absolute")
if not args.errors_path.exists():
raise ValueError(f"Errors yaml directory '{args.errors_path}' does not exist")
if not args.errors_path.is_dir():
raise ValueError("Errors yaml directory path is not a directory")
if not args.target_file.parent.exists():
args.target_file.parent.mkdir(parents=True, exist_ok=True)
env = jinja2.Environment(
loader=jinja2.FileSystemLoader(args.template_dir),
trim_blocks=True,
lstrip_blocks=True
)
env.filters['rst_indent'] = rst_indent
env.filters['make_rst_ref'] = make_rst_ref
env.filters['literal_rst'] = literal_rst_filter
template_file_name = args.template_file.relative_to(args.template_dir)
template = env.get_template(str(template_file_name))
data=yaml.safe_load(args.data_file.read_text())
data["errors_sanitized"] = {}
data['error_definitions'] = {}
if args.errors_path and "errors" in data.keys():
for err in data["errors"]:
error_path = err['reference'].split('#')[0]
filename = Path(args.errors_path, error_path.split('/')[-1])
with open(filename.with_suffix(".yaml")) as f:
text = f.read()
yaml_content = yaml.safe_load(text)
data['error_definitions'][error_path] = {}
for err_def in yaml_content['errors']:
data['error_definitions'][error_path][err_def['name']] = err_def['description']
if not error_path in data["errors_sanitized"]:
data["errors_sanitized"][error_path] = []
if len(err['reference'].split('#')) > 1:
data["errors_sanitized"][error_path].append(err['reference'].split('#')[1][1:])
else:
for error in data['error_definitions'][error_path].keys():
data["errors_sanitized"][error_path].append(error)
output = template.render(
name=args.name,
handwritten_module_doc=args.module_handwritten_doc,
data=data,
)
args.target_file.write_text(output)
if __name__ == "__main__":
try:
main()
except Exception as e:
print(f"Error: {e}")
exit(1)