magmascript 3.1.1__tar.gz → 3.2.0__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.
- {magmascript-3.1.1 → magmascript-3.2.0}/PKG-INFO +30 -1
- {magmascript-3.1.1 → magmascript-3.2.0}/README.md +29 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/__init__.py +1 -1
- magmascript-3.2.0/magmascript/lang/domain_bridge.py +198 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/interpreter.py +18 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript.egg-info/PKG-INFO +30 -1
- {magmascript-3.1.1 → magmascript-3.2.0}/pyproject.toml +1 -1
- magmascript-3.2.0/tests/test_domain_bridge.py +368 -0
- magmascript-3.1.1/magmascript/lang/domain_bridge.py +0 -119
- magmascript-3.1.1/tests/test_domain_bridge.py +0 -178
- {magmascript-3.1.1 → magmascript-3.2.0}/LICENSE +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/cli.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/core/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/core/cache.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/core/commands.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/core/config.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/core/exceptions.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/core/github.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/core/output.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/core/registry.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/core/rpc.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/core/runner.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/archive/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/archive/client.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/gh/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/gh/client.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/gh/tools.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/lastfm/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/lastfm/client.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/mac/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/mac/client.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/mac/tools.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/mb/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/mb/client.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/mc1/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/mc1/client.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/mc1/tools.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/mcp/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/mcp/client.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/mcp/tools.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/media/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/media/client.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/media/providers/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/media/providers/archive.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/media/providers/met_museum.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/media/providers/openverse.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/media/providers/pexels.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/media/providers/pixabay.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/media/providers/smithsonian.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/media/tools.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/pi/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/pi/client.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/pi/tools.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/rights/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/rights/client.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/rights/tools.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/scores/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/scores/client.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/scores/tools.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/search/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/domains/search/client.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/ast_nodes.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/astheno/__init__.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/astheno/arena.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/astheno/dump.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/astheno/floorplan.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/astheno/numeric.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/builtins.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/environment.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/hypnagogia.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/lexer.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/parser.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/pygments_mgs.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/tokens.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/lang/util.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript/repl.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript.egg-info/SOURCES.txt +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript.egg-info/dependency_links.txt +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript.egg-info/entry_points.txt +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript.egg-info/requires.txt +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/magmascript.egg-info/top_level.txt +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/setup.cfg +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/tests/test_astheno.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/tests/test_cache.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/tests/test_classes.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/tests/test_cli.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/tests/test_commands.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/tests/test_import.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/tests/test_lang.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/tests/test_mac.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/tests/test_mc1.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/tests/test_mcp_client.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/tests/test_rpc.py +0 -0
- {magmascript-3.1.1 → magmascript-3.2.0}/tests/test_try_catch.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: magmascript
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Scripting toolkit with domain-first subcommands
|
|
5
5
|
Author: Jake McCoy
|
|
6
6
|
License-Expression: MIT
|
|
@@ -584,6 +584,35 @@ magmascript toast search-index # Remove search index
|
|
|
584
584
|
magmascript toast all # Clear everything
|
|
585
585
|
```
|
|
586
586
|
|
|
587
|
+
### Domains from other packages
|
|
588
|
+
|
|
589
|
+
A package can publish its own domain by declaring an entry point; it then
|
|
590
|
+
appears in `.mgs` scripts and the REPL without any change to magmascript.
|
|
591
|
+
|
|
592
|
+
```toml
|
|
593
|
+
# in the other package's pyproject.toml
|
|
594
|
+
[project.entry-points."magmascript.domains"]
|
|
595
|
+
texastoast = "texastoast.mgs:TexastoastDomain"
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
The value names a class taking a single `config` argument. It is constructed on
|
|
599
|
+
first use, not at startup, so a domain may hold real resources. Built-in domains
|
|
600
|
+
win a name clash, and an entry point that fails to import is skipped rather than
|
|
601
|
+
breaking the interpreter.
|
|
602
|
+
|
|
603
|
+
[texastoast](https://github.com/magmacrunchmedia/texastoast) is the first:
|
|
604
|
+
`pip install texastoast` and `.mgs` scripts can drive a game engine. It
|
|
605
|
+
registers under two names, `texastoast` and the shorter `tt`.
|
|
606
|
+
|
|
607
|
+
```magmascript
|
|
608
|
+
g = tt.game({"title": "hello", "width": 400, "height": 300})
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
A package may register the same class under several names this way; nothing
|
|
612
|
+
stops it, and the clients stay independent. Note that none of this touches the
|
|
613
|
+
`magmascript toast` CLI verb, which still clears caches — registry domains are
|
|
614
|
+
not dispatched as subcommands.
|
|
615
|
+
|
|
587
616
|
## Python Library
|
|
588
617
|
|
|
589
618
|
```python
|
|
@@ -549,6 +549,35 @@ magmascript toast search-index # Remove search index
|
|
|
549
549
|
magmascript toast all # Clear everything
|
|
550
550
|
```
|
|
551
551
|
|
|
552
|
+
### Domains from other packages
|
|
553
|
+
|
|
554
|
+
A package can publish its own domain by declaring an entry point; it then
|
|
555
|
+
appears in `.mgs` scripts and the REPL without any change to magmascript.
|
|
556
|
+
|
|
557
|
+
```toml
|
|
558
|
+
# in the other package's pyproject.toml
|
|
559
|
+
[project.entry-points."magmascript.domains"]
|
|
560
|
+
texastoast = "texastoast.mgs:TexastoastDomain"
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
The value names a class taking a single `config` argument. It is constructed on
|
|
564
|
+
first use, not at startup, so a domain may hold real resources. Built-in domains
|
|
565
|
+
win a name clash, and an entry point that fails to import is skipped rather than
|
|
566
|
+
breaking the interpreter.
|
|
567
|
+
|
|
568
|
+
[texastoast](https://github.com/magmacrunchmedia/texastoast) is the first:
|
|
569
|
+
`pip install texastoast` and `.mgs` scripts can drive a game engine. It
|
|
570
|
+
registers under two names, `texastoast` and the shorter `tt`.
|
|
571
|
+
|
|
572
|
+
```magmascript
|
|
573
|
+
g = tt.game({"title": "hello", "width": 400, "height": 300})
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
A package may register the same class under several names this way; nothing
|
|
577
|
+
stops it, and the clients stay independent. Note that none of this touches the
|
|
578
|
+
`magmascript toast` CLI verb, which still clears caches — registry domains are
|
|
579
|
+
not dispatched as subcommands.
|
|
580
|
+
|
|
552
581
|
## Python Library
|
|
553
582
|
|
|
554
583
|
```python
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""magmascript — a scripting toolkit with domain-first subcommands."""
|
|
2
2
|
|
|
3
|
-
__version__ = "3.
|
|
3
|
+
__version__ = "3.2.0"
|
|
4
4
|
|
|
5
5
|
from magmascript.core.config import Config, GHConfig, MediaConfig, MC1Config, PIConfig, get_config, load_config, set_config
|
|
6
6
|
from magmascript.core.registry import get_domain, list_domains, register_domain
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import fields, is_dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from magmascript.core.registry import get_domain, list_domains, register_domain
|
|
7
|
+
|
|
8
|
+
# Packages outside this repo publish domains under this entry-point group.
|
|
9
|
+
ENTRY_POINT_GROUP = "magmascript.domains"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DomainProxy:
|
|
13
|
+
"""A domain, exposed to scripts under its name.
|
|
14
|
+
|
|
15
|
+
The client is built on first use, not at interpreter startup. Every domain
|
|
16
|
+
used to be constructed for every script and every REPL session, which cost
|
|
17
|
+
startup time for domains nobody touched and ruled out any domain whose
|
|
18
|
+
construction does something — opening a window, holding a socket.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, name: str, client: Any = None, *,
|
|
22
|
+
factory: Any = None) -> None:
|
|
23
|
+
object.__setattr__(self, "_name", name)
|
|
24
|
+
object.__setattr__(self, "_factory", factory)
|
|
25
|
+
object.__setattr__(self, "_client_cache", client)
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def _client(self) -> Any:
|
|
29
|
+
client = object.__getattribute__(self, "_client_cache")
|
|
30
|
+
if client is None:
|
|
31
|
+
factory = object.__getattribute__(self, "_factory")
|
|
32
|
+
if factory is None:
|
|
33
|
+
name = object.__getattribute__(self, "_name")
|
|
34
|
+
raise AttributeError(f"domain '{name}' has no client")
|
|
35
|
+
client = factory()
|
|
36
|
+
object.__setattr__(self, "_client_cache", client)
|
|
37
|
+
return client
|
|
38
|
+
|
|
39
|
+
def __getattr__(self, name: str) -> Any:
|
|
40
|
+
if name.startswith("_"):
|
|
41
|
+
raise AttributeError(name)
|
|
42
|
+
|
|
43
|
+
method = getattr(self._client, name, None)
|
|
44
|
+
if method is None:
|
|
45
|
+
raise AttributeError(f"'{self._name}' has no method '{name}'")
|
|
46
|
+
|
|
47
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
48
|
+
result = method(*args, **kwargs)
|
|
49
|
+
return wrap_result(result)
|
|
50
|
+
|
|
51
|
+
return wrapper
|
|
52
|
+
|
|
53
|
+
def close(self) -> None:
|
|
54
|
+
# Only a client that was actually built can need closing, and touching
|
|
55
|
+
# self._client here would construct one just to tear it down.
|
|
56
|
+
client = object.__getattribute__(self, "_client_cache")
|
|
57
|
+
if client is not None and hasattr(client, "close"):
|
|
58
|
+
try:
|
|
59
|
+
client.close()
|
|
60
|
+
except Exception:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
def __del__(self) -> None:
|
|
64
|
+
self.close()
|
|
65
|
+
|
|
66
|
+
def __repr__(self) -> str:
|
|
67
|
+
return f"<domain:{self._name}>"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class DataclassWrapper:
|
|
71
|
+
def __init__(self, obj: Any) -> None:
|
|
72
|
+
object.__setattr__(self, "_obj", obj)
|
|
73
|
+
|
|
74
|
+
def __getattr__(self, name: str) -> Any:
|
|
75
|
+
obj = object.__getattribute__(self, "_obj")
|
|
76
|
+
if name.startswith("_"):
|
|
77
|
+
raise AttributeError(name)
|
|
78
|
+
if is_dataclass(obj) and not isinstance(obj, type):
|
|
79
|
+
for f in fields(obj):
|
|
80
|
+
if f.name == name:
|
|
81
|
+
return wrap_result(getattr(obj, name))
|
|
82
|
+
# Not a field, but dataclasses carry properties and methods too, and a
|
|
83
|
+
# script has no way to tell which is which. InputState.dx and
|
|
84
|
+
# ControllerState.up are properties over the fields; without this a
|
|
85
|
+
# script could read the raw buttons byte but not the buttons.
|
|
86
|
+
try:
|
|
87
|
+
return wrap_result(getattr(obj, name))
|
|
88
|
+
except AttributeError:
|
|
89
|
+
raise AttributeError(
|
|
90
|
+
f"'{type(obj).__name__}' has no attribute '{name}'"
|
|
91
|
+
) from None
|
|
92
|
+
|
|
93
|
+
def __repr__(self) -> str:
|
|
94
|
+
obj = object.__getattribute__(self, "_obj")
|
|
95
|
+
return repr(obj)
|
|
96
|
+
|
|
97
|
+
def __str__(self) -> str:
|
|
98
|
+
obj = object.__getattribute__(self, "_obj")
|
|
99
|
+
return str(obj)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ListWrapper:
|
|
103
|
+
def __init__(self, items: list[Any]) -> None:
|
|
104
|
+
self._items = [wrap_result(item) for item in items]
|
|
105
|
+
|
|
106
|
+
def __getitem__(self, index: int) -> Any:
|
|
107
|
+
return self._items[index]
|
|
108
|
+
|
|
109
|
+
def __len__(self) -> int:
|
|
110
|
+
return len(self._items)
|
|
111
|
+
|
|
112
|
+
def __iter__(self) -> Any:
|
|
113
|
+
return iter(self._items)
|
|
114
|
+
|
|
115
|
+
def __repr__(self) -> str:
|
|
116
|
+
return repr(self._items)
|
|
117
|
+
|
|
118
|
+
def __str__(self) -> str:
|
|
119
|
+
return str(self._items)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def wrap_result(result: Any) -> Any:
|
|
123
|
+
from magmascript.lang.interpreter import MgsInstance, MgsClass
|
|
124
|
+
from magmascript.lang.astheno import Fixed, Pine
|
|
125
|
+
if result is None:
|
|
126
|
+
return None
|
|
127
|
+
if isinstance(result, (int, float, str, bool)):
|
|
128
|
+
return result
|
|
129
|
+
# Asthenosphere values are dataclasses but are language primitives, not
|
|
130
|
+
# host objects to be introspected. They must not be wrapped.
|
|
131
|
+
if isinstance(result, (Fixed, Pine)):
|
|
132
|
+
return result
|
|
133
|
+
if callable(result):
|
|
134
|
+
return result
|
|
135
|
+
if isinstance(result, (MgsInstance, MgsClass)):
|
|
136
|
+
return result
|
|
137
|
+
if isinstance(result, list):
|
|
138
|
+
return ListWrapper(result)
|
|
139
|
+
if is_dataclass(result) and not isinstance(result, type):
|
|
140
|
+
return DataclassWrapper(result)
|
|
141
|
+
return result
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _domain_factory(name: str) -> Any:
|
|
145
|
+
"""Build the thunk that constructs one domain's client on first use.
|
|
146
|
+
|
|
147
|
+
A constructor that raises now surfaces at the call that needed it, rather
|
|
148
|
+
than the domain silently not existing and the script reporting an undefined
|
|
149
|
+
variable several lines later.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
def build() -> Any:
|
|
153
|
+
from magmascript.core.config import get_config
|
|
154
|
+
|
|
155
|
+
return get_domain(name)(get_config())
|
|
156
|
+
|
|
157
|
+
return build
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def discover_domains() -> None:
|
|
161
|
+
"""Register domains published by other installed packages.
|
|
162
|
+
|
|
163
|
+
A package declares one in its own metadata::
|
|
164
|
+
|
|
165
|
+
[project.entry-points."magmascript.domains"]
|
|
166
|
+
texastoast = "texastoast.mgs:TexastoastDomain"
|
|
167
|
+
|
|
168
|
+
which is what lets a domain live in the project it belongs to rather than
|
|
169
|
+
in this repo. Built-ins win on a name clash, and a broken entry point is
|
|
170
|
+
skipped rather than taking the interpreter down with it.
|
|
171
|
+
"""
|
|
172
|
+
from importlib.metadata import entry_points
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
found = entry_points(group=ENTRY_POINT_GROUP)
|
|
176
|
+
except Exception:
|
|
177
|
+
# Broken installed metadata is not this interpreter's problem to solve,
|
|
178
|
+
# and must not stop a script that uses only built-in domains.
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
builtins = set(list_domains())
|
|
182
|
+
for entry in found:
|
|
183
|
+
if entry.name in builtins:
|
|
184
|
+
continue
|
|
185
|
+
try:
|
|
186
|
+
register_domain(entry.name, entry.load())
|
|
187
|
+
except Exception:
|
|
188
|
+
# A third-party package that fails to import must not stop a script
|
|
189
|
+
# that never mentions it from running.
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def create_domain_proxies() -> dict[str, DomainProxy]:
|
|
194
|
+
discover_domains()
|
|
195
|
+
return {
|
|
196
|
+
name: DomainProxy(name, factory=_domain_factory(name))
|
|
197
|
+
for name in list_domains()
|
|
198
|
+
}
|
|
@@ -1110,6 +1110,24 @@ class Interpreter:
|
|
|
1110
1110
|
obj[node.property] = value
|
|
1111
1111
|
return value
|
|
1112
1112
|
|
|
1113
|
+
# A host object from a domain — a game entity, a config, anything the
|
|
1114
|
+
# bridge handed back. Reading its attributes already works
|
|
1115
|
+
# (exec_PropertyAccess falls through to getattr), so writing them has
|
|
1116
|
+
# to as well, or a script can inspect an object and never change it.
|
|
1117
|
+
if not node.property.startswith("_") and hasattr(obj, node.property):
|
|
1118
|
+
if isinstance(value, astheno.Fixed):
|
|
1119
|
+
# Widths are an Asthenosphere concept. Handing one to a host
|
|
1120
|
+
# object poisons its arithmetic downstream, so store the number.
|
|
1121
|
+
value = value.value
|
|
1122
|
+
try:
|
|
1123
|
+
setattr(obj, node.property, value)
|
|
1124
|
+
except AttributeError as e:
|
|
1125
|
+
raise self.error(
|
|
1126
|
+
f"Cannot set '{node.property}' on {type(obj).__name__}: {e}",
|
|
1127
|
+
node,
|
|
1128
|
+
) from None
|
|
1129
|
+
return value
|
|
1130
|
+
|
|
1113
1131
|
raise self.error(
|
|
1114
1132
|
f"Cannot set property on {type(obj).__name__}",
|
|
1115
1133
|
node,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: magmascript
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Scripting toolkit with domain-first subcommands
|
|
5
5
|
Author: Jake McCoy
|
|
6
6
|
License-Expression: MIT
|
|
@@ -584,6 +584,35 @@ magmascript toast search-index # Remove search index
|
|
|
584
584
|
magmascript toast all # Clear everything
|
|
585
585
|
```
|
|
586
586
|
|
|
587
|
+
### Domains from other packages
|
|
588
|
+
|
|
589
|
+
A package can publish its own domain by declaring an entry point; it then
|
|
590
|
+
appears in `.mgs` scripts and the REPL without any change to magmascript.
|
|
591
|
+
|
|
592
|
+
```toml
|
|
593
|
+
# in the other package's pyproject.toml
|
|
594
|
+
[project.entry-points."magmascript.domains"]
|
|
595
|
+
texastoast = "texastoast.mgs:TexastoastDomain"
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
The value names a class taking a single `config` argument. It is constructed on
|
|
599
|
+
first use, not at startup, so a domain may hold real resources. Built-in domains
|
|
600
|
+
win a name clash, and an entry point that fails to import is skipped rather than
|
|
601
|
+
breaking the interpreter.
|
|
602
|
+
|
|
603
|
+
[texastoast](https://github.com/magmacrunchmedia/texastoast) is the first:
|
|
604
|
+
`pip install texastoast` and `.mgs` scripts can drive a game engine. It
|
|
605
|
+
registers under two names, `texastoast` and the shorter `tt`.
|
|
606
|
+
|
|
607
|
+
```magmascript
|
|
608
|
+
g = tt.game({"title": "hello", "width": 400, "height": 300})
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
A package may register the same class under several names this way; nothing
|
|
612
|
+
stops it, and the clients stay independent. Note that none of this touches the
|
|
613
|
+
`magmascript toast` CLI verb, which still clears caches — registry domains are
|
|
614
|
+
not dispatched as subcommands.
|
|
615
|
+
|
|
587
616
|
## Python Library
|
|
588
617
|
|
|
589
618
|
```python
|