topkit 0.2.0a3__py3-none-any.whl
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.
- TopKit/__init__.py +82 -0
- TopKit/access.py +357 -0
- TopKit/contracts.py +360 -0
- TopKit/declarations.py +1010 -0
- TopKit/errors.py +138 -0
- TopKit/fields.py +165 -0
- TopKit/geometry.py +120 -0
- TopKit/lifecycle.py +220 -0
- TopKit/overlay.py +791 -0
- TopKit/queries.py +90 -0
- TopKit/state.py +862 -0
- TopKit/tags.py +251 -0
- TopKit/transactions.py +474 -0
- topkit-0.2.0a3.dist-info/METADATA +136 -0
- topkit-0.2.0a3.dist-info/RECORD +18 -0
- topkit-0.2.0a3.dist-info/WHEEL +5 -0
- topkit-0.2.0a3.dist-info/licenses/LICENSE +202 -0
- topkit-0.2.0a3.dist-info/top_level.txt +1 -0
TopKit/__init__.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""TopKit: the Python reference implementation of Tag-Oriented Programming."""
|
|
2
|
+
|
|
3
|
+
from .contracts import Contract
|
|
4
|
+
from .declarations import Action
|
|
5
|
+
from .declarations import Delete
|
|
6
|
+
from .declarations import Flag
|
|
7
|
+
from .declarations import Imprint
|
|
8
|
+
from .declarations import Operation
|
|
9
|
+
from .declarations import Pin
|
|
10
|
+
from .declarations import Post
|
|
11
|
+
from .declarations import Postcondition
|
|
12
|
+
from .declarations import Pre
|
|
13
|
+
from .declarations import Precondition
|
|
14
|
+
from .declarations import Public
|
|
15
|
+
from .declarations import Record
|
|
16
|
+
from .declarations import Report
|
|
17
|
+
from .declarations import Requirement
|
|
18
|
+
from .declarations import Rip
|
|
19
|
+
from .declarations import Secret
|
|
20
|
+
from .declarations import Underlay
|
|
21
|
+
from .errors import TagCompositionError
|
|
22
|
+
from .errors import TagContractError
|
|
23
|
+
from .errors import TagContractWarning
|
|
24
|
+
from .errors import TagDeclarationError
|
|
25
|
+
from .errors import TagError
|
|
26
|
+
from .errors import TagImprintError
|
|
27
|
+
from .errors import TagOverwriteWarning
|
|
28
|
+
from .errors import TagPostconditionError
|
|
29
|
+
from .errors import TagPreconditionError
|
|
30
|
+
from .errors import TagResolutionError
|
|
31
|
+
from .errors import TagRogueAccessError
|
|
32
|
+
from .lifecycle import At_Exit
|
|
33
|
+
from .lifecycle import Scope
|
|
34
|
+
from .queries import Apply
|
|
35
|
+
from .queries import Form
|
|
36
|
+
from .queries import Keyword
|
|
37
|
+
from .queries import Outline
|
|
38
|
+
from .queries import Tags
|
|
39
|
+
from .tags import Tag
|
|
40
|
+
from .tags import Tagged
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
"Action",
|
|
45
|
+
"Apply",
|
|
46
|
+
"At_Exit",
|
|
47
|
+
"Contract",
|
|
48
|
+
"Delete",
|
|
49
|
+
"Flag",
|
|
50
|
+
"Form",
|
|
51
|
+
"Keyword",
|
|
52
|
+
"Imprint",
|
|
53
|
+
"Operation",
|
|
54
|
+
"Outline",
|
|
55
|
+
"Pin",
|
|
56
|
+
"Post",
|
|
57
|
+
"Postcondition",
|
|
58
|
+
"Pre",
|
|
59
|
+
"Precondition",
|
|
60
|
+
"Public",
|
|
61
|
+
"Record",
|
|
62
|
+
"Report",
|
|
63
|
+
"Requirement",
|
|
64
|
+
"Rip",
|
|
65
|
+
"Scope",
|
|
66
|
+
"Secret",
|
|
67
|
+
"Tag",
|
|
68
|
+
"TagCompositionError",
|
|
69
|
+
"TagContractError",
|
|
70
|
+
"TagContractWarning",
|
|
71
|
+
"TagDeclarationError",
|
|
72
|
+
"TagError",
|
|
73
|
+
"TagImprintError",
|
|
74
|
+
"TagOverwriteWarning",
|
|
75
|
+
"TagPostconditionError",
|
|
76
|
+
"TagPreconditionError",
|
|
77
|
+
"TagResolutionError",
|
|
78
|
+
"TagRogueAccessError",
|
|
79
|
+
"Tagged",
|
|
80
|
+
"Tags",
|
|
81
|
+
"Underlay",
|
|
82
|
+
]
|
TopKit/access.py
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
"""Access: the hooks on the runtime type and the Agent-bound Tag views.
|
|
2
|
+
|
|
3
|
+
Three access forms, three meanings:
|
|
4
|
+
|
|
5
|
+
agent.name the current visible Overlay (Agent scope)
|
|
6
|
+
agent.Wizard.name the Overlay as it was right after Wizard applied
|
|
7
|
+
Wizard.name the Tag itself (Tag scope)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from functools import partial
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from .errors import TagCompositionError
|
|
16
|
+
from .errors import TagResolutionError
|
|
17
|
+
from .declarations import _MISSING
|
|
18
|
+
from .state import _Bound
|
|
19
|
+
from .state import _Pinned_Operation
|
|
20
|
+
from .state import _Snapshot
|
|
21
|
+
from .state import _State
|
|
22
|
+
from .state import _name_of
|
|
23
|
+
from .state import _state_of
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# ------------------------------------------------------------------
|
|
27
|
+
# Hooks placed on every runtime type
|
|
28
|
+
# ------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _host_member(
|
|
32
|
+
host_type: type,
|
|
33
|
+
name: str,
|
|
34
|
+
) -> Any:
|
|
35
|
+
"""A special method the host itself defines, or None."""
|
|
36
|
+
|
|
37
|
+
for klass in host_type.__mro__:
|
|
38
|
+
if klass is object:
|
|
39
|
+
break
|
|
40
|
+
|
|
41
|
+
member = klass.__dict__.get(name)
|
|
42
|
+
|
|
43
|
+
if member is not None:
|
|
44
|
+
return member
|
|
45
|
+
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _hooks_for(
|
|
50
|
+
host_type: type,
|
|
51
|
+
has_posts: bool,
|
|
52
|
+
has_flags: bool,
|
|
53
|
+
) -> dict[str, Any]:
|
|
54
|
+
hooks: dict[str, Any] = {
|
|
55
|
+
"__getattr__": _agent_getattr,
|
|
56
|
+
"__del__": _agent_del,
|
|
57
|
+
"_TOPKIT_HOST_TYPE": host_type,
|
|
58
|
+
"_TOPKIT_HOST_GETATTR": _host_member(host_type, "__getattr__"),
|
|
59
|
+
"_TOPKIT_HOST_DEL": _host_member(host_type, "__del__"),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if has_posts and _host_member(host_type, "__bool__") is None:
|
|
63
|
+
hooks["__bool__"] = _agent_bool
|
|
64
|
+
|
|
65
|
+
if _host_member(host_type, "__format__") is None:
|
|
66
|
+
hooks["__format__"] = _agent_format
|
|
67
|
+
|
|
68
|
+
if has_flags and _host_member(host_type, "__contains__") is None:
|
|
69
|
+
hooks["__contains__"] = _agent_contains
|
|
70
|
+
|
|
71
|
+
if _host_member(host_type, "__copy__") is None:
|
|
72
|
+
hooks["__copy__"] = _agent_copy
|
|
73
|
+
|
|
74
|
+
if _host_member(host_type, "__deepcopy__") is None:
|
|
75
|
+
hooks["__deepcopy__"] = _agent_deepcopy
|
|
76
|
+
|
|
77
|
+
return hooks
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _agent_getattr(
|
|
81
|
+
agent: object,
|
|
82
|
+
name: str,
|
|
83
|
+
) -> Any:
|
|
84
|
+
"""Miss path only: Tag views by name, then the host's own __getattr__."""
|
|
85
|
+
|
|
86
|
+
state = _state_of(agent)
|
|
87
|
+
|
|
88
|
+
if state is not None:
|
|
89
|
+
if name in state.secrets and state.pinned is not None:
|
|
90
|
+
return _secret_of_tag(
|
|
91
|
+
agent,
|
|
92
|
+
state,
|
|
93
|
+
name,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
for tag in reversed(state.active):
|
|
97
|
+
if tag.__name__ == name:
|
|
98
|
+
return _view_of(
|
|
99
|
+
agent,
|
|
100
|
+
tag,
|
|
101
|
+
state,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
host_getattr = type(agent).__dict__.get("_TOPKIT_HOST_GETATTR")
|
|
105
|
+
|
|
106
|
+
if host_getattr is not None:
|
|
107
|
+
return host_getattr(
|
|
108
|
+
agent,
|
|
109
|
+
name,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
raise AttributeError(
|
|
113
|
+
f"{_name_of(agent)} has no member {name!r}"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _secret_of_tag(
|
|
118
|
+
tag: type,
|
|
119
|
+
state: _State,
|
|
120
|
+
name: str,
|
|
121
|
+
) -> Any:
|
|
122
|
+
"""A Pin's @Secret member on a Tag: held in the state, resolved only
|
|
123
|
+
while the Tag's own protocols or pinned Operations run."""
|
|
124
|
+
|
|
125
|
+
if state.composing == 0:
|
|
126
|
+
raise AttributeError(
|
|
127
|
+
f"{name!r} is a secret member of {tag.__name__}; it is"
|
|
128
|
+
" reachable only from its Pins' own Actions and protocols"
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
value = state.secret_values.get(
|
|
132
|
+
name,
|
|
133
|
+
_MISSING,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
if value is _MISSING:
|
|
137
|
+
raise AttributeError(
|
|
138
|
+
f"{tag.__name__} has no visible member {name!r}"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if isinstance(value, _Pinned_Operation):
|
|
142
|
+
return value.__get__(
|
|
143
|
+
None,
|
|
144
|
+
tag,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
return value
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _agent_format(
|
|
151
|
+
agent: object,
|
|
152
|
+
spec: str,
|
|
153
|
+
) -> str:
|
|
154
|
+
"""``f"{agent:tags}"``, ``f"{agent:outline}"``, ``f"{agent:contract}"``."""
|
|
155
|
+
|
|
156
|
+
if spec == "":
|
|
157
|
+
return str(agent)
|
|
158
|
+
|
|
159
|
+
if spec == "tags":
|
|
160
|
+
from .queries import Tags
|
|
161
|
+
|
|
162
|
+
return ", ".join(
|
|
163
|
+
tag.__name__
|
|
164
|
+
for tag in Tags(agent)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
if spec == "outline":
|
|
168
|
+
from .queries import Outline
|
|
169
|
+
|
|
170
|
+
return Outline(agent)
|
|
171
|
+
|
|
172
|
+
if spec == "contract":
|
|
173
|
+
from .contracts import Contract
|
|
174
|
+
|
|
175
|
+
return Contract.Display(agent)
|
|
176
|
+
|
|
177
|
+
raise ValueError(
|
|
178
|
+
f"unknown format spec {spec!r} for an Agent; use 'tags',"
|
|
179
|
+
" 'outline', or 'contract'"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _agent_contains(
|
|
184
|
+
agent: object,
|
|
185
|
+
probe: object,
|
|
186
|
+
) -> bool:
|
|
187
|
+
"""``"Undead" in ghoul`` and ``Undead in ghoul``: an active Flag, by
|
|
188
|
+
name or by class."""
|
|
189
|
+
|
|
190
|
+
return _keyword(
|
|
191
|
+
agent,
|
|
192
|
+
probe,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _keyword(
|
|
197
|
+
agent: object,
|
|
198
|
+
probe: object,
|
|
199
|
+
) -> bool:
|
|
200
|
+
from .declarations import _is_flag
|
|
201
|
+
|
|
202
|
+
state = _state_of(agent)
|
|
203
|
+
|
|
204
|
+
if state is None:
|
|
205
|
+
return False
|
|
206
|
+
|
|
207
|
+
if isinstance(probe, str):
|
|
208
|
+
return any(
|
|
209
|
+
_is_flag(tag) and tag.__name__ == probe
|
|
210
|
+
for tag in state.active
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
probe in state.active
|
|
215
|
+
and _is_flag(probe)
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _agent_bool(
|
|
220
|
+
agent: object,
|
|
221
|
+
) -> bool:
|
|
222
|
+
from .contracts import _holds
|
|
223
|
+
|
|
224
|
+
return _holds(agent)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _agent_del(
|
|
228
|
+
agent: object,
|
|
229
|
+
) -> None:
|
|
230
|
+
# Best effort: run remaining teardowns, then the host's finalizer.
|
|
231
|
+
# Python does not promise finalizers at shutdown or inside cycles;
|
|
232
|
+
# Scope() is the guaranteed path.
|
|
233
|
+
try:
|
|
234
|
+
from .lifecycle import _teardown_all
|
|
235
|
+
|
|
236
|
+
_teardown_all(agent)
|
|
237
|
+
|
|
238
|
+
host_del = type(agent).__dict__.get("_TOPKIT_HOST_DEL")
|
|
239
|
+
|
|
240
|
+
if host_del is not None:
|
|
241
|
+
host_del(agent)
|
|
242
|
+
except Exception:
|
|
243
|
+
pass
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _agent_copy(
|
|
247
|
+
agent: object,
|
|
248
|
+
) -> object:
|
|
249
|
+
raise TagCompositionError(
|
|
250
|
+
"copying an Agent is domain work: build a new Target and apply"
|
|
251
|
+
" its Tags again (Tags(agent) lists them)"
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _agent_deepcopy(
|
|
256
|
+
agent: object,
|
|
257
|
+
memo: dict[int, Any],
|
|
258
|
+
) -> object:
|
|
259
|
+
return _agent_copy(agent)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
# ------------------------------------------------------------------
|
|
263
|
+
# Agent-bound Tag views
|
|
264
|
+
# ------------------------------------------------------------------
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class _Tag_View:
|
|
268
|
+
"""The Overlay snapshot captured right after one Tag applied."""
|
|
269
|
+
|
|
270
|
+
__slots__ = (
|
|
271
|
+
"_agent",
|
|
272
|
+
"_tag",
|
|
273
|
+
"_snapshot",
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
def __init__(
|
|
277
|
+
view,
|
|
278
|
+
agent: object,
|
|
279
|
+
tag: type,
|
|
280
|
+
snapshot: _Snapshot,
|
|
281
|
+
) -> None:
|
|
282
|
+
object.__setattr__(view, "_agent", agent)
|
|
283
|
+
object.__setattr__(view, "_tag", tag)
|
|
284
|
+
object.__setattr__(view, "_snapshot", snapshot)
|
|
285
|
+
|
|
286
|
+
def __getattr__(
|
|
287
|
+
view,
|
|
288
|
+
name: str,
|
|
289
|
+
) -> Any:
|
|
290
|
+
snapshot: _Snapshot = view._snapshot
|
|
291
|
+
agent = view._agent
|
|
292
|
+
|
|
293
|
+
if name in snapshot.deleted:
|
|
294
|
+
raise AttributeError(
|
|
295
|
+
f"{view._tag.__name__} deleted {name!r}"
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
if name in snapshot.secrets and _state_of(agent).composing == 0:
|
|
299
|
+
raise AttributeError(
|
|
300
|
+
f"{name!r} is a secret member of {view._tag.__name__}"
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
if name in snapshot.actions:
|
|
304
|
+
return _Bound(
|
|
305
|
+
snapshot.actions[name],
|
|
306
|
+
agent,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
if name in snapshot.records:
|
|
310
|
+
return snapshot.records[name]
|
|
311
|
+
|
|
312
|
+
if name in snapshot.reports:
|
|
313
|
+
return snapshot.reports[name][1]
|
|
314
|
+
|
|
315
|
+
if name in snapshot.operations:
|
|
316
|
+
origin, operation = snapshot.operations[name]
|
|
317
|
+
|
|
318
|
+
return partial(
|
|
319
|
+
operation,
|
|
320
|
+
origin,
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
raise AttributeError(
|
|
324
|
+
f"{view._tag.__name__} view has no member {name!r}"
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
def __setattr__(
|
|
328
|
+
view,
|
|
329
|
+
name: str,
|
|
330
|
+
value: Any,
|
|
331
|
+
) -> None:
|
|
332
|
+
raise AttributeError("a Tag view is a snapshot; it is read-only")
|
|
333
|
+
|
|
334
|
+
def __repr__(
|
|
335
|
+
view,
|
|
336
|
+
) -> str:
|
|
337
|
+
return f"<{view._tag.__name__} view of {_name_of(view._agent)}>"
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _view_of(
|
|
341
|
+
agent: object,
|
|
342
|
+
tag: type,
|
|
343
|
+
state: _State | None = None,
|
|
344
|
+
) -> _Tag_View:
|
|
345
|
+
if state is None:
|
|
346
|
+
state = _state_of(agent)
|
|
347
|
+
|
|
348
|
+
if state is None or tag not in state.active:
|
|
349
|
+
raise TagResolutionError(
|
|
350
|
+
f"{tag.__name__} is not active on this Agent"
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
return _Tag_View(
|
|
354
|
+
agent,
|
|
355
|
+
tag,
|
|
356
|
+
state.snapshots[tag],
|
|
357
|
+
)
|