🚧 process matcher module

This commit is contained in:
yanyongyu 2021-11-09 00:57:59 +08:00
parent 72f5aeea54
commit 08f56db385
3 changed files with 31 additions and 74 deletions

View File

@ -178,6 +178,7 @@ class Matcher(metaclass=MatcherMeta):
block: bool = False, block: bool = False,
*, *,
plugin: Optional["Plugin"] = None, plugin: Optional["Plugin"] = None,
module: Optional[ModuleType] = None,
expire_time: Optional[datetime] = None, expire_time: Optional[datetime] = None,
default_state: Optional[T_State] = None, default_state: Optional[T_State] = None,
default_state_factory: Optional[T_StateFactory] = None, default_state_factory: Optional[T_StateFactory] = None,
@ -200,6 +201,7 @@ class Matcher(metaclass=MatcherMeta):
* ``priority: int``: 响应优先级 * ``priority: int``: 响应优先级
* ``block: bool``: 是否阻止事件向更低优先级的响应器传播 * ``block: bool``: 是否阻止事件向更低优先级的响应器传播
* ``plugin: Optional[Plugin]``: 事件响应器所在插件 * ``plugin: Optional[Plugin]``: 事件响应器所在插件
* ``module: Optional[ModuleType]``: 事件响应器所在模块
* ``default_state: Optional[T_State]``: 默认状态 ``state`` * ``default_state: Optional[T_State]``: 默认状态 ``state``
* ``default_state_factory: Optional[T_StateFactory]``: 默认状态 ``state`` 的工厂函数 * ``default_state_factory: Optional[T_StateFactory]``: 默认状态 ``state`` 的工厂函数
* ``expire_time: Optional[datetime]``: 事件响应器最终有效时间点过时即被删除 * ``expire_time: Optional[datetime]``: 事件响应器最终有效时间点过时即被删除
@ -210,18 +212,15 @@ class Matcher(metaclass=MatcherMeta):
""" """
NewMatcher = type( NewMatcher = type(
"Matcher", "Matcher", (Matcher,), {
(Matcher,),
{
"plugin": "plugin":
plugin, plugin,
"module": "module":
plugin and plugin. module,
module, # FIXME: matcher module may different from plugin module
"plugin_name": "plugin_name":
plugin and plugin.name, plugin and plugin.name,
"module_name": "module_name":
plugin and plugin.module_name, module and module.__name__,
"type": "type":
type_, type_,
"rule": "rule":

View File

@ -1,8 +1,6 @@
import sys import sys
import uuid
import pkgutil import pkgutil
import importlib import importlib
from hashlib import md5
from pathlib import Path from pathlib import Path
from types import ModuleType from types import ModuleType
from collections import Counter from collections import Counter
@ -14,23 +12,17 @@ from .export import Export
from . import _current_plugin from . import _current_plugin
from .plugin import Plugin, _new_plugin from .plugin import Plugin, _new_plugin
_manager_stack: List["PluginManager"] = []
# TODO # TODO
class PluginManager: class PluginManager:
def __init__(self, def __init__(
namespace: str, self,
plugins: Optional[Iterable[str]] = None, plugins: Optional[Iterable[str]] = None,
search_path: Optional[Iterable[str]] = None, search_path: Optional[Iterable[str]] = None,
*, ):
id: Optional[str] = None):
self.namespace: str = namespace
self.namespace_module: ModuleType = self._setup_namespace(namespace)
self.id: str = id or str(uuid.uuid4())
self.internal_id: str = md5(
((self.namespace or "") + self.id).encode()).hexdigest()
self.internal_module = self._setup_internal_module(self.internal_id)
# simple plugin not in search path # simple plugin not in search path
self.plugins: Set[str] = set(plugins or []) self.plugins: Set[str] = set(plugins or [])
@ -38,55 +30,6 @@ class PluginManager:
# ensure can be loaded # ensure can be loaded
self.list_plugins() self.list_plugins()
def _setup_namespace(self, namespace: str) -> ModuleType:
try:
module = importlib.import_module(namespace)
except ImportError:
module = _NamespaceModule(namespace)
if "." in namespace:
parent = importlib.import_module(namespace.rsplit(".", 1)[0])
setattr(parent, namespace.rsplit(".", 1)[1], module)
sys.modules[namespace] = module
return module
def _setup_internal_module(self, internal_id: str) -> ModuleType:
if hasattr(_internal_space, internal_id):
raise RuntimeError("Plugin manager already exists!")
index = 2
prefix: str = _internal_space.__name__
while True:
try:
frame = sys._getframe(index)
except ValueError:
break
# check if is called in plugin
if "__plugin_name__" not in frame.f_globals:
index += 1
continue
prefix = frame.f_globals.get("__name__", _internal_space.__name__)
break
if not prefix.startswith(_internal_space.__name__):
prefix = _internal_space.__name__
module = _InternalModule(prefix, self)
sys.modules[module.__name__] = module # type: ignore
setattr(_internal_space, internal_id, module)
return module
def __enter__(self):
if self in _manager_stack:
raise RuntimeError("Plugin manager already activated!")
_manager_stack.append(self)
return self
def __exit__(self, exc_type, exc_value, traceback):
try:
_manager_stack.pop()
except IndexError:
pass
def search_plugins(self) -> List[str]: def search_plugins(self) -> List[str]:
return [ return [
module_info.name module_info.name
@ -116,14 +59,12 @@ class PluginManager:
def load_plugin(self, name) -> ModuleType: def load_plugin(self, name) -> ModuleType:
if name in self.plugins: if name in self.plugins:
with self: return importlib.import_module(name)
return importlib.import_module(name)
if "." in name: if "." in name:
raise ValueError("Plugin name cannot contain '.'") raise ValueError("Plugin name cannot contain '.'")
with self: return importlib.import_module(f"{self.namespace}.{name}")
return importlib.import_module(f"{self.namespace}.{name}")
def load_all_plugins(self) -> List[ModuleType]: def load_all_plugins(self) -> List[ModuleType]:
return [self.load_plugin(name) for name in self.list_plugins()] return [self.load_plugin(name) for name in self.list_plugins()]

View File

@ -1,4 +1,7 @@
import re import re
import sys
import inspect
from types import ModuleType
from typing import (TYPE_CHECKING, Any, Set, Dict, List, Type, Tuple, Union, from typing import (TYPE_CHECKING, Any, Set, Dict, List, Type, Tuple, Union,
Optional) Optional)
@ -22,6 +25,15 @@ def _store_matcher(matcher: Type[Matcher]) -> None:
plugin.matcher.add(matcher) plugin.matcher.add(matcher)
def _get_matcher_module(depth: int = 1) -> Optional[ModuleType]:
current_frame = inspect.currentframe()
if current_frame is None:
return None
frame = inspect.getouterframes(current_frame)[depth + 1].frame
module_name = frame.f_globals["__name__"]
return sys.modules.get(module_name)
def on(type: str = "", def on(type: str = "",
rule: Optional[Union[Rule, T_RuleChecker]] = None, rule: Optional[Union[Rule, T_RuleChecker]] = None,
permission: Optional[Permission] = None, permission: Optional[Permission] = None,
@ -61,6 +73,7 @@ def on(type: str = "",
block=block, block=block,
handlers=handlers, handlers=handlers,
plugin=_current_plugin.get(), plugin=_current_plugin.get(),
module=_get_matcher_module(),
default_state=state, default_state=state,
default_state_factory=state_factory) default_state_factory=state_factory)
_store_matcher(matcher) _store_matcher(matcher)
@ -103,6 +116,7 @@ def on_metaevent(
block=block, block=block,
handlers=handlers, handlers=handlers,
plugin=_current_plugin.get(), plugin=_current_plugin.get(),
module=_get_matcher_module(),
default_state=state, default_state=state,
default_state_factory=state_factory) default_state_factory=state_factory)
_store_matcher(matcher) _store_matcher(matcher)
@ -146,6 +160,7 @@ def on_message(rule: Optional[Union[Rule, T_RuleChecker]] = None,
block=block, block=block,
handlers=handlers, handlers=handlers,
plugin=_current_plugin.get(), plugin=_current_plugin.get(),
module=_get_matcher_module(),
default_state=state, default_state=state,
default_state_factory=state_factory) default_state_factory=state_factory)
_store_matcher(matcher) _store_matcher(matcher)
@ -187,6 +202,7 @@ def on_notice(rule: Optional[Union[Rule, T_RuleChecker]] = None,
block=block, block=block,
handlers=handlers, handlers=handlers,
plugin=_current_plugin.get(), plugin=_current_plugin.get(),
module=_get_matcher_module(),
default_state=state, default_state=state,
default_state_factory=state_factory) default_state_factory=state_factory)
_store_matcher(matcher) _store_matcher(matcher)
@ -228,6 +244,7 @@ def on_request(rule: Optional[Union[Rule, T_RuleChecker]] = None,
block=block, block=block,
handlers=handlers, handlers=handlers,
plugin=_current_plugin.get(), plugin=_current_plugin.get(),
module=_get_matcher_module(),
default_state=state, default_state=state,
default_state_factory=state_factory) default_state_factory=state_factory)
_store_matcher(matcher) _store_matcher(matcher)