pongo2: github.com/flosch/pongo2 Index | Files

package pongo2

import "github.com/flosch/pongo2"

A Django-syntax like template-engine

Blog posts about pongo2 (including introduction and migration): https://www.florian-schlachter.de/?tag=pongo2

Complete documentation on the template language: https://docs.djangoproject.com/en/dev/topics/templates/

Try out pongo2 live in the pongo2 playground: https://www.florian-schlachter.de/pongo2/

Make sure to read README.md in the repository as well.

A tiny example with template strings:

(Snippet on playground: https://www.florian-schlachter.de/pongo2/?id=1206546277)

// Compile the template first (i. e. creating the AST)
tpl, err := pongo2.FromString("Hello {{ name|capfirst }}!")
if err != nil {
    panic(err)
}
// Now you can render the template with the given
// pongo2.Context how often you want to.
out, err := tpl.Execute(pongo2.Context{"name": "fred"})
if err != nil {
    panic(err)
}
fmt.Println(out) // Output: Hello Fred!

Index

Package Files

context.go doc.go error.go filters.go filters_builtin.go helpers.go lexer.go nodes.go nodes_html.go nodes_wrapper.go options.go parser.go parser_document.go parser_expression.go pongo2.go tags.go tags_autoescape.go tags_block.go tags_comment.go tags_cycle.go tags_extends.go tags_filter.go tags_firstof.go tags_for.go tags_if.go tags_ifchanged.go tags_ifequal.go tags_ifnotequal.go tags_import.go tags_include.go tags_lorem.go tags_macro.go tags_now.go tags_set.go tags_spaceless.go tags_ssi.go tags_templatetag.go tags_widthratio.go tags_with.go template.go template_loader.go template_sets.go value.go variable.go

Constants

const (
    TokenError = iota
    EOF

    TokenHTML

    TokenKeyword
    TokenIdentifier
    TokenString
    TokenNumber
    TokenSymbol
)
const Version = "dev"

Version string

Variables

var (

    // Available symbols in pongo2 (within filters/tag)
    TokenSymbols = []string{

        "{{-", "-}}", "{%-", "-%}",

        "==", ">=", "<=", "&&", "||", "{{", "}}", "{%", "%}", "!=", "<>",

        "(", ")", "+", "-", "*", "<", ">", "/", "^", ",", ".", "!", "|", ":", "=", "%",
    }

    // Available keywords in pongo2
    TokenKeywords = []string{"in", "and", "or", "not", "true", "false", "as", "export"}
)
var (

    // DefaultLoader allows the default un-sandboxed access to the local file
    // system and is being used by the DefaultSet.
    DefaultLoader = MustNewLocalFileSystemLoader("")

    // DefaultSet is a set created for you for convinience reasons.
    DefaultSet = NewSet("default", DefaultLoader)

    // Methods on the default set
    FromString           = DefaultSet.FromString
    FromBytes            = DefaultSet.FromBytes
    FromFile             = DefaultSet.FromFile
    FromCache            = DefaultSet.FromCache
    RenderTemplateString = DefaultSet.RenderTemplateString
    RenderTemplateFile   = DefaultSet.RenderTemplateFile

    // Globals for the default set
    Globals = DefaultSet.Globals
)

func ApplyFilter Uses

func ApplyFilter(name string, value *Value, param *Value) (*Value, *Error)

ApplyFilter applies a filter to a given value using the given parameters. Returns a *pongo2.Value or an error.

func FilterExists Uses

func FilterExists(name string) bool

FilterExists returns true if the given filter is already registered

func RegisterFilter Uses

func RegisterFilter(name string, fn FilterFunction) error

RegisterFilter registers a new filter. If there's already a filter with the same name, RegisterFilter will panic. You usually want to call this function in the filter's init() function: http://golang.org/doc/effective_go.html#init

See http://www.florian-schlachter.de/post/pongo2/ for more about writing filters and tags.

func RegisterTag Uses

func RegisterTag(name string, parserFn TagParser) error

Registers a new tag. You usually want to call this function in the tag's init() function: http://golang.org/doc/effective_go.html#init

See http://www.florian-schlachter.de/post/pongo2/ for more about writing filters and tags.

func ReplaceFilter Uses

func ReplaceFilter(name string, fn FilterFunction) error

ReplaceFilter replaces an already registered filter with a new implementation. Use this function with caution since it allows you to change existing filter behaviour.

func ReplaceTag Uses

func ReplaceTag(name string, parserFn TagParser) error

Replaces an already registered tag with a new implementation. Use this function with caution since it allows you to change existing tag behaviour.

func SetAutoescape Uses

func SetAutoescape(newValue bool)

type Context Uses

type Context map[string]interface{}

A Context type provides constants, variables, instances or functions to a template.

pongo2 automatically provides meta-information or functions through the "pongo2"-key. Currently, context["pongo2"] contains the following keys:

1. version: returns the version string

Template examples for accessing items from your context:

{{ myconstant }}
{{ myfunc("test", 42) }}
{{ user.name }}
{{ pongo2.version }}

func (Context) Update Uses

func (c Context) Update(other Context) Context

Update updates this context with the key/value-pairs from another context.

type Error Uses

type Error struct {
    Template  *Template
    Filename  string
    Line      int
    Column    int
    Token     *Token
    Sender    string
    OrigError error
}

The Error type is being used to address an error during lexing, parsing or execution. If you want to return an error object (for example in your own tag or filter) fill this object with as much information as you have. Make sure "Sender" is always given (if you're returning an error within a filter, make Sender equals 'filter:yourfilter'; same goes for tags: 'tag:mytag'). It's okay if you only fill in ErrorMsg if you don't have any other details at hand.

func (*Error) Error Uses

func (e *Error) Error() string

Returns a nice formatted error string.

func (*Error) RawLine Uses

func (e *Error) RawLine() (line string, available bool, outErr error)

RawLine returns the affected line from the original template, if available.

type ExecutionContext Uses

type ExecutionContext struct {
    Autoescape bool
    Public     Context
    Private    Context
    Shared     Context
    // contains filtered or unexported fields
}

ExecutionContext contains all data important for the current rendering state.

If you're writing a custom tag, your tag's Execute()-function will have access to the ExecutionContext. This struct stores anything about the current rendering process's Context including the Context provided by the user (field Public). You can safely use the Private context to provide data to the user's template (like a 'forloop'-information). The Shared-context is used to share data between tags. All ExecutionContexts share this context.

Please be careful when accessing the Public data. PLEASE DO NOT MODIFY THE PUBLIC CONTEXT (read-only).

To create your own execution context within tags, use the NewChildExecutionContext(parent) function.

func NewChildExecutionContext Uses

func NewChildExecutionContext(parent *ExecutionContext) *ExecutionContext

func (*ExecutionContext) Error Uses

func (ctx *ExecutionContext) Error(msg string, token *Token) *Error

func (*ExecutionContext) Logf Uses

func (ctx *ExecutionContext) Logf(format string, args ...interface{})

func (*ExecutionContext) OrigError Uses

func (ctx *ExecutionContext) OrigError(err error, token *Token) *Error

type Expression Uses

type Expression struct {
    // contains filtered or unexported fields
}

func (*Expression) Evaluate Uses

func (expr *Expression) Evaluate(ctx *ExecutionContext) (*Value, *Error)

func (*Expression) Execute Uses

func (expr *Expression) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error

func (*Expression) FilterApplied Uses

func (expr *Expression) FilterApplied(name string) bool

func (*Expression) GetPositionToken Uses

func (expr *Expression) GetPositionToken() *Token

type FilterFunction Uses

type FilterFunction func(in *Value, param *Value) (out *Value, err *Error)

FilterFunction is the type filter functions must fulfil

type IEvaluator Uses

type IEvaluator interface {
    INode
    GetPositionToken() *Token
    Evaluate(*ExecutionContext) (*Value, *Error)
    FilterApplied(name string) bool
}

type INode Uses

type INode interface {
    Execute(*ExecutionContext, TemplateWriter) *Error
}

type INodeTag Uses

type INodeTag interface {
    INode
}

type LocalFilesystemLoader Uses

type LocalFilesystemLoader struct {
    // contains filtered or unexported fields
}

LocalFilesystemLoader represents a local filesystem loader with basic BaseDirectory capabilities. The access to the local filesystem is unrestricted.

func MustNewLocalFileSystemLoader Uses

func MustNewLocalFileSystemLoader(baseDir string) *LocalFilesystemLoader

MustNewLocalFileSystemLoader creates a new LocalFilesystemLoader instance and panics if there's any error during instantiation. The parameters are the same like NewLocalFileSystemLoader.

func NewLocalFileSystemLoader Uses

func NewLocalFileSystemLoader(baseDir string) (*LocalFilesystemLoader, error)

NewLocalFileSystemLoader creates a new LocalFilesystemLoader and allows templatesto be loaded from disk (unrestricted). If any base directory is given (or being set using SetBaseDir), this base directory is being used for path calculation in template inclusions/imports. Otherwise the path is calculated based relatively to the including template's path.

func (*LocalFilesystemLoader) Abs Uses

func (fs *LocalFilesystemLoader) Abs(base, name string) string

Abs resolves a filename relative to the base directory. Absolute paths are allowed. When there's no base dir set, the absolute path to the filename will be calculated based on either the provided base directory (which might be a path of a template which includes another template) or the current working directory.

func (*LocalFilesystemLoader) Get Uses

func (fs *LocalFilesystemLoader) Get(path string) (io.Reader, error)

Get reads the path's content from your local filesystem.

func (*LocalFilesystemLoader) SetBaseDir Uses

func (fs *LocalFilesystemLoader) SetBaseDir(path string) error

SetBaseDir sets the template's base directory. This directory will be used for any relative path in filters, tags and From*-functions to determine your template. See the comment for NewLocalFileSystemLoader as well.

type NodeWrapper Uses

type NodeWrapper struct {
    Endtag string
    // contains filtered or unexported fields
}

func (*NodeWrapper) Execute Uses

func (wrapper *NodeWrapper) Execute(ctx *ExecutionContext, writer TemplateWriter) *Error

type Options Uses

type Options struct {
    // If this is set to true the first newline after a block is removed (block, not variable tag!). Defaults to false.
    TrimBlocks bool

    // If this is set to true leading spaces and tabs are stripped from the start of a line to a block. Defaults to false
    LStripBlocks bool
}

Options allow you to change the behavior of template-engine. You can change the options before calling the Execute method.

func (*Options) Update Uses

func (opt *Options) Update(other *Options) *Options

Update updates this options from another options.

type Parser Uses

type Parser struct {
    // contains filtered or unexported fields
}

The parser provides you a comprehensive and easy tool to work with the template document and arguments provided by the user for your custom tag.

The parser works on a token list which will be provided by pongo2. A token is a unit you can work with. Tokens are either of type identifier, string, number, keyword, HTML or symbol.

(See Token's documentation for more about tokens)

func (*Parser) Consume Uses

func (p *Parser) Consume()

Consume one token. It will be gone forever.

func (*Parser) ConsumeN Uses

func (p *Parser) ConsumeN(count int)

Consume N tokens. They will be gone forever.

func (*Parser) Count Uses

func (p *Parser) Count() int

Returns the total token count.

func (*Parser) Current Uses

func (p *Parser) Current() *Token

Returns the current token.

func (*Parser) Error Uses

func (p *Parser) Error(msg string, token *Token) *Error

Error produces a nice error message and returns an error-object. The 'token'-argument is optional. If provided, it will take the token's position information. If not provided, it will automatically use the CURRENT token's position information.

func (*Parser) Get Uses

func (p *Parser) Get(i int) *Token

Returns tokens[i] or NIL (if i >= len(tokens))

func (*Parser) GetR Uses

func (p *Parser) GetR(shift int) *Token

Returns tokens[current-position + shift] or NIL (if (current-position + i) >= len(tokens))

func (*Parser) Match Uses

func (p *Parser) Match(typ TokenType, val string) *Token

Returns the CURRENT token if the given type AND value matches. Consumes this token on success.

func (*Parser) MatchOne Uses

func (p *Parser) MatchOne(typ TokenType, vals ...string) *Token

Returns the CURRENT token if the given type AND *one* of the given values matches. Consumes this token on success.

func (*Parser) MatchType Uses

func (p *Parser) MatchType(typ TokenType) *Token

Returns the CURRENT token if the given type matches. Consumes this token on success.

func (*Parser) ParseExpression Uses

func (p *Parser) ParseExpression() (IEvaluator, *Error)

func (*Parser) Peek Uses

func (p *Parser) Peek(typ TokenType, val string) *Token

Returns the CURRENT token if the given type AND value matches. It DOES NOT consume the token.

func (*Parser) PeekN Uses

func (p *Parser) PeekN(shift int, typ TokenType, val string) *Token

Returns the tokens[current position + shift] token if the given type AND value matches for that token. DOES NOT consume the token.

func (*Parser) PeekOne Uses

func (p *Parser) PeekOne(typ TokenType, vals ...string) *Token

Returns the CURRENT token if the given type AND *one* of the given values matches. It DOES NOT consume the token.

func (*Parser) PeekType Uses

func (p *Parser) PeekType(typ TokenType) *Token

Returns the CURRENT token if the given type matches. It DOES NOT consume the token.

func (*Parser) PeekTypeN Uses

func (p *Parser) PeekTypeN(shift int, typ TokenType) *Token

Returns the tokens[current position + shift] token if the given type matches. DOES NOT consume the token for that token.

func (*Parser) Remaining Uses

func (p *Parser) Remaining() int

Returns the UNCONSUMED token count.

func (*Parser) SkipUntilTag Uses

func (p *Parser) SkipUntilTag(names ...string) *Error

Skips all nodes between starting tag and "{% endtag %}"

func (*Parser) WrapUntilTag Uses

func (p *Parser) WrapUntilTag(names ...string) (*NodeWrapper, *Parser, *Error)

Wraps all nodes between starting tag and "{% endtag %}" and provides one simple interface to execute the wrapped nodes. It returns a parser to process provided arguments to the tag.

type SandboxedFilesystemLoader Uses

type SandboxedFilesystemLoader struct {
    *LocalFilesystemLoader
}

SandboxedFilesystemLoader is still WIP.

func NewSandboxedFilesystemLoader Uses

func NewSandboxedFilesystemLoader(baseDir string) (*SandboxedFilesystemLoader, error)

NewSandboxedFilesystemLoader creates a new sandboxed local file system instance.

type TagParser Uses

type TagParser func(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error)

This is the function signature of the tag's parser you will have to implement in order to create a new tag.

'doc' is providing access to the whole document while 'arguments' is providing access to the user's arguments to the tag:

{% your_tag_name some "arguments" 123 %}

start_token will be the *Token with the tag's name in it (here: your_tag_name).

Please see the Parser documentation on how to use the parser. See RegisterTag()'s documentation for more information about writing a tag as well.

type Template Uses

type Template struct {

    // Options allow you to change the behavior of template-engine.
    // You can change the options before calling the Execute method.
    Options *Options
    // contains filtered or unexported fields
}

func Must Uses

func Must(tpl *Template, err error) *Template

Must panics, if a Template couldn't successfully parsed. This is how you would use it:

var baseTemplate = pongo2.Must(pongo2.FromFile("templates/base.html"))

func (*Template) Execute Uses

func (tpl *Template) Execute(context Context) (string, error)

Executes the template and returns the rendered template as a string

func (*Template) ExecuteBlocks Uses

func (tpl *Template) ExecuteBlocks(context Context, blocks []string) (map[string]string, error)

func (*Template) ExecuteBytes Uses

func (tpl *Template) ExecuteBytes(context Context) ([]byte, error)

Executes the template and returns the rendered template as a []byte

func (*Template) ExecuteWriter Uses

func (tpl *Template) ExecuteWriter(context Context, writer io.Writer) error

Executes the template with the given context and writes to writer (io.Writer) on success. Context can be nil. Nothing is written on error; instead the error is being returned.

func (*Template) ExecuteWriterUnbuffered Uses

func (tpl *Template) ExecuteWriterUnbuffered(context Context, writer io.Writer) error

Same as ExecuteWriter. The only difference between both functions is that this function might already have written parts of the generated template in the case of an execution error because there's no intermediate buffer involved for performance reasons. This is handy if you need high performance template generation or if you want to manage your own pool of buffers.

type TemplateLoader Uses

type TemplateLoader interface {
    // Abs calculates the path to a given template. Whenever a path must be resolved
    // due to an import from another template, the base equals the parent template's path.
    Abs(base, name string) string

    // Get returns an io.Reader where the template's content can be read from.
    Get(path string) (io.Reader, error)
}

TemplateLoader allows to implement a virtual file system.

type TemplateSet Uses

type TemplateSet struct {

    // Globals will be provided to all templates created within this template set
    Globals Context

    // If debug is true (default false), ExecutionContext.Logf() will work and output
    // to STDOUT. Furthermore, FromCache() won't cache the templates.
    // Make sure to synchronize the access to it in case you're changing this
    // variable during program execution (and template compilation/execution).
    Debug bool

    // Options allow you to change the behavior of template-engine.
    // You can change the options before calling the Execute method.
    Options *Options
    // contains filtered or unexported fields
}

TemplateSet allows you to create your own group of templates with their own global context (which is shared among all members of the set) and their own configuration. It's useful for a separation of different kind of templates (e. g. web templates vs. mail templates).

func NewSet Uses

func NewSet(name string, loaders ...TemplateLoader) *TemplateSet

NewSet can be used to create sets with different kind of templates (e. g. web from mail templates), with different globals or other configurations.

func (*TemplateSet) AddLoader Uses

func (set *TemplateSet) AddLoader(loaders ...TemplateLoader)

func (*TemplateSet) BanFilter Uses

func (set *TemplateSet) BanFilter(name string) error

BanFilter bans a specific filter for this template set. See more in the documentation for TemplateSet.

func (*TemplateSet) BanTag Uses

func (set *TemplateSet) BanTag(name string) error

BanTag bans a specific tag for this template set. See more in the documentation for TemplateSet.

func (*TemplateSet) CleanCache Uses

func (set *TemplateSet) CleanCache(filenames ...string)

CleanCache cleans the template cache. If filenames is not empty, it will remove the template caches of those filenames. Or it will empty the whole template cache. It is thread-safe.

func (*TemplateSet) FromBytes Uses

func (set *TemplateSet) FromBytes(tpl []byte) (*Template, error)

FromBytes loads a template from bytes and returns a Template instance.

func (*TemplateSet) FromCache Uses

func (set *TemplateSet) FromCache(filename string) (*Template, error)

FromCache is a convenient method to cache templates. It is thread-safe and will only compile the template associated with a filename once. If TemplateSet.Debug is true (for example during development phase), FromCache() will not cache the template and instead recompile it on any call (to make changes to a template live instantaneously).

func (*TemplateSet) FromFile Uses

func (set *TemplateSet) FromFile(filename string) (*Template, error)

FromFile loads a template from a filename and returns a Template instance.

func (*TemplateSet) FromString Uses

func (set *TemplateSet) FromString(tpl string) (*Template, error)

FromString loads a template from string and returns a Template instance.

func (*TemplateSet) RenderTemplateBytes Uses

func (set *TemplateSet) RenderTemplateBytes(b []byte, ctx Context) (string, error)

RenderTemplateBytes is a shortcut and renders template bytes directly.

func (*TemplateSet) RenderTemplateFile Uses

func (set *TemplateSet) RenderTemplateFile(fn string, ctx Context) (string, error)

RenderTemplateFile is a shortcut and renders a template file directly.

func (*TemplateSet) RenderTemplateString Uses

func (set *TemplateSet) RenderTemplateString(s string, ctx Context) (string, error)

RenderTemplateString is a shortcut and renders a template string directly.

type TemplateWriter Uses

type TemplateWriter interface {
    io.Writer
    WriteString(string) (int, error)
}

type Token Uses

type Token struct {
    Filename        string
    Typ             TokenType
    Val             string
    Line            int
    Col             int
    TrimWhitespaces bool
}

func (*Token) String Uses

func (t *Token) String() string

type TokenType Uses

type TokenType int

type Value Uses

type Value struct {
    // contains filtered or unexported fields
}

func AsSafeValue Uses

func AsSafeValue(i interface{}) *Value

AsSafeValue works like AsValue, but does not apply the 'escape' filter.

func AsValue Uses

func AsValue(i interface{}) *Value

AsValue converts any given value to a pongo2.Value Usually being used within own functions passed to a template through a Context or within filter functions.

Example:

AsValue("my string")

func MustApplyFilter Uses

func MustApplyFilter(name string, value *Value, param *Value) *Value

MustApplyFilter behaves like ApplyFilter, but panics on an error.

func (*Value) Bool Uses

func (v *Value) Bool() bool

Bool returns the underlying value as bool. If the value is not bool, false will always be returned. If you're looking for true/false-evaluation of the underlying value, have a look on the IsTrue()-function.

func (*Value) CanSlice Uses

func (v *Value) CanSlice() bool

CanSlice checks whether the underlying value is of type array, slice or string. You normally would use CanSlice() before using the Slice() operation.

func (*Value) Contains Uses

func (v *Value) Contains(other *Value) bool

Contains checks whether the underlying value (which must be of type struct, map, string, array or slice) contains of another Value (e. g. used to check whether a struct contains of a specific field or a map contains a specific key).

Example:

AsValue("Hello, World!").Contains(AsValue("World")) == true

func (*Value) EqualValueTo Uses

func (v *Value) EqualValueTo(other *Value) bool

EqualValueTo checks whether two values are containing the same value or object.

func (*Value) Float Uses

func (v *Value) Float() float64

Float returns the underlying value as a float (converts the underlying value, if necessary). If it's not possible to convert the underlying value, it will return 0.0.

func (*Value) Index Uses

func (v *Value) Index(i int) *Value

Index gets the i-th item of an array, slice or string. Otherwise it will return NIL.

func (*Value) Integer Uses

func (v *Value) Integer() int

Integer returns the underlying value as an integer (converts the underlying value, if necessary). If it's not possible to convert the underlying value, it will return 0.

func (*Value) Interface Uses

func (v *Value) Interface() interface{}

Interface gives you access to the underlying value.

func (*Value) IsBool Uses

func (v *Value) IsBool() bool

IsBool checks whether the underlying value is a bool

func (*Value) IsFloat Uses

func (v *Value) IsFloat() bool

IsFloat checks whether the underlying value is a float

func (*Value) IsInteger Uses

func (v *Value) IsInteger() bool

IsInteger checks whether the underlying value is an integer

func (*Value) IsNil Uses

func (v *Value) IsNil() bool

IsNil checks whether the underlying value is NIL

func (*Value) IsNumber Uses

func (v *Value) IsNumber() bool

IsNumber checks whether the underlying value is either an integer or a float.

func (*Value) IsString Uses

func (v *Value) IsString() bool

IsString checks whether the underlying value is a string

func (*Value) IsTrue Uses

func (v *Value) IsTrue() bool

IsTrue tries to evaluate the underlying value the Pythonic-way:

Returns TRUE in one the following cases:

* int != 0
* uint != 0
* float != 0.0
* len(array/chan/map/slice/string) > 0
* bool == true
* underlying value is a struct

Otherwise returns always FALSE.

func (*Value) Iterate Uses

func (v *Value) Iterate(fn func(idx, count int, key, value *Value) bool, empty func())

Iterate iterates over a map, array, slice or a string. It calls the function's first argument for every value with the following arguments:

idx      current 0-index
count    total amount of items
key      *Value for the key or item
value    *Value (only for maps, the respective value for a specific key)

If the underlying value has no items or is not one of the types above, the empty function (function's second argument) will be called.

func (*Value) IterateOrder Uses

func (v *Value) IterateOrder(fn func(idx, count int, key, value *Value) bool, empty func(), reverse bool, sorted bool)

IterateOrder behaves like Value.Iterate, but can iterate through an array/slice/string in reverse. Does not affect the iteration through a map because maps don't have any particular order. However, you can force an order using the `sorted` keyword (and even use `reversed sorted`).

func (*Value) Len Uses

func (v *Value) Len() int

Len returns the length for an array, chan, map, slice or string. Otherwise it will return 0.

func (*Value) Negate Uses

func (v *Value) Negate() *Value

Negate tries to negate the underlying value. It's mainly used for the NOT-operator and in conjunction with a call to return_value.IsTrue() afterwards.

Example:

AsValue(1).Negate().IsTrue() == false

func (*Value) Slice Uses

func (v *Value) Slice(i, j int) *Value

Slice slices an array, slice or string. Otherwise it will return an empty []int.

func (*Value) String Uses

func (v *Value) String() string

String returns a string for the underlying value. If this value is not of type string, pongo2 tries to convert it. Currently the following types for underlying values are supported:

1. string
2. int/uint (any size)
3. float (any precision)
4. bool
5. time.Time
6. String() will be called on the underlying value if provided

NIL values will lead to an empty string. Unsupported types are leading to their respective type name.

Package pongo2 imports 20 packages (graph) and is imported by 243 packages. Updated 2019-05-05. Refresh now. Tools for package owners.