ansible/ansible
Filter plugins
Filter plugins implement Jinja's {{ x | filter }} syntax. ansible-core ships a large library of filters under lib/ansible/plugins/filter/, plus collections that add hundreds more.
Where they live
lib/ansible/plugins/filter/
├── __init__.py
├── core.py # Big bag of Python-only filters (now mostly moved to per-filter files)
├── encryption.py # vault, password_hash, hashlib helpers
├── mathstuff.py # zip, intersect, difference, log, root, ...
├── urlsplit.py
├── urls.py
├── *.yml # Sidecar YAML docs for each filterIn modern ansible-core most filters are declared as sidecar YAML files next to the implementation. b64encode.yml, basename.yml, bool.yml, combine.yml, to_json.yml, etc. are pure-data definitions that point at a Python implementation in core.py (or wherever).
The full list is long — b64decode, b64encode, basename, bool, checksum, combinations, combine, comment, commonpath, dict2items, dirname, expandvars, extract, fileglob, flatten, from_json, from_yaml, from_yaml_all, groupby, hash, intersect, items2dict, join, log, mandatory, password_hash, path_join, permutations, quote, random, regex_findall, regex_replace, regex_search, realpath, relpath, splitext, subelements, symmetric_difference, ternary, to_datetime, to_json, to_nice_json, to_nice_yaml, to_uuid, to_yaml, type_debug, union, unique, urldecode, urlencode, urlsplit, vault, version, zip, zip_longest, etc.
How a filter gets bound into Jinja
- The plugin loader's
filter_loaderwalks each filter source path. - For each Python file with a
FilterModuleclass, it reads thefilters()method (returns a dict ofname → callable). - For each YAML sidecar, it reads the
name:field plus a Python-callable reference. - The
lib/ansible/_internal/_templating/_jinja_plugins.pyshim wraps each callable so undefined-handling, Marker propagation, and tag preservation work correctly. - The wrapped callable is bound into the
AnsibleEnvironment'sfiltersdict.
A filter is then accessible as {{ x | name }} and {{ x | namespace.collection.name }}.
Writing a filter
Inline (legacy) form:
class FilterModule:
def filters(self):
return {
'shouty': self.shouty,
}
def shouty(self, value):
return str(value).upper() + '!'Sidecar (modern) form: place the implementation in lib/ansible/plugins/filter/core.py:shouty and a sidecar shouty.yml next to the filter file with documentation:
---
name: shouty
short_description: Convert to uppercase with an exclamation mark
description: Calls upper() on the string and appends '!'.
positional: _input
options:
_input:
description: The string to shout.
type: str
required: true
version_added: '2.16'
author:
- Ansible ProjectThe sidecar is what ansible-doc -t filter shouty renders.
Common pitfalls
- Filters get unrendered values. A user's
{{ undefined | default('x') }}passes aMarker(Ansible's hardenedUndefined) into the filter. If you call regular Python on it, you'll get a runtime error. Most filters should useif value is undefined or isinstance(value, Marker): return ...early. - Filters should be pure functions. They get re-evaluated on every templating pass. Don't read files, hit the network, or print. Use a lookup for that.
- Type handling. Ansible's lazy containers (
_AnsibleLazyTemplateMixin) wrap dicts/lists. Filters that iterate through them are fine; filters that doisinstance(value, dict)should be careful — useisinstance(value, Mapping)fromcollections.abc.
Filters that are really plugins of another type
A few filters are convenience-named lookups:
vault(encrypt/decrypt) — wraps the vault library.password_hash— wrapspasslib.to_uuid— wrapsuuid.uuid5.
Most filters, though, are pure-Python data transforms: to_json, combine, flatten, etc.
Test plugins, briefly
{{ x is foo }} syntax is implemented by test plugins under lib/ansible/plugins/test/. They're closely related to filters but answer a boolean question rather than transforming a value. See Test plugins.
Integration points
- Imported by:
lib/ansible/_internal/_templating/_jinja_plugins.pyfor binding into Jinja. - Loaded by:
filter_loaderinlib/ansible/plugins/loader.py.
Entry points for modification
- A new filter — write a plugin in a collection. Subclass nothing; provide a
FilterModuleclass with afilters()method, or use the sidecar form. - Fixing a built-in filter — find it in
lib/ansible/plugins/filter/. Most live incore.pyor a per-filter Python file.
Cross-links
- Templating — how filters get bound into the Jinja environment.
- Test plugins — the boolean-test sibling.
- Lookup plugins — for things filters shouldn't do (I/O).
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.