from dotenv import load_dotenv import jinja2 from jinja2.ext import Extension from jinja2.lexer import Token from markupsafe import Markup import os from pyramid.settings import asbool import textwrap def asbool_filter(value): return asbool(value) def multiline_filter(value): # strip only the leading whitespaces from the first line value = value.strip(' ').strip('\n') if value: lines = textwrap.dedent(value).split('\n') return '\n ' + '\n '.join(lines) return '' def ini_filter(value): if isinstance(value, Markup): return value if not isinstance(value, str): return value return value.replace('%', '%%') class IniEverythingExtension(Extension): """ Insert a `|ini` filter at the end of every variable substitution. This will ensure that all injected values are converted to INI. """ def filter_stream(self, stream): # This is based on # https://github.com/indico/indico/blob/master/indico/web/flask/templating.py. for token in stream: if token.type == 'variable_end': yield Token(token.lineno, 'pipe', '|') yield Token(token.lineno, 'name', 'ini') yield token def render_template(path, context): base, name = os.path.split(path) if not base: base = os.getcwd() env = jinja2.Environment( loader=jinja2.FileSystemLoader([base]), undefined=jinja2.StrictUndefined, extensions=[IniEverythingExtension], ) env.filters['bool'] = asbool_filter env.filters['multiline'] = multiline_filter env.filters['ini'] = ini_filter template = env.get_template(name) return template.render(context) def parse_args(): parser = argparse.ArgumentParser() parser.add_argument('--env') parser.add_argument('--env-file') parser.add_argument('--template', default='site.ini.in') parser.add_argument('-o', '--output-file', default='site.ini') def main(cli, args): if args.env_file: load_dotenv(args.env_file) else: override = args.env or os.environ.get('DOTENV') for path in ( f'.env.{override}.local' if override else None, f'.env.{override}' if override else None, '.env.local', '.env', ): if path and os.path.exists(path): load_dotenv(path) result = render_template(args.template, { 'env': os.environ, }) with open(args.output_file, 'w', encoding='utf8') as fp: fp.write(result.rstrip() + '\n')