hypern 0.3.6__cp310-cp310-win32.whl → 0.3.8__cp310-cp310-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.
@@ -0,0 +1,263 @@
1
+ # import os
2
+ # import sys
3
+ # import inspect
4
+ # import importlib
5
+ # import hashlib
6
+ # import argparse
7
+ # from datetime import datetime
8
+ # from typing import List, Type, Dict
9
+
10
+ # from hypern.config import get_config
11
+ # from .model import Model
12
+
13
+
14
+ # class MigrationManager:
15
+ # """Manages database migrations and schema changes."""
16
+
17
+ # def __init__(self, migrations_dir: str = "migrations"):
18
+ # self.migrations_dir = migrations_dir
19
+ # self.config = get_config()
20
+ # self.ensure_migrations_dir()
21
+
22
+ # def ensure_migrations_dir(self):
23
+ # """Ensure migrations directory exists."""
24
+ # if not os.path.exists(self.migrations_dir):
25
+ # os.makedirs(self.migrations_dir)
26
+ # # Create __init__.py to make it a package
27
+ # with open(os.path.join(self.migrations_dir, "__init__.py"), "w") as f:
28
+ # pass
29
+
30
+ # def collect_models(self) -> Dict[str, Type[Model]]:
31
+ # """Collect all model classes from the project."""
32
+ # models = {}
33
+ # # Scan all Python files in the project directory
34
+ # for root, _, files in os.walk("."):
35
+ # if "venv" in root or "migrations" in root:
36
+ # continue
37
+ # for file in files:
38
+ # if file.endswith(".py"):
39
+ # module_path = os.path.join(root, file)
40
+ # module_name = module_path.replace("/", ".").replace("\\", ".")[2:-3]
41
+ # try:
42
+ # module = importlib.import_module(module_name)
43
+ # for name, obj in inspect.getmembers(module):
44
+ # if inspect.isclass(obj) and issubclass(obj, Model) and obj != Model:
45
+ # models[obj.__name__] = obj
46
+ # except (ImportError, AttributeError):
47
+ # continue
48
+ # return models
49
+
50
+ # def generate_migration(self, name: str):
51
+ # """Generate a new migration file."""
52
+ # timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
53
+ # migration_id = f"{timestamp}_{name}"
54
+ # filename = f"{migration_id}.py"
55
+ # filepath = os.path.join(self.migrations_dir, filename)
56
+
57
+ # models = self.collect_models()
58
+
59
+ # # Generate migration content
60
+ # content = self._generate_migration_content(migration_id, models)
61
+
62
+ # with open(filepath, "w") as f:
63
+ # f.write(content)
64
+
65
+ # print(f"Created migration: {filename}")
66
+
67
+ # def _generate_migration_content(self, migration_id: str, models: Dict[str, Type[Model]]) -> str:
68
+ # """Generate the content for a migration file."""
69
+ # content = [
70
+ # "from typing import List",
71
+ # "from hypern.migrations import Migration\n",
72
+ # ]
73
+
74
+ # # Import all models
75
+ # for model_name in models.keys():
76
+ # content.append(f"from app.models import {model_name}")
77
+
78
+ # content.extend([
79
+ # "\n\nclass " + migration_id + "(Migration):",
80
+ # " \"\"\"",
81
+ # " Auto-generated migration.",
82
+ # " \"\"\"",
83
+ # "",
84
+ # " def up(self) -> List[str]:",
85
+ # " return [",
86
+ # ])
87
+
88
+ # # Add CREATE TABLE statements
89
+ # for model in models.values():
90
+ # content.append(f" '''{model.create_table_sql()}''',")
91
+
92
+ # content.extend([
93
+ # " ]",
94
+ # "",
95
+ # " def down(self) -> List[str]:",
96
+ # " return [",
97
+ # ])
98
+
99
+ # # Add DROP TABLE statements in reverse order
100
+ # for model_name in reversed(list(models.keys())):
101
+ # content.append(f" '''DROP TABLE IF EXISTS {model_name.lower()} CASCADE;''',")
102
+
103
+ # content.extend([
104
+ # " ]",
105
+ # ""
106
+ # ])
107
+
108
+ # return "\n".join(content)
109
+
110
+ # def get_applied_migrations(self) -> List[str]:
111
+ # """Get list of applied migrations from database."""
112
+ # session = get_session_database()
113
+ # try:
114
+ # result = session.execute("""
115
+ # SELECT migration_id FROM migrations
116
+ # ORDER BY applied_at;
117
+ # """)
118
+ # return [row[0] for row in result]
119
+ # except Exception:
120
+ # # Migrations table doesn't exist yet
121
+ # return []
122
+
123
+ # def apply_migrations(self, target: str = None):
124
+ # """Apply pending migrations up to target (or all if target is None)."""
125
+ # # Create migrations table if it doesn't exist
126
+ # self._ensure_migrations_table()
127
+
128
+ # # Get applied and available migrations
129
+ # applied = set(self.get_applied_migrations())
130
+ # available = self._get_available_migrations()
131
+
132
+ # # Determine which migrations to apply
133
+ # to_apply = []
134
+ # for migration_id, module in available.items():
135
+ # if migration_id not in applied:
136
+ # to_apply.append((migration_id, module))
137
+
138
+ # if target and migration_id == target:
139
+ # break
140
+
141
+ # # Apply migrations
142
+ # session = get_session_database()
143
+ # for migration_id, module in to_apply:
144
+ # print(f"Applying migration: {migration_id}")
145
+
146
+ # migration = module()
147
+ # for sql in migration.up():
148
+ # session.execute(sql)
149
+
150
+ # # Record migration
151
+ # session.execute(
152
+ # "INSERT INTO migrations (migration_id, applied_at) VALUES (%s, NOW())",
153
+ # (migration_id,)
154
+ # )
155
+ # session.commit()
156
+
157
+ # def rollback_migrations(self, target: str = None):
158
+ # """Rollback migrations up to target (or last one if target is None)."""
159
+ # applied = self.get_applied_migrations()
160
+ # available = self._get_available_migrations()
161
+
162
+ # # Determine which migrations to rollback
163
+ # to_rollback = []
164
+ # rollback_all = target == "zero"
165
+
166
+ # for migration_id in reversed(applied):
167
+ # to_rollback.append((migration_id, available[migration_id]))
168
+
169
+ # if not rollback_all and (target == migration_id or target is None):
170
+ # break
171
+
172
+ # # Rollback migrations
173
+ # session = get_session_database()
174
+ # for migration_id, module in to_rollback:
175
+ # print(f"Rolling back migration: {migration_id}")
176
+
177
+ # migration = module()
178
+ # for sql in migration.down():
179
+ # session.execute(sql)
180
+
181
+ # # Remove migration record
182
+ # session.execute(
183
+ # "DELETE FROM migrations WHERE migration_id = %s",
184
+ # (migration_id,)
185
+ # )
186
+ # session.commit()
187
+
188
+ # def _ensure_migrations_table(self):
189
+ # """Ensure migrations table exists."""
190
+ # session = get_session_database()
191
+ # session.execute("""
192
+ # CREATE TABLE IF NOT EXISTS migrations (
193
+ # migration_id VARCHAR(255) PRIMARY KEY,
194
+ # applied_at TIMESTAMP NOT NULL
195
+ # );
196
+ # """)
197
+ # session.commit()
198
+
199
+ # def _get_available_migrations(self) -> Dict[str, Type['Migration']]:
200
+ # """Get available migrations from migrations directory."""
201
+ # migrations = {}
202
+
203
+ # for filename in sorted(os.listdir(self.migrations_dir)):
204
+ # if filename.endswith(".py") and not filename.startswith("__"):
205
+ # migration_id = filename[:-3]
206
+ # module_name = f"{self.migrations_dir}.{migration_id}"
207
+ # module = importlib.import_module(module_name)
208
+
209
+ # for name, obj in inspect.getmembers(module):
210
+ # if (inspect.isclass(obj) and
211
+ # name == migration_id and
212
+ # hasattr(obj, 'up') and
213
+ # hasattr(obj, 'down')):
214
+ # migrations[migration_id] = obj
215
+
216
+ # return migrations
217
+
218
+
219
+ # class Migration:
220
+ # """Base class for database migrations."""
221
+
222
+ # def up(self) -> List[str]:
223
+ # """Return list of SQL statements to apply migration."""
224
+ # raise NotImplementedError
225
+
226
+ # def down(self) -> List[str]:
227
+ # """Return list of SQL statements to rollback migration."""
228
+ # raise NotImplementedError
229
+
230
+
231
+ # def main():
232
+ # parser = argparse.ArgumentParser(description="Database migration tool")
233
+
234
+ # subparsers = parser.add_subparsers(dest="command", help="Commands")
235
+
236
+ # # makemigrations command
237
+ # make_parser = subparsers.add_parser("makemigrations", help="Generate new migration")
238
+ # make_parser.add_argument("name", help="Migration name")
239
+
240
+ # # migrate command
241
+ # migrate_parser = subparsers.add_parser("migrate", help="Apply migrations")
242
+ # migrate_parser.add_argument("--target", help="Target migration (default: latest)")
243
+
244
+ # # rollback command
245
+ # rollback_parser = subparsers.add_parser("rollback", help="Rollback migrations")
246
+ # rollback_parser.add_argument("--target", help="Target migration (default: last applied)")
247
+
248
+ # args = parser.parse_args()
249
+
250
+ # manager = MigrationManager()
251
+
252
+ # if args.command == "makemigrations":
253
+ # manager.generate_migration(args.name)
254
+ # elif args.command == "migrate":
255
+ # manager.apply_migrations(args.target)
256
+ # elif args.command == "rollback":
257
+ # manager.rollback_migrations(args.target)
258
+ # else:
259
+ # parser.print_help()
260
+
261
+
262
+ # if __name__ == "__main__":
263
+ # main()
@@ -5,7 +5,7 @@ from hypern.config import context_store
5
5
  from hypern.exceptions import OutOfScopeApplicationException
6
6
  from hypern.hypern import get_session_database
7
7
 
8
- from .field import Field, ForeignKey
8
+ from .field import Field, ForeignKeyField
9
9
  from .query import QuerySet
10
10
 
11
11
 
@@ -77,7 +77,7 @@ class Model(metaclass=MetaModel):
77
77
  fields_sql.append(cls._get_field_sql(name, field))
78
78
  if field.index:
79
79
  indexes_sql.append(cls._get_index_sql(name))
80
- if isinstance(field, ForeignKey):
80
+ if isinstance(field, ForeignKeyField):
81
81
  foreign_keys.append(cls._get_foreign_key_sql(name, field))
82
82
 
83
83
  fields_sql.extend(foreign_keys)
@@ -109,7 +109,8 @@ class Model(metaclass=MetaModel):
109
109
 
110
110
  @classmethod
111
111
  def _get_foreign_key_sql(cls, name, field) -> str:
112
- return f"FOREIGN KEY ({name}) REFERENCES {field.to_model}({field.related_field}) ON DELETE {field.on_delete} ON UPDATE {field.on_update}"
112
+ target_table = field.to_model.__name__.lower() if not isinstance(field.to_model, str) else field.to_model.lower()
113
+ return f"FOREIGN KEY ({name}) REFERENCES {target_table}({field.related_field}) ON DELETE {field.on_delete} ON UPDATE {field.on_update}"
113
114
 
114
115
  def save(self):
115
116
  query_object = QuerySet(self)
@@ -1,6 +1,6 @@
1
1
  from enum import Enum
2
2
  from typing import Any, Dict, List, Tuple, Union
3
- from hypern.database.sql.field import ForeignKey
3
+ from hypern.database.sql.field import ForeignKeyField
4
4
 
5
5
 
6
6
  class JoinType(Enum):
@@ -553,7 +553,7 @@ class QuerySet:
553
553
  """
554
554
  qs = self.clone()
555
555
  for field in fields:
556
- if field in qs.model._fields and isinstance(qs.model._fields[field], ForeignKey):
556
+ if field in qs.model._fields and isinstance(qs.model._fields[field], ForeignKeyField):
557
557
  qs._selected_related.add(field)
558
558
  return qs
559
559
 
hypern/datastructures.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from typing import Optional
2
2
  from enum import Enum
3
- from pydantic import BaseModel, AnyUrl, EmailStr
3
+ from pydantic import BaseModel, AnyUrl
4
4
 
5
5
 
6
6
  class BaseModelWithConfig(BaseModel):
@@ -10,7 +10,7 @@ class BaseModelWithConfig(BaseModel):
10
10
  class Contact(BaseModelWithConfig):
11
11
  name: Optional[str] = None
12
12
  url: Optional[AnyUrl] = None
13
- email: Optional[EmailStr] = None
13
+ email: Optional[str] = None
14
14
 
15
15
 
16
16
  class License(BaseModelWithConfig):
Binary file
hypern/hypern.pyi CHANGED
@@ -7,7 +7,7 @@ from enum import Enum
7
7
  @dataclass
8
8
  class BaseSchemaGenerator:
9
9
  remove_converter: Callable[[str], str]
10
- parse_docstring: Callable[[Callable[..., Any]], str]
10
+ parse_docstring: Callable[..., str]
11
11
 
12
12
  @dataclass
13
13
  class SwaggerUI:
@@ -181,21 +181,19 @@ class Server:
181
181
  def set_router(self, router: Router) -> None: ...
182
182
  def set_websocket_router(self, websocket_router: WebsocketRouter) -> None: ...
183
183
  def start(self, socket: SocketHeld, worker: int, max_blocking_threads: int) -> None: ...
184
- def inject(self, key: str, value: Any) -> None: ...
185
- def set_injected(self, injected: Dict[str, Any]) -> None: ...
186
184
  def set_before_hooks(self, hooks: List[FunctionInfo]) -> None: ...
187
185
  def set_after_hooks(self, hooks: List[FunctionInfo]) -> None: ...
188
186
  def set_response_headers(self, headers: Dict[str, str]) -> None: ...
189
187
  def set_startup_handler(self, on_startup: FunctionInfo) -> None: ...
190
188
  def set_shutdown_handler(self, on_shutdown: FunctionInfo) -> None: ...
191
- def set_auto_compression(self, enabled: bool) -> None: ...
192
189
  def set_database_config(self, config: DatabaseConfig) -> None: ...
193
- def set_mem_pool_capacity(self, min_capacity: int, max_capacity: int) -> None: ...
190
+ def set_dependencies(self, dependencies: Dict[str, Any]) -> None: ...
194
191
 
195
192
  class Route:
196
193
  path: str
197
194
  function: FunctionInfo
198
195
  method: str
196
+ doc: str | None = None
199
197
 
200
198
  def matches(self, path: str, method: str) -> str: ...
201
199
  def clone_route(self) -> Route: ...
@@ -277,7 +275,7 @@ class UploadedFile:
277
275
  path: str
278
276
  size: int
279
277
  content: bytes
280
- filename: str
278
+ file_name: str
281
279
 
282
280
  @dataclass
283
281
  class BodyData:
@@ -330,8 +328,5 @@ class DatabaseTransaction:
330
328
  def bulk_change(self, query: str, params: List[List[Any]], batch_size: int) -> int | None: ...
331
329
  def commit(self) -> None: ...
332
330
  def rollback(self) -> None: ...
333
- def __del__(self) -> None: ...
334
- def __enter__(self) -> None: ...
335
- def __exit__(self, _exc_type, _exc_value, _traceback) -> None: ...
336
331
 
337
332
  def get_session_database(context_id: str) -> DatabaseTransaction: ...
hypern/openapi/schemas.py CHANGED
@@ -37,17 +37,15 @@ class SchemaGenerator(BaseSchemaGenerator):
37
37
  def get_schema(self, app) -> dict[str, typing.Any]:
38
38
  schema = dict(self.base_schema)
39
39
  schema.setdefault("paths", {})
40
- endpoints_info = self.get_endpoints(app.router.routes)
41
-
42
- for endpoint in endpoints_info:
43
- parsed = self.parse_docstring(endpoint.func)
40
+ for route in app.router.routes:
41
+ parsed = self.parse_docstring(route.doc)
44
42
 
45
43
  if not parsed:
46
44
  continue
47
45
 
48
- if endpoint.path not in schema["paths"]:
49
- schema["paths"][endpoint.path] = {}
46
+ if route.path not in schema["paths"]:
47
+ schema["paths"][route.path] = {}
50
48
 
51
- schema["paths"][endpoint.path][endpoint.http_method] = orjson.loads(parsed)
49
+ schema["paths"][route.path][route.method.lower()] = orjson.loads(parsed)
52
50
 
53
51
  return schema
hypern/routing/route.py CHANGED
@@ -224,8 +224,8 @@ class Route:
224
224
  raise ValueError(f"No handler found for route: {self.path}")
225
225
 
226
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()))
227
+ for route in self.functional_handlers:
228
+ router.add_route(route=route)
229
229
  if not self.endpoint:
230
230
  return router
231
231
 
@@ -234,9 +234,10 @@ class Route:
234
234
  if name.upper() in self.http_methods:
235
235
  sig = inspect.signature(func)
236
236
  doc = self.swagger_generate(sig, func.__doc__)
237
- self.endpoint.dispatch.__doc__ = doc
238
237
  endpoint_obj = self.endpoint()
239
- router.add_route(route=self.make_internal_route(path="/", handler=endpoint_obj.dispatch, method=name.upper()))
238
+ route = self.make_internal_route(path="/", handler=endpoint_obj.dispatch, method=name.upper())
239
+ route.doc = doc
240
+ router.add_route(route=route)
240
241
  del endpoint_obj # free up memory
241
242
  return router
242
243
 
@@ -250,15 +251,10 @@ class Route:
250
251
  return await dispatch(func, request, inject)
251
252
 
252
253
  sig = inspect.signature(func)
253
- functional_wrapper.__doc__ = self.swagger_generate(sig, func.__doc__)
254
+ route = self.make_internal_route(path=path, handler=functional_wrapper, method=method.upper())
255
+ route.doc = self.swagger_generate(sig, func.__doc__)
254
256
 
255
- self.functional_handlers.append(
256
- {
257
- "path": path,
258
- "method": method,
259
- "func": functional_wrapper,
260
- }
261
- )
257
+ self.functional_handlers.append(route)
262
258
 
263
259
  return decorator
264
260