Pythorment
Pythorment is a randomized subset of Python. Every day, Pythorment allows a different set of AST nodes that can be used.
Since the usage of AST nodes changes in conjunction with the Python syntax, one could append a version name to clarify the Python version used.
Naming
On the assignment (which I do not wish to disclose) where I introduced Pythorment to my assistant who grades them, I wrote this:
It doesn’t have a name, but for this report I’m calling it Pythorment, in the spirit of Javagony.
—User:Gilbert189's assignment
Motivation
I came up with a concept of Pythorment back in 2022, as a silly attempt to mix Python and TMMLPTEALPAITAFNFAL.[1]. In 2026, I came back to revisit this concept as a contender for the Category:No set computational class. I've been eyeing this category since 2025, as it is one the most barren categories that I found here.
Taking the question from the category page, "What is the computational class of this programming language?", the answer for languages in that category is always in the form "it depends on some criteria". For EA Script, It's in the code., the answer is "it depends on how much you want to pay". For languages made by REGH, the answer is "it depends on the seed". For Pythorment, the answer is "it depends on the date today".
Criteria
This function decides if a node is allowed or not. A node has a 50% chance of being allowed.
import hashlib, ast
from datetime import date
def allowed(node, dt=date.today()):
# (ast.AST, date) -> bool
name = type(node).__name__
day = (dt - date.min).days
digest = hashlib.md5(f"{name}@{day}".encode("utf-8")).digest()
return digest[0] % 2 == 1
If Pythorment finds a node that's disallowed, a SyntaxError should be raised.
Computability class
Most of the time, Pythorment is a constant language. This is especially apparent when any of the root node types are disallowed, rendering any code unacceptable. Another node in a similar vain are ast.Name and ast.Load, representing variables and variable reads, respectively.
Sometimes, just enough nodes are allowed to put Pythorment higher on the computability class. This Hello, world! program can be run on 9 May, 26 May, 12 June, 5 July, 6 August, 17 August, and 18 October 2026, in version 3.8.0b4.
print("Hello, world")
More complex Python programs are less likely to run in Pythorment. This looping counter implementation uses 12 node types in 3.14.2, and can only be run on 11 November 2049 at the earliest (it's 2026 when I'm writing this):
a="" while 1:print(a:=a+"*")
Nevertheless, there are times where Pythorment is Turing complete. Here's an implementation of CT economized for node type. This should work on 30 January 2084 in 3.14.2.
prog = [0, 1, 0, 0, 0, 1, 2, 1, 0, 0, 2, 1, 0, 0, 1, 0, 0, 1, 0, 0, 2, 2, 2, 2] # program here, 2 is semicolon
data = [1, 0, 0, 1, 0, 0, 1, 0, 0]
# instead of using While, use For with an infinite iterator
for _ in __import__("itertools").count():
for op in prog:
if op == 2:
data = data[slice(1, None)] # using the slice function here, saves from using Slice
elif data[0] == 1:
data.append(op)
print(data)
For a more obfuscated example, this simple brainfuck to Python compiler produces Pythorment code that uses merely 8 node types in 3.8.0b4:
code = input()
indent = 0
translation = {
# TIP: use dunders that primarily uses Attribute
"+": """globals().__getitem__("tape").__setitem__(globals().__getitem__("ip"), globals().__getitem__("tape").get(globals().__getitem__("ip"), 0).__add__(1).__mod__(256))""",
"-": """globals().__getitem__("tape").__setitem__(globals().__getitem__("ip"), globals().__getitem__("tape").get(globals().__getitem__("ip"), 0).__sub__(1).__mod__(256))""",
">": """globals().__setitem__("ip", globals().__getitem__("ip").__add__(1))""",
"<": """globals().__setitem__("ip", globals().__getitem__("ip").__sub__(1))""",
".": """__import__("sys").stdout.write(chr(globals().__getitem__("tape").get(globals().__getitem__("ip"))))""",
",": """globals().__getitem__("tape").__setitem__(globals().__getitem__("ip"), ord(__import__("sys").stdin.read(1)))""",
"[": """while globals().__getitem__("tape").get(globals().__getitem__("ip"), 0):""",
"]": """""",
}
# initialization code
print("""globals().__setitem__("ip", 0)
globals().__setitem__("tape", dict())""")
for op in code:
print("\t"*indent + translation[op])
if op == "[": indent += 1
if op == "]": indent -= 1
Extreme cases
Brute-forcing shows that there's no date from 1 January 1 (date.min) to 31 December 9999 (date.max) where Pythorment 3.11.13 allows and disallows all node types. Under this range, at least 29 node types are allowed, and there are 5 dates allowing this many nodes:
| Date | Node types |
|---|---|
| 10 April 1265 | Expression, Return, Assign, For, AsyncFor, Assert, Expr, UnaryOp, ListComp, Await, Yield, FormattedValue, Subscript, Tuple, Store, Or, FloorDiv, Invert, Lt, LtE, Is, IsNot, NotIn, ExceptHandler, MatchValue, MatchMapping, MatchOr, Index, ExtSlice |
| 8 January 2696 | FunctionType, FunctionDef, Return, TryStar, Assert, Global, Pass, Dict, DictComp, GeneratorExp, Await, Constant, Subscript, Starred, Tuple, Slice, Load, Add, Mod, Pow, LShift, BitXor, USub, NotEq, LtE, GtE, NotIn, MatchClass, ExtSlice |
| 21 January 4701 | Module, FunctionDef, ClassDef, If, With, Try, Assert, BoolOp, IfExp, DictComp, Yield, Compare, Call, JoinedStr, Constant, Attribute, Sub, Div, Pow, BitOr, BitXor, FloorDiv, UAdd, Lt, In, ExceptHandler, MatchSingleton, MatchStar, MatchAs |
| 7 February 8484 | Interactive, ClassDef, Delete, Assign, AnnAssign, AsyncFor, Match, Try, Assert, Import, Global, Expr, BinOp, SetComp, DictComp, Call, Name, Slice, And, Add, Mult, Mod, Pow, USub, Eq, NotEq, Is, MatchValue, TypeIgnore |
| 21 January 9419 | Expression, AsyncFunctionDef, Delete, Raise, TryStar, Assert, Global, Break, BinOp, SetComp, DictComp, Await, FormattedValue, Starred, Name, List, Store, Del, AugLoad, Or, Add, Mult, Div, RShift, UAdd, Gt, MatchMapping, MatchStar, MatchAs |
Conversely, at most 80 nodes are allowed on 13 January 3781, and this is the only date like this. The types allowed are Module, Interactive, Expression, FunctionType, Suite, FunctionDef, ClassDef, Return, Delete, Assign, AugAssign, AnnAssign, For, While, If, With, Match, Try, TryStar, Assert, Import, Global, Nonlocal, Expr, Pass, Continue, BinOp, UnaryOp, Lambda, IfExp, Set, ListComp, Await, Yield, Call, FormattedValue, Constant, Attribute, Subscript, Name, List, Slice, Load, Del, AugLoad, AugStore, Param, And, Or, Add, Mult, MatMult, Div, Mod, Pow, LShift, RShift, BitOr, FloorDiv, Invert, Not, UAdd, Eq, NotEq, Lt, LtE, Gt, GtE, In, ExceptHandler, MatchValue, MatchSingleton, MatchMapping, MatchClass, MatchStar, MatchAs, MatchOr, TypeIgnore, Index, ExtSlice.
On eval and exec
Pythorment's node checking is cascading by default—that is, it also checks code run with eval and exec. This is so that these functions can't be used to "cheat" the node check:
exec("""
# Without this check, you can put any Python code here
# and it will run as consistently as the Hello, world! program above.
""")
Implementations may choose to make Pythorment dull (not cascading) under a command line switch.
See also
- TMMLPTEALPAITAFNFAL, also a language that allows some of their functions every day
References
- ↑ An initial implementation of Pythorment, with a different rule of allowed nodes