• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..08-Oct-2020-

README.mdH A D08-Oct-202011.7 KiB391292

__init__.pyH A D08-Oct-20200 10

eolian.pyH A D08-Oct-202049.8 KiB1,7001,273

eolian_lib.pyH A D08-Oct-202036.4 KiB784365

generator.pyH A D08-Oct-20208.4 KiB223160

pyratemp.pyH A D08-Oct-202047.9 KiB1,259973

test_eolian.pyH A D08-Oct-202027.9 KiB715585

test_gen_class.templateH A D08-Oct-20201.3 KiB5547

test_gen_namespace.templateH A D08-Oct-20201.3 KiB6858

README.md

1
2Pyolian: Eolian python bindings and a template-based Eolian generator
3=====================================================================
4
5If you need to generate something based on the Eolian database you are in the
6right place! Pyolian is the python bindings for Eolian and provide a clean and
7pythonic API to Eolian; as a bonus it also provide a template-based
8generator to render custom template files, filling them with a full eolian
9context available.
10
11The template engine is really powerfull and provide all the functionalities you
12would expect from a full blow template engine. It is comparable in syntax and
13features to [Jinja2](http://jinja.pocoo.org/) or the Django template system. If
14you are interested on the implementation detail this generator is bases on the
15great [Pyratemp](https://www.simple-is-better.org/template/pyratemp.html).
16
17
18Installation
19============
20
21There is nothing to install to use the bindings or the generator, everything is
22included in the efl source tree and it is intended to work directly inside the
23tree, usually at efl compilation time (before the install step).
24
25The only requirement is that **the source tree must be already built** (not
26installed) because pyolian search the eolian .so/.dll inside the source tree.
27
28If you built the efl tree in a custom location (fe, you build out-of-tree) you
29can tell pyolian where to find the built eolian .so files using the
30`EOLIAN_SO_DIR` environment variable.
31
32
33Command line usage
34==================
35The simplest way to use the generator is from the command line, using the
36`src/scripts/pyolian/generator.py` command, the `--help` option state:
37
38```
39usage: generator.py [-h] [--output FILE] [--cls CLASS_NAME] [--ns NAMESPACE]
40                    [--struct STRUCT_NAME] [--enum ENUM_NAME]
41                    [--alias ALIAS_NAME]
42                    template
43
44Pyolian template based generator.
45
46positional arguments:
47  template              The template file to use. (REQUIRED)
48
49optional arguments:
50  -h, --help            show this help message and exit
51  --output FILE, -o FILE
52                        Where to write the rendered output. If not given will
53                        print to stdout.
54  --cls CLASS_NAME      The full name of the class to render, ex:
55                        Efl.Loop.Timer
56  --ns NAMESPACE        The namespace to render, ex: Efl.Loop
57  --struct STRUCT_NAME  The name of the struct to render, ex:
58                        Efl.Loop.Arguments
59  --enum ENUM_NAME      The name of the enum to render, ex:
60                        Efl.Loop.Handler.Flags
61  --alias ALIAS_NAME    The name of the alias to render, ex: Efl.Font.Size
62```
63
64Basically you just need a template file, then call the generator with at least
65the template file and one of the context options (cls, ns, struct, enum or alias)
66
67Two example templates can be found in the `efl/src/scripts/pyolian` folder,
68from that directory you can run:
69
70```
71./generator.py test_gen_class.template --cls Efl.Loop.Timer
72```
73
74This will rendere the template test_gen_class.template with Efl.Loop.Timer as
75**cls** available in the template, you can of course pass any other class you
76want.
77
78As we did not pass the --output-file parameter the rendered text is printed
79on standard out.
80
81
82Python usage
83============
84
85To use pyolian from python code you need to import **eolian** and/or the
86**Template** class from the pyolian directory. As this python package is not
87installed you need a little hack to import the package, something like this
88should work:
89
90```
91# Use pyolian from source (not installed)
92pyolian_path = os.path.join(<efl_root_path>, 'src', 'scripts')
93sys.path.insert(0, pyolian_path)
94from pyolian import eolian
95from pyolian.generator import Template
96```
97
98Now you can use the full eolian library defined in eolian.py (read the file
99to see all available classes and their properties) or just the Template class
100like this:
101
102```
103t = Template('test_gen_class.template')
104t.render('output.txt', cls='Efl.Loop.Timer', verbose=True)
105```
106
107
108Template syntax
109===============
110
111A template is a normal text file, containing some special placeholders and
112control-structures. there are 3 syntax blocks available:
113
114
115Expressions
116-----------
117
118A template needs some kind of "programming-language" to access variables,
119calculate things, format data, check conditions etc. inside the template. So,
120you could invent and implement an own language therefore -- or you could use an
121already existing and well designed language: Python.
122
123The template engine uses embedded Python-expressions. An expression is
124everything which evaluates to a value, e.g. variables, arithmetics,
125comparisons, boolean expressions, function/method calls, list comprehensions
126etc. And such Python expressions can be directly used in the template.
127
128```
129${expression}$
130```
131
132**expression** can be any expression (e.g. a variable-name, an arithmetic
133calculation, a function-/macro-call etc.), and is evaluated when the template
134is rendered. Whitespace after `${` and before `}$` is ignored.
135
136Generic examples:
137
138  * numbers, strings, lists, tuples, dictionaries, ...: 12.34, "hello", [1,2,3], ...
139  * variable-access: var, mylist[i], mydict["key"], myclass.attr
140  * function/method call: myfunc(...), "foobar".upper()
141  * comparison: (i < 0  or  j > 1024)
142  * arithmetics: 1+2
143
144For details, please read the Python-documentation about Python expressions.
145
146Examples (for a template rendered with cls='Efl.Loop.Timer'):
147
148  * `${cls.full_name}$` => 'Efl.Loop.Timer'
149  * `${cls.full_name.replace('.', '_').lower()}$` => 'efl_loop_timer'
150  * `${cls.base_class.full_name if cls.base_class else None}$` => 'Efl.Loop.Consumer' or None
151  * `${', '.join([i.full_name for i in cls.hierarchy])}$` => 'Efl.Loop.Consumer, Efl.Object'
152
153note the incredible flexibility of **expression**, they are basically small
154pieces of python code, so you can use quite all the python syntax in them.
155
156Also note that the context of the template (**cls** in this example) is always
157an instance of a class defined in eolian.py (eolian.Class in this example), you
158must read this file to understand all the available classes with their
159properties and methods.
160
161
162Comments
163--------
164
165Comments can be used in a template, and they do not appear in the result. They
166are especially useful for (a) documenting the template, (b) temporarily
167disabling parts of the template or (c) suppressing whitespace.
168
169  * `#!...!#` - single-line comment with start and end tag
170  * `#!...`   - single-line-comment until end-of-line, incl. newline!
171
172The second version also comments out the newline, and so can be used at the end
173of a line to remove that newline in the result.
174
175Comments can contain anything, but comments may not appear inside substitutions
176or block-tags.
177
178
179Blocks
180------
181
182The control structures, macros etc. have a special syntax, which consists of a
183start tag (which is named according to the block), optional additional tags,
184and an end-tag:
185
186```
187<!--(...)-->        #! start tag
188   ..
189   ..
190<!--(...)-->        #! optional additional tags (e.g. for elif)
191   ..
192   ..
193<!--(end)-->        #! end tag
194```
195
196All tags must stand on their own line, and no code (except a `#!...` comment) is
197allowed after the tag.
198
199All tags which belong to the same block **must have the same indent!** The
200contents of the block does not need to be indented, although it might improve
201the readability (e.g. in HTML) to indent the contents as well.
202
203Nesting blocks is possible, but the tags of nested blocks must have a different
204indent than the tags of the enclosing blocks. Note that you should either use
205spaces or tabs for indentation. Since the template distinguishes between spaces
206and tabs, if you mix spaces and tabs, two indentations might look the same
207(e.g. 8 spaces or 1 tab) but still be different, which might lead to unexpected
208errors.
209
210There's also a single-line version of a block, which does not need to stand on
211its own line, but can be inserted anywhere in the template. But note that this
212version does not support nesting :
213
214```
215...<!--(...)-->...<!--(...)-->...<!--(end)-->...
216```
217
218if/elif/else
219------------
220
221```
222<!--(if EXPR)-->
223...
224<!--(elif EXPR)-->
225...
226<!--(else)-->
227...
228<!--(end)-->
229```
230
231The `elif` and `else` branches are optional, and there can be any number of
232`elif` branches.
233
234
235for/else
236--------
237
238```
239<!--(for VARS in EXPR)-->
240...
241<!--(else)-->
242...
243<!--(end)-->
244```
245
246VARS can be a single variable name (e.g. myvar) or a comma-separated list of
247variable-names (e.g. i,val).
248
249The `else` branch is optional, and is executed only if the for loop doesn't
250iterate at all.
251
252
253macro
254-----
255
256Macros are user-defined "sub-templates", and so can contain anything a template
257itself can contain. They can have parameters and are normally used to
258encapsulate parts of a template and to create user-defined "functions". Macros
259can be used in expressions, just like a normal variable or function.
260
261```
262<!--(macro MACRONAME)-->
263...
264<!--(end)-->
265```
266
267Note that the last newline (before `<!--(end)-->`) is removed from the macro, so
268that defining and using a macro does not add additional empty lines.
269
270Usage in expressions:
271
272  * `MACRONAME`
273  * `MACRONAME(KEYWORD_ARGS)`
274
275KEYWORD_ARGS can be any number of comma-separated name-value-pairs (name=value,
276...), and these names then will be locally defined inside the macro, in
277addition to those already defined for the whole template.
278
279
280raw
281---
282
283```
284<!--(raw)-->
285...
286<!--(end)-->
287```
288
289Everything inside a `raw` block is passed verbatim to the result.
290
291
292include
293-------
294
295```
296<!--(include)-->FILENAME<!--(end)-->
297```
298
299Include another template-file. Only a single filename (+whitespace) is allowed
300inside of the block; if you want to include several files, use several
301include-blocks.
302
303Note that inclusion of other templates is only supported when loading the
304template from a file. For simplicity and security, FILENAME may not contain a
305path, and only files which are in the same directory as the template itself can
306be included.
307
308
309
310Template context
311================
312
313The following Python-built-in values/functions are available by default in the
314template:
315
316 * `True`
317 * `False`
318 * `None`
319 * `abs()`
320 * `chr()`
321 * `divmod()`
322 * `hash()`
323 * `hex()`
324 * `isinstance()`
325 * `len()`
326 * `max()`
327 * `min()`
328 * `oct()`
329 * `ord()`
330 * `pow()`
331 * `range()`
332 * `round()`
333 * `sorted()`
334 * `sum()`
335 * `unichr()`
336 * `zip()`
337 * `bool()`
338 * `bytes()`
339 * `complex()`
340 * `dict()`
341 * `enumerate()`
342 * `float()`
343 * `int()`
344 * `list()`
345 * `long()`
346 * `reversed()`
347 * `set()`
348 * `str()`
349 * `tuple()`
350 * `unicode()`
351 * `dir()`
352
353Additionally, the functions exists(), default(), setvar() and escape() are
354defined as follows:
355
356 * `exists("varname")`  Test if a variable (or any other object) with the given name exists
357 * `default("expr", default=None)` Tries to evaluate the expression expr.
358 If the evaluation succeeds and the result is not None, its value is returned;
359 otherwise, if the expression contains undefined variables/attributes, the
360 default-value is returned instead. Note that expr has to be quoted.
361 * `setvar("name", "expr")` Although there is often a more elegant way,
362 sometimes it is useful or necessary to set variables in the template.
363 Can also be used to capture the output of e.g. an evaluated macro.
364
365Moreover all the Eolian classes and enums (as defined in eolian.py) are available
366in the template, fe:
367
368 * `Function` eolian.Function (class)
369 * `Eolian_Class_Type` eolian.Eolian_Class_Type (enum)
370
371And some other general utilities:
372
373 * `template_file` name of the template used to generate the output
374 * `date` python datetime object (generation time)
375
376
377Where to find more info
378=======================
379
380 * read the eolian.py file (it declare the full eolian API)
381 * read the generator.py file (it's super simple)
382 * read the original [pyratemp docs](https://www.simple-is-better.org/template/pyratemp.html)
383
384
385Note
386====
387
388This markdown file is mirrored in efl src tree (src/scripts/pyolian) and in
389phab wiki (phab.enlightenment.org/w/pyolian). Don't forget to update the other
390if you change one!
391