Archive

Tag Archives: python template mako jinja2

In game development performance is very important factor, especially in case of hi-load on-line multi player games in social networks. As we know one of useful tricks is to hard code as much as possible to avoid huge reading or initialization.

In the other hand, it’s hard to support such hard-coded solution (they even call it anti-pattern). For example, we have list of items in the game. It should be the same on servers side and on client side. If list is pretty long then it’s easy to make a fatal mistake, so, lists would be different. Better approach is to hold all the data in single place handy to modify/support and automation system to synchronize the data with all other places in the source code. There are a lot of benefits. As it’s single place then it’s easy to support and avoid difference mistakes. As it automated then we don’t need developer to spend his time to update it.

I created such system. I called it transistor. There are 3 parts:

  • The python script transistor.py
  • Data composed into CSV
  • Source code with hard coded section and template to fill it

We can use one or more CSV tables. One table may have reference(s) as index(es) to another. CSV content should be available via HTTP or locally. I use Google Docs Spreadsheets for that. First line of each CSV table should have case-sensitive fields (columns) names. Other rows have the data. Later the script will parse those data into the list of dictionaries.

My first try was using of pure python string formatting as template. It means that the template and its logic were in python code on the script side. There were bad design and huge ugly implementation. Well, if you have a lot of code then it is almost always bad architecture. There should be very small amount of code. So, I declined that prototype. The second (and the best) design approach was to make that script universal and move all specific template data and logic to the template situated in the same source code file (in comments).

/*// gunTypes begin{% set gunTypes = tables.gunTypes -%}{%- for g in gunTypes -%}{%- if g.shoot_width -%}{%- if loop.index0 > 0 %},{% endif -%}new GunType({{g.id}}, new Shape({{g.base_width}}, {{g.base_height}}, new byte[]{                        {{g.base_shape|replace('n', 'n                                                        ')}}        }), new Shape({{g.shoot_width}}, {{g.shoot_height}}, new byte[]{                        {{g.shoot_shape|replace('n', 'n                                                        ')}}        })){%- endif %}{%- endfor %}// gunTypes template end */        // ... here would be generated content ...// gunTypes end

Hard-coded section in the source code file has header, footer and template delimiter. There are the template between header and template delimiter and the generated content between delimiter and the footer. Usage example:

python transistor.py --file=GunTypesDict.cs --section=gunTypes                --table=gunTypes                --url="https://docs.google.com/spreadsheet/pub?...&output=csv"
or more complicated with 2 tables:
python transistor.py --file=LocationsDict.cs --section=locations                --table=fields --table=locations                --url="https://docs.google.com/spreadsheet/pub?...&output=csv"                --url="https://docs.google.com/spreadsheet/pub?...&output=csv"
After such data publishing we have the same source code like:
/*// gunTypes begin{% set gunTypes = tables.gunTypes -%}{%- for g in gunTypes -%}{%- if g.shoot_width -%}{%- if loop.index0 > 0 %},{% endif -%}new GunType({{g.id}}, new Shape({{g.base_width}}, {{g.base_height}}, new byte[]{                        {{g.base_shape|replace('n', 'n                                                        ')}}        }), new Shape({{g.shoot_width}}, {{g.shoot_height}}, new byte[]{                        {{g.shoot_shape|replace('n', 'n                                                        ')}}        })){%- endif %}{%- endfor %}// gunTypes template end */new GunType(0, new Shape(1, 2, new byte[]{                        1,1        }), new Shape(1, 1, new byte[]{                        5        })),new GunType(1, new Shape(2, 1, new byte[]{                        1,1        }), new Shape(2, 2, new byte[]{                        0,5,                        5,0        }))// gunTypes end
Advertisements
At line of development process automation, I designed and built special script and methodology for easy and fast generation of hard coded data definition in the source code by provided relative data composed separately in the spreadsheets. The script loads csv data and render it into special section in existing source code file by the template provided in the same file.
The script is implemented in python, so, I need some cool template engine. I was looking at Mako and Jinja2.
After brief research I was try Mako. But it was very ugly at line of source code generation. Just look at this example:

# coma separator example

print Template("""<% c = 0 %>% for i in list:<%    coma = ''    if c < len(list) - 1: coma = ','    c += 1%>${i}${coma}% endfor""").render(list=(0,1,2))

So, I decided to use Jinja2. It’s much better for source code generation and looks like more power template engine as a whole. Here is several cool features I’m able to implement with jinja2:

  • separator
  • multiline indentation
  • correct new lines

Template:

{% set gunTypes = tables.gunTypes -%}{%- for g in gunTypes -%}{%- if g.shoot_width -%}{%- if loop.index0 > 0 %},{% endif -%}new GunType(                   {{g.id}},                   new Shape({{g.base_width}}, {{g.base_height}}, new <int>[                           {{g.base_shape|replace('n', 'n                               ')}}                   ]),                   new Shape({{g.shoot_width}}, {{g.shoot_height}}, new <int>[                           {{g.shoot_shape|replace('n', 'n                               ')}}                   ])           ){%- endif %}{%- endfor %}

Result:

new GunType(                   0,                   new Shape(1, 2, new <int>[                           1,1                   ]),                   new Shape(1, 1, new <int>[                           5                   ])           ),new GunType(                   1,                   new Shape(2, 1, new <int>[                           1,1                   ]),                   new Shape(2, 2, new <int>[                           0,5,                           5,0                   ])           )