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.2, 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 tess_el

expr = tess_el.compile_expression("'low' if battery < 20 else f'{battery}%'", language="0.1")
expr.evaluate(tess_el.MapScope({"battery": 63}))  # '63%'
expr.evaluate(tess_el.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 = [tess_el.compile_condition(t, language="0.1") for t in ("unread > 0", "muted")]
tess_el.select(chain, tess_el.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 = tess_el.fill("battery < $limit or name in $names", {"limit": 20, "names": ["a", "b"]})
# "battery < 20 or name in ['a', 'b']"
tess_el.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):

tess_el.compile_expression("a<20", language="0.1").canonical  # 'a < 20'
tess_el.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 = tess_el.compile_repeater("w in workspaces", key="w.id", name="Workspace", language="0.1")
items = rep.items(tess_el.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 = tess_el.MapScope({"workspaces": [{"id": 7}, {"id": 9}]})
result = rep.reconcile(both, tess_el.RepeaterState())
[e.name for e in result.entries]  # ['Workspace_0001', 'Workspace_0002']
result = rep.reconcile(tess_el.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 = tess_el.compile_repeater_parts(
    "w",
    "workspaces",
    key="w.id",
    name="Workspace",
    language="0.1",
    names_origin=tess_el.Origin("A.tdl", 12, 9),
    items_origin=tess_el.Origin("A.tdl", 12, 15),
    key_origin=tess_el.Origin("A.tdl", 12, 35),
)
template = tess_el.compile_template("Battery {{ battery }}%, {{ state }}", language="0.1")
template.evaluate(
    tess_el.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 = tess_el.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 = tess_el.compile_expression("'low' if battery < 20 else state", language="0.1")
value, reads = expr.evaluate_tracked(tess_el.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 (tess_el.TelError, with a stable code, a line and a column), never a hang: every size and every loop is limited (tess_el.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. Import name: tess_el (tel until 0.1.1).

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.2

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.2
File Size Uploaded Provenance Actions
tesserae_el-0.1.2.tar.gz 90.7 kB With provenance Details for tesserae_el-0.1.2.tar.gz

Built distribution (wheel)

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

Total release size: 156.8 kB

Release files / tesserae_el-0.1.2.tar.gz

Download URL tesserae_el-0.1.2.tar.gz
Size 90.7 kB
Tags Source
SHA-256 checksum
How to use checksums
339f331ce6d2846ab0d0203a6d1bfdcb27f3fa6ec873c0982b8f204a0bca4f2f
BLAKE2b-256 checksum
How to use checksums
6f7b55382a22e7284dc5cd1fe814c47aefcf06007b02be52aab8b1e365fba378
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 3, 2026.

Transparency log

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

Download URL tesserae_el-0.1.2-py3-none-any.whl
Size 66.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d882b7446c0d04d2d2126d5cd64c98441485ef0b504d27e4e05c74c279fe81cd
BLAKE2b-256 checksum
How to use checksums
4144e78d49aeb3cb9cfabef9c5269df9fb114bb215528605af3f10d37f6d172e
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 3, 2026.

Transparency log

Security for tesserae-el 0.1.2

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.2.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 3, 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