• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# Copyright 2015 gRPC authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""Simple Mako renderer.
16
17Just a wrapper around the mako rendering library.
18"""
19
20import getopt
21import importlib.util
22import os
23import pickle
24import shutil
25import sys
26
27import yaml
28from mako.lookup import TemplateLookup
29from mako.runtime import Context
30from mako.template import Template
31
32import bunch
33
34
35# Imports a plugin
36def import_plugin(path):
37    module_name = os.path.basename(path).replace('.py', '')
38    spec = importlib.util.spec_from_file_location(module_name, path)
39    module = importlib.util.module_from_spec(spec)
40    sys.modules[module_name] = module
41    spec.loader.exec_module(module)
42    return module
43
44
45def out(msg):
46    print(msg, file=sys.stderr)
47
48
49def showhelp():
50    out('mako-renderer.py [-o out] [-m cache] [-P preprocessed_input] [-d dict] [-d dict...]'
51        ' [-t template] [-w preprocessed_output]')
52
53
54def main(argv):
55    got_input = False
56    module_directory = None
57    preprocessed_output = None
58    dictionary = {}
59    json_dict = {}
60    got_output = False
61    plugins = []
62    output_name = None
63    got_preprocessed_input = False
64    output_merged = None
65
66    try:
67        opts, args = getopt.getopt(argv, 'hM:m:d:o:p:t:P:w:')
68    except getopt.GetoptError:
69        out('Unknown option')
70        showhelp()
71        sys.exit(2)
72
73    for opt, arg in opts:
74        if opt == '-h':
75            out('Displaying showhelp')
76            showhelp()
77            sys.exit()
78        elif opt == '-o':
79            if got_output:
80                out('Got more than one output')
81                showhelp()
82                sys.exit(3)
83            got_output = True
84            output_name = arg
85        elif opt == '-m':
86            if module_directory is not None:
87                out('Got more than one cache directory')
88                showhelp()
89                sys.exit(4)
90            module_directory = arg
91        elif opt == '-M':
92            if output_merged is not None:
93                out('Got more than one output merged path')
94                showhelp()
95                sys.exit(5)
96            output_merged = arg
97        elif opt == '-P':
98            assert not got_preprocessed_input
99            assert json_dict == {}
100            sys.path.insert(
101                0,
102                os.path.abspath(
103                    os.path.join(os.path.dirname(sys.argv[0]), 'plugins')))
104            with open(arg, 'rb') as dict_file:
105                dictionary = pickle.load(dict_file)
106            got_preprocessed_input = True
107        elif opt == '-d':
108            assert not got_preprocessed_input
109            with open(arg, 'r') as dict_file:
110                bunch.merge_json(
111                    json_dict,
112                    yaml.load(dict_file.read(), Loader=yaml.FullLoader))
113        elif opt == '-p':
114            plugins.append(import_plugin(arg))
115        elif opt == '-w':
116            preprocessed_output = arg
117
118    if not got_preprocessed_input:
119        for plugin in plugins:
120            plugin.mako_plugin(json_dict)
121        if output_merged:
122            with open(output_merged, 'w') as yaml_file:
123                yaml_file.write(yaml.dump(json_dict))
124        for k, v in json_dict.items():
125            dictionary[k] = bunch.to_bunch(v)
126
127    if preprocessed_output:
128        with open(preprocessed_output, 'wb') as dict_file:
129            pickle.dump(dictionary, dict_file)
130
131    cleared_dir = False
132    for arg in args:
133        got_input = True
134        with open(arg) as f:
135            srcs = list(yaml.load_all(f.read(), Loader=yaml.FullLoader))
136        for src in srcs:
137            if isinstance(src, str):
138                assert len(srcs) == 1
139                template = Template(src,
140                                    filename=arg,
141                                    module_directory=module_directory,
142                                    lookup=TemplateLookup(directories=['.']))
143                with open(output_name, 'w') as output_file:
144                    template.render_context(Context(output_file, **dictionary))
145            else:
146                # we have optional control data: this template represents
147                # a directory
148                if not cleared_dir:
149                    if not os.path.exists(output_name):
150                        pass
151                    elif os.path.isfile(output_name):
152                        os.unlink(output_name)
153                    else:
154                        shutil.rmtree(output_name, ignore_errors=True)
155                    cleared_dir = True
156                items = []
157                if 'foreach' in src:
158                    for el in dictionary[src['foreach']]:
159                        if 'cond' in src:
160                            args = dict(dictionary)
161                            args['selected'] = el
162                            if not eval(src['cond'], {}, args):
163                                continue
164                        items.append(el)
165                    assert items
166                else:
167                    items = [None]
168                for item in items:
169                    args = dict(dictionary)
170                    args['selected'] = item
171                    item_output_name = os.path.join(
172                        output_name,
173                        Template(src['output_name']).render(**args))
174                    if not os.path.exists(os.path.dirname(item_output_name)):
175                        os.makedirs(os.path.dirname(item_output_name))
176                    template = Template(
177                        src['template'],
178                        filename=arg,
179                        module_directory=module_directory,
180                        lookup=TemplateLookup(directories=['.']))
181                    with open(item_output_name, 'w') as output_file:
182                        template.render_context(Context(output_file, **args))
183
184    if not got_input and not preprocessed_output:
185        out('Got nothing to do')
186        showhelp()
187
188
189if __name__ == '__main__':
190    main(sys.argv[1:])
191