hypern 0.2.0__cp312-none-win_amd64.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.
Files changed (66) hide show
  1. hypern/__init__.py +4 -0
  2. hypern/application.py +412 -0
  3. hypern/auth/__init__.py +0 -0
  4. hypern/auth/authorization.py +2 -0
  5. hypern/background.py +4 -0
  6. hypern/caching/__init__.py +0 -0
  7. hypern/caching/base/__init__.py +8 -0
  8. hypern/caching/base/backend.py +3 -0
  9. hypern/caching/base/key_maker.py +8 -0
  10. hypern/caching/cache_manager.py +56 -0
  11. hypern/caching/cache_tag.py +10 -0
  12. hypern/caching/custom_key_maker.py +11 -0
  13. hypern/caching/redis_backend.py +3 -0
  14. hypern/cli/__init__.py +0 -0
  15. hypern/cli/commands.py +0 -0
  16. hypern/config.py +149 -0
  17. hypern/datastructures.py +40 -0
  18. hypern/db/__init__.py +0 -0
  19. hypern/db/nosql/__init__.py +25 -0
  20. hypern/db/nosql/addons/__init__.py +4 -0
  21. hypern/db/nosql/addons/color.py +16 -0
  22. hypern/db/nosql/addons/daterange.py +30 -0
  23. hypern/db/nosql/addons/encrypted.py +53 -0
  24. hypern/db/nosql/addons/password.py +134 -0
  25. hypern/db/nosql/addons/unicode.py +10 -0
  26. hypern/db/sql/__init__.py +179 -0
  27. hypern/db/sql/addons/__init__.py +14 -0
  28. hypern/db/sql/addons/color.py +16 -0
  29. hypern/db/sql/addons/daterange.py +23 -0
  30. hypern/db/sql/addons/datetime.py +22 -0
  31. hypern/db/sql/addons/encrypted.py +58 -0
  32. hypern/db/sql/addons/password.py +171 -0
  33. hypern/db/sql/addons/ts_vector.py +46 -0
  34. hypern/db/sql/addons/unicode.py +15 -0
  35. hypern/db/sql/repository.py +290 -0
  36. hypern/enum.py +13 -0
  37. hypern/exceptions.py +97 -0
  38. hypern/hypern.cp312-win_amd64.pyd +0 -0
  39. hypern/hypern.pyi +266 -0
  40. hypern/i18n/__init__.py +0 -0
  41. hypern/logging/__init__.py +3 -0
  42. hypern/logging/logger.py +82 -0
  43. hypern/middleware/__init__.py +5 -0
  44. hypern/middleware/base.py +18 -0
  45. hypern/middleware/cors.py +38 -0
  46. hypern/middleware/i18n.py +1 -0
  47. hypern/middleware/limit.py +176 -0
  48. hypern/openapi/__init__.py +5 -0
  49. hypern/openapi/schemas.py +53 -0
  50. hypern/openapi/swagger.py +3 -0
  51. hypern/processpool.py +106 -0
  52. hypern/py.typed +0 -0
  53. hypern/response/__init__.py +3 -0
  54. hypern/response/response.py +134 -0
  55. hypern/routing/__init__.py +4 -0
  56. hypern/routing/dispatcher.py +67 -0
  57. hypern/routing/endpoint.py +30 -0
  58. hypern/routing/parser.py +100 -0
  59. hypern/routing/route.py +284 -0
  60. hypern/scheduler.py +5 -0
  61. hypern/security.py +44 -0
  62. hypern/worker.py +30 -0
  63. hypern-0.2.0.dist-info/METADATA +127 -0
  64. hypern-0.2.0.dist-info/RECORD +66 -0
  65. hypern-0.2.0.dist-info/WHEEL +4 -0
  66. hypern-0.2.0.dist-info/licenses/LICENSE +24 -0
@@ -0,0 +1,284 @@
1
+ # -*- coding: utf-8 -*-
2
+ import asyncio
3
+ import inspect
4
+ from enum import Enum
5
+ from typing import Any, Callable, Dict, List, Type, Union, get_args, get_origin
6
+
7
+ import yaml # type: ignore
8
+ from pydantic import BaseModel
9
+ from pydantic.fields import FieldInfo
10
+
11
+ from hypern.auth.authorization import Authorization
12
+ from hypern.datastructures import HTTPMethod
13
+ from hypern.hypern import FunctionInfo, Request, Router
14
+ from hypern.hypern import Route as InternalRoute
15
+
16
+ from .dispatcher import dispatch
17
+
18
+
19
+ def get_field_type(field):
20
+ return field.outer_type_
21
+
22
+
23
+ def pydantic_to_swagger(model: type[BaseModel] | dict):
24
+ if isinstance(model, dict):
25
+ # Handle the case when a dict is passed instead of a Pydantic model
26
+ schema = {}
27
+ for name, field_type in model.items():
28
+ schema[name] = _process_field(name, field_type)
29
+ return schema
30
+
31
+ schema = {
32
+ model.__name__: {
33
+ "type": "object",
34
+ "properties": {},
35
+ }
36
+ }
37
+
38
+ for name, field in model.model_fields.items():
39
+ schema[model.__name__]["properties"][name] = _process_field(name, field)
40
+
41
+ return schema
42
+
43
+
44
+ class SchemaProcessor:
45
+ @staticmethod
46
+ def process_union(args: tuple) -> Dict[str, Any]:
47
+ """Process Union types"""
48
+ if type(None) in args:
49
+ inner_type = next(arg for arg in args if arg is not type(None))
50
+ schema = SchemaProcessor._process_field("", inner_type)
51
+ schema["nullable"] = True
52
+ return schema
53
+ return {"oneOf": [SchemaProcessor._process_field("", arg) for arg in args]}
54
+
55
+ @staticmethod
56
+ def process_enum(annotation: Type[Enum]) -> Dict[str, Any]:
57
+ """Process Enum types"""
58
+ return {"type": "string", "enum": [e.value for e in annotation.__members__.values()]}
59
+
60
+ @staticmethod
61
+ def process_primitive(annotation: type) -> Dict[str, str]:
62
+ """Process primitive types"""
63
+ type_mapping = {int: "integer", float: "number", str: "string", bool: "boolean"}
64
+ return {"type": type_mapping.get(annotation, "object")}
65
+
66
+ @staticmethod
67
+ def process_list(annotation: type) -> Dict[str, Any]:
68
+ """Process list types"""
69
+ schema = {"type": "array"}
70
+
71
+ args = get_args(annotation)
72
+ if args:
73
+ item_type = args[0]
74
+ schema["items"] = SchemaProcessor._process_field("item", item_type)
75
+ else:
76
+ schema["items"] = {}
77
+ return schema
78
+
79
+ @staticmethod
80
+ def process_dict(annotation: type) -> Dict[str, Any]:
81
+ """Process dict types"""
82
+ schema = {"type": "object"}
83
+
84
+ args = get_args(annotation)
85
+ if args:
86
+ key_type, value_type = args
87
+ if key_type == str: # noqa: E721
88
+ schema["additionalProperties"] = SchemaProcessor._process_field("value", value_type)
89
+ return schema
90
+
91
+ @classmethod
92
+ def _process_field(cls, name: str, field: Any) -> Dict[str, Any]:
93
+ """Process a single field"""
94
+ if isinstance(field, FieldInfo):
95
+ annotation = field.annotation
96
+ else:
97
+ annotation = field
98
+
99
+ # Process Union types
100
+ origin = get_origin(annotation)
101
+ if origin is Union:
102
+ return cls.process_union(get_args(annotation))
103
+
104
+ # Process Enum types
105
+ if isinstance(annotation, type) and issubclass(annotation, Enum):
106
+ return cls.process_enum(annotation)
107
+
108
+ # Process primitive types
109
+ if annotation in {int, float, str, bool}:
110
+ return cls.process_primitive(annotation)
111
+
112
+ # Process list types
113
+ if annotation == list or origin is list: # noqa: E721
114
+ return cls.process_list(annotation)
115
+
116
+ # Process dict types
117
+ if annotation == dict or origin is dict: # noqa: E721
118
+ return cls.process_dict(annotation)
119
+
120
+ # Process Pydantic models
121
+ if isinstance(annotation, type) and issubclass(annotation, BaseModel):
122
+ return pydantic_to_swagger(annotation)
123
+
124
+ # Fallback for complex types
125
+ return {"type": "object"}
126
+
127
+
128
+ def _process_field(name: str, field: Any) -> Dict[str, Any]:
129
+ """
130
+ Process a field and return its schema representation
131
+
132
+ Args:
133
+ name: Field name
134
+ field: Field type or FieldInfo object
135
+
136
+ Returns:
137
+ Dictionary representing the JSON schema for the field
138
+ """
139
+ return SchemaProcessor._process_field(name, field)
140
+
141
+
142
+ class Route:
143
+ def __init__(
144
+ self,
145
+ path: str,
146
+ endpoint: Callable[..., Any] | None = None,
147
+ *,
148
+ name: str | None = None,
149
+ tags: List[str] | None = None,
150
+ ) -> None:
151
+ self.path = path
152
+ self.endpoint = endpoint
153
+ self.tags = tags or ["Default"]
154
+ self.name = name
155
+
156
+ self.http_methods = {
157
+ "GET": HTTPMethod.GET,
158
+ "POST": HTTPMethod.POST,
159
+ "PUT": HTTPMethod.PUT,
160
+ "DELETE": HTTPMethod.DELETE,
161
+ "PATCH": HTTPMethod.PATCH,
162
+ "HEAD": HTTPMethod.HEAD,
163
+ "OPTIONS": HTTPMethod.OPTIONS,
164
+ }
165
+ self.functional_handlers = []
166
+
167
+ def _process_authorization(self, item: type, docs: Dict) -> None:
168
+ if isinstance(item, type) and issubclass(item, Authorization):
169
+ auth_obj = item()
170
+ docs["security"] = [{auth_obj.name: []}]
171
+
172
+ def _process_model_params(self, key: str, item: type, docs: Dict) -> None:
173
+ if not (isinstance(item, type) and issubclass(item, BaseModel)):
174
+ return
175
+
176
+ if key == "form_data":
177
+ docs["requestBody"] = {"content": {"application/json": {"schema": pydantic_to_swagger(item).get(item.__name__)}}}
178
+ elif key == "query_params":
179
+ docs["parameters"] = [{"name": param, "in": "query", "schema": _process_field(param, field)} for param, field in item.model_fields.items()]
180
+ elif key == "path_params":
181
+ path_params = [
182
+ {"name": param, "in": "path", "required": True, "schema": _process_field(param, field)} for param, field in item.model_fields.items()
183
+ ]
184
+ docs.setdefault("parameters", []).extend(path_params)
185
+
186
+ def _process_response(self, response_type: type, docs: Dict) -> None:
187
+ if isinstance(response_type, type) and issubclass(response_type, BaseModel):
188
+ docs["responses"] = {
189
+ "200": {
190
+ "description": "Successful response",
191
+ "content": {"application/json": {"schema": response_type.model_json_schema()}},
192
+ }
193
+ }
194
+
195
+ def swagger_generate(self, signature: inspect.Signature, summary: str = "Document API") -> str:
196
+ _inputs = signature.parameters.values()
197
+ _inputs_dict = {_input.name: _input.annotation for _input in _inputs}
198
+ _docs: Dict = {"summary": summary, "tags": self.tags, "responses": [], "name": self.name}
199
+
200
+ for key, item in _inputs_dict.items():
201
+ self._process_authorization(item, _docs)
202
+ self._process_model_params(key, item, _docs)
203
+
204
+ self._process_response(signature.return_annotation, _docs)
205
+ return yaml.dump(_docs)
206
+
207
+ def _combine_path(self, path1: str, path2: str) -> str:
208
+ if path1.endswith("/") and path2.startswith("/"):
209
+ return path1 + path2[1:]
210
+ if not path1.endswith("/") and not path2.startswith("/"):
211
+ return path1 + "/" + path2
212
+ return path1 + path2
213
+
214
+ def make_internal_route(self, path, handler, method) -> InternalRoute:
215
+ is_async = asyncio.iscoroutinefunction(handler)
216
+ func_info = FunctionInfo(handler=handler, is_async=is_async)
217
+ return InternalRoute(path=path, function=func_info, method=method)
218
+
219
+ def __call__(self, app, *args: Any, **kwds: Any) -> Any:
220
+ router = Router(self.path)
221
+
222
+ # Validate handlers
223
+ if not self.endpoint and not self.functional_handlers:
224
+ raise ValueError(f"No handler found for route: {self.path}")
225
+
226
+ # Handle functional routes
227
+ for h in self.functional_handlers:
228
+ router.add_route(route=self.make_internal_route(path=h["path"], handler=h["func"], method=h["method"].upper()))
229
+ if not self.endpoint:
230
+ return router
231
+
232
+ # Handle class-based routes
233
+ for name, func in self.endpoint.__dict__.items():
234
+ if name.upper() in self.http_methods:
235
+ sig = inspect.signature(func)
236
+ doc = self.swagger_generate(sig, func.__doc__)
237
+ self.endpoint.dispatch.__doc__ = doc
238
+ endpoint_obj = self.endpoint()
239
+ router.add_route(route=self.make_internal_route(path="/", handler=endpoint_obj.dispatch, method=name.upper()))
240
+ del endpoint_obj # free up memory
241
+ return router
242
+
243
+ def add_route(
244
+ self,
245
+ path: str,
246
+ method: str,
247
+ ) -> Callable:
248
+ def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
249
+ async def functional_wrapper(request: Request, inject: Dict[str, Any]) -> Any:
250
+ return await dispatch(func, request, inject)
251
+
252
+ sig = inspect.signature(func)
253
+ functional_wrapper.__doc__ = self.swagger_generate(sig, func.__doc__)
254
+
255
+ self.functional_handlers.append(
256
+ {
257
+ "path": path,
258
+ "method": method,
259
+ "func": functional_wrapper,
260
+ }
261
+ )
262
+
263
+ return decorator
264
+
265
+ def get(self, path: str) -> Callable:
266
+ return self.add_route(path, "GET")
267
+
268
+ def post(self, path: str) -> Callable:
269
+ return self.add_route(path, "POST")
270
+
271
+ def put(self, path: str) -> Callable:
272
+ return self.add_route(path, "PUT")
273
+
274
+ def delete(self, path: str) -> Callable:
275
+ return self.add_route(path, "DELETE")
276
+
277
+ def patch(self, path: str) -> Callable:
278
+ return self.add_route(path, "PATCH")
279
+
280
+ def head(self, path: str) -> Callable:
281
+ return self.add_route(path, "HEAD")
282
+
283
+ def options(self, path: str) -> Callable:
284
+ return self.add_route(path, "OPTIONS")
hypern/scheduler.py ADDED
@@ -0,0 +1,5 @@
1
+ from .hypern import Scheduler
2
+
3
+ __all__ = [
4
+ "Scheduler",
5
+ ]
hypern/security.py ADDED
@@ -0,0 +1,44 @@
1
+ # -*- coding: utf-8 -*-
2
+ from abc import ABC, abstractmethod
3
+ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
4
+ from cryptography.hazmat.backends import default_backend
5
+ from base64 import b64encode, b64decode
6
+
7
+ import typing
8
+
9
+
10
+ class EDEngine(ABC):
11
+ @abstractmethod
12
+ def encrypt(self, data: str) -> str:
13
+ raise NotImplementedError("Method not implemented")
14
+
15
+ @abstractmethod
16
+ def decrypt(self, data: str) -> str:
17
+ raise NotImplementedError("Method not implemented")
18
+
19
+
20
+ class AESEngine(EDEngine):
21
+ def __init__(self, secret_key: bytes, iv: bytes, padding_class: typing.Type) -> None:
22
+ super().__init__()
23
+ self.secret_key = secret_key
24
+ self.iv = iv
25
+ self.padding = padding_class(128)
26
+
27
+ def encrypt(self, data: str) -> bytes:
28
+ bytes_data = data.encode("utf-8")
29
+ encryptor = Cipher(algorithms.AES(self.secret_key), modes.GCM(self.iv), backend=default_backend()).encryptor()
30
+ padder = self.padding.padder()
31
+ padded_data = padder.update(bytes_data) + padder.finalize()
32
+ enctyped_data = encryptor.update(padded_data) + encryptor.finalize()
33
+ tag = encryptor.tag
34
+ return b64encode(tag + enctyped_data)
35
+
36
+ def decrypt(self, data: bytes) -> str:
37
+ data = b64decode(data)
38
+ tag = data[:16]
39
+ encrypted_data = data[16:]
40
+ decryptor = Cipher(algorithms.AES(self.secret_key), modes.GCM(self.iv, tag), backend=default_backend()).decryptor()
41
+ unpadder = self.padding.unpadder()
42
+ decrypted_data = decryptor.update(encrypted_data) + decryptor.finalize()
43
+ unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()
44
+ return unpadded_data.decode("utf-8")
hypern/worker.py ADDED
@@ -0,0 +1,30 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import Any
3
+ from celery import Celery
4
+ from asgiref.sync import async_to_sync
5
+
6
+
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
12
+
13
+ def __init__(self, *args, **kwargs) -> None:
14
+ super().__init__(*args, **kwargs)
15
+ self.patch_task()
16
+
17
+ def patch_task(self) -> None:
18
+ TaskBase = self.Task
19
+
20
+ class ContextTask(TaskBase): # type: ignore
21
+ abstract = True
22
+
23
+ def _run(self, *args, **kwargs):
24
+ result = async_to_sync(TaskBase.__call__)(self, *args, **kwargs)
25
+ return result
26
+
27
+ def __call__(self, *args, **kwargs):
28
+ return self._run(*args, **kwargs)
29
+
30
+ self.Task = ContextTask
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.3
2
+ Name: hypern
3
+ Version: 0.2.0
4
+ Classifier: Programming Language :: Rust
5
+ Classifier: Programming Language :: Python :: Implementation :: CPython
6
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
7
+ Requires-Dist: sqlalchemy[asyncio] ==2.0.31
8
+ Requires-Dist: pydantic[email] ==2.8.2
9
+ Requires-Dist: passlib ==1.7.4
10
+ Requires-Dist: pyjwt ==2.8.0
11
+ Requires-Dist: pydash ==8.0.3
12
+ Requires-Dist: sentry-sdk ==2.11.0
13
+ Requires-Dist: pydantic-settings ==2.3.4
14
+ Requires-Dist: celery ==5.4.0
15
+ Requires-Dist: asgiref ==3.8.1
16
+ Requires-Dist: psycopg ==3.2.3
17
+ Requires-Dist: pyyaml ==6.0.2
18
+ Requires-Dist: mongoengine ==0.29.1
19
+ Requires-Dist: argon2-cffi ==23.1.0
20
+ Requires-Dist: bcrypt ==4.2.0
21
+ Requires-Dist: orjson ==3.10.11
22
+ Requires-Dist: multiprocess ==0.70.17
23
+ Requires-Dist: uvloop ==0.21.0 ; sys_platform != 'win32' and platform_python_implementation == 'CPython' and platform_machine != 'armv7l'
24
+ Requires-Dist: cryptography ==43.0.3
25
+ License-File: LICENSE
26
+ Summary: A Fast Async Python backend with a Rust runtime.
27
+ Author-email: Martin Dang <vannghiem848@gmail.com>
28
+ Requires-Python: >=3.10
29
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
30
+
31
+
32
+ # Hypern
33
+
34
+ Hypern: A Versatile Python and Rust Framework
35
+
36
+ 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.
37
+
38
+ 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.
39
+
40
+
41
+ ### 🏁 Get started
42
+
43
+ ### ⚙️ To Develop Locally
44
+
45
+ - Setup a virtual environment:
46
+ ```
47
+ python3 -m venv venv
48
+ source venv/bin/activate
49
+ ```
50
+ - Install required packages
51
+
52
+ ```
53
+ pip install pre-commit poetry maturin
54
+ ```
55
+ - Install development dependencies
56
+ ```
57
+ poetry install --with dev --with test
58
+ ```
59
+ - Install pre-commit git hooks
60
+ ```
61
+ pre-commit install
62
+ ```
63
+ - Build & install Rust package
64
+ ```
65
+ maturin develop
66
+ ```
67
+
68
+ ## 🤔 Usage
69
+
70
+ ### 🏃 Run your code
71
+
72
+ You will then have access to a server on the `localhost:5005`,
73
+ ```python
74
+ # main.py
75
+ from hypern import Hypern
76
+ from hypern.routing import Route, HTTPEndpoint
77
+
78
+ class MyEndpoint(HTTPEndpoint):
79
+
80
+ async def get(self, request):
81
+ return {"data": "Hello World"}
82
+
83
+ routing = [
84
+ Route("/hello", MyEndpoint)
85
+ ]
86
+
87
+ app = Hypern(routing)
88
+
89
+ if __name__ == "__main__":
90
+ app.start(host='localhost', port=5005)
91
+ ```
92
+
93
+ ```
94
+ $ python3 main.py
95
+ ```
96
+ You can open swagger UI at path `/docs`
97
+
98
+
99
+ ## 💡 Features
100
+
101
+ ### ⚡ High Performance
102
+ - Rust-powered core with Python flexibility
103
+ - Multi-process architecture for optimal CPU utilization
104
+ - Async/await support for non-blocking operations
105
+ - Built on top of production-ready Axum web framework
106
+
107
+ ### 🛠 Development Experience
108
+ - Type hints and IDE support
109
+ - Built-in Swagger/OpenAPI documentation
110
+ - Hot reload during development
111
+ - Comprehensive error handling and logging
112
+
113
+ ### 🔌 Integration & Extensions
114
+ - Easy dependency injection
115
+ - Middleware support (before/after request hooks)
116
+ - WebSocket support (Comming soon)
117
+ - Background task scheduling
118
+ - File upload handling
119
+
120
+ ### 🔒 Security
121
+ - Built-in authentication/authorization (Comming soon)
122
+ - CORS configuration
123
+ - Rate limiting
124
+ - Request validation
125
+
126
+
127
+
@@ -0,0 +1,66 @@
1
+ hypern-0.2.0.dist-info/METADATA,sha256=0dwMDftVhFSbT6b6f41rTkO-2VTmxvLyUNBgblWBXc0,3722
2
+ hypern-0.2.0.dist-info/WHEEL,sha256=VGMDu5jnp_XpyXOq-L1-cbr3x9_PzCpsKi1udyLAeSI,95
3
+ hypern-0.2.0.dist-info/licenses/LICENSE,sha256=qbYKAIJLS6jYg5hYncKE7OtWmqOtpVTvKNkwOa0Iwwg,1328
4
+ hypern/application.py,sha256=W_eltIwV2WcTaAQonxstMebGGgvMkIujowNvLYTX0nc,15183
5
+ hypern/auth/authorization.py,sha256=-NprZsI0np889ZN1fp-MiVFrPoMNzUtatBJaCMtkllM,32
6
+ hypern/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ hypern/background.py,sha256=xy38nQZSJsYFRXr3-uFJeNW9E1GiXXOC7lSe5pC0eyE,124
8
+ hypern/caching/base/backend.py,sha256=nHl5_nH95LSYWBS9RmOvOzsps_p19HcnQsb4h4W7cP8,68
9
+ hypern/caching/base/key_maker.py,sha256=-W1r3ywfQ-K6saniiK3aTMaW3iy3aXai2pvQqM8f74I,232
10
+ hypern/caching/base/__init__.py,sha256=M-B56YGTkSwCkKW_I6GcV1LSIFfxZ6KuXowB1_aJQpQ,155
11
+ hypern/caching/cache_manager.py,sha256=TF6UosJ54950JgsrPVZUS3MH2R8zafAu5PTryNJ0sRs,2053
12
+ hypern/caching/cache_tag.py,sha256=bZcjivMNETAzAHAIobuLN0S2wHgPgLLL8Gg4uso_qbk,267
13
+ hypern/caching/custom_key_maker.py,sha256=88RIIJjpQYFnv857wOlCKgWWBbK_S23zNHsIrJz_4PY,394
14
+ hypern/caching/redis_backend.py,sha256=IgQToCnHYGpKEErq2CNZkR5woo01z456Ef3C-XRPRV8,70
15
+ hypern/caching/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ hypern/cli/commands.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ hypern/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ hypern/config.py,sha256=frZSdXBI8GaM0tkw1Rs-XydZ9-XjGLRPj6DL4d51-y4,4930
19
+ hypern/datastructures.py,sha256=zZGGSP07kPc9KJDf11hX5uYhAyRE-Ck5wezW5QtOVXw,897
20
+ hypern/db/nosql/addons/color.py,sha256=bAGRuARCAYwZ1nO4jK0lzGYKmavTDtS34BxvrsetF74,446
21
+ hypern/db/nosql/addons/daterange.py,sha256=hGUSoVFqatNY-TB5wjZTq62iZpHpdsyRJIsHxsj1uDs,1192
22
+ hypern/db/nosql/addons/encrypted.py,sha256=B0M-uDqvZHVmIZcFdwcuC2MGsv0pGJFQ1lrOg8klR9U,1741
23
+ hypern/db/nosql/addons/password.py,sha256=jfZxvWFm6nV9EWpXq5Mj-jpqnl9QbokZj9WT14n7dKE,5035
24
+ hypern/db/nosql/addons/unicode.py,sha256=LaDpLfdoTcJuASPE-8fqOVD05H_uOx8gOdnyDn5Iu0c,268
25
+ hypern/db/nosql/addons/__init__.py,sha256=WEtPM8sPHilvga7zxwqvINeTkF0hdcfgPcAnHc4MASE,125
26
+ hypern/db/nosql/__init__.py,sha256=MH9YvlbRlbBCrQVNOdfTaK-hINwJxbJLmxwY9Mei7I8,644
27
+ hypern/db/sql/addons/color.py,sha256=Jj8q4lkT0ukKyjVyZjBx7fokGrX7AIJpKOGzwsBpfnU,480
28
+ hypern/db/sql/addons/daterange.py,sha256=qEfQN9c4jQdzeXNYKgQ4VIJ3Qc0HXHWRouzNF1se-RA,853
29
+ hypern/db/sql/addons/datetime.py,sha256=Bp2jMja2lb_b2WnzRnfbjXXTHBgBTyph1ECsItIwvvg,643
30
+ hypern/db/sql/addons/encrypted.py,sha256=pXsg4ImPpK-VkLDruKrv2gUtdp2kajQX7xDbRDxa-SI,1930
31
+ hypern/db/sql/addons/password.py,sha256=9pypORygWaINj3oiAOBRIOGgpuA0lcjPq4rh1pqJxq0,5789
32
+ hypern/db/sql/addons/ts_vector.py,sha256=bQyXYvQ1bAfFpYcS-sFwM7fU6L1lg9_7nGDRGp0CoUo,1361
33
+ hypern/db/sql/addons/unicode.py,sha256=rbqyHlsPUqRDWIjYQSRFF3zkHncnwxo0sdlzqUlcrUw,411
34
+ hypern/db/sql/addons/__init__.py,sha256=mLN_AvwgpSAbrWZvVHZuO7ff0gk1T_JbVwd5wug5nlw,359
35
+ hypern/db/sql/repository.py,sha256=ue6vWOTrnEPyDevlyh3v-7PU6GSfrZHYKrbXVuoS8UA,9516
36
+ hypern/db/sql/__init__.py,sha256=1UoWQi2CIcUAbQj3FadR-8V0o_b286nI2wYvOsvtbFc,6478
37
+ hypern/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ hypern/enum.py,sha256=KcVziJj7vWvyie0r2rtxhrLzdtkZAsf0DY58oJ4tQl4,360
39
+ hypern/exceptions.py,sha256=nHTkF0YdNBMKfSiNtjRMHMNKoY3RMUm68YYluuW15us,2428
40
+ hypern/hypern.pyi,sha256=PsoQ-UV3q_xJs0BsW3In8gVWJjmqrffCY4xTC0hSDfk,6915
41
+ hypern/i18n/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
+ hypern/logging/logger.py,sha256=ebLT-DX5OOt1UqP2OajFTm4I9tL6RSBN_GbJEKe4UI4,3256
43
+ hypern/logging/__init__.py,sha256=6eVriyncsJ4J73fGYhoejv9MX7aGTkRezTpPxO4DX1I,52
44
+ hypern/middleware/base.py,sha256=mJoz-i-7lqw1eDxZ8Bb0t8sz60lx5TE-OjZT4UR75e4,541
45
+ hypern/middleware/cors.py,sha256=x90DnCOJSfp4ojm1krttn_EdtlqeDazyUzVg66NES4A,1681
46
+ hypern/middleware/i18n.py,sha256=jHzVzjTx1nnjbraZtIVOprrnSaeKMxZB8RuSqRp2I4s,16
47
+ hypern/middleware/limit.py,sha256=S2abCkQb3yGEQlXZ8azGIlpzTmE8eS6gt-c546IC9_c,8148
48
+ hypern/middleware/__init__.py,sha256=lXwR3fdmpVK4Z7QWaLsgf3Sazy5NPPFXIOxIEv1xDC8,273
49
+ hypern/openapi/schemas.py,sha256=YHfMlPUeP5DzDX5ao3YH8p_25Vvyaf616dh6XDCUZRc,1677
50
+ hypern/openapi/swagger.py,sha256=naqUY3rFAEYA1ZLIlmDsMYaol0yIm6TVebdkFa5cMTc,64
51
+ hypern/openapi/__init__.py,sha256=4rEVD8pa0kdSpsy7ZkJ5JY0Z2XF0NGSKDMwYAd7YZpE,141
52
+ hypern/processpool.py,sha256=_vtC9mmEisfDWuSH9prvgaipbHdP5tszPWEwqsxQGo4,3031
53
+ hypern/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
+ hypern/response/response.py,sha256=-dnboAraPic8asf503PxwmDuxhNllUO5h97_DGmbER4,4582
55
+ hypern/response/__init__.py,sha256=_w3u3TDNuYx5ejnnN1unqnTY8NlBgUATQi6wepEB_FQ,226
56
+ hypern/routing/dispatcher.py,sha256=i2wLAAW1ZXgpi5K2heGXhTODnP1WdQzaR5WlUjs1o9c,2368
57
+ hypern/routing/endpoint.py,sha256=RKVhvqOEGL9IKBXQ3KJgPi9bgJj9gfWC5BdZc5U_atc,1026
58
+ hypern/routing/parser.py,sha256=R-4lcN9Ha1iMeAjlqDe8HwkjjMVG-c-ubQLZyWKXj6M,3554
59
+ hypern/routing/route.py,sha256=tW6orvfWz9O1XDWD295IuovALa4rBd4zwTsRwWaMZJk,10374
60
+ hypern/routing/__init__.py,sha256=7rw7EAxougCXtmkgJjrmLP3N5RXctIpI_3JmG9FcKVU,101
61
+ hypern/scheduler.py,sha256=-k3tW2AGCnHYSthKXk-FOs_SCtWp3yIxQzwzUJMJsbo,67
62
+ hypern/security.py,sha256=3E86Yp_eOSVa1emUvBrDgoF0Sn6eNX0CfLnt87w5CPI,1773
63
+ hypern/worker.py,sha256=WQrhY_awR6zjMwY4Q7izXi4E4fFrDqt7jIblUW8Bzcg,924
64
+ hypern/__init__.py,sha256=9Ww_aUQ0vJls0tOq7Yw1_TVOCRsa5bHJ-RtnSeComwk,119
65
+ hypern/hypern.cp312-win_amd64.pyd,sha256=tW0O9hPiYkkNB-ev14wiefs0CIvcZHVnmUbkOEUywYo,6056960
66
+ hypern-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.7.4)
3
+ Root-Is-Purelib: false
4
+ Tag: cp312-none-win_amd64
@@ -0,0 +1,24 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2024, Dang Van Nghiem
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.