future-framework 3.0.0__tar.gz → 3.0.1__tar.gz

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 (83) hide show
  1. {future_framework-3.0.0 → future_framework-3.0.1}/PKG-INFO +1 -1
  2. {future_framework-3.0.0 → future_framework-3.0.1}/future/databases/MySQLDatabase.py +21 -4
  3. {future_framework-3.0.0 → future_framework-3.0.1}/future/databases/PostgresDatabase.py +21 -4
  4. {future_framework-3.0.0 → future_framework-3.0.1}/future/databases/SQLiteDatabase.py +21 -4
  5. {future_framework-3.0.0 → future_framework-3.0.1}/future/migrations.py +20 -0
  6. {future_framework-3.0.0 → future_framework-3.0.1}/pyproject.toml +1 -1
  7. {future_framework-3.0.0 → future_framework-3.0.1}/LICENSE +0 -0
  8. {future_framework-3.0.0 → future_framework-3.0.1}/README.md +0 -0
  9. {future_framework-3.0.0 → future_framework-3.0.1}/future/__init__.py +0 -0
  10. {future_framework-3.0.0 → future_framework-3.0.1}/future/application.py +0 -0
  11. {future_framework-3.0.0 → future_framework-3.0.1}/future/authentication/Auth0Authentication.py +0 -0
  12. {future_framework-3.0.0 → future_framework-3.0.1}/future/authentication/AzureADAuthentication.py +0 -0
  13. {future_framework-3.0.0 → future_framework-3.0.1}/future/authentication/BasicAuthentication.py +0 -0
  14. {future_framework-3.0.0 → future_framework-3.0.1}/future/authentication/KerberosAuthentication.py +0 -0
  15. {future_framework-3.0.0 → future_framework-3.0.1}/future/authentication/KeycloakAuthentication.py +0 -0
  16. {future_framework-3.0.0 → future_framework-3.0.1}/future/authentication/OAuth2Authentication.py +0 -0
  17. {future_framework-3.0.0 → future_framework-3.0.1}/future/authentication/OpenIdConnectAuthentication.py +0 -0
  18. {future_framework-3.0.0 → future_framework-3.0.1}/future/authentication/SAMLAuthentication.py +0 -0
  19. {future_framework-3.0.0 → future_framework-3.0.1}/future/authentication/__init__.py +0 -0
  20. {future_framework-3.0.0 → future_framework-3.0.1}/future/cli.py +0 -0
  21. {future_framework-3.0.0 → future_framework-3.0.1}/future/controllers/DebugController.py +0 -0
  22. {future_framework-3.0.0 → future_framework-3.0.1}/future/controllers/GraphQLController.py +0 -0
  23. {future_framework-3.0.0 → future_framework-3.0.1}/future/controllers/OpenAPIController.py +0 -0
  24. {future_framework-3.0.0 → future_framework-3.0.1}/future/controllers/WebSocketController.py +0 -0
  25. {future_framework-3.0.0 → future_framework-3.0.1}/future/controllers/WelcomeController.py +0 -0
  26. {future_framework-3.0.0 → future_framework-3.0.1}/future/controllers/__init__.py +0 -0
  27. {future_framework-3.0.0 → future_framework-3.0.1}/future/database.py +0 -0
  28. {future_framework-3.0.0 → future_framework-3.0.1}/future/databases/ClickhouseDatabase.py +0 -0
  29. {future_framework-3.0.0 → future_framework-3.0.1}/future/databases/ElasticsearchDatabase.py +0 -0
  30. {future_framework-3.0.0 → future_framework-3.0.1}/future/databases/MongoDBDatabase.py +0 -0
  31. {future_framework-3.0.0 → future_framework-3.0.1}/future/databases/RedisDatabase.py +0 -0
  32. {future_framework-3.0.0 → future_framework-3.0.1}/future/databases/__init__.py +0 -0
  33. {future_framework-3.0.0 → future_framework-3.0.1}/future/exceptions.py +0 -0
  34. {future_framework-3.0.0 → future_framework-3.0.1}/future/interfaces/IAuthentication.py +0 -0
  35. {future_framework-3.0.0 → future_framework-3.0.1}/future/interfaces/IController.py +0 -0
  36. {future_framework-3.0.0 → future_framework-3.0.1}/future/interfaces/IDatabase.py +0 -0
  37. {future_framework-3.0.0 → future_framework-3.0.1}/future/interfaces/IMiddleware.py +0 -0
  38. {future_framework-3.0.0 → future_framework-3.0.1}/future/interfaces/IModel.py +0 -0
  39. {future_framework-3.0.0 → future_framework-3.0.1}/future/interfaces/IPlugin.py +0 -0
  40. {future_framework-3.0.0 → future_framework-3.0.1}/future/interfaces/ITask.py +0 -0
  41. {future_framework-3.0.0 → future_framework-3.0.1}/future/interfaces/__init__.py +0 -0
  42. {future_framework-3.0.0 → future_framework-3.0.1}/future/interfacing.py +0 -0
  43. {future_framework-3.0.0 → future_framework-3.0.1}/future/lifespan.py +0 -0
  44. {future_framework-3.0.0 → future_framework-3.0.1}/future/logger.py +0 -0
  45. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/BruteforcePreventionMiddleware.py +0 -0
  46. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/CORSMiddleware.py +0 -0
  47. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/CSRFMiddleware.py +0 -0
  48. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/GZipMiddleware.py +0 -0
  49. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/HTAccessConfuserMiddleware.py +0 -0
  50. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/HeaderConfuserMiddleware.py +0 -0
  51. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/RateLimitMiddleware.py +0 -0
  52. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/ResponseCodeConfuserMiddleware.py +0 -0
  53. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/SQLiConfuserMiddleware.py +0 -0
  54. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/ScopeValidationMiddleware.py +0 -0
  55. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/SessionMiddleware.py +0 -0
  56. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/TestMiddlewareRequest.py +0 -0
  57. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/TestMiddlewareResponse.py +0 -0
  58. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/WebServerConfuserMiddleware.py +0 -0
  59. {future_framework-3.0.0 → future_framework-3.0.1}/future/middleware/__init__.py +0 -0
  60. {future_framework-3.0.0 → future_framework-3.0.1}/future/models/PostModel.py +0 -0
  61. {future_framework-3.0.0 → future_framework-3.0.1}/future/models/Query.py +0 -0
  62. {future_framework-3.0.0 → future_framework-3.0.1}/future/models/UserModel.py +0 -0
  63. {future_framework-3.0.0 → future_framework-3.0.1}/future/models/__init__.py +0 -0
  64. {future_framework-3.0.0 → future_framework-3.0.1}/future/openapi.py +0 -0
  65. {future_framework-3.0.0 → future_framework-3.0.1}/future/plugins/ElasticsearchPlugin.py +0 -0
  66. {future_framework-3.0.0 → future_framework-3.0.1}/future/plugins/__init__.py +0 -0
  67. {future_framework-3.0.0 → future_framework-3.0.1}/future/request.py +0 -0
  68. {future_framework-3.0.0 → future_framework-3.0.1}/future/response.py +0 -0
  69. {future_framework-3.0.0 → future_framework-3.0.1}/future/routing.py +0 -0
  70. {future_framework-3.0.0 → future_framework-3.0.1}/future/seeder.py +0 -0
  71. {future_framework-3.0.0 → future_framework-3.0.1}/future/settings.py +0 -0
  72. {future_framework-3.0.0 → future_framework-3.0.1}/future/tasks/CheckDNSTask.py +0 -0
  73. {future_framework-3.0.0 → future_framework-3.0.1}/future/tasks/CheckDiskUsageTask.py +0 -0
  74. {future_framework-3.0.0 → future_framework-3.0.1}/future/tasks/CheckHttpStatusTask.py +0 -0
  75. {future_framework-3.0.0 → future_framework-3.0.1}/future/tasks/CheckMemoryUsageTask.py +0 -0
  76. {future_framework-3.0.0 → future_framework-3.0.1}/future/tasks/CheckSSHBannerTask.py +0 -0
  77. {future_framework-3.0.0 → future_framework-3.0.1}/future/tasks/CheckSystemUptimeTask.py +0 -0
  78. {future_framework-3.0.0 → future_framework-3.0.1}/future/tasks/DailyBackupTask.py +0 -0
  79. {future_framework-3.0.0 → future_framework-3.0.1}/future/tasks/SyncTaskExample.py +0 -0
  80. {future_framework-3.0.0 → future_framework-3.0.1}/future/tasks/__init__.py +0 -0
  81. {future_framework-3.0.0 → future_framework-3.0.1}/future/taskscheduler.py +0 -0
  82. {future_framework-3.0.0 → future_framework-3.0.1}/future/testclient.py +0 -0
  83. {future_framework-3.0.0 → future_framework-3.0.1}/future/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: future-framework
3
- Version: 3.0.0
3
+ Version: 3.0.1
4
4
  Summary: ASGI-based framework for web APIs
5
5
  License: LICENSE
6
6
  License-File: LICENSE
@@ -172,7 +172,6 @@ class MySQLDatabase(IDatabase):
172
172
  async with self.client.connect() as connection:
173
173
  rows = (await connection.execute(sql, {"table": blueprint.name})).mappings().all()
174
174
  existing = {row["COLUMN_NAME"] for row in rows}
175
- common = [column.name for column in blueprint.columns if column.name in existing]
176
175
  temporary = f"_future_migrate_{blueprint.name}"
177
176
  backup = f"_future_backup_{blueprint.name}"
178
177
  parts = self._schema_parts(blueprint)
@@ -180,9 +179,27 @@ class MySQLDatabase(IDatabase):
180
179
  await connection.execute(text(f"DROP TABLE IF EXISTS `{temporary}`"))
181
180
  await connection.execute(text(f"DROP TABLE IF EXISTS `{backup}`"))
182
181
  await connection.execute(text(f"CREATE TABLE `{temporary}` ({', '.join(parts)})"))
183
- if common:
184
- columns = ", ".join(f"`{name}`" for name in common)
185
- await connection.execute(text(f"INSERT INTO `{temporary}` ({columns}) SELECT {columns} FROM `{blueprint.name}`"))
182
+ targets = []
183
+ sources = []
184
+ backfills = {}
185
+ for index, column in enumerate(blueprint.columns):
186
+ quoted = f"`{column.name}`"
187
+ if column.name in existing:
188
+ source = quoted
189
+ if not column.is_nullable:
190
+ key = f"future_backfill_{index}"
191
+ source = f"COALESCE({quoted}, :{key})"
192
+ backfills[key] = column.backfill_value()
193
+ elif not column.is_nullable:
194
+ key = f"future_backfill_{index}"
195
+ source = f":{key}"
196
+ backfills[key] = column.backfill_value()
197
+ else:
198
+ continue
199
+ targets.append(quoted)
200
+ sources.append(source)
201
+ if targets:
202
+ await connection.execute(text(f"INSERT INTO `{temporary}` ({', '.join(targets)}) SELECT {', '.join(sources)} FROM `{blueprint.name}`"), backfills)
186
203
  await connection.execute(text(f"RENAME TABLE `{blueprint.name}` TO `{backup}`, `{temporary}` TO `{blueprint.name}`"))
187
204
  await connection.execute(text(f"DROP TABLE `{backup}`"))
188
205
  return {"status": "updated", "table": blueprint.name, "columns": sorted(column.name for column in blueprint.columns)}
@@ -180,15 +180,32 @@ class PostgresDatabase(IDatabase):
180
180
  async with self.client.connect() as connection:
181
181
  rows = (await connection.execute(sql, {"table": blueprint.name})).mappings().all()
182
182
  existing = {row["column_name"] for row in rows}
183
- common = [column.name for column in blueprint.columns if column.name in existing]
184
183
  temporary = f"_future_{uuid4().hex[:8]}_{blueprint.name}"[:63]
185
184
  parts, indexes = self._schema_parts(blueprint)
186
185
  async with self.client.begin() as connection:
187
186
  await connection.execute(text(f'DROP TABLE IF EXISTS "{temporary}"'))
188
187
  await connection.execute(text(f'CREATE TABLE "{temporary}" ({", ".join(parts)})'))
189
- if common:
190
- columns = ", ".join(f'"{name}"' for name in common)
191
- await connection.execute(text(f'INSERT INTO "{temporary}" ({columns}) SELECT {columns} FROM "{blueprint.name}"'))
188
+ targets = []
189
+ sources = []
190
+ backfills = {}
191
+ for index, column in enumerate(blueprint.columns):
192
+ quoted = f'"{column.name}"'
193
+ if column.name in existing:
194
+ source = quoted
195
+ if not column.is_nullable:
196
+ key = f"future_backfill_{index}"
197
+ source = f"COALESCE({quoted}, :{key})"
198
+ backfills[key] = column.backfill_value()
199
+ elif not column.is_nullable:
200
+ key = f"future_backfill_{index}"
201
+ source = f":{key}"
202
+ backfills[key] = column.backfill_value()
203
+ else:
204
+ continue
205
+ targets.append(quoted)
206
+ sources.append(source)
207
+ if targets:
208
+ await connection.execute(text(f'INSERT INTO "{temporary}" ({", ".join(targets)}) SELECT {", ".join(sources)} FROM "{blueprint.name}"'), backfills)
192
209
  await connection.execute(text(f'DROP TABLE "{blueprint.name}"'))
193
210
  await connection.execute(text(f'ALTER TABLE "{temporary}" RENAME TO "{blueprint.name}"'))
194
211
  for index_name in indexes:
@@ -178,16 +178,33 @@ class SQLiteDatabase(IDatabase):
178
178
  rows = (await connection.execute(text(f'PRAGMA table_info("{blueprint.name}")'))).mappings().all()
179
179
  existing = {row["name"] for row in rows}
180
180
  desired = {column.name for column in blueprint.columns}
181
- common = [column.name for column in blueprint.columns if column.name in existing]
182
181
  temporary = f"_future_migrate_{blueprint.name}"
183
182
  definitions, _ = self._schema_parts(blueprint)
184
183
  _, indexes = self._schema_parts(blueprint, index_table=blueprint.name)
185
184
  async with self.client.begin() as connection:
186
185
  await connection.execute(text(f'DROP TABLE IF EXISTS "{temporary}"'))
187
186
  await connection.execute(text(f'CREATE TABLE "{temporary}" ({", ".join(definitions)})'))
188
- if common:
189
- columns = ", ".join(f'"{name}"' for name in common)
190
- await connection.execute(text(f'INSERT INTO "{temporary}" ({columns}) SELECT {columns} FROM "{blueprint.name}"'))
187
+ targets = []
188
+ sources = []
189
+ backfills = {}
190
+ for index, column in enumerate(blueprint.columns):
191
+ quoted = f'"{column.name}"'
192
+ if column.name in existing:
193
+ source = quoted
194
+ if not column.is_nullable:
195
+ key = f"future_backfill_{index}"
196
+ source = f"COALESCE({quoted}, :{key})"
197
+ backfills[key] = column.backfill_value()
198
+ elif not column.is_nullable:
199
+ key = f"future_backfill_{index}"
200
+ source = f":{key}"
201
+ backfills[key] = column.backfill_value()
202
+ else:
203
+ continue
204
+ targets.append(quoted)
205
+ sources.append(source)
206
+ if targets:
207
+ await connection.execute(text(f'INSERT INTO "{temporary}" ({", ".join(targets)}) SELECT {", ".join(sources)} FROM "{blueprint.name}"'), backfills)
191
208
  await connection.execute(text(f'DROP TABLE "{blueprint.name}"'))
192
209
  await connection.execute(text(f'ALTER TABLE "{temporary}" RENAME TO "{blueprint.name}"'))
193
210
  for index_sql in indexes:
@@ -55,6 +55,21 @@ class Column:
55
55
  self.default_value = value
56
56
  return self
57
57
 
58
+ def backfill_value(self):
59
+ if self.default_value is not None:
60
+ return self.default_value
61
+ if self.type in ("string", "text"):
62
+ return ""
63
+ if self.type == "integer":
64
+ return 0
65
+ if self.type == "float":
66
+ return 0.0
67
+ if self.type == "boolean":
68
+ return False
69
+ if self.type == "datetime":
70
+ return datetime(1970, 1, 1)
71
+ raise ValueError(f"Unsupported column type: {self.type}")
72
+
58
73
 
59
74
  class Blueprint:
60
75
  def __init__(self, name, connection_name, action="create"):
@@ -268,6 +283,11 @@ class MigrationGenerator:
268
283
  for field, annotation in annotations.items():
269
284
  line = self.column_line(field, annotation)
270
285
  if line is not None:
286
+ default_value = vars(model).get(field)
287
+ if default_value is not None:
288
+ if isinstance(default_value, datetime):
289
+ default_value = default_value.isoformat(sep=" ")
290
+ line += f".default({default_value!r})"
271
291
  lines.append(f" {line}")
272
292
  if use_timestamps:
273
293
  lines.append(" table.timestamps()")
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "future-framework"
3
- version = "3.0.0"
3
+ version = "3.0.1"
4
4
  description = "ASGI-based framework for web APIs"
5
5
  authors = ["nicolaipre"]
6
6
  license = "LICENSE"