state-rules 2026.3.29__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "state-rules"
|
|
3
|
+
version = "2026.3.29"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.14"
|
|
7
|
+
dependencies = [
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
[dependency-groups]
|
|
11
|
+
dev = [
|
|
12
|
+
'fire',
|
|
13
|
+
"marimo", "pyzmq",
|
|
14
|
+
'pre-commit',
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.scripts]
|
|
18
|
+
dyrules = "state_rules:main"
|
|
19
|
+
|
|
20
|
+
[build-system]
|
|
21
|
+
requires = ["uv_build>=0.10.8,<0.11.0"]
|
|
22
|
+
build-backend = "uv_build"
|
|
File without changes
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
class types:
|
|
2
|
+
state_keys = int | str
|
|
3
|
+
state = dict # can it be something else? just need mapping and iter
|
|
4
|
+
from typing import Callable
|
|
5
|
+
argmap = dict[state_keys, state_keys | Callable ] # callable[state keys,-> ]
|
|
6
|
+
|
|
7
|
+
class Rules:
|
|
8
|
+
|
|
9
|
+
def __init__(self, state: types.state = {}, log: bool=True):
|
|
10
|
+
self.state = state
|
|
11
|
+
self.funcs = []
|
|
12
|
+
self.log = [] if log is True else False
|
|
13
|
+
|
|
14
|
+
def register(self, argmap: types.argmap | None = None):
|
|
15
|
+
"""decorator on a function"""
|
|
16
|
+
from inspect import signature
|
|
17
|
+
def add_func(f, argmap=argmap):
|
|
18
|
+
if argmap is None:
|
|
19
|
+
argmap = {p:p for p in signature(f).parameters}
|
|
20
|
+
for s in signature(f).parameters:
|
|
21
|
+
if s not in argmap:
|
|
22
|
+
argmap[s] = s
|
|
23
|
+
|
|
24
|
+
if 'return' not in argmap:
|
|
25
|
+
argmap['return'] = f'{f.__module__}.{f.__name__}({','.join(v for v in argmap.values())})'
|
|
26
|
+
|
|
27
|
+
f.argmap = argmap
|
|
28
|
+
f._argmap_no_return = {fa:s for fa,s in f.argmap.items() if (fa != 'return') }
|
|
29
|
+
self.funcs.append(f)
|
|
30
|
+
return add_func
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def _argmapf2args(cls, am: types.argmap, state: types.state):
|
|
34
|
+
for fa, s in am.items():
|
|
35
|
+
if callable(s):
|
|
36
|
+
for sk,sv in state.items():
|
|
37
|
+
ssk = s(sk)
|
|
38
|
+
if ssk == True:
|
|
39
|
+
yield fa, sv
|
|
40
|
+
else:
|
|
41
|
+
assert(ssk == False)
|
|
42
|
+
|
|
43
|
+
def __iter__(self):
|
|
44
|
+
state = self.state
|
|
45
|
+
for f in self.funcs:
|
|
46
|
+
_ = {fa:state[s] for fa,s in f._argmap_no_return.items() if not callable(s) } # simple case
|
|
47
|
+
_.update(self._argmapf2args(f._argmap_no_return, state ) )
|
|
48
|
+
_ = f(**_)
|
|
49
|
+
state[f.argmap['return']] = _
|
|
50
|
+
yield state
|
|
51
|
+
|
|
52
|
+
def run(self, maxiter = 10):
|
|
53
|
+
i = 0
|
|
54
|
+
from types import SimpleNamespace as NS
|
|
55
|
+
class Iteration(NS): pass
|
|
56
|
+
|
|
57
|
+
if self.log is not False:
|
|
58
|
+
self.log.append(Iteration(i=i, state=self.state.copy()))
|
|
59
|
+
|
|
60
|
+
while True:
|
|
61
|
+
if i >= maxiter: break
|
|
62
|
+
oldstate = self.state.copy() # shallow vs deep?
|
|
63
|
+
_ = iter(self)
|
|
64
|
+
self.state = newstate = next(_)
|
|
65
|
+
|
|
66
|
+
if self.log is not False:
|
|
67
|
+
self.log.append(Iteration(i=i+1, state=newstate.copy()))
|
|
68
|
+
|
|
69
|
+
if newstate == oldstate:
|
|
70
|
+
break
|
|
71
|
+
else:
|
|
72
|
+
i = i+1
|
|
73
|
+
newstate = oldstate
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
return self.state
|
|
77
|
+
|
|
78
|
+
|