what JSON is to JavaScript, Zen is to Python.
Think about what JSON is. It's a subset of JavaScript syntax that only knows how to express data. No functions, no classes, no side effects โ just literal values.
JSON is "the part of JavaScript that's safe to treat as data."
Python already has a similar subset. It's called Zen (or Zef Entity Notation). The idea is identical โ only, for Python.
# strings
'a plain string'
"or with double quotes"
# numbers (and it really is "numbers" โ int, float, scientific)
1234
-56
3.14
1e10
# booleans + None
True
False
None
# lists
[1, 2, 3, 'four', None, True]
# dicts
{'key1': 'value1', 'key2': 42}
# SETS โ unlike JSON!
{1, 'two', 3.0, None}
# tuples
(1, 'two', 3.0)
That's it. Every Zen expression is valid Python, and every Python literal data structure is valid Zen.
true, false, nullTrue, False, None# comments OKIt deliberately excludes anything that can execute code:
# โ NOT Zen โ these have side effects
print("hello")
open("file.txt")
my_list.append(1)
# โ NOT Zen โ definitions, not data
def f(x): return x
class Foo: pass
x = 42
# โ NOT Zen โ references runtime state
my_variable
config['key']
If a piece of text only describes values, it's Zen. If it does anything (assigns, calls, defines), it's not.
Why draw this line? Because you can safely ast.literal_eval any
Zen expression. You can store it in a file, send it over a socket, pipe it
between processes โ and the worst thing it can do is be wrong. It
can't execute code.
Configuration files: Zen. Database entries: Zen. Entity definitions: Zen. Test cases: Zen. Error payloads: Zen. Once you start looking, you see it.
# ~/.config/zef/config.zen.py โ a zef config file
ET.ZefConfig(
zef_source_dir='/home/me/code/zef',
vault_=['~/zef-vault', '~/shared-vault'],
)
That's a valid Python expression you can from some_file import *
from. It's also a data value you can ship around.
Zen extends to entity constructors. These are valid Python and valid Zen:
ET.Person(name='Alice', age=30)
ET.Company(
name='Acme',
founded=1948,
employees_=[
ET.Person(name='Alice', role='CEO'),
ET.Person(name='Bob', role='CTO'),
],
)
Your entire data model โ the "schema" + the values โ can be expressed in Python literal syntax. No YAML, no TOML, no custom DSL. The file with your entities is executable Python that evaluates to data.
Zef leans on this heavily: the file-backed DB stores Zen, the config system uses Zen, tests are Zen, docs embed Zen snippets that the runtime can parse.
Because every Zen expression is Python, and the parser is the Python parser, you can always convert in either direction:
import ast
zen_text = """
{
'name': 'Alice',
'tags': ['admin', 'python'],
'meta': {'active': True, 'visits': 42},
}
"""
data = ast.literal_eval(zen_text) # safe โ no code execution
Write it, edit it, store it, ship it. Safe as plain text.
ET.Person(...)Write a Zen expression for a small company:
{
'name': 'Green Widgets',
'tags': {'b-corp', 'founded-2020'},
'employees': [
{'name': 'Alice', 'age': 30, 'role': 'admin'},
{'name': 'Bob', 'age': 25, 'role': 'dev'},
{'name': 'Carol', 'age': 40, 'role': 'ceo'},
],
}
Next up: types are sets โ and that single sentence rewrites how you think about typing. โ