Python S-expression emulation using tuple-like objects.
Project Links
Meta
Author: Brandon T. Willard
Maintainer: Brandon T. Willard
Requires Python: >=3.9
Classifiers
Development Status
- 4 - Beta
Intended Audience
- Science/Research
- Developers
Operating System
- OS Independent
Programming Language
- Python
- Python :: 3
- Python :: 3.9
- Python :: 3.10
- Python :: 3.11
- Python :: 3.12
- Python :: Implementation :: CPython
- Python :: Implementation :: PyPy
etuples
Python S-expression emulation using tuple-like objects.
Examples
etuples are like tuples:
>>> from operator import add
>>> from etuples import etuple, etuplize
>>> et = etuple(add, 1, 2)
>>> et
ExpressionTuple((<built-in function add>, 1, 2))
>>> from IPython.lib.pretty import pprint
>>> pprint(et)
e(<function _operator.add(a, b, /)>, 1, 2)
>>> et[0:2]
ExpressionTuple((<built-in function add>, 1))
etuples can also be evaluated:
>>> et.evaled_obj
3
Evaluated etuples are cached:
>>> et = etuple(add, "a", "b")
>>> et.evaled_obj
'ab'
>>> et.evaled_obj is et.evaled_obj
True
Reconstructed etuples and their evaluation results are preserved across tuple operations:
>>> et_new = (et[0],) + et[1:]
>>> et_new is et
True
>>> et_new.evaled_obj is et.evaled_obj
True
rator, rands, and apply will return the operator, the operands, and apply the operation to the operands:
>>> from etuples import rator, rands, apply
>>> et = etuple(add, 1, 2)
>>> rator(et)
<built-in function add>
>>> rands(et)
ExpressionTuple((1, 2))
>>> apply(rator(et), rands(et))
3
rator and rands are multipledispatch functions that can be extended to handle arbitrary objects:
from etuples.core import ExpressionTuple
from collections.abc import Sequence
class Node:
def __init__(self, rator, rands):
self.rator, self.rands = rator, rands
def __eq__(self, other):
return self.rator == other.rator and self.rands == other.rands
class Operator:
def __init__(self, op_name):
self.op_name = op_name
def __call__(self, *args):
return Node(Operator(self.op_name), args)
def __repr__(self):
return self.op_name
def __eq__(self, other):
return self.op_name == other.op_name
rands.add((Node,), lambda x: x.rands)
rator.add((Node,), lambda x: x.rator)
@apply.register(Operator, (Sequence, ExpressionTuple))
def apply_Operator(rator, rands):
return Node(rator, rands)
>>> mul_op, add_op = Operator("*"), Operator("+")
>>> mul_node = Node(mul_op, [1, 2])
>>> add_node = Node(add_op, [mul_node, 3])
etuplize will convert non-tuple objects into their corresponding etuple form:
>>> et = etuplize(add_node)
>>> pprint(et)
e(+, e(*, 1, 2), 3)
>>> et.evaled_obj is add_node
True
etuplize can also do shallow object-to-etuple conversions:
>>> et = etuplize(add_node, shallow=True)
>>> pprint(et)
e(+, <__main__.Node at 0x7f347361a080>, 3)
Installation
Using pip:
pip install etuples
Development
First obtain the project source:
git clone git@github.com:pythological/etuples.git
Create a virtual environment and install the development dependencies:
$ pip install -r requirements.txt
Set up pre-commit hooks:
$ pre-commit install --install-hooks
Tests can be run with the provided Makefile:
make check
0.3.10
Jul 14, 2025
0.3.9
May 17, 2023
0.3.8
Sep 06, 2022
0.3.7
Sep 02, 2022
0.3.6
Sep 02, 2022
0.3.5
Mar 26, 2022
0.3.4
Jan 19, 2022
0.3.3
Jun 28, 2021
0.3.2
Feb 29, 2020
0.3.1
Feb 21, 2020
0.3.0
Feb 21, 2020
0.2.0
Feb 20, 2020
0.1.4
Feb 02, 2020
0.1.3
Feb 02, 2020
0.1.2
Dec 28, 2019
0.1.1
Dec 27, 2019
0.1.0
Dec 24, 2019