hypern 0.3.6__cp312-cp312-win32.whl → 0.3.8__cp312-cp312-win32.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.
hypern/worker.py CHANGED
@@ -1,30 +1,274 @@
1
- # -*- coding: utf-8 -*-
2
- from typing import Any
1
+ import asyncio
2
+ import logging
3
+ import os
4
+ import time
5
+ import traceback
6
+ from concurrent.futures import ThreadPoolExecutor
7
+ from functools import partial, wraps
8
+ from typing import Callable, Dict, List
9
+
3
10
  from celery import Celery
4
- from asgiref.sync import async_to_sync
11
+ from celery.result import AsyncResult
12
+ from celery.signals import (
13
+ after_setup_logger,
14
+ after_setup_task_logger,
15
+ task_failure,
16
+ task_postrun,
17
+ task_prerun,
18
+ worker_ready,
19
+ worker_shutdown,
20
+ )
21
+ from kombu import Exchange, Queue
22
+
23
+
24
+ class Worker(Celery):
25
+ def __init__(
26
+ self,
27
+ main: str = None,
28
+ broker_url: str = None,
29
+ result_backend: str = "rpc://",
30
+ queues: Dict[str, Dict] = None,
31
+ task_routes: Dict[str, str] = None,
32
+ imports: List[str] = None,
33
+ **kwargs,
34
+ ):
35
+ super().__init__(main, **kwargs)
36
+
37
+ self._executor = ThreadPoolExecutor()
38
+ self._task_timings = {}
39
+
40
+ self.default_exchange = Exchange("default", type="direct")
41
+ self.priority_exchange = Exchange("priority", type="direct")
42
+
43
+ default_queues = {
44
+ "default": {"exchange": self.default_exchange, "routing_key": "default"},
45
+ "high_priority": {"exchange": self.priority_exchange, "routing_key": "high"},
46
+ "low_priority": {"exchange": self.priority_exchange, "routing_key": "low"},
47
+ }
48
+ if queues:
49
+ default_queues.update(queues)
50
+
51
+ self._queues = {
52
+ name: Queue(
53
+ name,
54
+ exchange=config.get("exchange", self.default_exchange),
55
+ routing_key=config.get("routing_key", name),
56
+ queue_arguments=config.get("arguments", {}),
57
+ )
58
+ for name, config in default_queues.items()
59
+ }
60
+
61
+ self.conf.update(
62
+ broker_url=broker_url,
63
+ result_backend=result_backend,
64
+ # Worker Pool Configuration
65
+ worker_pool="solo",
66
+ worker_pool_restarts=True,
67
+ broker_connection_retry_on_startup=True,
68
+ # Worker Configuration
69
+ worker_prefetch_multiplier=1,
70
+ worker_max_tasks_per_child=1000,
71
+ worker_concurrency=os.cpu_count(),
72
+ # Task Settings
73
+ task_acks_late=True,
74
+ task_reject_on_worker_lost=True,
75
+ task_time_limit=3600,
76
+ task_soft_time_limit=3000,
77
+ task_default_retry_delay=300,
78
+ task_max_retries=3,
79
+ # Memory Management
80
+ worker_max_memory_per_child=200000, # 200MB
81
+ # Task Routing
82
+ task_routes=task_routes,
83
+ task_queues=list(self._queues.values()),
84
+ # Performance Settings
85
+ task_compression="gzip",
86
+ result_compression="gzip",
87
+ task_serializer="json",
88
+ result_serializer="json",
89
+ accept_content=["json"],
90
+ imports=imports,
91
+ task_default_exchange=self.default_exchange.name,
92
+ task_default_routing_key="default",
93
+ )
94
+
95
+ self._setup_signals()
96
+
97
+ def _setup_signals(self):
98
+ @worker_ready.connect
99
+ def on_worker_ready(sender, **kwargs):
100
+ self.logger.info(f"Worker {sender.hostname} is ready")
101
+
102
+ @worker_shutdown.connect
103
+ def on_worker_shutdown(sender, **kwargs):
104
+ self.logger.info(f"Worker {sender.hostname} is shutting down")
105
+ self._executor.shutdown(wait=True)
106
+
107
+ @task_prerun.connect
108
+ def task_prerun_handler(task_id, task, *args, **kwargs):
109
+ self._task_timings[task_id] = {"start": time.time()}
110
+ self.logger.info(f"Task {task.name}[{task_id}] started")
111
+
112
+ @task_postrun.connect
113
+ def task_postrun_handler(task_id, task, *args, retval=None, **kwargs):
114
+ if task_id in self._task_timings:
115
+ start_time = self._task_timings[task_id]["start"]
116
+ duration = time.time() - start_time
117
+ self.logger.info(f"Task {task.name}[{task_id}] completed in {duration:.2f}s")
118
+ del self._task_timings[task_id]
119
+
120
+ @task_failure.connect
121
+ def task_failure_handler(task_id, exc, task, *args, **kwargs):
122
+ self.logger.error(f"Task {task.name}[{task_id}] failed: {exc}\n{traceback.format_exc()}")
123
+
124
+ @after_setup_logger.connect
125
+ def setup_celery_logger(logger, *args, **kwargs):
126
+ existing_logger = logging.getLogger("hypern")
127
+ logger.handlers = existing_logger.handlers
128
+ logger.filters = existing_logger.filters
129
+ logger.level = existing_logger.level
130
+
131
+ @after_setup_task_logger.connect
132
+ def setup_task_logger(logger, *args, **kwargs):
133
+ existing_logger = logging.getLogger("hypern")
134
+ logger.handlers = existing_logger.handlers
135
+ logger.filters = existing_logger.filters
136
+ logger.level = existing_logger.level
137
+
138
+ def add_task_routes(self, routes: Dict[str, str]) -> None:
139
+ """
140
+ Example:
141
+ app.add_task_routes({
142
+ 'tasks.email.*': 'email_queue',
143
+ 'tasks.payment.process': 'payment_queue',
144
+ 'tasks.high_priority.*': 'high_priority'
145
+ })
146
+ """
147
+ for task_pattern, queue in routes.items():
148
+ self.add_task_route(task_pattern, queue)
149
+
150
+ def add_task_route(self, task_pattern: str, queue: str) -> None:
151
+ """
152
+ Add a task route to the Celery app
153
+
154
+ Example:
155
+ app.add_task_route('tasks.email.send', 'email_queue')
156
+ app.add_task_route('tasks.payment.*', 'payment_queue')
157
+ """
158
+ if queue not in self._queues:
159
+ raise ValueError(f"Queue '{queue}' does not exist. Create it first using create_queue()")
160
+
161
+ self._task_route_mapping[task_pattern] = queue
162
+
163
+ # Update Celery task routes
164
+ routes = self.conf.task_routes or {}
165
+ routes[task_pattern] = {"queue": queue}
166
+ self.conf.task_routes = routes
167
+
168
+ self.logger.info(f"Added route: {task_pattern} -> {queue}")
169
+
170
+ def task(self, *args, **opts):
171
+ """
172
+ Decorator modified to support sync and async functions
173
+ """
174
+ base_task = Celery.task.__get__(self)
175
+
176
+ def decorator(func):
177
+ is_async = asyncio.iscoroutinefunction(func)
178
+
179
+ if is_async:
180
+
181
+ @wraps(func)
182
+ async def async_wrapper(*fargs, **fkwargs):
183
+ return await func(*fargs, **fkwargs)
184
+
185
+ @base_task(*args, **opts)
186
+ def wrapped(*fargs, **fkwargs):
187
+ loop = asyncio.new_event_loop()
188
+ asyncio.set_event_loop(loop)
189
+ try:
190
+ return loop.run_until_complete(async_wrapper(*fargs, **fkwargs))
191
+ finally:
192
+ loop.close()
193
+
194
+ return wrapped
195
+ else:
196
+ return base_task(*args, **opts)(func)
197
+
198
+ return decorator
199
+
200
+ async def async_send_task(self, task_name: str, *args, **kwargs) -> AsyncResult:
201
+ """
202
+ Version of send_task() that is async
203
+ """
204
+ loop = asyncio.get_event_loop()
205
+ return await loop.run_in_executor(self._executor, partial(self.send_task, task_name, args=args, kwargs=kwargs))
206
+
207
+ async def async_result(self, task_id: str) -> Dict:
208
+ """
209
+ Get the result of a task asynchronously
210
+ """
211
+ async_result = self.AsyncResult(task_id)
212
+ loop = asyncio.get_event_loop()
213
+
214
+ result = await loop.run_in_executor(
215
+ self._executor,
216
+ lambda: {
217
+ "task_id": task_id,
218
+ "status": async_result.status,
219
+ "result": async_result.result,
220
+ "traceback": async_result.traceback,
221
+ "date_done": async_result.date_done,
222
+ },
223
+ )
224
+ return result
225
+
226
+ def get_queue_length(self, queue_name: str) -> int:
227
+ """
228
+ Get the number of messages in a queue
229
+ """
230
+ with self.connection_or_acquire() as conn:
231
+ channel = conn.channel()
232
+ queue = Queue(queue_name, channel=channel)
233
+ return queue.queue_declare(passive=True).message_count
5
234
 
235
+ async def chain_tasks(self, tasks: list) -> AsyncResult:
236
+ """
237
+ Function to chain multiple tasks together
238
+ """
239
+ chain = tasks[0]
240
+ for task in tasks[1:]:
241
+ chain = chain | task
242
+ return await self.adelay_task(chain)
6
243
 
7
- class AsyncCelery(Celery):
8
- def __new__(cls, *args, **kwargs) -> Any:
9
- if not hasattr(cls, "instance") or not cls.instance: # type: ignore
10
- cls.instance = super().__new__(cls)
11
- return cls.instance
244
+ def register_task_middleware(self, middleware: Callable):
245
+ """
246
+ Register a middleware function to be called before each task
247
+ """
12
248
 
13
- def __init__(self, *args, **kwargs) -> None:
14
- super().__init__(*args, **kwargs)
15
- self.patch_task()
249
+ def task_middleware(task):
250
+ @wraps(task)
251
+ def _wrapped(*args, **kwargs):
252
+ return middleware(task, *args, **kwargs)
16
253
 
17
- def patch_task(self) -> None:
18
- TaskBase = self.Task
254
+ return _wrapped
19
255
 
20
- class ContextTask(TaskBase): # type: ignore
21
- abstract = True
256
+ self.task = task_middleware(self.task)
22
257
 
23
- def _run(self, *args, **kwargs):
24
- result = async_to_sync(TaskBase.__call__)(self, *args, **kwargs)
25
- return result
258
+ def monitor_task(self, task_id: str) -> dict:
259
+ """
260
+ Get monitoring data for a task
261
+ """
262
+ result = self.AsyncResult(task_id)
263
+ timing_info = self._task_timings.get(task_id, {})
26
264
 
27
- def __call__(self, *args, **kwargs):
28
- return self._run(*args, **kwargs)
265
+ monitoring_data = {
266
+ "task_id": task_id,
267
+ "status": result.status,
268
+ "start_time": timing_info.get("start"),
269
+ "duration": time.time() - timing_info["start"] if timing_info.get("start") else None,
270
+ "result": result.result if result.ready() else None,
271
+ "traceback": result.traceback,
272
+ }
29
273
 
30
- self.Task = ContextTask
274
+ return monitoring_data
@@ -1,11 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hypern
3
- Version: 0.3.6
3
+ Version: 0.3.8
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
7
7
  Requires-Dist: sqlalchemy[asyncio] ==2.0.31
8
- Requires-Dist: pydantic[email] ==2.8.2
9
8
  Requires-Dist: passlib ==1.7.4
10
9
  Requires-Dist: pyjwt ==2.8.0
11
10
  Requires-Dist: pydash ==8.0.3
@@ -26,6 +25,7 @@ Requires-Dist: watchdog ==6.0.0
26
25
  Requires-Dist: jsonschema ==4.23.0
27
26
  Requires-Dist: psutil ==6.1.0
28
27
  Requires-Dist: msgpack ==1.1.0
28
+ Requires-Dist: redis ==5.2.1
29
29
  License-File: LICENSE
30
30
  Summary: A Fast Async Python backend with a Rust runtime.
31
31
  Author-email: Martin Dang <vannghiem848@gmail.com>
@@ -37,7 +37,7 @@ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
37
37
 
38
38
  Hypern: A Versatile Python and Rust Framework
39
39
 
40
- Hypern is a flexible, open-source framework built on the [Axum](https://github.com/tokio-rs/axum), designed to jumpstart your high-performance web development endeavors. By providing a pre-configured structure and essential components, Hypern empowers you to rapidly develop custom web applications that leverage the combined power of Python and Rust.
40
+ Hypern is a flexible, open-source framework built on the [Rust](https://github.com/rust-lang/rust), designed to jumpstart your high-performance web development endeavors. By providing a pre-configured structure and essential components, Hypern empowers you to rapidly develop custom web applications that leverage the combined power of Python and Rust.
41
41
 
42
42
  With Hypern, you can seamlessly integrate asynchronous features and build scalable solutions for RESTful APIs and dynamic web applications. Its intuitive design and robust tooling allow developers to focus on creating high-quality code while maximizing performance. Embrace the synergy of Python and Rust to elevate your web development experience.
43
43
 
@@ -91,7 +91,7 @@ routing = [
91
91
  app = Hypern(routing)
92
92
 
93
93
  if __name__ == "__main__":
94
- app.start(host='localhost', port=5005)
94
+ app.start()
95
95
  ```
96
96
 
97
97
  ```
@@ -99,6 +99,16 @@ $ python3 main.py
99
99
  ```
100
100
  You can open swagger UI at path `/docs`
101
101
 
102
+ ## CLI
103
+
104
+ - host (str): The host address to bind to. Defaults to '127.0.0.1'.
105
+ - port (int): The port number to bind to. Defaults to 5000.
106
+ - processes (int): The number of processes to use. Defaults to 1.
107
+ - workers (int): The number of worker threads to use. Defaults to 1.
108
+ - max_blocking_threads (int): The maximum number of blocking threads. Defaults to 32.
109
+ - reload (bool): If True, the server will restart on file changes.
110
+ - auto_workers (bool): If True, sets the number of workers and max-blocking-threads automatically.
111
+
102
112
 
103
113
  ## 💡 Features
104
114
 
@@ -106,7 +116,7 @@ You can open swagger UI at path `/docs`
106
116
  - Rust-powered core with Python flexibility
107
117
  - Multi-process architecture for optimal CPU utilization
108
118
  - Async/await support for non-blocking operations
109
- - Built on top of production-ready Axum web framework
119
+ - Built on top of production-ready Rust language
110
120
 
111
121
  ### 🛠 Development Experience
112
122
  - Type hints and IDE support
@@ -117,7 +127,7 @@ You can open swagger UI at path `/docs`
117
127
  ### 🔌 Integration & Extensions
118
128
  - Easy dependency injection
119
129
  - Middleware support (before/after request hooks)
120
- - WebSocket support (Comming soon)
130
+ - WebSocket support
121
131
  - Background task scheduling
122
132
  - File upload handling
123
133
 
@@ -1,8 +1,8 @@
1
- hypern-0.3.6.dist-info/METADATA,sha256=NZSUrAbL7D7aleTF03qoRJiO2r8cQ7OQdwYlPu9hRoM,3850
2
- hypern-0.3.6.dist-info/WHEEL,sha256=SK_cql1gpDHx6aBV-LOSvGbTt4TUC8AJJOzjOP2tdpI,92
3
- hypern-0.3.6.dist-info/licenses/LICENSE,sha256=qbYKAIJLS6jYg5hYncKE7OtWmqOtpVTvKNkwOa0Iwwg,1328
4
- hypern/application.py,sha256=DCYFtU8e8NhQtmfaXbUfOxR2_Y3fEn-pzce9OOs6S4U,18396
5
- hypern/args_parser.py,sha256=zTfLfBoKBvYWxdPjabTfZsCtYF3La3PT0TD8dfLMeM4,2815
1
+ hypern-0.3.8.dist-info/METADATA,sha256=0bMLw-pA0VeoGBbQxXr4cqCO8dS6esga5NjluEeV5MY,4356
2
+ hypern-0.3.8.dist-info/WHEEL,sha256=19xj5Waw2omQTyAh5Pnrm7rXeZkfzX1OuBYghlKYN-I,92
3
+ hypern-0.3.8.dist-info/licenses/LICENSE,sha256=qbYKAIJLS6jYg5hYncKE7OtWmqOtpVTvKNkwOa0Iwwg,1328
4
+ hypern/application.py,sha256=07QGDd3w7tQRL8njfbNVinG6l8W7O0V-rN_SkO5O3B4,18002
5
+ hypern/args_parser.py,sha256=BjGCooHdZjOpyN-AGL9PCKCKdzROVThE3dzQgC97rng,2035
6
6
  hypern/auth/authorization.py,sha256=-NprZsI0np889ZN1fp-MiVFrPoMNzUtatBJaCMtkllM,32
7
7
  hypern/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  hypern/background.py,sha256=xy38nQZSJsYFRXr3-uFJeNW9E1GiXXOC7lSe5pC0eyE,124
@@ -31,12 +31,13 @@ hypern/database/nosql/addons/password.py,sha256=jfZxvWFm6nV9EWpXq5Mj-jpqnl9QbokZ
31
31
  hypern/database/nosql/addons/unicode.py,sha256=LaDpLfdoTcJuASPE-8fqOVD05H_uOx8gOdnyDn5Iu0c,268
32
32
  hypern/database/nosql/addons/__init__.py,sha256=WEtPM8sPHilvga7zxwqvINeTkF0hdcfgPcAnHc4MASE,125
33
33
  hypern/database/nosql/__init__.py,sha256=MH9YvlbRlbBCrQVNOdfTaK-hINwJxbJLmxwY9Mei7I8,644
34
- hypern/database/sql/field.py,sha256=tSs8iaYjy-K6nplJJ-1X4OQddzW76cfBlx9xTrG_NbQ,20073
35
- hypern/database/sql/model.py,sha256=BLRmOlmfn6ibedR9Bv_rHErSruudJ24B9-nDbRHqWm4,3913
36
- hypern/database/sql/query.py,sha256=tQ7Wss2NAIqsAH0M-fT5m9DU_MsiBR0DcoyTbS_aatU,33335
37
- hypern/database/sql/__init__.py,sha256=lCOGNTHaXNSJbuLLIOe2IWWNmX0MFQFPNCl2yytD2Xs,261
34
+ hypern/database/sql/field.py,sha256=gV9u_BvMIoxoDT3_J7sL5XJNa5XFsAO9w324ThwHbNs,9121
35
+ hypern/database/sql/migrate.py,sha256=BTtAs3-iMyMDzIWl6B3rM9sj7XGggLDRjD0h_WgGPtc,9742
36
+ hypern/database/sql/model.py,sha256=C8_rJA1Adw1yPWthjmAGh26hjTBuwwlEdtH45ADxvL0,4044
37
+ hypern/database/sql/query.py,sha256=En19t27zt6iUDQbFgO_wLEWPQCkPeBuH3s37fzlhMVc,33345
38
+ hypern/database/sql/__init__.py,sha256=dbSAz2nP0DPKK4Bb_jJdObSaSYQfgZ8D4U1TJdc4e7c,645
38
39
  hypern/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
- hypern/datastructures.py,sha256=zZGGSP07kPc9KJDf11hX5uYhAyRE-Ck5wezW5QtOVXw,897
40
+ hypern/datastructures.py,sha256=Pxr9KsZZTFfp0KC1-A4v5AkQfmrUyvVwxKuToQUOLoE,882
40
41
  hypern/enum.py,sha256=KcVziJj7vWvyie0r2rtxhrLzdtkZAsf0DY58oJ4tQl4,360
41
42
  hypern/exceptions/base.py,sha256=5AgfyEea79JjKk5MeAIJ-wy44FG5XEU0Jn3KXKScPiI,2017
42
43
  hypern/exceptions/common.py,sha256=0E8wHRRTWjYOmtOCkTDvZ5NMwL6vRW6aiDD9X1eYA30,227
@@ -49,7 +50,7 @@ hypern/gateway/gateway.py,sha256=26K2qvJUR-0JnN4IlhwvSSt7EYcpYrBVDuzZ1ivQQ34,147
49
50
  hypern/gateway/proxy.py,sha256=w1wcTplDnVrfjn7hb0M0yBVth5TGl88irF-MUYHysQQ,2463
50
51
  hypern/gateway/service.py,sha256=PkRaM08olqM_j_4wRjEJCR8X8ZysAF2WOcfhWjaX2eo,1701
51
52
  hypern/gateway/__init__.py,sha256=TpFWtqnJerW1-jCWq5fjypJcw9Y6ytyrkvkzby1Eg0E,235
52
- hypern/hypern.pyi,sha256=yXaWGPt598gwPN-CT1ARDdwOSqryZCBFuDLQC8gRd1U,9345
53
+ hypern/hypern.pyi,sha256=Urc3uHhPupsN-RRYluyWiw8B_xYWzE91Og3Zft40NgU,9010
53
54
  hypern/i18n/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
55
  hypern/logging/logger.py,sha256=WACam_IJiCMXX0hGVKMGSxUQpY4DgAXy7M1dD3q-Z9s,3256
55
56
  hypern/logging/__init__.py,sha256=6eVriyncsJ4J73fGYhoejv9MX7aGTkRezTpPxO4DX1I,52
@@ -61,7 +62,7 @@ hypern/middleware/i18n.py,sha256=jHzVzjTx1nnjbraZtIVOprrnSaeKMxZB8RuSqRp2I4s,16
61
62
  hypern/middleware/limit.py,sha256=eAYARPjqxq8Ue0TCpnxlVRB5hv7hwBF0PxeD-bG6Sl0,8252
62
63
  hypern/middleware/security.py,sha256=fGBSF7n2iKBtDHE2QW4q_sQE4awYgaYxVUFKsDHkMXg,7675
63
64
  hypern/middleware/__init__.py,sha256=V-Gnv-Jf-14BVuA28z7PN7GBVQ9BBiBdab6-QnTPCfY,493
64
- hypern/openapi/schemas.py,sha256=YHfMlPUeP5DzDX5ao3YH8p_25Vvyaf616dh6XDCUZRc,1677
65
+ hypern/openapi/schemas.py,sha256=hsqSPpwsOETQ5NoGiR9Ay0qEp6GxJ2xhh69rzwxx0CY,1598
65
66
  hypern/openapi/swagger.py,sha256=naqUY3rFAEYA1ZLIlmDsMYaol0yIm6TVebdkFa5cMTc,64
66
67
  hypern/openapi/__init__.py,sha256=4rEVD8pa0kdSpsy7ZkJ5JY0Z2XF0NGSKDMwYAd7YZpE,141
67
68
  hypern/processpool.py,sha256=qEsu9WXWc3_Cl0Frn1jGs7jUJho45zck5L5Ww81Vm70,3883
@@ -73,17 +74,16 @@ hypern/routing/dispatcher.py,sha256=NAVjILlEJjYrixJZ4CO4N1CKkuqbk4TGZOjnQNTTEu4,
73
74
  hypern/routing/endpoint.py,sha256=RKVhvqOEGL9IKBXQ3KJgPi9bgJj9gfWC5BdZc5U_atc,1026
74
75
  hypern/routing/parser.py,sha256=0tJVVNwHC3pWDsehwH6SwJv8_gEuDjltVXrNQWbHyrU,3426
75
76
  hypern/routing/queue.py,sha256=NtFBbogU22ddyyX-CuQMip1XFDPZdMCVMIeUCQ-CR6Y,7176
76
- hypern/routing/route.py,sha256=IUnWU5ra-0R9rrRDpxJiwiw7vaEefn-We2dZ4EocJGw,10403
77
+ hypern/routing/route.py,sha256=kan47-UeL-OPwcpp0rEhmBaaum6hN7FUj13Y8pZDEYA,10256
77
78
  hypern/routing/__init__.py,sha256=U4xW5fDRsn03z4cVLT4dJHHGGU6SVxyv2DL86LXodeE,162
78
79
  hypern/scheduler.py,sha256=-k3tW2AGCnHYSthKXk-FOs_SCtWp3yIxQzwzUJMJsbo,67
79
80
  hypern/security.py,sha256=3E86Yp_eOSVa1emUvBrDgoF0Sn6eNX0CfLnt87w5CPI,1773
80
- hypern/worker.py,sha256=WQrhY_awR6zjMwY4Q7izXi4E4fFrDqt7jIblUW8Bzcg,924
81
+ hypern/worker.py,sha256=ksJW8jWQg3HbIYnIZ5qdAmO-yh5hLpwvTT3dKkHR4Eo,9761
81
82
  hypern/ws/channel.py,sha256=0ns2qmeoFJOpGLXS_hqldhywDQm_DxHwj6KloQx4Q3I,3183
82
83
  hypern/ws/heartbeat.py,sha256=sWMXzQm6cbDHHA2NHc-gFjv7G_E56XtxswHQ93_BueM,2861
83
84
  hypern/ws/room.py,sha256=0_L6Nun0n007F0rfNY8yX5x_A8EuXuI67JqpMkJ4RNI,2598
84
85
  hypern/ws/route.py,sha256=fGQ2RC708MPOiiIHPUo8aZ-oK379TTAyQYm4htNA5jM,803
85
86
  hypern/ws/__init__.py,sha256=dhRoRY683_rfPfSPM5qUczfTuyYDeuLOCFxY4hIdKt8,131
86
- hypern/ws.py,sha256=F6SA2Z1KVnqTEX8ssvOXqCtudUS4eo30JsiIsvfbHnE,394
87
- hypern/__init__.py,sha256=9Ww_aUQ0vJls0tOq7Yw1_TVOCRsa5bHJ-RtnSeComwk,119
88
- hypern/hypern.cp312-win32.pyd,sha256=UEZnqNJkk-53t73JL5FWC2oDcmxOUmLAT8rc1R0E4DI,9796096
89
- hypern-0.3.6.dist-info/RECORD,,
87
+ hypern/__init__.py,sha256=p3AtJQbsPs1RYEiN1thxH-k5UP8FfLeiJSoP0Vt3MDg,639
88
+ hypern/hypern.cp312-win32.pyd,sha256=rvh5yYWRidA1p4JK6cneYi7yjaJ8okvTNHUIpwn6c9k,7591936
89
+ hypern-0.3.8.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: maturin (1.7.8)
2
+ Generator: maturin (1.8.1)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp312-cp312-win32
hypern/ws.py DELETED
@@ -1,16 +0,0 @@
1
- from .hypern import WebsocketRoute as WebsocketRouteInternal, WebSocketSession
2
-
3
-
4
- class WebsocketRoute:
5
- def __init__(self) -> None:
6
- self.routes = []
7
-
8
- def on(self, path):
9
- def wrapper(func):
10
- self.routes.append(WebsocketRouteInternal(path, func))
11
- return func
12
-
13
- return wrapper
14
-
15
-
16
- __all__ = ["WebsocketRoute", "WebSocketSession"]