matrix-python 1.4.5a0__py3-none-any.whl → 1.4.7a0__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.
matrix/_version.py CHANGED
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.4.5a0'
22
- __version_tuple__ = version_tuple = (1, 4, 5, 'a0')
21
+ __version__ = version = '1.4.7a0'
22
+ __version_tuple__ = version_tuple = (1, 4, 7, 'a0')
23
23
 
24
24
  __commit_id__ = commit_id = None
matrix/bot.py CHANGED
@@ -184,10 +184,6 @@ class Bot(Registry):
184
184
  await handler(error)
185
185
  return
186
186
 
187
- if self._fallback_error_handler:
188
- await self._fallback_error_handler(error)
189
- return
190
-
191
187
  await self._dispatch("on_error", error)
192
188
 
193
189
  async def on_command(self, _ctx: Context) -> None:
@@ -245,8 +241,7 @@ class Bot(Registry):
245
241
  :func:`asyncio.run`, and ensures the client is closed gracefully
246
242
  on interruption.
247
243
  """
248
- if config is not None:
249
- self._load_config(config)
244
+ self._load_config(config)
250
245
 
251
246
  try:
252
247
  asyncio.run(self.run())
@@ -264,7 +259,7 @@ class Bot(Registry):
264
259
  calls the :meth:`on_ready` hook, and starts the long-running
265
260
  sync loop for receiving events.
266
261
  """
267
- self.client.user = self.config.user_id
262
+ self.client.user = self.config.username
268
263
 
269
264
  self.start_at = time.time()
270
265
  self.log.info("starting – timestamp=%s", self.start_at)
@@ -327,15 +322,19 @@ class Bot(Registry):
327
322
 
328
323
  async def _process_commands(self, room: Room, event: Event) -> None:
329
324
  """Parse and execute commands"""
330
- ctx = await self._build_context(room, event)
325
+ try:
326
+ ctx = await self._build_context(room, event)
331
327
 
332
- if ctx.command:
333
- for check in self._checks:
334
- if not await check(ctx):
335
- raise CheckError(ctx.command, check)
328
+ if ctx.command:
329
+ for check in self._checks:
330
+ if not await check(ctx):
331
+ raise CheckError(ctx.command, check)
336
332
 
337
- await self._on_command(ctx)
338
- await ctx.command(ctx)
333
+ await self._on_command(ctx)
334
+ await ctx.command(ctx)
335
+ except Exception as error:
336
+ ctx = Context(bot=self, room=room, event=event)
337
+ await self._on_command_error(ctx, error)
339
338
 
340
339
  async def _build_context(self, matrix_room: Room, event: Event) -> Context:
341
340
  room = self.get_room(matrix_room.room_id)
matrix/config.py CHANGED
@@ -1,61 +1,125 @@
1
- import yaml
1
+ from typing import Any
2
+
3
+ from envyaml import EnvYAML
4
+
2
5
  from .errors import ConfigError
3
- from typing import Optional
4
6
 
5
7
 
6
8
  class Config:
7
- """
8
- Configuration handler for Matrix client settings. Including the following:
9
-
10
- homeserver: Defaults to 'https://matrix.org'
11
- user_id: The Matrix user ID (username).
12
- password: (Optional) One of the password or token must be provided.
13
- token: (Optional) One of the password or token must be provided.
14
- prefix: Defaults to '!' if not specified in the config file.
15
-
16
- :param config_path: Path to the YAML configuration file.
17
- :param homeserver: The Matrix homeserver URL.
18
- :param username: The Matrix user ID (username).
19
- :param password: The password for the Matrix user.
20
- :param token: The access token for the Matrix user.
21
- :param prefix: The command prefix.
22
-
23
- :raises FileNotFoundError: If the configuration file does not exist.
24
- :raises yaml.YAMLError: If the configuration file cannot be parsed.
25
- :raises ConfigError: If neither password or token has been provided.
9
+ """Configuration handler for Matrix client settings.
10
+
11
+ Manages all settings required to connect and authenticate with a Matrix
12
+ homeserver. Configuration can be loaded from a YAML file or provided
13
+ directly via constructor parameters. At least one authentication method
14
+ must be provided.
15
+
16
+ # Example
17
+
18
+ ```python
19
+ # Load from file
20
+ config = Config(config_path="path/to/config..yaml")
21
+
22
+ # Manual configuration
23
+ config = Config(username="@bot:matrix.org", password="secret")
24
+ ```
26
25
  """
27
26
 
28
27
  def __init__(
29
28
  self,
30
- config_path: Optional[str] = None,
29
+ config_path: str | None = None,
31
30
  *,
32
- homeserver: Optional[str] = None,
33
- username: Optional[str] = None,
34
- password: Optional[str] = None,
35
- token: Optional[str] = None,
36
- prefix: Optional[str] = None,
31
+ homeserver: str | None = None,
32
+ username: str | None = None,
33
+ password: str | None = None,
34
+ token: str | None = None,
35
+ prefix: str | None = None,
37
36
  ) -> None:
37
+ """Initialize the bot configuration.
38
+
39
+ Loads configuration from a YAML file if provided, otherwise uses
40
+ the provided parameters directly. At least one of password or token
41
+ must be supplied.
42
+
43
+ # Example
44
+
45
+ ```python
46
+ config = Config(
47
+ username="@bot:matrix.org",
48
+ password="secret",
49
+ prefix="!",
50
+ )
51
+ ```
52
+ """
53
+ self._data: dict[str, Any] = {}
54
+
38
55
  self.homeserver: str = homeserver or "https://matrix.org"
39
- self.user_id: Optional[str] = username
40
- self.password: Optional[str] = password
41
- self.token: Optional[str] = token
56
+ self.username: str | None = username
57
+ self.password: str | None = password
58
+ self.token: str | None = token
42
59
  self.prefix: str = prefix or "!"
43
60
 
44
61
  if config_path:
45
62
  self.load_from_file(config_path)
46
- elif not (self.password or self.token):
47
- raise ConfigError("username and password or token")
63
+ else:
64
+ if not self.password and not self.token:
65
+ raise ConfigError("username and password or token")
66
+
67
+ self._data = {
68
+ "HOMESERVER": self.homeserver,
69
+ "USERNAME": self.username,
70
+ "PASSWORD": self.password,
71
+ "TOKEN": self.token,
72
+ "PREFIX": self.prefix,
73
+ }
48
74
 
49
75
  def load_from_file(self, config_path: str) -> None:
50
- """Load Matrix client settings via YAML config file."""
51
- with open(config_path, "r") as f:
52
- config = yaml.safe_load(f)
53
-
54
- if not (config.get("PASSWORD") or config.get("TOKEN")):
55
- raise ConfigError("USERNAME and PASSWORD or TOKEN")
56
-
57
- self.homeserver = config.get("HOMESERVER", "https://matrix.org")
58
- self.user_id = config.get("USERNAME")
59
- self.password = config.get("PASSWORD", None)
60
- self.token = config.get("TOKEN", None)
61
- self.prefix = config.get("PREFIX", "!")
76
+ """Load Matrix client settings from a YAML config file.
77
+
78
+ Supports environment variable substitution via EnvYAML. Values in
79
+ the YAML file can reference environment variables using ${VAR} syntax.
80
+
81
+ # Example
82
+
83
+ ```python
84
+ config = Config()
85
+ config.load_from_file("path/to/config.yaml")
86
+ ```
87
+ """
88
+ self._data = dict(EnvYAML(config_path))
89
+
90
+ password = self._data.get("PASSWORD", None)
91
+ token = self._data.get("TOKEN", None)
92
+
93
+ if not password and not token:
94
+ raise ConfigError("USERNAME and PASSWORD or TOKEN")
95
+
96
+ self.homeserver = self._data.get("HOMESERVER", "https://matrix.org")
97
+ self.username = self._data.get("USERNAME")
98
+ self.password = password
99
+ self.token = token
100
+ self.prefix = self._data.get("PREFIX", "!")
101
+
102
+ def get(self, key: str, *, section: str | None = None, default: Any = None) -> Any:
103
+ """Access a config value by key, optionally scoped to a section.
104
+
105
+ # Example
106
+
107
+ ```python
108
+ config.get(key="main_channel", section="bot")
109
+ config.get(key="log_level", default="INFO")
110
+ ```
111
+ """
112
+ if section in self._data:
113
+ return self._data.get(section, {}).get(key, default)
114
+ return self._data.get(key, default)
115
+
116
+ def __getitem__(self, key: str) -> Any:
117
+ """Access a config value by key, raising KeyError if not found.
118
+
119
+ # Example
120
+
121
+ ```python
122
+ config["bot"]["main_channel"]
123
+ ```
124
+ """
125
+ return self._data[key]
matrix/registry.py CHANGED
@@ -65,7 +65,6 @@ class Registry:
65
65
 
66
66
  self._event_handlers: Dict[Type[Event], List[Callback]] = defaultdict(list)
67
67
  self._hook_handlers: Dict[str, List[Callback]] = defaultdict(list)
68
- self._fallback_error_handler: Optional[ErrorCallback] = None
69
68
  self._error_handlers: Dict[type[Exception], ErrorCallback] = {}
70
69
  self._command_error_handlers: Dict[type[Exception], CommandErrorCallback] = {}
71
70
 
@@ -378,14 +377,15 @@ class Registry:
378
377
  ```
379
378
  """
380
379
 
380
+ if not exception:
381
+ exception = Exception
382
+
381
383
  def wrapper(func: ErrorCallback) -> ErrorCallback:
382
384
  if not inspect.iscoroutinefunction(func):
383
385
  raise TypeError("Error handlers must be coroutines")
384
386
 
385
- if exception:
386
- self._error_handlers[exception] = func
387
- else:
388
- self._fallback_error_handler = func
387
+ self._error_handlers[exception] = func
388
+
389
389
  logger.debug(
390
390
  "registered error handler '%s' on %s",
391
391
  func.__name__,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: matrix-python
3
- Version: 1.4.5a0
3
+ Version: 1.4.7a0
4
4
  Summary: An easy-to-use Matrix bot framework designed for quick development and minimal setup
5
5
  Author: Simon Roy, Chris Dedman Rollet
6
6
  Maintainer-email: Code Society Lab <admin@codesociety.xyz>
@@ -689,6 +689,7 @@ Requires-Dist: logger
689
689
  Requires-Dist: PyYAML==6.0.3
690
690
  Requires-Dist: markdown==3.10.2
691
691
  Requires-Dist: APScheduler==3.11.2
692
+ Requires-Dist: envyaml==1.10.211231
692
693
  Provides-Extra: dev
693
694
  Requires-Dist: pytest==9.0.3; extra == "dev"
694
695
  Requires-Dist: pytest-asyncio==1.3.0; extra == "dev"
@@ -1,9 +1,9 @@
1
1
  matrix/__init__.py,sha256=g8yEFjELnnwlvOKns-Ug6LgOezkjAFZ-Opt7esbBHKg,728
2
- matrix/_version.py,sha256=X8Lfu8HB9IHEZFC88NkrWaj4moo7WlSXkDThKiafsrE,528
3
- matrix/bot.py,sha256=4gwL2vawLFEREwZD4cEydv6XSXm_SP6pztLD8uwB86M,11944
2
+ matrix/_version.py,sha256=cIY61sFj4baHEv-2SFzhHnMonmSbAkIpGKrCbxZ8dPU,528
3
+ matrix/bot.py,sha256=tbcn1Ra025I9plM-gf0NpzPKSTBlYe0_L8_RLO8w9GM,11984
4
4
  matrix/checks.py,sha256=F_7432_OcFO-im4fRAj62MUsyv1mXywT4OsGC_7xbBQ,486
5
5
  matrix/command.py,sha256=GrP3WsT07sKehGX7PHfnT7gRX22d99877VPd0X2ViEw,10514
6
- matrix/config.py,sha256=wPLfcHGpSapkBqbZIuI7zBdxh52OHTD8cQS0WqQkMeU,2388
6
+ matrix/config.py,sha256=JW_BBs-msIhtv1AGebZumLsx4td-Gp-NZaNYPJxRsEo,3680
7
7
  matrix/content.py,sha256=z5_E2rTvHsODE52OiDkhDHNQAryx5NLhyHjBb65Xe-U,3853
8
8
  matrix/context.py,sha256=-CbxY-LtK9-jgHERhvJH73B3SpO-Uk5ty0j1TMKfzuI,4032
9
9
  matrix/errors.py,sha256=HKGb5NUeFuZvieXgpLlVSmUxK4jpA0ODuiPQqQlbQTE,1676
@@ -11,14 +11,14 @@ matrix/extension.py,sha256=RbCx58CdRXF8kGUgS-ec1aZdd-K5hQedhCCQ0-YR4Vg,2272
11
11
  matrix/group.py,sha256=TRIX7PE3lcB2ZWu3xY2W2OAmE_a8-i2zHNBYnX5uj28,3691
12
12
  matrix/message.py,sha256=w6pu86goylxdrX5fgXPUMB_tW0bOFIk6tKy6qkXTjl4,5136
13
13
  matrix/protocols.py,sha256=nFb4tLanwtrKWoIhZ96xMwXPjD3RF5ITca_yXtakXC4,166
14
- matrix/registry.py,sha256=jxXg_f_pVl6nuwLdVOIjWJ9Yl9hDJSLwoChnnr3ztNs,12753
14
+ matrix/registry.py,sha256=PgdmbBADS-yqdFdj9x3QSsVfefx8Li848CU_UBbqCLQ,12646
15
15
  matrix/room.py,sha256=PBuMWQo8mKy2d2XIeMbBlVBTnnqZjOPPGpKLp4K1AVM,14038
16
16
  matrix/scheduler.py,sha256=EXsL9i8IDXhcpdW8lti0BR5XcIgkmud4iwOPaqcE9Gw,1727
17
17
  matrix/types.py,sha256=UFjC7p8RAf7piEPvp2X3NuWdqBwkM9Yc3He7KWb9icc,384
18
18
  matrix/help/__init__.py,sha256=1u7V7T_-VgYDeQCTXsc4y8Fo-8gJhOqYJq2U3cUjMWg,168
19
19
  matrix/help/help_command.py,sha256=xCLmKklw74LEMjbUfgQR9eaPMFvi3sPtDw2n2pnAnVQ,12800
20
20
  matrix/help/pagination.py,sha256=sJk0wC46sFHf7xl7WsGRAUc4FC7b9hPqmwQDmvcjwgM,2717
21
- matrix_python-1.4.5a0.dist-info/METADATA,sha256=JQOpjcfDsm5WIJIijrutXlKgF7H4vU1KGX11JpkOfn8,44636
22
- matrix_python-1.4.5a0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
23
- matrix_python-1.4.5a0.dist-info/top_level.txt,sha256=BvHVM9c7-5SLzg-1OCRpHKgqAubWhRN1e38e6coHs-g,7
24
- matrix_python-1.4.5a0.dist-info/RECORD,,
21
+ matrix_python-1.4.7a0.dist-info/METADATA,sha256=i0D7FpTrWeuERNEsaKTyljaqwmO6UF11pGf97xr2sgY,44672
22
+ matrix_python-1.4.7a0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
23
+ matrix_python-1.4.7a0.dist-info/top_level.txt,sha256=BvHVM9c7-5SLzg-1OCRpHKgqAubWhRN1e38e6coHs-g,7
24
+ matrix_python-1.4.7a0.dist-info/RECORD,,