A transpiler for mutually-referential reflective programming in Python. It is available as a library (pyreflect) and a command-line tool (pyreflect).
PyReflect is a transpiler that makes mutually-referential programs — programs made of several nodes — possible. Every node can reference the source code of itself and of the other nodes, without relying on any external source (e.g. file, stdin, registry).
Suppose node A and node B each want to reference the other's code. The naïve approach is to embed A's code inside B and B's code inside A. But then A's code has changed, so the copy of A embedded in B must be updated; that changes B's code, so the copy of B embedded in A must be updated; that changes A's code again, ad infinitum. In general this "fixed point" problem has no solution: for A to contain B we need
This infinite regress can be resolved by Kleene's second recursion theorem. Instead of embedding the code, each node embeds a code generator together with its input data. The generator can reconstruct the exact code of every node (itself and its peers) from the embedded input data — derived intrinsically, never fetched from any external source.
uv pip install git+https://github.com/acompany-develop/PyReflectThis exposes the pyreflect command and the importable pyreflect package.
from pyreflect import parse_template, transpile
with open(path, encoding="utf-8") as f:
# Read
text = f.read()
# Parse
template = parse_template(text)
# Transpile
nodes = transpile(template)# Input from file
pyreflect TEMPLATE.json OUTPUT_DIR
# Input from stdin
cat TEMPLATE.json | pyreflect - OUTPUT_DIRIt writes one file per node (node_<id>.py) with a manifest.json mapping node-id to filename.
The pyreflect transpiler converts a template into N standalone Python programs. A template is a JSON list of node objects:
[ {"node-id": "__NODE1", "code": "<python source>"},
{"node-id": "__NODE2", "code": "<python source>"},
... ]Each node has a node-id and a code body that may reference any subset of the node-ids (itself included).
The transpiler does two things:
- rewrites every node-id token in a code body into
__pyreflect_render__("<target-node-id>"); - wraps each body with an identical framework, including the definition of the
__pyreflect_render__function.
The __pyreflect_render__ function reconstructs the exact source code of the specified node from data embedded in the emitted source code without any external source of information.
The framework exposes the following API to each body:
__pyreflect_SELF__ this node's id
__pyreflect_DATA__ Base64-encoded template
__pyreflect_self_id__() this node's id
__pyreflect_node_ids__() all node ids, in template order
__pyreflect_render__(target) exact source code (string) of the target node
A body must not redefine the reserved names of the form __pyreflect_*__.
Single node that prints its own code.
# Transpile
pyreflect examples/quine/template.json examples/quine/
# Run Node: Display its own code
uv run examples/quine/node___NODE.py
# Verify
cat examples/quine/node___NODE.pyTwo nodes, each of which prints the other's SHA-256 digest. The peer value is obtained intrinsically — node 1 reconstructs node 2's source from its own embedded data and hashes it, and vice versa — so node 1's self hash equals the value node 2 reports as its expected peer reference, and vice versa.
# Transpile
pyreflect examples/mutual_quine/template.json examples/mutual_quine/
# Run Node 1: Display the SHA-256 digest of Node 2
uv run examples/mutual_quine/node___NODE1.py
# Run Node 2: Display the SHA-256 digest of Node 1
uv run examples/mutual_quine/node___NODE2.py
# Verify
sha256sum examples/mutual_quine/*.pyA variant of mutual_quine with three nodes wired into a cycle: node 1 prints node 2's digest, node 2 prints node 3's, node 3 prints node 1's (1 → 2 → 3 → 1). It demonstrates that the transpiler handles arbitrary n-node reference graphs, not just the symmetric two-node case.
# Transpile
pyreflect examples/trinity_quine/template.json examples/trinity_quine/
# Run Node 1: Display the SHA-256 digest of Node 2
uv run examples/trinity_quine/node___NODE1.py
# Run Node 2: Display the SHA-256 digest of Node 3
uv run examples/trinity_quine/node___NODE2.py
# Run Node 3: Display the SHA-256 digest of Node 1
uv run examples/trinity_quine/node___NODE3.py
# Verify
sha256sum examples/trinity_quine/*.py