devalerts 0.2.1__tar.gz → 0.2.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devalerts
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Send unhandled Python exceptions straight to a Telegram chat. No backend, no account, no database — just your own bot token.
5
5
  Keywords: telegram,error-tracking,exception-monitoring,alerts,logging,monitoring,asgi
6
6
  Author: sslinNn
@@ -151,16 +151,28 @@ devalerts.init(bot_token="...", chat_id=123456789, tags={"env": "production"})
151
151
  🖥️ prod-web-2 (env=production)
152
152
  ```
153
153
 
154
- Add ad-hoc tags to a single call — they override `init()`'s tags on a key
155
- collision:
154
+ Add ad-hoc tags to a single call — they override `init()`'s tags (and each
155
+ other) on a key collision:
156
156
 
157
157
  ```python
158
158
  devalerts.report(extra={"request_id": "abc123"})
159
+ ```
160
+
161
+ Used as a decorator, `capture()` tags the alert with the wrapped function's
162
+ name as `job` automatically — no `extra` needed:
159
163
 
160
- @devalerts.capture(extra={"job": "nightly-sync"})
161
- def run_job(): ...
164
+ ```python
165
+ @devalerts.capture()
166
+ def nightly_sync(): ...
162
167
  ```
163
168
 
169
+ ```
170
+ 🔴 ValueError: boom
171
+ 🖥️ prod-web-2 (job=nightly_sync)
172
+ ```
173
+
174
+ Pass `extra={"job": "..."}` explicitly to override the auto-detected name.
175
+
164
176
  ## Manually reporting a caught exception
165
177
 
166
178
  ```python
@@ -124,16 +124,28 @@ devalerts.init(bot_token="...", chat_id=123456789, tags={"env": "production"})
124
124
  🖥️ prod-web-2 (env=production)
125
125
  ```
126
126
 
127
- Add ad-hoc tags to a single call — they override `init()`'s tags on a key
128
- collision:
127
+ Add ad-hoc tags to a single call — they override `init()`'s tags (and each
128
+ other) on a key collision:
129
129
 
130
130
  ```python
131
131
  devalerts.report(extra={"request_id": "abc123"})
132
+ ```
133
+
134
+ Used as a decorator, `capture()` tags the alert with the wrapped function's
135
+ name as `job` automatically — no `extra` needed:
132
136
 
133
- @devalerts.capture(extra={"job": "nightly-sync"})
134
- def run_job(): ...
137
+ ```python
138
+ @devalerts.capture()
139
+ def nightly_sync(): ...
135
140
  ```
136
141
 
142
+ ```
143
+ 🔴 ValueError: boom
144
+ 🖥️ prod-web-2 (job=nightly_sync)
145
+ ```
146
+
147
+ Pass `extra={"job": "..."}` explicitly to override the auto-detected name.
148
+
137
149
  ## Manually reporting a caught exception
138
150
 
139
151
  ```python
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "devalerts"
3
- version = "0.2.1"
3
+ version = "0.2.2"
4
4
  description = "Send unhandled Python exceptions straight to a Telegram chat. No backend, no account, no database — just your own bot token."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -130,17 +130,29 @@ def report(
130
130
  class capture(contextlib.ContextDecorator):
131
131
  """Context manager / decorator: report any exception raised inside the block or
132
132
  function, then re-raise it. Use ``@capture()`` on a function instead of wrapping
133
- its body in a manual ``try/except`` or ``with`` block."""
133
+ its body in a manual ``try/except`` or ``with`` block.
134
+
135
+ Used as a decorator, the wrapped function's name is tagged automatically as
136
+ ``job`` -- no need to pass ``extra={"job": ...}`` by hand. Used as a bare
137
+ ``with capture():`` block, there's no function to name, so only explicit
138
+ ``extra`` tags apply."""
134
139
 
135
140
  def __init__(self, *, extra: dict[str, str] | None = None) -> None:
136
141
  self._extra = extra
142
+ self._job_name: str | None = None
143
+
144
+ def __call__(self, func):
145
+ self._job_name = func.__qualname__
146
+ return super().__call__(func)
137
147
 
138
148
  def __enter__(self) -> "capture":
139
149
  return self
140
150
 
141
151
  def __exit__(self, exc_type, exc_value, tb) -> Literal[False]:
142
152
  if exc_type is not None:
143
- _send_exception(exc_type, exc_value, tb, extra=self._extra)
153
+ tags = {"job": self._job_name} if self._job_name else {}
154
+ tags.update(self._extra or {})
155
+ _send_exception(exc_type, exc_value, tb, extra=tags)
144
156
  return False
145
157
 
146
158
 
File without changes