sonolus.py 0.9.3__py3-none-any.whl → 0.10.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.
Potentially problematic release.
This version of sonolus.py might be problematic. Click here for more details.
- sonolus/backend/visitor.py +46 -21
- sonolus/build/cli.py +10 -8
- sonolus/build/compile.py +12 -11
- sonolus/build/dev_server.py +143 -32
- sonolus/build/engine.py +26 -24
- sonolus/build/project.py +8 -3
- sonolus/script/archetype.py +8 -8
- sonolus/script/bucket.py +1 -1
- sonolus/script/debug.py +18 -5
- sonolus/script/internal/context.py +55 -27
- sonolus/script/options.py +2 -2
- sonolus/script/project.py +2 -5
- sonolus/script/runtime.py +37 -37
- sonolus/script/stream.py +3 -3
- {sonolus_py-0.9.3.dist-info → sonolus_py-0.10.0.dist-info}/METADATA +1 -1
- {sonolus_py-0.9.3.dist-info → sonolus_py-0.10.0.dist-info}/RECORD +19 -19
- {sonolus_py-0.9.3.dist-info → sonolus_py-0.10.0.dist-info}/WHEEL +0 -0
- {sonolus_py-0.9.3.dist-info → sonolus_py-0.10.0.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.9.3.dist-info → sonolus_py-0.10.0.dist-info}/licenses/LICENSE +0 -0
sonolus/backend/visitor.py
CHANGED
|
@@ -47,9 +47,14 @@ def compile_and_call[**P, R](fn: Callable[P, R], /, *args: P.args, **kwargs: P.k
|
|
|
47
47
|
def compile_and_call_at_definition[**P, R](fn: Callable[P, R], /, *args: P.args, **kwargs: P.kwargs) -> R:
|
|
48
48
|
if not ctx():
|
|
49
49
|
return fn(*args, **kwargs)
|
|
50
|
-
if ctx().no_eval:
|
|
51
|
-
return compile_and_call(fn, *args, **kwargs)
|
|
52
50
|
source_file, node = get_function(fn)
|
|
51
|
+
if ctx().no_eval:
|
|
52
|
+
debug_stack = ctx().callback_state.debug_stack
|
|
53
|
+
try:
|
|
54
|
+
debug_stack.append(f'File "{source_file}", line {node.lineno}, in <callback>')
|
|
55
|
+
return compile_and_call(fn, *args, **kwargs)
|
|
56
|
+
finally:
|
|
57
|
+
debug_stack.pop()
|
|
53
58
|
location_args = {
|
|
54
59
|
"lineno": node.lineno,
|
|
55
60
|
"col_offset": node.col_offset,
|
|
@@ -64,10 +69,16 @@ def compile_and_call_at_definition[**P, R](fn: Callable[P, R], /, *args: P.args,
|
|
|
64
69
|
**location_args,
|
|
65
70
|
)
|
|
66
71
|
)
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
|
|
73
|
+
debug_stack = ctx().callback_state.debug_stack
|
|
74
|
+
try:
|
|
75
|
+
debug_stack.append(f'File "{source_file}", line {node.lineno}, in <callback>')
|
|
76
|
+
return eval(
|
|
77
|
+
compile(expr, filename=source_file, mode="eval"),
|
|
78
|
+
{"fn": lambda: compile_and_call(fn, *args, **kwargs), "_filter_traceback_": True, "_traceback_root_": True},
|
|
79
|
+
)
|
|
80
|
+
finally:
|
|
81
|
+
debug_stack.pop()
|
|
71
82
|
|
|
72
83
|
|
|
73
84
|
def generate_fn_impl(fn: Callable):
|
|
@@ -111,7 +122,9 @@ def eval_fn(fn: Callable, /, *args, **kwargs):
|
|
|
111
122
|
**fn.__globals__,
|
|
112
123
|
**nonlocal_vars,
|
|
113
124
|
}
|
|
114
|
-
return Visitor(
|
|
125
|
+
return Visitor(
|
|
126
|
+
source_file, bound_args, global_vars, parent=None, function_name=getattr(fn, "__name__", "<unnamed>")
|
|
127
|
+
).run(node)
|
|
115
128
|
|
|
116
129
|
|
|
117
130
|
unary_ops = {
|
|
@@ -232,14 +245,16 @@ class Visitor(ast.NodeVisitor):
|
|
|
232
245
|
resume_ctxs: list[Context] # Contexts after yield statements
|
|
233
246
|
active_ctx: Context | None # The active context for use in nested functions
|
|
234
247
|
parent: Visitor | None # The parent visitor for use in nested functions
|
|
235
|
-
used_parent_binding_values: dict[str, Value] # Values of parent bindings used in this
|
|
248
|
+
used_parent_binding_values: dict[str, Value] # Values of parent bindings used in this
|
|
249
|
+
function_name: str
|
|
236
250
|
|
|
237
251
|
def __init__(
|
|
238
252
|
self,
|
|
239
253
|
source_file: str,
|
|
240
254
|
bound_args: inspect.BoundArguments,
|
|
241
255
|
global_vars: dict[str, Any],
|
|
242
|
-
parent: Visitor | None
|
|
256
|
+
parent: Visitor | None,
|
|
257
|
+
function_name: str,
|
|
243
258
|
):
|
|
244
259
|
self.source_file = source_file
|
|
245
260
|
self.globals = global_vars
|
|
@@ -253,6 +268,7 @@ class Visitor(ast.NodeVisitor):
|
|
|
253
268
|
self.active_ctx = None
|
|
254
269
|
self.parent = parent
|
|
255
270
|
self.used_parent_binding_values = {}
|
|
271
|
+
self.function_name = function_name
|
|
256
272
|
|
|
257
273
|
def run(self, node):
|
|
258
274
|
before_ctx = ctx()
|
|
@@ -433,7 +449,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
433
449
|
self.source_file,
|
|
434
450
|
bound,
|
|
435
451
|
self.globals,
|
|
436
|
-
self,
|
|
452
|
+
parent=self,
|
|
453
|
+
function_name=name,
|
|
437
454
|
).run(node)
|
|
438
455
|
|
|
439
456
|
fn._meta_fn_ = True
|
|
@@ -951,7 +968,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
951
968
|
self.source_file,
|
|
952
969
|
bound,
|
|
953
970
|
self.globals,
|
|
954
|
-
self,
|
|
971
|
+
parent=self,
|
|
972
|
+
function_name="<lambda>",
|
|
955
973
|
).run(node)
|
|
956
974
|
|
|
957
975
|
fn._meta_fn_ = True
|
|
@@ -1002,7 +1020,9 @@ class Visitor(ast.NodeVisitor):
|
|
|
1002
1020
|
|
|
1003
1021
|
def visit_GeneratorExp(self, node):
|
|
1004
1022
|
self.active_ctx = ctx()
|
|
1005
|
-
return Visitor(
|
|
1023
|
+
return Visitor(
|
|
1024
|
+
self.source_file, inspect.Signature([]).bind(), self.globals, parent=self, function_name="<genexp>"
|
|
1025
|
+
).run(node)
|
|
1006
1026
|
|
|
1007
1027
|
def visit_Await(self, node):
|
|
1008
1028
|
raise NotImplementedError("Await expressions are not supported")
|
|
@@ -1326,15 +1346,20 @@ class Visitor(ast.NodeVisitor):
|
|
|
1326
1346
|
) -> R | Value:
|
|
1327
1347
|
"""Handles a call to the given callable."""
|
|
1328
1348
|
self.active_ctx = ctx()
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1349
|
+
debug_stack = self.active_ctx.callback_state.debug_stack
|
|
1350
|
+
try:
|
|
1351
|
+
debug_stack.append(f'File "{self.source_file}", line {node.lineno}, in {self.function_name}')
|
|
1352
|
+
if (
|
|
1353
|
+
isinstance(fn, Value)
|
|
1354
|
+
and fn._is_py_()
|
|
1355
|
+
and isinstance(fn._as_py_(), type)
|
|
1356
|
+
and issubclass(fn._as_py_(), Value)
|
|
1357
|
+
):
|
|
1358
|
+
return validate_value(self.execute_at_node(node, fn._as_py_(), *args, **kwargs))
|
|
1359
|
+
else:
|
|
1360
|
+
return self.execute_at_node(node, lambda: validate_value(compile_and_call(fn, *args, **kwargs)))
|
|
1361
|
+
finally:
|
|
1362
|
+
debug_stack.pop()
|
|
1338
1363
|
|
|
1339
1364
|
def handle_getitem(self, node: ast.stmt | ast.expr, target: Value, key: Value) -> Value:
|
|
1340
1365
|
with self.reporting_errors_at_node(node):
|
sonolus/build/cli.py
CHANGED
|
@@ -14,6 +14,7 @@ from sonolus.build.dev_server import run_server
|
|
|
14
14
|
from sonolus.build.engine import no_gil, package_engine, validate_engine
|
|
15
15
|
from sonolus.build.level import package_level_data
|
|
16
16
|
from sonolus.build.project import build_project_to_collection, get_project_schema
|
|
17
|
+
from sonolus.script.internal.context import ProjectContextState
|
|
17
18
|
from sonolus.script.internal.error import CompilationError
|
|
18
19
|
from sonolus.script.project import BuildConfig, Project
|
|
19
20
|
|
|
@@ -88,12 +89,18 @@ def validate_project(project: Project, config: BuildConfig):
|
|
|
88
89
|
validate_engine(project.engine.data, config)
|
|
89
90
|
|
|
90
91
|
|
|
91
|
-
def build_collection(
|
|
92
|
+
def build_collection(
|
|
93
|
+
project: Project,
|
|
94
|
+
build_dir: Path,
|
|
95
|
+
config: BuildConfig | None,
|
|
96
|
+
cache: CompileCache | None = None,
|
|
97
|
+
project_state: ProjectContextState | None = None,
|
|
98
|
+
):
|
|
92
99
|
site_dir = build_dir / "site"
|
|
93
100
|
shutil.rmtree(site_dir, ignore_errors=True)
|
|
94
101
|
site_dir.mkdir(parents=True, exist_ok=True)
|
|
95
102
|
|
|
96
|
-
collection = build_project_to_collection(project, config, cache=cache)
|
|
103
|
+
collection = build_project_to_collection(project, config, cache=cache, project_state=project_state)
|
|
97
104
|
collection.write(site_dir)
|
|
98
105
|
|
|
99
106
|
|
|
@@ -217,14 +224,9 @@ def main():
|
|
|
217
224
|
print(f"Project built successfully to '{build_dir.resolve()}' in {end_time - start_time:.2f}s")
|
|
218
225
|
elif args.command == "dev":
|
|
219
226
|
build_dir = Path(args.build_dir)
|
|
220
|
-
start_time = perf_counter()
|
|
221
227
|
config = get_config(args)
|
|
222
|
-
cache = CompileCache()
|
|
223
|
-
build_collection(project, build_dir, config, cache=cache)
|
|
224
|
-
end_time = perf_counter()
|
|
225
|
-
print(f"Build finished in {end_time - start_time:.2f}s")
|
|
226
228
|
run_server(
|
|
227
|
-
build_dir / "site", args.port, project_module.__name__, core_module_names, build_dir, config,
|
|
229
|
+
build_dir / "site", args.port, project_module.__name__, core_module_names, build_dir, config, project
|
|
228
230
|
)
|
|
229
231
|
elif args.command == "schema":
|
|
230
232
|
print(json.dumps(get_project_schema(project), indent=2))
|
sonolus/build/compile.py
CHANGED
|
@@ -17,8 +17,8 @@ from sonolus.script.internal.callbacks import CallbackInfo
|
|
|
17
17
|
from sonolus.script.internal.context import (
|
|
18
18
|
CallbackContextState,
|
|
19
19
|
Context,
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
ModeContextState,
|
|
21
|
+
ProjectContextState,
|
|
22
22
|
context_to_cfg,
|
|
23
23
|
ctx,
|
|
24
24
|
using_ctx,
|
|
@@ -54,7 +54,7 @@ class CompileCache:
|
|
|
54
54
|
|
|
55
55
|
def compile_mode(
|
|
56
56
|
mode: Mode,
|
|
57
|
-
|
|
57
|
+
project_state: ProjectContextState,
|
|
58
58
|
archetypes: list[type[_BaseArchetype]] | None,
|
|
59
59
|
global_callbacks: list[tuple[CallbackInfo, Callable]] | None,
|
|
60
60
|
passes: Sequence[CompilerPass] | None = None,
|
|
@@ -65,10 +65,9 @@ def compile_mode(
|
|
|
65
65
|
if passes is None:
|
|
66
66
|
passes = STANDARD_PASSES
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
mode_state = ModeContextState(
|
|
69
69
|
mode,
|
|
70
70
|
{a: i for i, a in enumerate(archetypes)} if archetypes is not None else None,
|
|
71
|
-
rom,
|
|
72
71
|
)
|
|
73
72
|
nodes = OutputNodeGenerator()
|
|
74
73
|
results = {}
|
|
@@ -84,7 +83,7 @@ def compile_mode(
|
|
|
84
83
|
- (cb_info.name, node_index) for global callbacks, or
|
|
85
84
|
- (cb_info.name, {"index": node_index, "order": cb_order}) for archetype callbacks.
|
|
86
85
|
"""
|
|
87
|
-
cfg = callback_to_cfg(
|
|
86
|
+
cfg = callback_to_cfg(project_state, mode_state, cb, cb_info.name, arch)
|
|
88
87
|
if validate_only:
|
|
89
88
|
if arch is not None:
|
|
90
89
|
cb_order = getattr(cb, "_callback_order_", 0)
|
|
@@ -185,27 +184,29 @@ def compile_mode(
|
|
|
185
184
|
|
|
186
185
|
|
|
187
186
|
def callback_to_cfg(
|
|
188
|
-
|
|
187
|
+
project_state: ProjectContextState,
|
|
188
|
+
mode_state: ModeContextState,
|
|
189
189
|
callback: Callable,
|
|
190
190
|
name: str,
|
|
191
191
|
archetype: type[_BaseArchetype] | None = None,
|
|
192
192
|
) -> BasicBlock:
|
|
193
193
|
try:
|
|
194
194
|
# Default to no_eval=True for performance unless there's an error.
|
|
195
|
-
return _callback_to_cfg(
|
|
195
|
+
return _callback_to_cfg(project_state, mode_state, callback, name, archetype, no_eval=True)
|
|
196
196
|
except CompilationError:
|
|
197
|
-
return _callback_to_cfg(
|
|
197
|
+
return _callback_to_cfg(project_state, mode_state, callback, name, archetype, no_eval=False)
|
|
198
198
|
|
|
199
199
|
|
|
200
200
|
def _callback_to_cfg(
|
|
201
|
-
|
|
201
|
+
project_state: ProjectContextState,
|
|
202
|
+
mode_state: ModeContextState,
|
|
202
203
|
callback: Callable,
|
|
203
204
|
name: str,
|
|
204
205
|
archetype: type[_BaseArchetype] | None,
|
|
205
206
|
no_eval: bool,
|
|
206
207
|
) -> BasicBlock:
|
|
207
208
|
callback_state = CallbackContextState(name, no_eval=no_eval)
|
|
208
|
-
context = Context(
|
|
209
|
+
context = Context(project_state, mode_state, callback_state)
|
|
209
210
|
with using_ctx(context):
|
|
210
211
|
if archetype is not None:
|
|
211
212
|
result = compile_and_call_at_definition(callback, archetype._for_compilation())
|
sonolus/build/dev_server.py
CHANGED
|
@@ -6,6 +6,7 @@ import http.server
|
|
|
6
6
|
import importlib
|
|
7
7
|
import queue
|
|
8
8
|
import shlex
|
|
9
|
+
import shutil
|
|
9
10
|
import socket
|
|
10
11
|
import socketserver
|
|
11
12
|
import sys
|
|
@@ -15,53 +16,97 @@ import traceback
|
|
|
15
16
|
from dataclasses import dataclass
|
|
16
17
|
from pathlib import Path
|
|
17
18
|
from time import perf_counter
|
|
18
|
-
from typing import TYPE_CHECKING, Protocol
|
|
19
|
+
from typing import TYPE_CHECKING, NamedTuple, Protocol
|
|
19
20
|
|
|
20
21
|
from sonolus.backend.excepthook import print_simple_traceback
|
|
21
22
|
from sonolus.backend.utils import get_function, get_functions, get_tree_from_file
|
|
22
23
|
from sonolus.build.compile import CompileCache
|
|
24
|
+
from sonolus.script.internal.context import ProjectContextState
|
|
23
25
|
from sonolus.script.internal.error import CompilationError
|
|
24
26
|
|
|
25
27
|
if TYPE_CHECKING:
|
|
26
|
-
from sonolus.script.project import BuildConfig
|
|
28
|
+
from sonolus.script.project import BuildConfig, Project
|
|
27
29
|
|
|
28
30
|
HELP_TEXT = """
|
|
29
31
|
[r]ebuild
|
|
32
|
+
[d]ecode <message_code>
|
|
33
|
+
[h]elp
|
|
30
34
|
[q]uit
|
|
31
35
|
""".strip()
|
|
32
36
|
|
|
33
37
|
HELP_TEXT = textwrap.dedent(HELP_TEXT)
|
|
34
38
|
|
|
35
39
|
|
|
40
|
+
class CommandHelp(NamedTuple):
|
|
41
|
+
alias: str
|
|
42
|
+
command: str
|
|
43
|
+
description: list[str]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
DETAILED_HELP_TEXT = [
|
|
47
|
+
CommandHelp(
|
|
48
|
+
alias="r",
|
|
49
|
+
command="rebuild",
|
|
50
|
+
description=[
|
|
51
|
+
("Rebuild the project with the latest changes from source files."),
|
|
52
|
+
],
|
|
53
|
+
),
|
|
54
|
+
CommandHelp(
|
|
55
|
+
alias="d",
|
|
56
|
+
command="decode <message_code>",
|
|
57
|
+
description=[
|
|
58
|
+
(
|
|
59
|
+
"Decode a debug message code to its original text with a stack trace. "
|
|
60
|
+
"Message codes are generated by assert statements, debug.error(), and debug.notify() calls. "
|
|
61
|
+
"The game is automatically paused when these are triggered in debug mode and the message code "
|
|
62
|
+
"can be found in the debug log."
|
|
63
|
+
),
|
|
64
|
+
"Example: d 42",
|
|
65
|
+
],
|
|
66
|
+
),
|
|
67
|
+
CommandHelp(
|
|
68
|
+
alias="h",
|
|
69
|
+
command="help",
|
|
70
|
+
description=[
|
|
71
|
+
"Show this help message.",
|
|
72
|
+
],
|
|
73
|
+
),
|
|
74
|
+
CommandHelp(
|
|
75
|
+
alias="q",
|
|
76
|
+
command="quit",
|
|
77
|
+
description=[
|
|
78
|
+
"Exit the development server.",
|
|
79
|
+
],
|
|
80
|
+
),
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass
|
|
85
|
+
class ServerState:
|
|
86
|
+
project: Project
|
|
87
|
+
project_module_name: str
|
|
88
|
+
core_module_names: set[str]
|
|
89
|
+
build_dir: Path
|
|
90
|
+
config: BuildConfig
|
|
91
|
+
cache: CompileCache
|
|
92
|
+
project_state: ProjectContextState
|
|
93
|
+
|
|
94
|
+
|
|
36
95
|
class Command(Protocol):
|
|
37
|
-
def execute(
|
|
38
|
-
self,
|
|
39
|
-
project_module_name: str,
|
|
40
|
-
core_module_names: set[str],
|
|
41
|
-
build_dir: Path,
|
|
42
|
-
config: BuildConfig,
|
|
43
|
-
cache: CompileCache,
|
|
44
|
-
) -> None: ...
|
|
96
|
+
def execute(self, server_state: ServerState) -> None: ...
|
|
45
97
|
|
|
46
98
|
|
|
47
99
|
@dataclass
|
|
48
100
|
class RebuildCommand:
|
|
49
|
-
def execute(
|
|
50
|
-
self,
|
|
51
|
-
project_module_name: str,
|
|
52
|
-
core_module_names: set[str],
|
|
53
|
-
build_dir: Path,
|
|
54
|
-
config: BuildConfig,
|
|
55
|
-
cache: CompileCache,
|
|
56
|
-
):
|
|
101
|
+
def execute(self, server_state: ServerState):
|
|
57
102
|
from sonolus.build.cli import build_collection
|
|
58
103
|
|
|
59
104
|
for module_name in tuple(sys.modules):
|
|
60
|
-
if module_name not in core_module_names:
|
|
105
|
+
if module_name not in server_state.core_module_names:
|
|
61
106
|
del sys.modules[module_name]
|
|
62
107
|
|
|
63
108
|
try:
|
|
64
|
-
project_module = importlib.import_module(project_module_name)
|
|
109
|
+
project_module = importlib.import_module(server_state.project_module_name)
|
|
65
110
|
except Exception:
|
|
66
111
|
print(traceback.format_exc())
|
|
67
112
|
return
|
|
@@ -72,7 +117,15 @@ class RebuildCommand:
|
|
|
72
117
|
print("Rebuilding...")
|
|
73
118
|
try:
|
|
74
119
|
start_time = perf_counter()
|
|
75
|
-
|
|
120
|
+
server_state.project_state = ProjectContextState()
|
|
121
|
+
server_state.project = project_module.project
|
|
122
|
+
build_collection(
|
|
123
|
+
server_state.project,
|
|
124
|
+
server_state.build_dir,
|
|
125
|
+
server_state.config,
|
|
126
|
+
cache=server_state.cache,
|
|
127
|
+
project_state=server_state.project_state,
|
|
128
|
+
)
|
|
76
129
|
end_time = perf_counter()
|
|
77
130
|
print(f"Rebuild completed in {end_time - start_time:.2f} seconds")
|
|
78
131
|
except CompilationError:
|
|
@@ -80,16 +133,47 @@ class RebuildCommand:
|
|
|
80
133
|
print_simple_traceback(*exc_info)
|
|
81
134
|
|
|
82
135
|
|
|
136
|
+
@dataclass
|
|
137
|
+
class DecodeCommand:
|
|
138
|
+
message_code: int
|
|
139
|
+
|
|
140
|
+
def execute(self, server_state: ServerState):
|
|
141
|
+
debug_str_mappings = server_state.project_state.debug_str_mappings
|
|
142
|
+
message = next((msg for msg, code in debug_str_mappings.items() if code == self.message_code), None)
|
|
143
|
+
|
|
144
|
+
if message is not None:
|
|
145
|
+
print(message)
|
|
146
|
+
else:
|
|
147
|
+
print(f"Unknown message code: {self.message_code}")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass
|
|
151
|
+
class HelpCommand:
|
|
152
|
+
def execute(self, server_state: ServerState):
|
|
153
|
+
terminal_width = shutil.get_terminal_size().columns
|
|
154
|
+
max_width = min(terminal_width, 120)
|
|
155
|
+
|
|
156
|
+
print("Available Commands:\n")
|
|
157
|
+
|
|
158
|
+
for entry in DETAILED_HELP_TEXT:
|
|
159
|
+
print(f" [{entry.alias}] {entry.command}")
|
|
160
|
+
|
|
161
|
+
for paragraph in entry.description:
|
|
162
|
+
initial_indent = " "
|
|
163
|
+
subsequent_indent = " "
|
|
164
|
+
wrapped = textwrap.fill(
|
|
165
|
+
paragraph,
|
|
166
|
+
width=max_width - len(initial_indent),
|
|
167
|
+
initial_indent=initial_indent,
|
|
168
|
+
subsequent_indent=subsequent_indent,
|
|
169
|
+
)
|
|
170
|
+
print(wrapped)
|
|
171
|
+
print()
|
|
172
|
+
|
|
173
|
+
|
|
83
174
|
@dataclass
|
|
84
175
|
class ExitCommand:
|
|
85
|
-
def execute(
|
|
86
|
-
self,
|
|
87
|
-
project_module_name: str,
|
|
88
|
-
core_module_names: set[str],
|
|
89
|
-
build_dir: Path,
|
|
90
|
-
config: BuildConfig,
|
|
91
|
-
cache: CompileCache,
|
|
92
|
-
):
|
|
176
|
+
def execute(self, server_state: ServerState):
|
|
93
177
|
print("Exiting...")
|
|
94
178
|
sys.exit(0)
|
|
95
179
|
|
|
@@ -99,12 +183,19 @@ def parse_dev_command(command_line: str) -> Command | None:
|
|
|
99
183
|
subparsers = parser.add_subparsers(dest="cmd")
|
|
100
184
|
|
|
101
185
|
subparsers.add_parser("rebuild", aliases=["r"])
|
|
186
|
+
decode_parser = subparsers.add_parser("decode", aliases=["d"])
|
|
187
|
+
decode_parser.add_argument("message_code", type=int, help="Message code to decode")
|
|
188
|
+
subparsers.add_parser("help", aliases=["h"])
|
|
102
189
|
subparsers.add_parser("quit", aliases=["q"])
|
|
103
190
|
|
|
104
191
|
try:
|
|
105
192
|
args = parser.parse_args(shlex.split(command_line))
|
|
106
193
|
if args.cmd in {"rebuild", "r"}:
|
|
107
194
|
return RebuildCommand()
|
|
195
|
+
elif args.cmd in {"decode", "d"}:
|
|
196
|
+
return DecodeCommand(message_code=args.message_code)
|
|
197
|
+
elif args.cmd in {"help", "h"}:
|
|
198
|
+
return HelpCommand()
|
|
108
199
|
elif args.cmd in {"quit", "q"}:
|
|
109
200
|
return ExitCommand()
|
|
110
201
|
return None
|
|
@@ -164,8 +255,18 @@ def run_server(
|
|
|
164
255
|
core_module_names: set[str] | None,
|
|
165
256
|
build_dir: Path,
|
|
166
257
|
config: BuildConfig,
|
|
167
|
-
|
|
258
|
+
project,
|
|
168
259
|
):
|
|
260
|
+
from sonolus.build.cli import build_collection
|
|
261
|
+
|
|
262
|
+
cache = CompileCache()
|
|
263
|
+
project_state = ProjectContextState()
|
|
264
|
+
|
|
265
|
+
start_time = perf_counter()
|
|
266
|
+
build_collection(project, build_dir, config, cache=cache, project_state=project_state)
|
|
267
|
+
end_time = perf_counter()
|
|
268
|
+
print(f"Build finished in {end_time - start_time:.2f}s")
|
|
269
|
+
|
|
169
270
|
interactive = project_module_name is not None and core_module_names is not None
|
|
170
271
|
|
|
171
272
|
class DirectoryHandler(http.server.SimpleHTTPRequestHandler):
|
|
@@ -187,6 +288,16 @@ def run_server(
|
|
|
187
288
|
print(f" http://{ip}:{port}")
|
|
188
289
|
|
|
189
290
|
if interactive:
|
|
291
|
+
server_state = ServerState(
|
|
292
|
+
project=project,
|
|
293
|
+
project_module_name=project_module_name,
|
|
294
|
+
core_module_names=core_module_names,
|
|
295
|
+
build_dir=build_dir,
|
|
296
|
+
config=config,
|
|
297
|
+
cache=cache,
|
|
298
|
+
project_state=project_state,
|
|
299
|
+
)
|
|
300
|
+
|
|
190
301
|
threading.Thread(target=httpd.serve_forever, daemon=True).start()
|
|
191
302
|
|
|
192
303
|
command_queue = queue.Queue()
|
|
@@ -202,12 +313,12 @@ def run_server(
|
|
|
202
313
|
while True:
|
|
203
314
|
try:
|
|
204
315
|
cmd = command_queue.get(timeout=0.5)
|
|
205
|
-
cmd.execute(
|
|
316
|
+
cmd.execute(server_state)
|
|
206
317
|
prompt_event.set()
|
|
207
318
|
except queue.Empty:
|
|
208
319
|
continue
|
|
209
320
|
except KeyboardInterrupt:
|
|
210
|
-
print("
|
|
321
|
+
print("Exiting...")
|
|
211
322
|
sys.exit(0)
|
|
212
323
|
finally:
|
|
213
324
|
httpd.shutdown()
|