zenx 0.9.0__py3-none-any.whl → 0.9.3__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.
zenx/clients/http.py CHANGED
@@ -113,7 +113,7 @@ class CurlCffi(HttpClient):
113
113
  async with AsyncSession() as session:
114
114
  try:
115
115
  req_at = get_time()
116
- response: CurlResponse = await session.request(
116
+ response: CurlResponse = await asyncio.wait_for(session.request(
117
117
  url=url,
118
118
  method=method,
119
119
  headers=headers,
@@ -121,10 +121,13 @@ class CurlCffi(HttpClient):
121
121
  verify=False,
122
122
  impersonate=impersonate,
123
123
  **kwargs,
124
- )
124
+ ), timeout=10)
125
125
  recv_at = get_time()
126
126
  latency = recv_at - req_at
127
127
  self.logger.debug("response", status=response.status_code, url=url, impersonate=impersonate, client=self.name, requested_at=req_at, responded_at=recv_at, latency_ms=latency)
128
+ except TimeoutError:
129
+ self.logger.error("timeout", url=url, impersonate=impersonate, client=self.name)
130
+ raise
128
131
  except Exception:
129
132
  self.logger.exception("request", url=url, client=self.name)
130
133
  raise
@@ -136,17 +139,20 @@ class CurlCffi(HttpClient):
136
139
  self.logger.debug("acquired_session", impersonate=session.impersonate, client=self.name)
137
140
  try:
138
141
  req_at = get_time()
139
- response: CurlResponse = await session.request(
142
+ response: CurlResponse = await asyncio.wait_for(session.request(
140
143
  url=url,
141
144
  method=method,
142
145
  headers=headers,
143
146
  proxy=proxy,
144
147
  verify=False,
145
148
  **kwargs,
146
- )
149
+ ), timeout=10)
147
150
  recv_at = get_time()
148
151
  latency = recv_at - req_at
149
152
  self.logger.debug("response", status=response.status_code, url=url, impersonate=session.impersonate, client=self.name, requested_at=req_at, responded_at=recv_at, latency_ms=latency)
153
+ except TimeoutError:
154
+ self.logger.error("timeout", url=url, impersonate=session.impersonate, client=self.name)
155
+ raise
150
156
  except Exception:
151
157
  self.logger.exception("request", url=url, client=self.name)
152
158
  raise
@@ -0,0 +1,45 @@
1
+ from httpx import AsyncClient
2
+ from typing import Dict
3
+ from structlog import BoundLogger
4
+
5
+ from zenx.pipelines.base import Pipeline
6
+ from zenx.clients.database import DBClient
7
+ from zenx.settings import Settings
8
+ from zenx.utils import log_processing_time
9
+
10
+
11
+
12
+ class SynopticDiscordPipeline(Pipeline):
13
+ name = "syncoptic_discord"
14
+ required_settings = ["SYNOPTIC_DISCORD_WEBHOOK"]
15
+
16
+
17
+ def __init__(self, logger: BoundLogger, db: DBClient, settings: Settings) -> None:
18
+ super().__init__(logger, db, settings)
19
+ self._uri = settings.SYNOPTIC_DISCORD_WEBHOOK
20
+ self._client = AsyncClient(headers={"Content-Type": "application/json"})
21
+
22
+
23
+ async def start(self) -> None:
24
+ for setting in self.required_settings:
25
+ if not getattr(self.settings, setting):
26
+ raise ValueError(f"Missing required setting: {setting}")
27
+
28
+
29
+ @log_processing_time
30
+ async def process_item(self, item: Dict, spider: str) -> Dict:
31
+ await self._process(item)
32
+ return item
33
+
34
+
35
+ async def _process(self, item: Dict) -> None:
36
+ try:
37
+ _item = {k: v for k, v in item.items() if not k.startswith("_")}
38
+ await self._client.post(self._uri, json=_item)
39
+ except Exception as e:
40
+ self.logger.error("processing", exception=str(e), id=item.get("_id"), pipeline=self.name)
41
+
42
+
43
+ async def close(self) -> None:
44
+ if hasattr(self, "_client") and self._client:
45
+ await self._client.aclose()
zenx/settings.py CHANGED
@@ -26,6 +26,8 @@ class Settings(BaseSettings):
26
26
  GRPC_ID: str | None = None
27
27
  GRPC_ID_HEADLINE: str | None = None
28
28
 
29
+ SYNOPTIC_DISCORD_WEBHOOK: str | None = None
30
+
29
31
  SYNOPTIC_API_KEY: str | None = None
30
32
  SYNOPTIC_STREAM_ID: str | None = None
31
33
 
zenx/spiders/base.py CHANGED
@@ -14,7 +14,7 @@ class Spider(ABC):
14
14
  # central registry
15
15
  name: ClassVar[str]
16
16
  _registry: ClassVar[Dict[str, Type[Spider]]] = {}
17
- pipelines: ClassVar[List[Literal["preprocess","synoptic_websocket","synoptic_grpc"]]]
17
+ pipelines: ClassVar[List[Literal["preprocess","synoptic_websocket","synoptic_grpc","synoptic_discord"]]]
18
18
  client_name: ClassVar[Literal["curl_cffi"]] = "curl_cffi"
19
19
 
20
20
 
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zenx
3
- Version: 0.9.0
3
+ Version: 0.9.3
4
4
  Summary: mini-framework
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: curl-cffi>=0.12.0
7
+ Requires-Dist: httpx>=0.28.1
7
8
  Requires-Dist: parsel>=1.10.0
8
9
  Requires-Dist: pebble>=5.1.1
9
10
  Requires-Dist: pydantic>=2.11.7
@@ -4,13 +4,14 @@ zenx/discovery.py,sha256=YANVGzy2IG1fYruUud-11Y-ynyO6iEp3EjlHnhIQJQI,1014
4
4
  zenx/engine.py,sha256=CYwvmPSaI1lC-y_DfuOzX_1Ii14fxU8IdfMM7AIrUUU,2551
5
5
  zenx/exceptions.py,sha256=BJXxzwwX2CU6inhppfblx8c8Z6Mhvsk7MAhQ1LAnhBg,37
6
6
  zenx/logger.py,sha256=lr45XGbV769NQcwn8-lAcPfFbR4yBN8LNh7o-i4Aa9M,1652
7
- zenx/settings.py,sha256=0RLeKcqHJdD1vuYIQtGeXkp2YIvv3tNyz9O8jw620VQ,1008
7
+ zenx/settings.py,sha256=sXRdoBjYE74ksqE84a7Zx_vGgo1UPUqMUeQluxrzR-I,1057
8
8
  zenx/utils.py,sha256=wxild17cpO6SPLJ4dADuVj4IQehNjvOdafOCDPHnsIs,661
9
9
  zenx/clients/__init__.py,sha256=CaAAuNa8DPyMdejR0KNSDDg_UzC3WxaTol5_QvwwwG8,132
10
10
  zenx/clients/database.py,sha256=AF-L7iYrWRNzUZKn7taveiihpu--mXXC6eWOrMNlqzQ,4806
11
- zenx/clients/http.py,sha256=LPGRURBR9WRTiEGCGspUVybFxf4VeqjlbpNAYiX_JUI,5868
11
+ zenx/clients/http.py,sha256=C2VLpxqvlRcKTbESDGXqjSi7LNrdt8Jr9pogXwrsQ8o,6252
12
12
  zenx/pipelines/__init__.py,sha256=IxkZ0UpEJdYjLdd-PMcC9PzzzArTBNNcpgKc7NiOe5Y,131
13
13
  zenx/pipelines/base.py,sha256=N_388z5DFMeaU6wMwcClZAbQFWKh4kpAF7eUJhpQevs,1863
14
+ zenx/pipelines/discord.py,sha256=g33RZGZzr5PIop0OHo2MVVaIRyYVOnfyaZRAV6Y3dUs,1492
14
15
  zenx/pipelines/google_rpc.py,sha256=nR7hNCqAcg1YGWYm3xoj8G8vOTs74lAeYc0dYk842n8,4490
15
16
  zenx/pipelines/manager.py,sha256=lm2A_4h8NNdurFfwrrLwx5Z1tqJ9yZp2qgYvwGWd1lc,2017
16
17
  zenx/pipelines/preprocess.py,sha256=PTXDYo-B_T3YAY5AeUx5ExTDKZovQUwqmB8pyHo6i6o,958
@@ -19,9 +20,9 @@ zenx/resources/proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
19
20
  zenx/resources/proto/feed_pb2.py,sha256=ZyICOLnyuXekkvV4bAHZ1nE1-wwzcYYRRrmRJCMrSoo,2810
20
21
  zenx/resources/proto/feed_pb2_grpc.py,sha256=Mim6FfBgIMj0PmTqHk036nVUMJH3A6I3ts6r1j3bQF8,7441
21
22
  zenx/spiders/__init__.py,sha256=rs5LuqdM2MQlUYiTGJrzkYhzN8_SSLTrR7wGjSRrrSo,25
22
- zenx/spiders/base.py,sha256=BGxOvNnjaz6gmZm7NIgYc0RddWDmpIp53ukxprUUd-Y,1932
23
- zenx-0.9.0.dist-info/METADATA,sha256=pt0I1qmDLl--YuOYOffsIA_dmgjbug3eFYkuBYbqFvk,1273
24
- zenx-0.9.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
25
- zenx-0.9.0.dist-info/entry_points.txt,sha256=8JXob2f1VtvzGFris-e9Usqywg7oca-cChDlH9moOZU,38
26
- zenx-0.9.0.dist-info/top_level.txt,sha256=JeXwvK86d7sB-2x-avugFnZIZa33zaHWKI8RHWJR6KY,5
27
- zenx-0.9.0.dist-info/RECORD,,
23
+ zenx/spiders/base.py,sha256=MeZ3wZOPOyOX4V2ufFXtYGCDtXHZO_mNfnXdKMkisuQ,1951
24
+ zenx-0.9.3.dist-info/METADATA,sha256=dkaucOEsJCCruQnSPPDYME-iSI-wH3bbthhvYqj5Io8,1302
25
+ zenx-0.9.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
+ zenx-0.9.3.dist-info/entry_points.txt,sha256=8JXob2f1VtvzGFris-e9Usqywg7oca-cChDlH9moOZU,38
27
+ zenx-0.9.3.dist-info/top_level.txt,sha256=JeXwvK86d7sB-2x-avugFnZIZa33zaHWKI8RHWJR6KY,5
28
+ zenx-0.9.3.dist-info/RECORD,,
File without changes