Skip to content

Commit 881c1f9

Browse files
committed
✨ $prefix.plugins support read from $files
1 parent 962d36b commit 881c1f9

6 files changed

Lines changed: 56 additions & 19 deletions

File tree

arclet/entari/command/plugin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def execute_hook(sub: Subscriber):
7575
SubscriberSlot(sub, exec_pub.id, sub.priority)
7676
)
7777
sub._recompile([exec_provider])
78+
sub.propagate(_after_execute)
7879

7980
self.plugin.collect(self.register_hooks.append(execute_hook))
8081
return self # type: ignore[return-value]

arclet/entari/config/file.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,9 @@ def plugin_names(self) -> list[str]:
129129
return list(chain.from_iterable(self._plugin_names.values()))
130130

131131
def _reload_plugins(self):
132-
self.plugin_extra_files: list[str] = self.plugin.get("$files", []) # type: ignore
132+
self.plugin_extra_files = self.plugin.get("$files", []) # type: ignore
133133
self.prelude_plugin = self.plugin.get("$prelude", []) # type: ignore
134-
plugin_prefixes = {item["key"]: item["plugins"] for item in self.plugin.get("$prefix", [])} # type: ignore
135-
inverted = defaultdict(list)
136-
for key, plugs in plugin_prefixes.items():
137-
for plug in plugs:
138-
inverted[plug].append(key)
139-
self.plugin_prefixes = dict(inverted)
134+
140135
for key in list(self.plugin.keys()):
141136
if key.startswith("$"):
142137
continue
@@ -149,17 +144,36 @@ def _reload_plugins(self):
149144
key = key[1:]
150145
value["$optional"] = True
151146
self.plugin[key] = value
147+
extra_plugins: dict[str, list[str]] = {}
152148
for file in self.plugin_extra_files:
153149
path = Path(file)
154150
if not path.exists():
155151
raise FileNotFoundError(file)
152+
_plugins = extra_plugins.setdefault(file, [])
156153
if path.is_dir():
157154
for _path in path.iterdir():
158155
if not _path.is_file() or _path.name.endswith(".schema.json"):
159156
continue
160157
self.plugin[_path.stem] = self.loader(_path)
158+
_plugins.append(_path.stem)
161159
elif path.name.endswith(".schema.json"):
162160
self.plugin[path.stem] = self.loader(path)
161+
_plugins.append(path.stem)
162+
163+
plugin_prefixes = {}
164+
for item in self.plugin.get("$prefix", []): # type: ignore
165+
if "key" not in item or "plugins" not in item:
166+
continue
167+
_prefixes = plugin_prefixes.setdefault(item["key"], [])
168+
if isinstance(item["plugins"], list):
169+
_prefixes.extend(item["plugins"])
170+
elif isinstance(item["plugins"], str) and item["plugins"] in extra_plugins:
171+
_prefixes.extend(extra_plugins[item["plugins"]])
172+
inverted = defaultdict(list)
173+
for key, plugs in plugin_prefixes.items():
174+
for plug in plugs:
175+
inverted[plug].append(key)
176+
self.plugin_prefixes = dict(inverted)
163177

164178
slots = [
165179
(name, self.plugin[name].get("$priority", 16))
@@ -302,7 +316,7 @@ def generate_schema(self, plugins: list["Plugin"]):
302316
else:
303317
plugins_properties[plug._config_key] = {"type": "object", "description": "No configuration required", "additionalProperties": True, "properties": plugin_meta_properties} # noqa: E501
304318
schemas = {
305-
"basic": config_model_schema(BasicConfig, ref_root="/properties/basic/"), "plugins": {"type": "object", "description": "Plugin configurations", "properties": {"$prefix": {"description": "List of prefix config", "items": {"properties": {"key": {"description": "Prefix key", "title": "Key", "type": "string"}, "plugins": {"description": "List of plugins under the prefix", "items": {"type": "string", "description": "Plugin name"}, "title": "Plugins", "type": "array", "uniqueItems": True}}, "required": ["key"], "title": "Prefix Config", "type": "object"}, "type": "array"}, "$prelude": {"type": "array", "items": {"type": "string", "description": "Plugin name"}, "description": "List of prelude plugins to load", "default": [], "uniqueItems": True}, "$files": {"type": "array", "items": {"type": "string", "description": "File path"}, "description": "List of configuration files to load", "default": [], "uniqueItems": True}, **plugins_properties}}, "adapters": {"type": "array", "description": "Adapter configurations", "items": {"type": "object", "description": "Adapter configuration", "properties": {"$path": {"type": "string", "description": "Adapter Module Path"}}, "required": ["$path"], "additionalProperties": True}} # noqa: E501
319+
"basic": config_model_schema(BasicConfig, ref_root="/properties/basic/"), "plugins": {"type": "object", "description": "Plugin configurations", "properties": {"$prefix": {"description": "List of prefix config", "items": {"properties": {"key": {"description": "Prefix key", "title": "Key", "type": "string"}, "plugins": {"anyOf": [{"type": "string"}, {"items": {"type": "string", "description": "Plugin name"}, "type": "array", "uniqueItems": True}], "description": "List of plugins under the prefix, or select an item of $files to apply plugins", "title": "Plugins"}}, "required": ["key"], "title": "Prefix Config", "type": "object"}, "type": "array"}, "$prelude": {"type": "array", "items": {"type": "string", "description": "Plugin name"}, "description": "List of prelude plugins to load", "default": [], "uniqueItems": True}, "$files": {"type": "array", "items": {"type": "string", "description": "File path"}, "description": "List of configuration files to load", "default": [], "uniqueItems": True}, **plugins_properties}}, "adapters": {"type": "array", "description": "Adapter configurations", "items": {"type": "object", "description": "Adapter configuration", "properties": {"$path": {"type": "string", "description": "Adapter Module Path"}}, "required": ["$path"], "additionalProperties": True}} # noqa: E501
306320
}
307321
with open(f"{self.path.stem}.schema.json", "w", encoding="utf-8") as f:
308322
json.dump({"$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "properties": schemas, "additionalProperties": False, "required": ["basic"]}, f, indent=2, ensure_ascii=False) # noqa: E501

arclet/entari/config/util.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,16 @@
99
_DESC_CACHE = {}
1010

1111

12-
class GetattrDict:
12+
class GetattrDict(Mapping):
1313
def __init__(self, source: Mapping):
1414
self._source = source
1515

16+
def __iter__(self):
17+
return iter(self._source)
18+
19+
def __len__(self):
20+
return len(self._source)
21+
1622
def __getitem__(self, item):
1723
ans = self._source[item]
1824
if isinstance(ans, Mapping):

arclet/entari/filter/parse.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
import operator
33
import os
44
import re
5+
from functools import wraps
56

67
import simpleeval
8+
from arclet.letoderea import STOP, Propagator
79
from satori import Channel, ChannelType, EventType, Guild, Member, Role, User
810

911
from ..config import EntariConfig
1012
from ..config.util import GetattrDict
1113
from ..session import Session
1214

15+
# simpleeval._PRIMITIVE_TYPES = frozenset({int, float, str, bool, type(None), bytes, complex})
1316
NAMES = {
1417
"type": EventType.MESSAGE_CREATED,
1518
"channel": Channel(id="123", type=ChannelType.TEXT),
@@ -85,8 +88,8 @@ def parse_filter(expr: str):
8588
try:
8689
parsed = s.parse(expr)
8790
s._eval(parsed)
88-
except (simpleeval.InvalidExpression, TypeError, ValueError, NameError, SyntaxError):
89-
raise RuntimeError(f"Invalid filter expression: {expr}") from None
91+
except (simpleeval.InvalidExpression, TypeError, ValueError, NameError, SyntaxError) as e:
92+
raise RuntimeError(f"Invalid filter expression ({e}): {expr}") from None
9093

9194
async def check(session: Session | None = None, is_reply_me: bool = False, is_notice_me: bool = False):
9295
if not session:
@@ -116,3 +119,17 @@ async def check(session: Session | None = None, is_reply_me: bool = False, is_no
116119
return bool(s._eval(parsed))
117120

118121
return check
122+
123+
124+
class FilterPropagator(Propagator):
125+
def __init__(self, expr: str):
126+
self.callable = parse_filter(expr)
127+
128+
def compose(self):
129+
130+
@wraps(self.callable)
131+
async def _(*args, **kwargs):
132+
if not await self.callable(*args, **kwargs):
133+
return STOP
134+
135+
yield _, True, 0

arclet/entari/plugin/__init__.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -421,17 +421,16 @@ async def disable_plugin(plugin: str):
421421
def component(name: str):
422422
_plugin = get_plugin(1)
423423

424-
def dispose():
425-
COMPONENTS.pop(f"component:{name}", None)
426-
427-
_plugin.collect(dispose)
428-
429424
def wrapper(func: Render[Session, Awaitable[Fragment]]):
430425
async def render(attrs: dict, children: list, session: Session):
431426
ans = await func(attrs, children, session)
432427
return await component_transform(session, MessageChain(ans))
433428

434-
COMPONENTS[f"component:{name}"] = render
429+
def _():
430+
COMPONENTS[f"component:{name}"] = render
431+
return lambda: COMPONENTS.pop(f"component:{name}", None)
432+
433+
_plugin.effect(_, f"component:{name}")
435434
return func
436435

437436
return wrapper

arclet/entari/plugin/model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
from ..event.config import ConfigReload
4141
from ..event.plugin import PluginLoadedFailed, PluginUnloaded
4242
from ..exceptions import RegisterNotInPluginError, ReusablePluginError, StaticPluginDispatchError
43-
from ..filter.parse import evaluate_disable, parse_filter
43+
from ..filter.parse import FilterPropagator, evaluate_disable
4444
from ..logger import log
4545
from .service import plugin_service
4646

@@ -378,7 +378,7 @@ def __post_init__(self):
378378
plugin_service.plugins[self.id] = self # type: ignore
379379
self._config_key = self.config.pop("$path", self.id)
380380
if filter_expr := self.config.get("$filter", ""):
381-
self._scope.propagators.append(enter_if.priority(0) & parse_filter(filter_expr))
381+
self._scope.propagators.append(FilterPropagator(filter_expr))
382382
# if self._metadata and self._metadata.depend_services:
383383
# self._scope.propagators.append(inject(*self._metadata.depend_services, _is_global=True)) # type: ignore
384384
# self._extra["injected_services"] = [

0 commit comments

Comments
 (0)