openscript 0.4.0__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.
Files changed (97) hide show
  1. openscript/__init__.py +40 -0
  2. openscript/__main__.py +62 -0
  3. openscript/accounting/__init__.py +74 -0
  4. openscript/accounting/analysis.py +174 -0
  5. openscript/accounting/charges.py +397 -0
  6. openscript/accounting/equity.py +234 -0
  7. openscript/accounting/report.py +82 -0
  8. openscript/accounting/shapes.py +74 -0
  9. openscript/accounting/statistics.py +300 -0
  10. openscript/accounting/trades.py +294 -0
  11. openscript/adapter/__init__.py +32 -0
  12. openscript/adapter/answers.py +215 -0
  13. openscript/adapter/channels.py +137 -0
  14. openscript/adapter/expectations.py +67 -0
  15. openscript/adapter/facts.py +127 -0
  16. openscript/adapter/matching.py +257 -0
  17. openscript/adapter/ordering.py +187 -0
  18. openscript/adapter/page.py +130 -0
  19. openscript/adapter/reading.py +357 -0
  20. openscript/adapter/reporting.py +244 -0
  21. openscript/adapter/running.py +449 -0
  22. openscript/adapter/serving.py +229 -0
  23. openscript/adapter/sessions.py +168 -0
  24. openscript/adapter/spellings.py +184 -0
  25. openscript/bars.py +157 -0
  26. openscript/budget.py +342 -0
  27. openscript/canonical.py +192 -0
  28. openscript/civil.py +196 -0
  29. openscript/contracts.py +165 -0
  30. openscript/dates.py +302 -0
  31. openscript/diagnostics.py +104 -0
  32. openscript/hours.py +165 -0
  33. openscript/inputs.py +239 -0
  34. openscript/intervals.py +60 -0
  35. openscript/library/__init__.py +76 -0
  36. openscript/library/arithmetic.py +128 -0
  37. openscript/library/averages.py +133 -0
  38. openscript/library/bars.py +60 -0
  39. openscript/library/bookkeeping.py +166 -0
  40. openscript/library/code_points.py +85 -0
  41. openscript/library/colour.py +202 -0
  42. openscript/library/composites.py +208 -0
  43. openscript/library/counting.py +218 -0
  44. openscript/library/deviation.py +155 -0
  45. openscript/library/elementary.py +206 -0
  46. openscript/library/extremes.py +122 -0
  47. openscript/library/flows.py +220 -0
  48. openscript/library/momentum.py +203 -0
  49. openscript/library/number_text.py +223 -0
  50. openscript/library/prices.py +36 -0
  51. openscript/library/ranges.py +105 -0
  52. openscript/library/rounding.py +123 -0
  53. openscript/library/series.py +213 -0
  54. openscript/library/stateful.py +442 -0
  55. openscript/library/stateless.py +261 -0
  56. openscript/library/strength.py +180 -0
  57. openscript/library/strings.py +228 -0
  58. openscript/library/trend.py +260 -0
  59. openscript/library/values.py +91 -0
  60. openscript/logbook.py +119 -0
  61. openscript/machine.py +499 -0
  62. openscript/memory.py +204 -0
  63. openscript/opcodes.py +166 -0
  64. openscript/program.py +146 -0
  65. openscript/run.py +368 -0
  66. openscript/strategy/__init__.py +78 -0
  67. openscript/strategy/calls.py +201 -0
  68. openscript/strategy/closable.py +182 -0
  69. openscript/strategy/fills.py +131 -0
  70. openscript/strategy/holdings.py +277 -0
  71. openscript/strategy/intents.py +162 -0
  72. openscript/strategy/ledger.py +270 -0
  73. openscript/strategy/placing.py +206 -0
  74. openscript/strategy/positions.py +124 -0
  75. openscript/strategy/refusals.py +293 -0
  76. openscript/strategy/rows.py +219 -0
  77. openscript/strategy/sizing.py +229 -0
  78. openscript/strategy/statuses.py +65 -0
  79. openscript/surface/__init__.py +115 -0
  80. openscript/surface/bands.py +103 -0
  81. openscript/surface/levels.py +44 -0
  82. openscript/surface/marks.py +52 -0
  83. openscript/surface/paints.py +58 -0
  84. openscript/surface/plots.py +44 -0
  85. openscript/surface/published.py +119 -0
  86. openscript/values.py +210 -0
  87. openscript/verify.py +301 -0
  88. openscript/verify_code.py +290 -0
  89. openscript/verify_requests.py +271 -0
  90. openscript/verify_shape.py +162 -0
  91. openscript/verify_tables.py +256 -0
  92. openscript/version.py +39 -0
  93. openscript/zones.py +118 -0
  94. openscript-0.4.0.dist-info/METADATA +82 -0
  95. openscript-0.4.0.dist-info/RECORD +97 -0
  96. openscript-0.4.0.dist-info/WHEEL +5 -0
  97. openscript-0.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,290 @@
1
+ """Checks 2 to 7 of ``compiled-program.md`` section 3.5, over one instruction list.
2
+
3
+ Verification is not optional and not a debug mode. An engine that skips it can
4
+ be handed a malformed list and will read past the end of an array or jump into
5
+ the middle of an expression, and the failure will look like a wrong number
6
+ rather than a broken program. What these checks buy is what the interpreter is
7
+ then allowed to assume and therefore not to test: a verified program cannot
8
+ underflow the stack, cannot jump out of bounds, cannot address a slot that does
9
+ not exist and cannot loop without charging the budget.
10
+
11
+ Check 6 is the one that looks small and is not. The target of every backward
12
+ jump must be a ``TICK``, so no cycle in the control flow graph can run without
13
+ charging the loop budget, and the whole of that guarantee is checkable by
14
+ looking at one instruction rather than by analysing the graph.
15
+ """
16
+
17
+ from dataclasses import dataclass, field
18
+ from typing import Any, Callable, Dict, List, Optional, Sequence
19
+
20
+ from .diagnostics import at_instruction
21
+ from .opcodes import (
22
+ INSTRUCTION_TAGS,
23
+ TABLE_NAMES,
24
+ depth_change,
25
+ is_opcode,
26
+ operand_count,
27
+ tables_for,
28
+ targets,
29
+ )
30
+ from .verify_shape import ShapeCheck
31
+
32
+
33
+ @dataclass
34
+ class ListLimits:
35
+ """How big each table an operand may index is, for the list being walked."""
36
+
37
+ slots: int
38
+ cells: int
39
+ states: int
40
+ registers: int
41
+ channels: int
42
+ libFunctions: int
43
+ callSites: int
44
+ loops: int
45
+ consts: int
46
+ bindings: int
47
+ argc_of: Callable[[int], int] = field(default=lambda site: 0)
48
+
49
+ def size(self, table: str) -> Optional[int]:
50
+ return getattr(self, table, None)
51
+
52
+
53
+ def check_list(
54
+ shape: ShapeCheck,
55
+ listing: str,
56
+ code: Sequence[Any],
57
+ limits: ListLimits,
58
+ terminator: str,
59
+ ) -> bool:
60
+ """Walks one instruction list: checks 2, 3, 4, 5 and 6.
61
+
62
+ ``terminator`` is ``HALT`` for the bar's own code and ``RET`` for a function
63
+ body or a read's body, which is check 4, and the depth at it must be zero,
64
+ which is check 5.
65
+ """
66
+ if len(code) == 0:
67
+ return shape.fail(listing, "an instruction list cannot be empty")
68
+
69
+ # Check 2, and every operand in range.
70
+ for at, instruction in enumerate(code):
71
+ where = at_instruction(listing, at)
72
+ if not shape.array(instruction, where):
73
+ return False
74
+ opcode = instruction[0] if instruction else None
75
+ if not is_opcode(opcode):
76
+ return shape.fail(where, f"{opcode} is not an opcode this engine implements")
77
+ operands = instruction[1:]
78
+ if len(operands) != operand_count(opcode):
79
+ return shape.fail(
80
+ where,
81
+ f"{opcode} takes {operand_count(opcode)} operands and carries {len(operands)}",
82
+ )
83
+ if not _check_operands(shape, where, opcode, operands, limits, len(code)):
84
+ return False
85
+
86
+ # Check 6: the target of a backward jump is a TICK.
87
+ for position in targets(opcode):
88
+ target = operands[position]
89
+ if target > at:
90
+ continue
91
+ landing = code[target]
92
+ if not isinstance(landing, list) or not landing or landing[0] != "TICK":
93
+ return shape.fail(
94
+ where,
95
+ f"it jumps back to instruction {target}, which is not a TICK, so the loop "
96
+ "would run without charging the budget",
97
+ )
98
+
99
+ # Check 4.
100
+ last = code[-1]
101
+ if not isinstance(last, list) or not last or last[0] != terminator:
102
+ return shape.fail(
103
+ at_instruction(listing, len(code) - 1),
104
+ f"the last instruction of this list must be {terminator}",
105
+ )
106
+ for at in range(len(code) - 1):
107
+ if code[at][0] == "HALT":
108
+ return shape.fail(at_instruction(listing, at), "HALT is only ever the last instruction")
109
+
110
+ return _check_depths(shape, listing, code, limits, terminator)
111
+
112
+
113
+ def _check_operands(
114
+ shape: ShapeCheck,
115
+ where: str,
116
+ opcode: str,
117
+ operands: Sequence[Any],
118
+ limits: ListLimits,
119
+ length: int,
120
+ ) -> bool:
121
+ tables = tables_for(opcode)
122
+ for at, operand in enumerate(operands):
123
+ if not shape.whole(operand, f"{where} operand {at}"):
124
+ return False
125
+ table = tables[at] if at < len(tables) else None
126
+ if table is None or table == "count":
127
+ if operand < 0:
128
+ return shape.fail(f"{where} operand {at}", "a count cannot be negative")
129
+ continue
130
+ if table == "target":
131
+ # Check 3. A target past the terminator is out of the list.
132
+ if operand < 0 or operand >= length:
133
+ return shape.fail(where, f"the jump target {operand} is outside a list of {length}")
134
+ continue
135
+ # A CALL_LIB with no state carries -1 rather than an index.
136
+ if opcode == "CALL_LIB" and table == "states" and operand == -1:
137
+ continue
138
+ size = limits.size(table)
139
+ if size is None:
140
+ continue
141
+ if operand < 0 or operand >= size:
142
+ named = TABLE_NAMES.get(table, table)
143
+ return shape.fail(
144
+ f"{where} operand {at}", f"{operand} is outside {named}, which holds {size}"
145
+ )
146
+ return True
147
+
148
+
149
+ def _check_depths(
150
+ shape: ShapeCheck,
151
+ listing: str,
152
+ code: Sequence[Any],
153
+ limits: ListLimits,
154
+ terminator: str,
155
+ ) -> bool:
156
+ """Check 5: the depth agrees on every path, never goes below zero, is zero at the end.
157
+
158
+ A disagreement at a join is a corrupt program and not a program with an
159
+ unusual shape: every instruction has a fixed effect, so two paths reaching
160
+ one instruction with different depths means one of them was built wrong.
161
+ """
162
+ depths: List[Optional[int]] = [None] * len(code)
163
+ depths[0] = 0
164
+ pending = [0]
165
+
166
+ def reach(index: int, depth: int, origin: int) -> bool:
167
+ if depth < 0:
168
+ return shape.fail(at_instruction(listing, origin), "it takes the stack below zero")
169
+ known = depths[index]
170
+ if known is None:
171
+ depths[index] = depth
172
+ pending.append(index)
173
+ return True
174
+ if known != depth:
175
+ return shape.fail(
176
+ at_instruction(listing, index),
177
+ f"two paths reach it with the stack {known} and {depth} deep",
178
+ )
179
+ return True
180
+
181
+ while pending:
182
+ index = pending.pop()
183
+ instruction = code[index]
184
+ depth = depths[index]
185
+ opcode = instruction[0]
186
+ operands = instruction[1:]
187
+ after = depth + depth_change(opcode, operands, limits.argc_of)
188
+ if opcode == terminator:
189
+ if after != 0:
190
+ return shape.fail(
191
+ at_instruction(listing, index),
192
+ f"the stack is {after} deep at {terminator} and must be empty",
193
+ )
194
+ continue
195
+ for position in targets(opcode):
196
+ if not reach(operands[position], after, index):
197
+ return False
198
+ if opcode not in ("JUMP", "RET", "HALT"):
199
+ if not reach(index + 1, after, index):
200
+ return False
201
+
202
+ return True
203
+
204
+
205
+ #: How high a write count is carried before it stops mattering.
206
+ #:
207
+ #: "More than once" is the whole of what check 7 needs to know, and without a
208
+ #: ceiling a loop around an ``EMIT`` would never settle.
209
+ _CAP = 2
210
+
211
+
212
+ def check_once(shape: ShapeCheck, code: Sequence[Any], once: Sequence[bool]) -> bool:
213
+ """Check 7: every channel declared ``once`` is written exactly once on every path.
214
+
215
+ That is how a plot column is guaranteed a value, or an explicit absence, for
216
+ every bar.
217
+ """
218
+ channels = len(once)
219
+ if channels == 0:
220
+ return True
221
+ least: List[Optional[List[int]]] = [None] * len(code)
222
+ most: List[Optional[List[int]]] = [None] * len(code)
223
+ least[0] = [0] * channels
224
+ most[0] = [0] * channels
225
+ pending = [0]
226
+
227
+ def merge(index: int, low: Sequence[int], high: Sequence[int]) -> None:
228
+ if index < 0 or index >= len(code):
229
+ return
230
+ known_low, known_high = least[index], most[index]
231
+ if known_low is None or known_high is None:
232
+ least[index] = list(low)
233
+ most[index] = list(high)
234
+ pending.append(index)
235
+ return
236
+ changed = False
237
+ for channel in range(channels):
238
+ lower = min(known_low[channel], low[channel])
239
+ upper = min(_CAP, max(known_high[channel], high[channel]))
240
+ if lower != known_low[channel]:
241
+ known_low[channel] = lower
242
+ changed = True
243
+ if upper != known_high[channel]:
244
+ known_high[channel] = upper
245
+ changed = True
246
+ if changed:
247
+ pending.append(index)
248
+
249
+ end = -1
250
+ while pending:
251
+ index = pending.pop()
252
+ instruction = code[index]
253
+ low, high = least[index], most[index]
254
+ opcode = instruction[0]
255
+ if opcode == "HALT":
256
+ end = index
257
+ continue
258
+ next_low, next_high = list(low), list(high)
259
+ if opcode == "EMIT":
260
+ channel = instruction[1]
261
+ next_low[channel] = min(_CAP, next_low[channel] + 1)
262
+ next_high[channel] = min(_CAP, next_high[channel] + 1)
263
+ for position in targets(opcode):
264
+ merge(instruction[position + 1], next_low, next_high)
265
+ if opcode not in ("JUMP", "RET"):
266
+ merge(index + 1, next_low, next_high)
267
+
268
+ if end < 0:
269
+ return shape.fail("code", "no path reaches HALT")
270
+ low, high = least[end], most[end]
271
+ for channel in range(channels):
272
+ if not once[channel]:
273
+ continue
274
+ if low[channel] != 1 or high[channel] != 1:
275
+ return shape.fail(
276
+ f"channels[{channel}]",
277
+ f"it is declared once and is written {low[channel]} to {high[channel]} times "
278
+ "on a path to HALT",
279
+ )
280
+ return True
281
+
282
+
283
+ def missing_tag(lists: Sequence[Sequence[Any]], requires: Sequence[str]) -> Optional[Dict[str, str]]:
284
+ """Check 8's program half: an instruction whose tag the program never declared."""
285
+ for code in lists:
286
+ for instruction in code:
287
+ tag = INSTRUCTION_TAGS.get(instruction[0])
288
+ if tag is not None and tag not in requires:
289
+ return {"opcode": instruction[0], "tag": tag}
290
+ return None
@@ -0,0 +1,271 @@
1
+ """Check 1 over the tables a machine indexes, and over ``requests`` and its bodies.
2
+
3
+ A read's body (section 2.16.1) holds the tables a program holds for the machine
4
+ of section 3, counted from zero, so it needs the same walk the program's own
5
+ tables get and for the same reason: every later step indexes into them, and an
6
+ index out of range that is not caught here is a read past the end of an array
7
+ during a bar.
8
+
9
+ **A body that was not walked is the hole verification exists to close.** It is
10
+ reached by the same interpreter, with the same instruction list, over another
11
+ instrument's history. Verifying the program and not the expression it carries
12
+ would leave exactly the failure section 3.5 is written to prevent, one level
13
+ down where nobody would look for it.
14
+
15
+ The walk is shared rather than written twice, because two copies would be two
16
+ places for a table to gain a field and only one of them to learn about it.
17
+ """
18
+
19
+ from dataclasses import dataclass
20
+ from typing import Any, List, Sequence, Set
21
+
22
+ from .verify_shape import ShapeCheck
23
+
24
+ READS = ("timeframe", "symbol")
25
+ MODES = ("confirmed", "developing", "lookahead")
26
+ CHART_FACTS = ("symbol", "exchange", "interval")
27
+
28
+ #: The five kinds section 2.10 declares, not the ones an engine happens to run.
29
+ #:
30
+ #: A verifier that listed only the kinds it executes would refuse a well formed
31
+ #: program with a message blaming the compiler that wrote it, and would pre-empt
32
+ #: the capability refusal that exists to say which feature is missing.
33
+ #: Verification answers whether this is a valid program; the capability check
34
+ #: answers whether this engine can run it.
35
+ REGISTER_KINDS = ("bar", "computed", "argument", "request", "input")
36
+ CELL_KINDS = ("var", "live")
37
+ LOOP_KINDS = ("for", "forIn", "while")
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class MachineTables:
42
+ """The tables one machine indexes, wherever in the program they sit."""
43
+
44
+ series: List[Any]
45
+ cells: List[Any]
46
+ states: List[Any]
47
+ functions: List[Any]
48
+ call_sites: List[Any]
49
+ loops: List[Any]
50
+ slots: int
51
+ lib_functions: int
52
+
53
+
54
+ def check_machine_tables(shape: ShapeCheck, prefix: str, tables: MachineTables) -> bool:
55
+ for at, register in enumerate(tables.series):
56
+ path = f"{prefix}series[{at}]"
57
+ if not shape.object(register, path):
58
+ return False
59
+ if register.get("id") != at:
60
+ return shape.fail(f"{path}.id", "a register id is its position")
61
+ if not shape.one(register.get("kind"), f"{path}.kind", REGISTER_KINDS):
62
+ return False
63
+ if register["kind"] == "bar" and not shape.string(register.get("field"), f"{path}.field"):
64
+ return False
65
+
66
+ for at, cell in enumerate(tables.cells):
67
+ path = f"{prefix}cells[{at}]"
68
+ if not shape.object(cell, path):
69
+ return False
70
+ if not shape.one(cell.get("kind"), f"{path}.kind", CELL_KINDS):
71
+ return False
72
+
73
+ for at, state in enumerate(tables.states):
74
+ path = f"{prefix}states[{at}]"
75
+ if not shape.object(state, path):
76
+ return False
77
+ if not shape.index(state.get("fn"), f"{path}.fn", tables.lib_functions, "lib.functions"):
78
+ return False
79
+
80
+ for at, one in enumerate(tables.functions):
81
+ path = f"{prefix}functions[{at}]"
82
+ if not shape.object(one, path):
83
+ return False
84
+ if not shape.string(one.get("name"), f"{path}.name"):
85
+ return False
86
+ if not shape.whole(one.get("params"), f"{path}.params"):
87
+ return False
88
+ if not shape.whole(one.get("slots"), f"{path}.slots"):
89
+ return False
90
+ if not shape.array(one.get("code"), f"{path}.code"):
91
+ return False
92
+ if one["params"] > one["slots"]:
93
+ return shape.fail(f"{path}.slots", "a frame cannot be smaller than its parameter list")
94
+
95
+ for at, site in enumerate(tables.call_sites):
96
+ path = f"{prefix}callSites[{at}]"
97
+ if not shape.object(site, path):
98
+ return False
99
+ if not shape.index(site.get("fn"), f"{path}.fn", len(tables.functions), "functions"):
100
+ return False
101
+ for name in ("argc", "cellBase", "stateBase"):
102
+ if not shape.whole(site.get(name), f"{path}.{name}"):
103
+ return False
104
+ if not shape.array(site.get("series"), f"{path}.series"):
105
+ return False
106
+ if site["argc"] != tables.functions[site["fn"]]["params"]:
107
+ return shape.fail(
108
+ f"{path}.argc", "a site passes exactly the function's parameter count"
109
+ )
110
+ for which, bound in enumerate(site["series"]):
111
+ if bound == -1:
112
+ continue
113
+ if not shape.index(bound, f"{path}.series[{which}]", len(tables.series), "series"):
114
+ return False
115
+
116
+ for at, loop in enumerate(tables.loops):
117
+ path = f"{prefix}loops[{at}]"
118
+ if not shape.object(loop, path):
119
+ return False
120
+ if not shape.one(loop.get("kind"), f"{path}.kind", LOOP_KINDS):
121
+ return False
122
+ for name in ("line", "col"):
123
+ if not shape.whole(loop.get(name), f"{path}.{name}"):
124
+ return False
125
+
126
+ return True
127
+
128
+
129
+ def check_requests(
130
+ shape: ShapeCheck,
131
+ prefix: str,
132
+ requests: Sequence[Any],
133
+ registers: int,
134
+ lib_functions: int,
135
+ keys: Set[str],
136
+ ) -> bool:
137
+ """``requests`` and every body inside it, section 2.16.
138
+
139
+ ``series`` is the register of the enclosing machine that the read's value
140
+ lands in, so it is checked against the table the caller passes; everything
141
+ inside ``body`` is the body's own and is checked against the body's.
142
+ """
143
+ return _walk(shape, prefix, requests, registers, lib_functions, keys, set())
144
+
145
+
146
+ def _walk(
147
+ shape: ShapeCheck,
148
+ prefix: str,
149
+ requests: Sequence[Any],
150
+ registers: int,
151
+ lib_functions: int,
152
+ keys: Set[str],
153
+ seen: Set[int],
154
+ ) -> bool:
155
+ for at, request in enumerate(requests):
156
+ path = f"{prefix}requests[{at}]"
157
+ if not shape.object(request, path):
158
+ return False
159
+ if not shape.whole(request.get("id"), f"{path}.id"):
160
+ return False
161
+ # The id is this read's handle and is what req.isReady is handed, so two
162
+ # reads sharing one would answer each other's question.
163
+ if request["id"] in seen:
164
+ handle = request["id"]
165
+ return shape.fail(f"{path}.id", f"{handle} is already another read's handle")
166
+ seen.add(request["id"])
167
+ if not shape.one(request.get("read"), f"{path}.read", READS):
168
+ return False
169
+ if not shape.one(request.get("mode"), f"{path}.mode", MODES):
170
+ return False
171
+ if not shape.index(request.get("series"), f"{path}.series", registers, "series"):
172
+ return False
173
+ warmup = request.get("warmup")
174
+ if warmup is not None and not shape.whole(warmup, f"{path}.warmup"):
175
+ return False
176
+ for name in ("symbol", "exchange", "timeframe"):
177
+ if not _check_identity(shape, request.get(name), f"{path}.{name}", keys):
178
+ return False
179
+ if not _check_body(shape, f"{path}.body.", request.get("body"), lib_functions, keys, seen):
180
+ return False
181
+ return True
182
+
183
+
184
+ #: The tables a read's body carries, section 2.16.1. Not the program's tables:
185
+ #: a body holds neither the constant pool nor the library manifest, because
186
+ #: neither holds anything that depends on a bar.
187
+ BODY_TABLES = (
188
+ "inputs",
189
+ "series",
190
+ "cells",
191
+ "states",
192
+ "functions",
193
+ "callSites",
194
+ "loops",
195
+ "requests",
196
+ "code",
197
+ "pos",
198
+ "fnPos",
199
+ )
200
+
201
+
202
+ def _check_body(
203
+ shape: ShapeCheck,
204
+ prefix: str,
205
+ raw: Any,
206
+ lib_functions: int,
207
+ keys: Set[str],
208
+ seen: Set[int],
209
+ ) -> bool:
210
+ at = prefix[:-1]
211
+ if not shape.object(raw, at):
212
+ return False
213
+ for name in BODY_TABLES:
214
+ if not shape.array(raw.get(name), f"{prefix}{name}"):
215
+ return False
216
+ if not shape.object(raw.get("frame"), f"{prefix}frame"):
217
+ return False
218
+ if not shape.whole(raw["frame"].get("slots"), f"{prefix}frame.slots"):
219
+ return False
220
+
221
+ series = raw["series"]
222
+ tables = MachineTables(
223
+ series=series,
224
+ cells=raw["cells"],
225
+ states=raw["states"],
226
+ functions=raw["functions"],
227
+ call_sites=raw["callSites"],
228
+ loops=raw["loops"],
229
+ slots=raw["frame"]["slots"],
230
+ lib_functions=lib_functions,
231
+ )
232
+ if not check_machine_tables(shape, prefix, tables):
233
+ return False
234
+
235
+ for which, one in enumerate(raw["inputs"]):
236
+ path = f"{prefix}inputs[{which}]"
237
+ if not shape.object(one, path):
238
+ return False
239
+ if not shape.string(one.get("input"), f"{path}.input"):
240
+ return False
241
+ if one["input"] not in keys:
242
+ named = one["input"]
243
+ return shape.fail(path, f"it names the input {named}, which inputs[] does not declare")
244
+ if not shape.index(one.get("series"), f"{path}.series", len(series), "the body's series"):
245
+ return False
246
+
247
+ return _walk(shape, prefix, raw["requests"], len(series), lib_functions, keys, seen)
248
+
249
+
250
+ def _check_identity(shape: ShapeCheck, value: Any, path: str, keys: Set[str]) -> bool:
251
+ """One of the three forms a request's identity field takes, section 2.16.
252
+
253
+ A value, the setting that supplies it, or one of the three chart facts that
254
+ identify a chart rather than describe it. The rest of the chart namespace
255
+ describes the chart, and a request built from one of those would name
256
+ nothing.
257
+ """
258
+ if value is None or isinstance(value, str):
259
+ return True
260
+ if not shape.object(value, path):
261
+ return False
262
+ if "input" in value:
263
+ if not shape.string(value["input"], f"{path}.input"):
264
+ return False
265
+ if value["input"] not in keys:
266
+ named = value["input"]
267
+ return shape.fail(path, f"it names the input {named}, which inputs[] does not declare")
268
+ return True
269
+ if "chart" not in value:
270
+ return shape.fail(path, "an object here names an input or a chart fact")
271
+ return shape.one(value["chart"], f"{path}.chart", CHART_FACTS)