Skip to content

General purpose static text generator using Jinja2 and other python template engines

License

Notifications You must be signed in to change notification settings

moremoban/moban

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mó bǎn - 模板 General purpose static text generator

https://raw.githubusercontent.com/pyexcel/pyexcel.github.io/master/images/patreon.png https://api.travis-ci.org/moremoban/moban.svg?branch=master https://dev.azure.com/moremoban/moban/_apis/build/status/moremoban.moban https://pepy.tech/badge/moban https://readthedocs.org/projects/moban/badge/?version=latest
Author:C.W. and its contributors (See contributors.rst)
Issues:https://github.com/moremoban/moban/issues
License:MIT

Announcement

In version 0.8.0, moban.plugins.jinja2.tests.files is moved to moban-ansible package. moban.plugins.jinja2.filters.github is moved to moban-jinja2-github package Please install them for backward compatibility.

Quick start

$ export HELLO="world"
$ moban "{{HELLO}}"
world

Or

$ export HELLO="world"
$ echo "{{HELLO}}" | moban

Or simply

$ HELLO="world" moban "{{HELLO}}"

A bit formal example:

$ moban -c data.yml -t my.template
world

Given data.yml as:

hello: world

and my.template as:

{{hello}}

Please note that data.yml will take precedence over environment variables.

Template inheritance and custom template directories

Suppose there exists shared/base.jj2, and two templates child1.jj2 and child2.jj2 derives from it. You can do:

$ moban -t child1.jj2 -td shared -o child1
$ moban -t child2.jj2 -td shared -o child2

Data overload and custom data directories

Effectively each data file you give to moban, it overrides environment variables. Still you can have different layers of data. For example, you can have shared/company_info.yml, use project1.yml for project 1 and project2.yml for project 2. In each of the derived data file, simply mention:

overrides: company_info.yml
...

Here is the command line to use your data:

$ moban -cd shared -c project1.yaml -t README.jj2

Custom jinja2 extension

moban allows the injection of user preferred jinja2 extensions:

$ moban -e jj2=jinja2_time.TimeExtension ...

Well, can I nick some existing functions as filters, tests? Or create a global from another library?

Sure, you can use the same '-e' syntax:

$ moban -e jinja2=filter:module.path.filter_function \
           jinja2=test:module.path.test_function \
           jinja2=global:identifier=module.path.variable

In this case, you would have to include the external library in your own requirements.txt

Here is an example:

$ moban -e jinja2=filter:moban.externals.file_system.url_join \
  jinja2=test:moban.externals.file_system.exists \
  jinja2=global:description=moban.constants.PROGRAM_DESCRIPTION \
  -t "{{ 'a'|url_join('b')}} {{'b' is exists}}"

Can I write my own jinja2 test, filter and/or globals?

moban allows the freedom of craftsmanship. Please refer to the docs for more details. Here is an example:

import sys
import base64

from moban.plugins.jinja2.extensions import JinjaFilter


@JinjaFilter()
def base64encode(string):
    if sys.version_info[0] > 2:
        content = base64.b64encode(string.encode("utf-8"))
        content = content.decode("utf-8")
    else:
        content = base64.b64encode(string)
    return content

And you can use it within your jinja2 template, mytest.jj2:

{{ 'abc' | base64encode }}

Assume that the custom example was saved in custom-jj2-plugin

$ moban -pd custom-jj2-plugin -t mytest.jj2 ...

Moban will then load your custom jinja2 functions

Slim template syntax for jinja2

with moban-slim installed,

Given a data.json file with the following content

{
  "person": {
    "firstname": "Smith",
    "lastname": "Jones",
  },
}
$ moban --template-type slim -c data.json  "{{person.firstname}} {{person.lastname}}"
Smith Jones

Handlebars.js template

With moban-handlebars installed,

Given a data.json file with the following content

{
  "person": {
    "firstname": "Yehuda",
    "lastname": "Katz",
  },
}
$ moban --template-type handlebars -c data.json  "{{person.firstname}} {{person.lastname}}"
Yehuda Katz

For handlebars.js users, yes, the example was copied from handlebarjs.com. The aim is to show off what we can do.

Let's continue with a bit more fancy feature:

$ moban --template-type handlebars -c data.json "{{#with person}}{{firstname}} {{lastname}} {{/with}}"

Moban's way of pybar3 usage:

Let's save the following file a script.py under helper_and_partial folder:

from moban_handlebars.api import Helper, register_partial

register_partial('header', '<h1>People</h1>')


@Helper('list')
def _list(this, options, items):
    result = [u'<ul>']
    for thing in items:
        result.append(u'<li>')
        result.extend(options['fn'](thing))
        result.append(u'</li>')
    result.append(u'</ul>')
    return result

And given data.json reads as the following:

{
    "people":[
        {"name": "Bill", "age": 100},
        {"name": "Bob", "age": 90},
        {"name": "Mark", "age": 25}
    ]
}

Let's invoke handlebar template:

$ moban --template-type hbs -pd helper_and_partial -c data.json "{{>header}}{{#list people}}{{name}} {{age}}{{/list}}"
Handlebars-ing {{>header}... to moban.output
Handlebarsed 1 file.
$ cat moban.output
<h1>People</h1><ul><li>Bill 100</li><li>Bob 90</li><li>Mark 25</li></ul>

Velocity template

With moban-velocity installed,

Given the following data.json:

{"people":
    [
        {"name": "Bill", "age": 100},
        {"name": "Bob", "age": 90},
        {"name": "Mark", "age": 25}
    ]
}

And given the following velocity.template:

Old people:
#foreach ($person in $people)
 #if($person.age > 70)
  $person.name
 #end
#end

Third person is $people[2].name

moban can do the template:

$ moban --template-type velocity -c data.json -t velocity.template
Old people:

Bill

Bob


Third person is Mark

Can I write my own template engine?

Yes and please check for more details.

Given the following template type function, and saved in custom-plugin dir:

from moban.core.content_processor import ContentProcessor


@ContentProcessor("de-duplicate", "De-duplicating", "De-duplicated")
def de_duplicate(content: str, options: dict) -> str:
    lines = content.split(b'\n')
    new_lines = []
    for line in lines:
        if line not in new_lines:
            new_lines.append(line)
    return b'\n'.join(new_lines)

You can start using it like this:

$ moban --template-type de-duplicate -pd custom-plugin -t duplicated_content.txt

TOML data format

moban-anyconfig should be installed first.

Given the following toml file, sample.toml:

title = "TOML Example"
[owner]
name = "Tom Preston-Werner"

You can do:

$ moban -c sample.toml "{{owner.name}} made {{title}}"
Tom Preston-Werner made TOML Example

Not limited to toml, you can supply moban with the following data formats:

Always supported formats, quoting from python-anyconfig
Format Type Requirement
JSON json json (standard lib) or simplejson
Ini-like ini configparser (standard lib)
Pickle pickle pickle (standard lib)
XML xml ElementTree (standard lib)
Java properties properties None (native implementation with standard lib)
B-sh shellvars None (native implementation with standard lib)

For any of the following data formats, you elect to install by yourself.

Supported formats by pluggable backend modules
Format Type Required backend
Amazon Ion ion anyconfig-ion-backend
BSON bson anyconfig-bson-backend
CBOR cbor anyconfig-cbor-backend or anyconfig-cbor2-backend
ConifgObj configobj anyconfig-configobj-backend
MessagePack msgpack anyconfig-msgpack-backend

Or you could choose to install all:

$ pip install moban-anyconfig[all-backends]

Why not to use python-anyconfig itself, but yet another package?

moban gives you a promise of any location which python-anyconfig does not support.

Why do it mean 'any location'?

Thanks to pyfilesystem 2, moban is able to read data back from git repo, pypi package, http(s), zip, tar, ftp, s3 or you name it.

Templates and configuration files over HTTP(S)

httpfs should be installed first.

With httpfs, moban can access any files over http(s) as its template or data file:

$ moban -t 'https://raw.githubusercontent.com/moremoban/pypi-mobans/dev/templates/_version.py.jj2'\
  -c 'https://raw.githubusercontent.com/moremoban/pypi-mobans/dev/config/data.yml'\
  -o _version.py

In an edge case, if github repo's public url is given, this github repo shall not have sub repos. This library will fail to translate sub-repo as url. No magic.

Templates and configuration files in a git repo

gitfs2 is optional since v0.7.0 but was installed by default since v0.6.1

You can do the following with moban:

$ moban -t 'git://github.com/moremoban/pypi-mobans.git!/templates/_version.py.jj2' \
        -c 'git://github.com/moremoban/pypi-mobans.git!/config/data.yml' \
        -o _version.py
Info: Found repo in /Users/jaska/Library/Caches/gitfs2/repos/pypi-mobans
Templating git://github.com/moremoban/pypi-mobans.git!/templates/_version.py.jj2 to _version.py
Templated 1 file.
$ cat _version.py
__version__ = "0.1.1rc3"
__author__ = "C.W."

Templates and configuration files in a python package

pypifs is optional since v0.7.0 but was installed by default since v0.6.1

You can do the following with moban:

$ moban -t 'pypi://pypi-mobans-pkg/resources/templates/_version.py.jj2' \
        -c 'pypi://pypi-mobans-pkg/resources/config/data.yml' \
        -o _version.py
Collecting pypi-mobans-pkg
....
Installing collected packages: pypi-mobans-pkg
Successfully installed pypi-mobans-pkg-0.0.7
Templating pypi://pypi-mobans-pkg/resources/templates/_version.py.jj2 to _version.py
Templated 1 file.
$ cat _version.py
__version__ = "0.1.1rc3"
__author__ = "C.W."

Work with S3 and other cloud based file systems

Please install fs-s3fs:

$ pip install fs-s3fs

Then you can access your files in s3 bucket:

$ moban -c s3://${client_id}:${client_secrect}@moremoban/s3data.yml \
        -o 'zip://my.zip!/moban.output' {{hello}}
$ unzip my.zip
$ cat moban.output
world

Where the configuration sits in a s3 bucket, the output is a file in a zip. The content of s3data.yaml is:

So what can I do with it

Here is a list of other usages:

  1. Django Mobans, templates for django, docker etc.
  2. Math Sheets, generate custom math sheets in pdf

At scale, continous templating for open source projects

https://github.com/moremoban/moban/raw/dev/docs/images/moban-in-pyexcel-demo.gif

moban enabled continuous templating in pyexcel and coala project to keep documentation consistent across the documentations of individual libraries in the same organisation. Here is the primary use case of moban, as of now:

https://github.com/moremoban/yehua/raw/dev/docs/source/_static/yehua-story.png

Usage beyond command line

All use cases are documented

Support

If you like moban, please support me on github, patreon or bounty source to maintain the project and develop it further.

With your financial support, I will be able to invest a little bit more time in coding, documentation and writing interesting extensions.

Vision

Any template, any data in any location

moban started with bringing the high performance template engine (JINJA2) for web into static text generation.

moban can use other python template engine: mako, handlebars, velocity, haml, slim and tornado, can read other data format: json and yaml, and can access both template file and configuration file in any location: zip, git, pypi package, s3, etc.

Credit

jinja2-fsloader is the key component to enable PyFilesystem2 support in moban v0.6x. Please show your stars there too!

Installation

You can install it via pip:

$ pip install moban

or clone it and install it:

$ git clone https://github.com/moremoban/moban.git
$ cd moban
$ python setup.py install

CLI documentation

usage: moban [-h] [-c CONFIGURATION] [-t TEMPLATE] [-o OUTPUT]
             [-td [TEMPLATE_DIR [TEMPLATE_DIR ...]]]
             [-pd [PLUGIN_DIR [PLUGIN_DIR ...]]] [-cd CONFIGURATION_DIR]
             [-m MOBANFILE] [-g GROUP] [--template-type TEMPLATE_TYPE]
             [-d DEFINE [DEFINE ...]] [-e EXTENSION [EXTENSION ...]] [-f]
             [--exit-code] [-V] [-v]
             [template]

Static text generator using any template, any data and any location.

positional arguments:
  template              string templates

optional arguments:
  -h, --help            show this help message and exit
  -c CONFIGURATION, --configuration CONFIGURATION
                        the data file
  -t TEMPLATE, --template TEMPLATE
                        the template file
  -o OUTPUT, --output OUTPUT
                        the output file

Advanced options:
  For better control

  -td [TEMPLATE_DIR [TEMPLATE_DIR ...]], --template_dir [TEMPLATE_DIR [TEMPLATE_DIR ...]]
                        add more directories for template file lookup
  -cd CONFIGURATION_DIR, --configuration_dir CONFIGURATION_DIR
                        the directory for configuration file lookup
  -pd [PLUGIN_DIR [PLUGIN_DIR ...]], --plugin_dir [PLUGIN_DIR [PLUGIN_DIR ...]]
                        add more directories for plugin lookup
  -m MOBANFILE, --mobanfile MOBANFILE
                        custom moban file
  -g GROUP, --group GROUP
                        a subset of targets
  --template-type TEMPLATE_TYPE
                        the template type, default is jinja2
  -d DEFINE [DEFINE ...], --define DEFINE [DEFINE ...]
                        to supply additional or override predefined variables,
                        format: VAR=VALUEs
  -e EXTENSION [EXTENSION ...], --extension EXTENSION [EXTENSION ...]
                        to to TEMPLATE_TYPE=EXTENSION_NAME
  -f                    force moban to template all files despite of
                        .moban.hashes

Developer options:
  For debugging and development

  --exit-code           by default, exist code 0 means no error, 1 means error
                        occured. It tells moban to change 1 for changes, 2 for
                        error occured
  -V, --version         show program's version number and exit
  -v                    show verbose, try -v, -vv, -vvv