wirely 0.1.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.
- wirely/__init__.py +406 -0
- wirely/__main__.py +45 -0
- wirely/ast.py +1135 -0
- wirely/block.py +542 -0
- wirely/block_type.py +342 -0
- wirely/blocks.lark +126 -0
- wirely/compiler.py +125 -0
- wirely/engine.py +418 -0
- wirely/enum.py +15 -0
- wirely/example.py +36 -0
- wirely/graphviz.py +107 -0
- wirely/knx/__init__.py +0 -0
- wirely/knx/connection_adapter.py +90 -0
- wirely/knx/connection_config.py +152 -0
- wirely/lib/abb_jra.wrl +64 -0
- wirely/lib/csv.wrl +32 -0
- wirely/lib/ext_prog.wrl +12 -0
- wirely/lib/gpio.wrl +23 -0
- wirely/lib/knx.wrl +80 -0
- wirely/lib/memory.wrl +45 -0
- wirely/lib/mqtt.wrl +66 -0
- wirely/lib/std.wrl +134 -0
- wirely/lib/time.wrl +93 -0
- wirely/lib/two_n.wrl +111 -0
- wirely/misc/__init__.py +0 -0
- wirely/misc/service.py +48 -0
- wirely/native/__init__.py +74 -0
- wirely/native/abb_jra.py +66 -0
- wirely/native/csv.py +194 -0
- wirely/native/ext_prog.py +77 -0
- wirely/native/gpio.py +125 -0
- wirely/native/knx.py +455 -0
- wirely/native/memory.py +113 -0
- wirely/native/mqtt.py +605 -0
- wirely/native/std.py +334 -0
- wirely/native/test_library.py +14 -0
- wirely/native/time.py +359 -0
- wirely/native/two_n.py +632 -0
- wirely/node.py +125 -0
- wirely/parser.py +175 -0
- wirely/program.py +155 -0
- wirely/run.py +143 -0
- wirely/service.py +65 -0
- wirely/test.py +569 -0
- wirely/time.py +172 -0
- wirely/two_n/__init__.py +0 -0
- wirely/two_n/cert.py +126 -0
- wirely/two_n/client.py +402 -0
- wirely/two_n/fake_server.py +415 -0
- wirely/two_n/grab_images.py +146 -0
- wirely/types.py +522 -0
- wirely/util.py +111 -0
- wirely/value.py +163 -0
- wirely-0.1.0.dist-info/METADATA +94 -0
- wirely-0.1.0.dist-info/RECORD +59 -0
- wirely-0.1.0.dist-info/WHEEL +5 -0
- wirely-0.1.0.dist-info/entry_points.txt +2 -0
- wirely-0.1.0.dist-info/licenses/LICENSE +21 -0
- wirely-0.1.0.dist-info/top_level.txt +1 -0
wirely/__init__.py
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import abc
|
|
3
|
+
import contextlib
|
|
4
|
+
import datetime
|
|
5
|
+
import wirely as blocks
|
|
6
|
+
|
|
7
|
+
from .time import MonotonicTime, Duration
|
|
8
|
+
from . import enum, types
|
|
9
|
+
from .types import Type, TypeParameter
|
|
10
|
+
from .value import Value, ConstantValue, ValueParameter
|
|
11
|
+
|
|
12
|
+
from .util import check_parameters
|
|
13
|
+
|
|
14
|
+
# TODO: Rename graph to block_type everywhere
|
|
15
|
+
|
|
16
|
+
from .node import Node, NodeInstance, BlockInput, BlockOutput, BlockTemporaryVariable, BlockStateVariable
|
|
17
|
+
|
|
18
|
+
from .block import Block, BlockInstance
|
|
19
|
+
|
|
20
|
+
def connect(source, target, source_element):
|
|
21
|
+
# TODO: Clean up
|
|
22
|
+
if not isinstance(source, Block.Output) and not isinstance(source, blocks.BlockInput) and not isinstance(source, blocks.BlockStateVariable) and not isinstance(source, blocks.BlockTemporaryVariable):
|
|
23
|
+
raise Exception('source parameter is not a Block.Output, BlockInput, BlockStateVariable, BlockTemporaryVariable')
|
|
24
|
+
# TODO: Clean up
|
|
25
|
+
if not isinstance(target, Block.Input) and not isinstance(target, blocks.BlockOutput):
|
|
26
|
+
raise Exception('target parameter is not a Block.Input and not a BlockOutput')
|
|
27
|
+
|
|
28
|
+
if source.block.graph != target.block.graph:
|
|
29
|
+
# TODO: Can this happen from program code? If yes, make error message clearer?
|
|
30
|
+
raise blocks.ProgramError(source_element, f'source has graph {source.block.graph} but target has graph {target.block.graph}')
|
|
31
|
+
|
|
32
|
+
# TODO: More checks for non-ConnectorSpecificationData objects, e.g. KNX connections
|
|
33
|
+
source_kind = None
|
|
34
|
+
if isinstance(source, blocks.BlockStateVariable):
|
|
35
|
+
source_kind = blocks.ConnectionKind.Value
|
|
36
|
+
elif isinstance(source, blocks.BlockTemporaryVariable):
|
|
37
|
+
source_kind = blocks.ConnectionKind.Value
|
|
38
|
+
elif isinstance(source.conn_spec, blocks.ConnectorSpecificationData):
|
|
39
|
+
source_kind = source.conn_spec.kind_spec.kind
|
|
40
|
+
target_kind = None
|
|
41
|
+
if isinstance(target.conn_spec, blocks.ConnectorSpecificationData):
|
|
42
|
+
target_kind = target.conn_spec.kind_spec.kind
|
|
43
|
+
# print(source_kind, target_kind)
|
|
44
|
+
if source_kind != target_kind:
|
|
45
|
+
hints = [
|
|
46
|
+
# TODO: Does this always work?
|
|
47
|
+
blocks.CompilerHint(source.block_output.conn_spec, 'Source was declared here'),
|
|
48
|
+
blocks.CompilerHint(target.block_input.conn_spec, 'Target was declared here'),
|
|
49
|
+
]
|
|
50
|
+
raise ProgramError(source_element, f'source has kind {source_kind} but target has kind {target_kind}', hints=hints)
|
|
51
|
+
|
|
52
|
+
# TODO: Allow automatic type conversions?
|
|
53
|
+
# TODO: Raise this error only when compiling?
|
|
54
|
+
source_type = source.var_type
|
|
55
|
+
if isinstance(source.block, blocks.Block) and source_type is not None:
|
|
56
|
+
source_type = source_type.resolve_self(source.block.par_values)
|
|
57
|
+
target_type = target.var_type
|
|
58
|
+
if isinstance(target.block, blocks.Block) and target_type is not None:
|
|
59
|
+
target_type = target_type.resolve_self(target.block.par_values)
|
|
60
|
+
# TODO: Handle knx_bus connections better than just setting the type to None
|
|
61
|
+
if source_type != target_type:
|
|
62
|
+
hints = [
|
|
63
|
+
# TODO: Does this always work?
|
|
64
|
+
# TODO: Point to type, not to conn_spec
|
|
65
|
+
blocks.CompilerHint(source.block_output.conn_spec, 'Source was declared here'),
|
|
66
|
+
blocks.CompilerHint(target.block_input.conn_spec, 'Target was declared here'),
|
|
67
|
+
]
|
|
68
|
+
raise ProgramError(source_element, f'source has type {source_type} but target has type {target_type}', hints=hints)
|
|
69
|
+
|
|
70
|
+
target.connect(source)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
debug_variable_resolution = False
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# TODO: Create "BlockInstance" class to replace the "path" values everywhere? Should there be exactly one such instance or can there be multiple BlockInstances with the same path?
|
|
77
|
+
|
|
78
|
+
def path_resolve_singleton(path):
|
|
79
|
+
if isinstance(path[-1], blocks.Block) and path[-1].block_type.is_singleton:
|
|
80
|
+
return (path[-1].block_type,)
|
|
81
|
+
else:
|
|
82
|
+
return path
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# Return the actual source for var in the context path
|
|
86
|
+
def resolve_variable(path, var):
|
|
87
|
+
if debug_variable_resolution:
|
|
88
|
+
print(f'resolve_variable({path}, {var})')
|
|
89
|
+
|
|
90
|
+
if isinstance(path[-1], blocks.Block) and path[-1].block_type.is_singleton:
|
|
91
|
+
raise Exception('isinstance(path[-1], blocks.Blocks) and path[-1].block_type.is_singleton')
|
|
92
|
+
|
|
93
|
+
if isinstance(path[-1], Block): # TODO: Handle singletons here?
|
|
94
|
+
# TODO: Clean this up?
|
|
95
|
+
# print(path, var)
|
|
96
|
+
if path[-1] == var.block:
|
|
97
|
+
block = var.block
|
|
98
|
+
elif var.block.graph == path[-1].block_type:
|
|
99
|
+
# Dependency on the result of a child block
|
|
100
|
+
# TODO: Make this more generic
|
|
101
|
+
# TODO: Modify path here?
|
|
102
|
+
path = path_resolve_singleton(path + (var.block,))
|
|
103
|
+
block = var.block
|
|
104
|
+
else:
|
|
105
|
+
raise Exception('path[-1] != var.block')
|
|
106
|
+
# print(type(var), type(var.block))
|
|
107
|
+
|
|
108
|
+
# TODO: This is somewhat of a hack
|
|
109
|
+
if (isinstance(var, blocks.Block.Output) or isinstance(var, blocks.Block.Temporary)) and isinstance(block, blocks.BlockInput):
|
|
110
|
+
input_name = block.input_name
|
|
111
|
+
new_path = path[:-1]
|
|
112
|
+
parent_block = new_path[-1]
|
|
113
|
+
input_var = parent_block.block_inputs_byname[input_name]
|
|
114
|
+
return resolve_variable(path_resolve_singleton(new_path), input_var)
|
|
115
|
+
|
|
116
|
+
# TODO: This is somewhat of a hack
|
|
117
|
+
if (isinstance(var, blocks.Block.Input)) and isinstance(block, blocks.BlockOutput):
|
|
118
|
+
output_name = block.output_name
|
|
119
|
+
new_path = path[:-1]
|
|
120
|
+
parent_block = new_path[-1]
|
|
121
|
+
output_var = parent_block.block_outputs_byname[output_name]
|
|
122
|
+
return resolve_variable(path_resolve_singleton(new_path), output_var)
|
|
123
|
+
|
|
124
|
+
if isinstance(var, blocks.Block.Input):
|
|
125
|
+
src = var.connected_source
|
|
126
|
+
if src is None:
|
|
127
|
+
raise Exception('src is None')
|
|
128
|
+
new_path = path[:-1] + (src.block,)
|
|
129
|
+
# print(src, type(src), src.block, new_path)
|
|
130
|
+
return resolve_variable(path_resolve_singleton(new_path), src)
|
|
131
|
+
|
|
132
|
+
if isinstance(var, blocks.Block.Output):
|
|
133
|
+
# TODO: Handle this differently? This is e.g. for Bus objects
|
|
134
|
+
if var.var_type is None:
|
|
135
|
+
return path, var
|
|
136
|
+
# Normal output
|
|
137
|
+
# TODO: Hack, should use var.temporary, but that is overwritten somewhere in some cases
|
|
138
|
+
temporary = var.orig_temporary
|
|
139
|
+
if temporary.block != path[-1]:
|
|
140
|
+
if isinstance(path[-1], blocks.Block): # TODO: Handle singletons here?
|
|
141
|
+
raise Exception(f'temporary.block {temporary.block} != path[-1] {path[-1]}')
|
|
142
|
+
if temporary.producer is None:
|
|
143
|
+
raise Exception(f'Temporary for output {var} does not have a producer')
|
|
144
|
+
if debug_variable_resolution:
|
|
145
|
+
print(f'resolve_variable: returning {path}, {temporary}')
|
|
146
|
+
return path, temporary
|
|
147
|
+
|
|
148
|
+
if isinstance(var, blocks.Block.Temporary):
|
|
149
|
+
temporary = var.temporary
|
|
150
|
+
if temporary != var:
|
|
151
|
+
raise Exception('temporary != var')
|
|
152
|
+
if temporary.block != path[-1]:
|
|
153
|
+
if isinstance(path[-1], blocks.Block): # TODO: Handle singletons here?
|
|
154
|
+
raise Exception(f'temporary.block {temporary.block} != path[-1] {path[-1]}')
|
|
155
|
+
if temporary.producer is None:
|
|
156
|
+
raise Exception('temporary.producer is None')
|
|
157
|
+
if debug_variable_resolution:
|
|
158
|
+
print(f'resolve_variable: returning {path}, {temporary}')
|
|
159
|
+
return path, temporary
|
|
160
|
+
|
|
161
|
+
if isinstance(var, blocks.BlockTemporaryVariable):
|
|
162
|
+
# TODO: Clean up
|
|
163
|
+
if path[-1] != var:
|
|
164
|
+
raise Exception('path[-1] != var')
|
|
165
|
+
block = path[-2]
|
|
166
|
+
var_inst = None
|
|
167
|
+
for t in block.block_temporaries:
|
|
168
|
+
if t.name == var.name:
|
|
169
|
+
var_inst = t
|
|
170
|
+
break
|
|
171
|
+
if var_inst is None:
|
|
172
|
+
raise Exception(f'Cannot find temporary {var.name!r}')
|
|
173
|
+
temporary = var_inst.temporary
|
|
174
|
+
if temporary != var_inst:
|
|
175
|
+
raise Exception('temporary != var_inst')
|
|
176
|
+
if temporary.block != path[-2]:
|
|
177
|
+
if isinstance(path[-2], blocks.Block): # TODO: Handle singletons here?
|
|
178
|
+
raise Exception(f'temporary.block {temporary.block} != path[-1] {path[-1]}')
|
|
179
|
+
if temporary.producer is None:
|
|
180
|
+
raise Exception('temporary.producer is None')
|
|
181
|
+
if debug_variable_resolution:
|
|
182
|
+
print(f'resolve_variable: returning {path[:-1]}, {temporary}')
|
|
183
|
+
return path[:-1], temporary
|
|
184
|
+
|
|
185
|
+
# TODO: Clean up
|
|
186
|
+
if isinstance(var, blocks.BlockInput):
|
|
187
|
+
if path[-1] != var:
|
|
188
|
+
raise Exception('path[-1] != var')
|
|
189
|
+
input_name = var.input_name
|
|
190
|
+
new_path = path[:-1]
|
|
191
|
+
parent_block = new_path[-1]
|
|
192
|
+
input_var = parent_block.block_inputs_byname[input_name]
|
|
193
|
+
return resolve_variable(path_resolve_singleton(new_path), input_var)
|
|
194
|
+
|
|
195
|
+
# TODO: Clean up
|
|
196
|
+
if isinstance(var, blocks.BlockStateVariable):
|
|
197
|
+
if path[-1] != var:
|
|
198
|
+
raise Exception('path[-1] != var')
|
|
199
|
+
# TODO: Implement this
|
|
200
|
+
return path, var
|
|
201
|
+
|
|
202
|
+
print(f'resolve_variable: Unhandled {path}, {var} {type(var)} {type(var.block)}')
|
|
203
|
+
|
|
204
|
+
if debug_variable_resolution:
|
|
205
|
+
print(f'resolve_variable: returning {path}, {var.temporary}')
|
|
206
|
+
# TODO: This code seems to be unused
|
|
207
|
+
# TODO: Avoid using .temporary (should be handled here)
|
|
208
|
+
return path, var.temporary
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# TODO: Rename to NativeBlockState?
|
|
212
|
+
class NativeState:
|
|
213
|
+
def __init__(self, path, engine, static_block_context):
|
|
214
|
+
self.block_path = path
|
|
215
|
+
self.engine = engine
|
|
216
|
+
self.static_block_context = static_block_context
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def block_type(self):
|
|
220
|
+
value = self.block_path[-1]
|
|
221
|
+
if isinstance(value, blocks.Block):
|
|
222
|
+
return value.block_type
|
|
223
|
+
else:
|
|
224
|
+
return value
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def block(self):
|
|
228
|
+
value = self.block_path[-1]
|
|
229
|
+
if isinstance(value, blocks.Block):
|
|
230
|
+
return value
|
|
231
|
+
else:
|
|
232
|
+
raise Exception(f'Trying to get block for a singleton block type ({value})')
|
|
233
|
+
|
|
234
|
+
# TODO: Remove, this is a hack
|
|
235
|
+
@property
|
|
236
|
+
def block_or_singleton(self):
|
|
237
|
+
value = self.block_path[-1]
|
|
238
|
+
if isinstance(value, blocks.Block):
|
|
239
|
+
return value
|
|
240
|
+
else:
|
|
241
|
+
return value.singleton_instance
|
|
242
|
+
|
|
243
|
+
def start_tasks(self, task_group, async_exit_stack: contextlib.AsyncExitStack):
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
# TODO: Merge start_tasks and start_tasks_async?
|
|
247
|
+
# TODO: Allow blocking behavior during startup?
|
|
248
|
+
# TODO: Use an AsyncExitStack or an ExitStack?
|
|
249
|
+
async def start_tasks_async(self, task_group, async_exit_stack: contextlib.AsyncExitStack):
|
|
250
|
+
pass
|
|
251
|
+
|
|
252
|
+
@staticmethod
|
|
253
|
+
@contextlib.contextmanager
|
|
254
|
+
def start_task_context(task_group, coroutine):
|
|
255
|
+
import wirely.misc.service
|
|
256
|
+
|
|
257
|
+
task = task_group.create_task(wirely.misc.service.should_never_return(coroutine))
|
|
258
|
+
try:
|
|
259
|
+
yield task
|
|
260
|
+
finally:
|
|
261
|
+
task.cancel()
|
|
262
|
+
|
|
263
|
+
@staticmethod
|
|
264
|
+
def start_task(task_group, async_exit_stack, coroutine):
|
|
265
|
+
return async_exit_stack.enter_context(NativeState.start_task_context(task_group, coroutine))
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class Variable:
|
|
269
|
+
def __init__(self, name, var_type):
|
|
270
|
+
if not isinstance(name, str):
|
|
271
|
+
raise Exception('name parameter is not a str')
|
|
272
|
+
if var_type.contains_parameters:
|
|
273
|
+
raise Exception('var_type parameter is not a concrete type')
|
|
274
|
+
|
|
275
|
+
self.name = str(name)
|
|
276
|
+
self.var_type = var_type
|
|
277
|
+
|
|
278
|
+
def __repr__(self):
|
|
279
|
+
return f'Variable[{self}]'
|
|
280
|
+
|
|
281
|
+
def __str__(self):
|
|
282
|
+
return f'{self.name}'
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class VariableBlock:
|
|
286
|
+
def __init__(self, variables, name):
|
|
287
|
+
self.name = name
|
|
288
|
+
|
|
289
|
+
self.variables = {}
|
|
290
|
+
self.variable_set = set()
|
|
291
|
+
|
|
292
|
+
for var in variables:
|
|
293
|
+
if not isinstance(var, Variable):
|
|
294
|
+
raise Exception('var parameter contains object that is not a Variable')
|
|
295
|
+
name = var.name
|
|
296
|
+
if name in self.variables:
|
|
297
|
+
raise Exception(f'Duplicate variable name {name!r}')
|
|
298
|
+
self.variables[name] = var
|
|
299
|
+
self.variable_set.add(var)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class VariableBlockInstance:
|
|
303
|
+
def __init__(self, variable_block):
|
|
304
|
+
if not isinstance(variable_block, VariableBlock):
|
|
305
|
+
raise Exception('variable_block parameter is not a VariableBlock')
|
|
306
|
+
self.variable_block = variable_block
|
|
307
|
+
|
|
308
|
+
self.data = {}
|
|
309
|
+
|
|
310
|
+
def add(self, variable, value):
|
|
311
|
+
# print(f'Setting {variable}')
|
|
312
|
+
if variable not in self.variable_block.variable_set:
|
|
313
|
+
raise Exception(f'Variable {variable!r} not in VariableBlock {self.variable_block.name}')
|
|
314
|
+
if variable in self.data:
|
|
315
|
+
raise Exception(f'Variable {variable} already set')
|
|
316
|
+
self.data[variable] = variable.var_type.verify_value(value)
|
|
317
|
+
|
|
318
|
+
def __getitem__(self, variable):
|
|
319
|
+
# print(f'Getting {variable}')
|
|
320
|
+
if variable not in self.variable_block.variable_set:
|
|
321
|
+
raise Exception(f'Variable {variable!r} not in VariableBlock {self.variable_block.name}')
|
|
322
|
+
if variable not in self.data:
|
|
323
|
+
raise Exception(f'Variable {variable} not yet set')
|
|
324
|
+
return self.data[variable]
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
# Things which will stay the same for one block instance
|
|
328
|
+
# TODO: Prevent modifications, more checks
|
|
329
|
+
# TODO: Merge this into BlockInstance? Or should this data (which will be needed at runtime) be kept separately?
|
|
330
|
+
class StaticBlockContext:
|
|
331
|
+
def __init__(self, *, parameter_values, parsed_native_instance):
|
|
332
|
+
self.parameter_values = parameter_values
|
|
333
|
+
# TODO: Rename?
|
|
334
|
+
self.parsed_native_instance = parsed_native_instance
|
|
335
|
+
|
|
336
|
+
def __repr__(self):
|
|
337
|
+
return f'StaticBlockContext({self.__dict__!r})'
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
# Things which will stay the same if the same instance of a procedure is executed multiple times
|
|
341
|
+
# TODO: Prevent modifications, more checks
|
|
342
|
+
class StaticProcedureContext:
|
|
343
|
+
def __init__(self, *, sources, targets, state, name, block_parameter_values, block=None):
|
|
344
|
+
self.sources = sources
|
|
345
|
+
self.targets = targets
|
|
346
|
+
self.state = state
|
|
347
|
+
self.name = name
|
|
348
|
+
self.block = block
|
|
349
|
+
self.block_parameter_values = block_parameter_values
|
|
350
|
+
|
|
351
|
+
def __repr__(self):
|
|
352
|
+
return f'StaticProcedureContext({self.__dict__!r})'
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
# TODO: Prevent modifications, more checks (also e.g. on assignments to results etc.)
|
|
356
|
+
class ProcedureContext:
|
|
357
|
+
def __init__(self, *, dep_values, results, state_old, state_new, static, step_info):
|
|
358
|
+
self.dep_values = dep_values
|
|
359
|
+
self.results = results
|
|
360
|
+
self.state_old = state_old
|
|
361
|
+
self.state_new = state_new
|
|
362
|
+
self.static = static
|
|
363
|
+
self.step_info = step_info
|
|
364
|
+
self.wakeup_time = None
|
|
365
|
+
|
|
366
|
+
def __repr__(self):
|
|
367
|
+
return f'ProcedureContext({self.__dict__!r})'
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
# TODO: Rename to CycleContext?
|
|
371
|
+
class StepContext:
|
|
372
|
+
def __init__(self):
|
|
373
|
+
self.stage_start_time_monotonic = {}
|
|
374
|
+
self.stage_start_time_utc = {}
|
|
375
|
+
self.stage_start_time = {}
|
|
376
|
+
|
|
377
|
+
def start_stage(self, engine, stage):
|
|
378
|
+
if stage in self.stage_start_time:
|
|
379
|
+
raise Exception(f'Stage {stage} already started')
|
|
380
|
+
|
|
381
|
+
now_monotonic = blocks.MonotonicTime.current_time()
|
|
382
|
+
if engine.fake_monotonic_time_offset is not None:
|
|
383
|
+
now_monotonic += engine.fake_monotonic_time_offset
|
|
384
|
+
# print(f'StepContext for {stage} started at {now_monotonic}')
|
|
385
|
+
|
|
386
|
+
now_utc = datetime.datetime.now(datetime.timezone.utc)
|
|
387
|
+
# Note: This should work with all python versions, even without PEP 495
|
|
388
|
+
# <https://peps.python.org/pep-0495/>
|
|
389
|
+
# Starting with Python 3.6 (PEP 495)
|
|
390
|
+
# datetime.datetime.now().astimezone() should return the same value.
|
|
391
|
+
now = now_utc.astimezone()
|
|
392
|
+
|
|
393
|
+
self.stage_start_time_monotonic[stage] = now_monotonic
|
|
394
|
+
self.stage_start_time_utc[stage] = now_utc
|
|
395
|
+
self.stage_start_time[stage] = now
|
|
396
|
+
|
|
397
|
+
def __repr__(self):
|
|
398
|
+
return f'StepContext({self.__dict__!r})'
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
from wirely.program import Program
|
|
402
|
+
from wirely.engine import Engine
|
|
403
|
+
|
|
404
|
+
from .ast import ProgramError, InternalCompilerError, CompilerHint, SourcePosition, Declaration, BlockConnectorTypeSpecifier, ListSpecifier, ProgramError, Identifier, IntNumber, FloatNumber, StringConstant, Reference, Declaration, DeclarationConnector, DeclarationParameter, BlockInstantiation, DeclarationBlockInstantiation, DeclarationConnection, DeclarationStateVariable, DeclarationTemporaryVariable, DeclarationProcedure, NativeSpecifier, OptionalSpecifier, StageDeclaration, DependsDeclaration, ProducesDeclaration, ConnectionKindSpecifier, ConnectorSpecification, ConnectorSpecificationData, ConnectorSpecificationKnxBus, ConnectorSpecificationMqttServer, ConnectorSpecificationTwoNDevice, DeclarationLibrary, BlockParameter, Scope, BlockTypeSpecifier, ListLiteral, FunctionCall, BracketOperator, ConnectionKind, RunStage
|
|
405
|
+
|
|
406
|
+
from .block_type import BlockType
|
wirely/__main__.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import argparse
|
|
3
|
+
import importlib
|
|
4
|
+
import asyncio
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
parser = argparse.ArgumentParser()
|
|
9
|
+
subparsers = parser.add_subparsers(title='subcommands', dest='command', required=True)
|
|
10
|
+
|
|
11
|
+
run_service_parser = subparsers.add_parser('run-service')
|
|
12
|
+
run_service_parser.add_argument('--verbose', action='store_true')
|
|
13
|
+
run_service_parser.add_argument('--debug', action='store_true')
|
|
14
|
+
run_service_parser.add_argument('input_filename', type=str, nargs='+')
|
|
15
|
+
run_service_parser.set_defaults(module_name='wirely.service')
|
|
16
|
+
|
|
17
|
+
compile_parser = subparsers.add_parser('compile')
|
|
18
|
+
program_group = compile_parser.add_mutually_exclusive_group()
|
|
19
|
+
program_group.add_argument('--create-program', action='store_true')
|
|
20
|
+
program_group.add_argument('--no-create-program', dest='create_program', action='store_false')
|
|
21
|
+
engine_group = compile_parser.add_mutually_exclusive_group()
|
|
22
|
+
engine_group.add_argument('--create-engine', action='store_true')
|
|
23
|
+
engine_group.add_argument('--no-create-engine', dest='create_engine', action='store_false')
|
|
24
|
+
compile_parser.add_argument('input_filename', nargs='+')
|
|
25
|
+
compile_parser.set_defaults(module_name='wirely.compiler',
|
|
26
|
+
create_program=True, create_engine=False)
|
|
27
|
+
|
|
28
|
+
show_graph_parser = subparsers.add_parser('show-graph')
|
|
29
|
+
show_graph_parser.add_argument('input_filename')
|
|
30
|
+
show_graph_parser.set_defaults(module_name='wirely.graphviz')
|
|
31
|
+
|
|
32
|
+
args = parser.parse_args()
|
|
33
|
+
module = importlib.import_module(args.module_name)
|
|
34
|
+
has_async_run = hasattr(module, 'async_run')
|
|
35
|
+
has_run = hasattr(module, 'run')
|
|
36
|
+
if has_async_run == has_run:
|
|
37
|
+
raise RuntimeError(f'{args.module_name} must define exactly one of run() or async_run()')
|
|
38
|
+
if has_async_run:
|
|
39
|
+
sys.exit(asyncio.run(module.async_run(args)))
|
|
40
|
+
else:
|
|
41
|
+
sys.exit(module.run(args))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == '__main__':
|
|
45
|
+
main()
|