hypern 0.3.11__cp311-cp311-macosx_11_0_arm64.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 (73) hide show
  1. hypern/__init__.py +24 -0
  2. hypern/application.py +495 -0
  3. hypern/args_parser.py +73 -0
  4. hypern/auth/__init__.py +0 -0
  5. hypern/auth/authorization.py +2 -0
  6. hypern/background.py +4 -0
  7. hypern/caching/__init__.py +6 -0
  8. hypern/caching/backend.py +31 -0
  9. hypern/caching/redis_backend.py +201 -0
  10. hypern/caching/strategies.py +208 -0
  11. hypern/cli/__init__.py +0 -0
  12. hypern/cli/commands.py +0 -0
  13. hypern/config.py +246 -0
  14. hypern/database/__init__.py +0 -0
  15. hypern/database/sqlalchemy/__init__.py +4 -0
  16. hypern/database/sqlalchemy/config.py +66 -0
  17. hypern/database/sqlalchemy/repository.py +290 -0
  18. hypern/database/sqlx/__init__.py +36 -0
  19. hypern/database/sqlx/field.py +246 -0
  20. hypern/database/sqlx/migrate.py +263 -0
  21. hypern/database/sqlx/model.py +117 -0
  22. hypern/database/sqlx/query.py +904 -0
  23. hypern/datastructures.py +40 -0
  24. hypern/enum.py +13 -0
  25. hypern/exceptions/__init__.py +34 -0
  26. hypern/exceptions/base.py +62 -0
  27. hypern/exceptions/common.py +12 -0
  28. hypern/exceptions/errors.py +15 -0
  29. hypern/exceptions/formatters.py +56 -0
  30. hypern/exceptions/http.py +76 -0
  31. hypern/gateway/__init__.py +6 -0
  32. hypern/gateway/aggregator.py +32 -0
  33. hypern/gateway/gateway.py +41 -0
  34. hypern/gateway/proxy.py +60 -0
  35. hypern/gateway/service.py +52 -0
  36. hypern/hypern.cpython-311-darwin.so +0 -0
  37. hypern/hypern.pyi +333 -0
  38. hypern/i18n/__init__.py +0 -0
  39. hypern/logging/__init__.py +3 -0
  40. hypern/logging/logger.py +82 -0
  41. hypern/middleware/__init__.py +17 -0
  42. hypern/middleware/base.py +13 -0
  43. hypern/middleware/cache.py +177 -0
  44. hypern/middleware/compress.py +78 -0
  45. hypern/middleware/cors.py +41 -0
  46. hypern/middleware/i18n.py +1 -0
  47. hypern/middleware/limit.py +177 -0
  48. hypern/middleware/security.py +184 -0
  49. hypern/openapi/__init__.py +5 -0
  50. hypern/openapi/schemas.py +51 -0
  51. hypern/openapi/swagger.py +3 -0
  52. hypern/processpool.py +139 -0
  53. hypern/py.typed +0 -0
  54. hypern/reload.py +46 -0
  55. hypern/response/__init__.py +3 -0
  56. hypern/response/response.py +142 -0
  57. hypern/routing/__init__.py +5 -0
  58. hypern/routing/dispatcher.py +70 -0
  59. hypern/routing/endpoint.py +30 -0
  60. hypern/routing/parser.py +98 -0
  61. hypern/routing/queue.py +175 -0
  62. hypern/routing/route.py +280 -0
  63. hypern/scheduler.py +5 -0
  64. hypern/worker.py +274 -0
  65. hypern/ws/__init__.py +4 -0
  66. hypern/ws/channel.py +80 -0
  67. hypern/ws/heartbeat.py +74 -0
  68. hypern/ws/room.py +76 -0
  69. hypern/ws/route.py +26 -0
  70. hypern-0.3.11.dist-info/METADATA +134 -0
  71. hypern-0.3.11.dist-info/RECORD +73 -0
  72. hypern-0.3.11.dist-info/WHEEL +4 -0
  73. hypern-0.3.11.dist-info/licenses/LICENSE +24 -0
@@ -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()
@@ -0,0 +1,117 @@
1
+ import re
2
+ from datetime import date, datetime
3
+
4
+ from hypern.config import context_store
5
+ from hypern.exceptions import OutOfScopeApplicationException
6
+ from hypern.hypern import get_session_database
7
+
8
+ from .field import Field, ForeignKeyField
9
+ from .query import QuerySet
10
+
11
+
12
+ class MetaModel(type):
13
+ def __new__(mcs, name, bases, attrs):
14
+ # Skip initialization for base Model class
15
+ if name == "Model" and not bases:
16
+ return super().__new__(mcs, name, bases, attrs)
17
+
18
+ fields = {}
19
+ table_name = attrs.get("__tablename__")
20
+
21
+ # If table name not specified, convert CamelCase to snake_case
22
+ if not table_name:
23
+ table_name = re.sub("(?!^)([A-Z])", r"_\1", name).lower()
24
+
25
+ # Collect all fields
26
+ for key, value in list(attrs.items()):
27
+ if isinstance(value, Field):
28
+ fields[key] = value
29
+ value.name = key
30
+
31
+ # Store metadata in class
32
+ attrs["_fields"] = fields
33
+ attrs["_table_name"] = table_name
34
+
35
+ return super().__new__(mcs, name, bases, attrs)
36
+
37
+
38
+ class Model(metaclass=MetaModel):
39
+ def __init__(self, **kwargs):
40
+ self._data = {}
41
+ # Set default values
42
+ for name, field in self._fields.items():
43
+ if field.default is not None:
44
+ self._data[name] = field.default
45
+
46
+ # Set provided values
47
+ for key, value in kwargs.items():
48
+ if key in self._fields:
49
+ self._fields[key].validate(value)
50
+ self._data[key] = value
51
+ else:
52
+ raise ValueError(f"Unknown field {key}")
53
+
54
+ @classmethod
55
+ def get_session(cls):
56
+ try:
57
+ context_id = context_store.get_context()
58
+ except Exception:
59
+ raise OutOfScopeApplicationException("Context not set")
60
+ return get_session_database(context_id)
61
+
62
+ @classmethod
63
+ def objects(cls) -> QuerySet:
64
+ return QuerySet(cls)
65
+
66
+ @classmethod
67
+ def table_name(cls) -> str:
68
+ return cls._table_name
69
+
70
+ @classmethod
71
+ def create_table_sql(cls) -> str:
72
+ fields_sql = []
73
+ indexes_sql = []
74
+ foreign_keys = []
75
+
76
+ for name, field in cls._fields.items():
77
+ fields_sql.append(cls._get_field_sql(name, field))
78
+ if field.index:
79
+ indexes_sql.append(cls._get_index_sql(name))
80
+ if isinstance(field, ForeignKeyField):
81
+ foreign_keys.append(cls._get_foreign_key_sql(name, field))
82
+
83
+ fields_sql.extend(foreign_keys)
84
+ joined_fields_sql = ", \n ".join(fields_sql)
85
+
86
+ create_table = f"CREATE TABLE {cls.table_name()} (\n {joined_fields_sql} \n"
87
+
88
+ return f"{create_table};\n" + ";\n".join(indexes_sql)
89
+
90
+ @classmethod
91
+ def _get_field_sql(cls, name, field) -> str:
92
+ field_def = [f"{name} {field.sql_type()}"]
93
+ if field.primary_key:
94
+ field_def.append("PRIMARY KEY")
95
+ if not field.null:
96
+ field_def.append("NOT NULL")
97
+ if field.unique:
98
+ field_def.append("UNIQUE")
99
+ if field.default is not None:
100
+ if isinstance(field.default, (str, datetime, date)):
101
+ field_def.append(f"DEFAULT '{field.default}'")
102
+ else:
103
+ field_def.append(f"DEFAULT {field.default}")
104
+ return " ".join(field_def)
105
+
106
+ @classmethod
107
+ def _get_index_sql(cls, name) -> str:
108
+ return f"CREATE INDEX idx_{cls.table_name()}_{name} ON {cls.table_name()} ({name})"
109
+
110
+ @classmethod
111
+ def _get_foreign_key_sql(cls, name, field) -> str:
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}"
114
+
115
+ def save(self):
116
+ query_object = QuerySet(self)
117
+ query_object.bulk_create([self])