Skip to main content

TEL

TEL, the Tesserae Expression Language, is a small, safe expression language and a Python library that parses and evaluates it. It lets plain YAML (or Python code) express conditions, repeaters and live values.

Status: alpha, package 0.1.0, language version 0.1. The API may change before 1.0 (a minor version may break it; see docs/versioning.md). The design is in docs/language.md; what is protected, how it is checked and what is not guaranteed is in docs/security-review.md.

TEL needs Python 3.14 or later and has no dependencies:

pip install tesserae-el

It evaluates expressions and {{ }} templates (with limits and positions in errors), tracks the names and attribute paths an evaluation read, takes host functions and methods, reads conditions (compile_condition, select) and repeaters (compile_repeater, reconcile) for a host that builds the tree, and builds expression text from Python values (fill, canonical).

import tel

expr = tel.compile_expression("'low' if battery < 20 else f'{battery}%'", language="0.1")
expr.evaluate(tel.MapScope({"battery": 63}))  # '63%'
expr.evaluate(tel.MapScope({"battery": 12}))  # 'low'

A conditional can also be written cond ? a : b: the same as a if cond else b, with the same precedence (lowest, right-associative), and it may not be mixed with if/else without brackets. In YAML, quote a value that contains :: a colon followed by a space ends a plain scalar, so x: a ? b : c is a YAML error and - a ? b : c is read as a mapping.

A condition reads an expression as yes or no. The host owns the if:, elif: and else: keys; select gives the index of the first condition that holds, or None for the else, and select_tracked also gives the paths read by every condition tested up to the winner.

chain = [tel.compile_condition(t, language="0.1") for t in ("unread > 0", "muted")]
tel.select(chain, tel.MapScope({"unread": 0, "muted": True}))  # 1

In YAML, give a condition or a key as text: quote a value with : in it, and quote a bare true or 1, which YAML loads as a bool or an int (TEL refuses a non-text value with a TypeError).

To build an expression in Python, fill placeholders with values: each $name (outside strings and comments) becomes a checked literal, so a value can only be data and a string is quoted. The result is text for any compile_* function.

text = tel.fill("battery < $limit or name in $names", {"limit": 20, "names": ["a", "b"]})
# "battery < 20 or name in ['a', 'b']"
tel.fill_template("Hello {{ $who }}!", {"who": "Ada"})  # "Hello {{ 'Ada' }}!"

Two spellings of one expression have one canonical text, for comparing an expression built in Python with one written as text (compare within one run; do not store it):

tel.compile_expression("a<20", language="0.1").canonical  # 'a < 20'
tel.compile_expression("c ? a : b", language="0.1").canonical  # 'a if c else b'

A repeater reads a header (item in items, or i, item in enumerate(items)) and a key expression, and gives the list's items with their keys. The host owns the for: and key: keys and builds the nodes; each item's scope(parent) is the scope for its children.

rep = tel.compile_repeater("w in workspaces", key="w.id", name="Workspace", language="0.1")
items = rep.items(tel.MapScope({"workspaces": [{"id": 7}, {"id": 9}]}))
[(item.index, item.key) for item in items]  # [(0, 7), (1, 9)]

# keep the state between calls: entries are new or kept, and keys can be removed
both = tel.MapScope({"workspaces": [{"id": 7}, {"id": 9}]})
result = rep.reconcile(both, tel.RepeaterState())
[e.name for e in result.entries]  # ['Workspace_0001', 'Workspace_0002']
result = rep.reconcile(tel.MapScope({"workspaces": [{"id": 9}]}), result.state)
(result.removed, result.entries[0].name)  # ((7,), 'Workspace_0002')

A file that holds the loop variable, the list and the key apart (the Shell's TDL) passes them separately, each with its own origin; the result is the same repeater:

rep = tel.compile_repeater_parts(
    "w",
    "workspaces",
    key="w.id",
    name="Workspace",
    language="0.1",
    names_origin=tel.Origin("A.tdl", 12, 9),
    items_origin=tel.Origin("A.tdl", 12, 15),
    key_origin=tel.Origin("A.tdl", 12, 35),
)
template = tel.compile_template("Battery {{ battery }}%, {{ state }}", language="0.1")
template.evaluate(
    tel.MapScope({"battery": 63, "state": "discharging"})
)  # 'Battery 63%, discharging'

A host adds its own functions and methods when it compiles. TEL calls only what the host listed:

expr = tel.compile_expression(
    "format_number(total, 1) + ' ' + price.formatted()",
    language="0.1",
    functions={"format_number": lambda x, digits=0: f"{x:,.{digits}f}"},
    methods={Price: {"formatted": Price.formatted}},
)

To know which names an evaluation actually read, so a host re-runs it only when one of them changes:

expr = tel.compile_expression("'low' if battery < 20 else state", language="0.1")
value, reads = expr.evaluate_tracked(tel.MapScope({"battery": 63, "state": "ok"}))
# value == 'ok', reads == {'battery', 'state'}; with battery=12 it reads only {'battery'}

A read is a name, or an attribute chain on a name: RootShell.DP1.Top_Bar.Width reports that whole path, so an expression depends only on what it reads. Tracked.names gives the top-level names, and expr.paths every path in the text, before any evaluation.

A scope is anything with lookup(name) (the value, or KeyError) and optionally names(). What TEL may ask of it, and what it must do in return (quick, no effects, plain values), is written clause by clause in docs/language.md section 12.2 and tested in tests/test_scope_contract.py.

A template that is exactly one {{ }} part gives that part's value with its type; any other text with parts gives text. Text with no part is a template that evaluates to itself (Template.is_text).

A bad expression is an error that says where (tel.TelError, with a stable code, a line and a column), never a hang: every size and every loop is limited (tel.Limits). An expression can read names from the scope you give it and call a few built-in functions and methods; it cannot reach files, the process, the network, or an object's private attributes.

PyPI name: tesserae-el (not yet published). Import name: tel.

Development

uv pip install -e . --group dev   # into the active venv (Python 3.14+)
python tools/check.py             # ruff, format check, mypy (strict), pytest: what CI runs
python tools/wrapup.py            # what is still behind before work is handed over
python tools/removal_check.py     # break each rule of the language on purpose: a test must fail
tools/status.sh                   # branch, unpushed work, version, mailbox, board

Work is tracked on the TEL (tess-el) board (python tools/board.py status). graphify update . rebuilds the local code graph. Benchmarks: docs/benchmarks.md (the expr.py baseline). Behaviour reference: docs/reference-cases.md. Agent instructions are in AGENTS.md; project skills are in .agents/skills/.

Versioning

See docs/versioning.md.

Licence

MIT

Metadata

Release files for tesserae-el 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tesserae-el 0.1.0
File Size Uploaded Provenance Actions
tesserae_el-0.1.0.tar.gz 89.2 kB With provenance Details for tesserae_el-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for tesserae-el 0.1.0
File Interpreter ABI Platform Actions
tesserae_el-0.1.0-py3-none-any.whl Python 3 none any Details for tesserae_el-0.1.0-py3-none-any.whl

Total release size: 154.3 kB

Release files / tesserae_el-0.1.0.tar.gz

Download URL tesserae_el-0.1.0.tar.gz
Size 89.2 kB
Tags Source
SHA-256 checksum
How to use checksums
5547cd9a3658d1e6ca3b3b141d3168cf2df056e9b395eb3099c0c82265b32950
BLAKE2b-256 checksum
How to use checksums
3d26a1869806351ce789569c839b7d16e35a246a9417b85850c9c938285cf7a6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Release files / tesserae_el-0.1.0-py3-none-any.whl

Download URL tesserae_el-0.1.0-py3-none-any.whl
Size 65.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0c0ded88b48afccb8fd9478ccbe4301a7762d201c0d66595815c08c717dc1d7f
BLAKE2b-256 checksum
How to use checksums
9c2c7da8234d5df6bd4c5242af8d328c408b39c8e8983bd67d06ff804cbbd9ca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

Security for tesserae-el 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for tesserae_el-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 2, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page