core-framework 0.12.13__tar.gz → 0.12.15__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 (142) hide show
  1. {core_framework-0.12.13 → core_framework-0.12.15}/PKG-INFO +1 -1
  2. {core_framework-0.12.13 → core_framework-0.12.15}/core/__init__.py +1 -1
  3. {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/engine.py +44 -1
  4. {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/operations.py +97 -18
  5. {core_framework-0.12.13 → core_framework-0.12.15}/pyproject.toml +1 -1
  6. {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_issues_fixes.py +25 -0
  7. {core_framework-0.12.13 → core_framework-0.12.15}/.gitignore +0 -0
  8. {core_framework-0.12.13 → core_framework-0.12.15}/README.md +0 -0
  9. {core_framework-0.12.13 → core_framework-0.12.15}/core/app.py +0 -0
  10. {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/__init__.py +0 -0
  11. {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/backends.py +0 -0
  12. {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/base.py +0 -0
  13. {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/decorators.py +0 -0
  14. {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/hashers.py +0 -0
  15. {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/helpers.py +0 -0
  16. {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/middleware.py +0 -0
  17. {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/models.py +0 -0
  18. {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/permissions.py +0 -0
  19. {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/schemas.py +0 -0
  20. {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/tokens.py +0 -0
  21. {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/views.py +0 -0
  22. {core_framework-0.12.13 → core_framework-0.12.15}/core/choices.py +0 -0
  23. {core_framework-0.12.13 → core_framework-0.12.15}/core/cli/__init__.py +0 -0
  24. {core_framework-0.12.13 → core_framework-0.12.15}/core/cli/main.py +0 -0
  25. {core_framework-0.12.13 → core_framework-0.12.15}/core/config.py +0 -0
  26. {core_framework-0.12.13 → core_framework-0.12.15}/core/database.py +0 -0
  27. {core_framework-0.12.13 → core_framework-0.12.15}/core/datetime.py +0 -0
  28. {core_framework-0.12.13 → core_framework-0.12.15}/core/dependencies.py +0 -0
  29. {core_framework-0.12.13 → core_framework-0.12.15}/core/deployment/__init__.py +0 -0
  30. {core_framework-0.12.13 → core_framework-0.12.15}/core/deployment/docker.py +0 -0
  31. {core_framework-0.12.13 → core_framework-0.12.15}/core/deployment/kubernetes.py +0 -0
  32. {core_framework-0.12.13 → core_framework-0.12.15}/core/deployment/pm2.py +0 -0
  33. {core_framework-0.12.13 → core_framework-0.12.15}/core/exceptions.py +0 -0
  34. {core_framework-0.12.13 → core_framework-0.12.15}/core/fields.py +0 -0
  35. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/__init__.py +0 -0
  36. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/avro.py +0 -0
  37. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/base.py +0 -0
  38. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/config.py +0 -0
  39. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/confluent/__init__.py +0 -0
  40. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/confluent/consumer.py +0 -0
  41. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/confluent/producer.py +0 -0
  42. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/decorators.py +0 -0
  43. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/kafka/__init__.py +0 -0
  44. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/kafka/admin.py +0 -0
  45. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/kafka/broker.py +0 -0
  46. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/kafka/consumer.py +0 -0
  47. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/kafka/producer.py +0 -0
  48. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/rabbitmq/__init__.py +0 -0
  49. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/rabbitmq/broker.py +0 -0
  50. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/rabbitmq/consumer.py +0 -0
  51. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/rabbitmq/producer.py +0 -0
  52. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/redis/__init__.py +0 -0
  53. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/redis/broker.py +0 -0
  54. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/redis/consumer.py +0 -0
  55. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/redis/producer.py +0 -0
  56. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/registry.py +0 -0
  57. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/topics.py +0 -0
  58. {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/workers.py +0 -0
  59. {core_framework-0.12.13 → core_framework-0.12.15}/core/middleware.py +0 -0
  60. {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/__init__.py +0 -0
  61. {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/analyzer.py +0 -0
  62. {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/cli.py +0 -0
  63. {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/migration.py +0 -0
  64. {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/state.py +0 -0
  65. {core_framework-0.12.13 → core_framework-0.12.15}/core/models.py +0 -0
  66. {core_framework-0.12.13 → core_framework-0.12.15}/core/permissions.py +0 -0
  67. {core_framework-0.12.13 → core_framework-0.12.15}/core/querysets.py +0 -0
  68. {core_framework-0.12.13 → core_framework-0.12.15}/core/relations.py +0 -0
  69. {core_framework-0.12.13 → core_framework-0.12.15}/core/routing.py +0 -0
  70. {core_framework-0.12.13 → core_framework-0.12.15}/core/serializers.py +0 -0
  71. {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/__init__.py +0 -0
  72. {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/base.py +0 -0
  73. {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/config.py +0 -0
  74. {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/decorators.py +0 -0
  75. {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/registry.py +0 -0
  76. {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/scheduler.py +0 -0
  77. {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/worker.py +0 -0
  78. {core_framework-0.12.13 → core_framework-0.12.15}/core/tenancy.py +0 -0
  79. {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/__init__.py +0 -0
  80. {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/assertions.py +0 -0
  81. {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/client.py +0 -0
  82. {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/database.py +0 -0
  83. {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/factories.py +0 -0
  84. {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/mocks.py +0 -0
  85. {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/plugin.py +0 -0
  86. {core_framework-0.12.13 → core_framework-0.12.15}/core/validation.py +0 -0
  87. {core_framework-0.12.13 → core_framework-0.12.15}/core/validators.py +0 -0
  88. {core_framework-0.12.13 → core_framework-0.12.15}/core/views.py +0 -0
  89. {core_framework-0.12.13 → core_framework-0.12.15}/docs/01-quickstart.md +0 -0
  90. {core_framework-0.12.13 → core_framework-0.12.15}/docs/02-viewsets.md +0 -0
  91. {core_framework-0.12.13 → core_framework-0.12.15}/docs/03-authentication.md +0 -0
  92. {core_framework-0.12.13 → core_framework-0.12.15}/docs/04-messaging.md +0 -0
  93. {core_framework-0.12.13 → core_framework-0.12.15}/docs/05-multi-service.md +0 -0
  94. {core_framework-0.12.13 → core_framework-0.12.15}/docs/06-tasks.md +0 -0
  95. {core_framework-0.12.13 → core_framework-0.12.15}/docs/07-deployment.md +0 -0
  96. {core_framework-0.12.13 → core_framework-0.12.15}/docs/08-complete-example.md +0 -0
  97. {core_framework-0.12.13 → core_framework-0.12.15}/docs/09-settings.md +0 -0
  98. {core_framework-0.12.13 → core_framework-0.12.15}/docs/10-migrations.md +0 -0
  99. {core_framework-0.12.13 → core_framework-0.12.15}/docs/11-permissions.md +0 -0
  100. {core_framework-0.12.13 → core_framework-0.12.15}/docs/12-auth-backends.md +0 -0
  101. {core_framework-0.12.13 → core_framework-0.12.15}/docs/13-validators.md +0 -0
  102. {core_framework-0.12.13 → core_framework-0.12.15}/docs/14-querysets.md +0 -0
  103. {core_framework-0.12.13 → core_framework-0.12.15}/docs/15-routing.md +0 -0
  104. {core_framework-0.12.13 → core_framework-0.12.15}/docs/16-serializers.md +0 -0
  105. {core_framework-0.12.13 → core_framework-0.12.15}/docs/17-datetime.md +0 -0
  106. {core_framework-0.12.13 → core_framework-0.12.15}/docs/18-dependencies.md +0 -0
  107. {core_framework-0.12.13 → core_framework-0.12.15}/docs/19-views.md +0 -0
  108. {core_framework-0.12.13 → core_framework-0.12.15}/docs/20-fields.md +0 -0
  109. {core_framework-0.12.13 → core_framework-0.12.15}/docs/21-tenancy.md +0 -0
  110. {core_framework-0.12.13 → core_framework-0.12.15}/docs/22-replicas.md +0 -0
  111. {core_framework-0.12.13 → core_framework-0.12.15}/docs/23-soft-delete.md +0 -0
  112. {core_framework-0.12.13 → core_framework-0.12.15}/docs/24-relations.md +0 -0
  113. {core_framework-0.12.13 → core_framework-0.12.15}/docs/25-exceptions.md +0 -0
  114. {core_framework-0.12.13 → core_framework-0.12.15}/docs/26-choices.md +0 -0
  115. {core_framework-0.12.13 → core_framework-0.12.15}/docs/27-workers.md +0 -0
  116. {core_framework-0.12.13 → core_framework-0.12.15}/docs/28-avro.md +0 -0
  117. {core_framework-0.12.13 → core_framework-0.12.15}/docs/29-topics.md +0 -0
  118. {core_framework-0.12.13 → core_framework-0.12.15}/docs/30-changelog-0.12.2.md +0 -0
  119. {core_framework-0.12.13 → core_framework-0.12.15}/docs/31-middleware.md +0 -0
  120. {core_framework-0.12.13 → core_framework-0.12.15}/docs/32-migration-guide-0.12.2.md +0 -0
  121. {core_framework-0.12.13 → core_framework-0.12.15}/docs/32-testing.md +0 -0
  122. {core_framework-0.12.13 → core_framework-0.12.15}/docs/33-changelog-0.12.3.md +0 -0
  123. {core_framework-0.12.13 → core_framework-0.12.15}/docs/99-faq-troubleshooting.md +0 -0
  124. {core_framework-0.12.13 → core_framework-0.12.15}/docs/GUIDE.md +0 -0
  125. {core_framework-0.12.13 → core_framework-0.12.15}/docs/README.md +0 -0
  126. {core_framework-0.12.13 → core_framework-0.12.15}/example/__init__.py +0 -0
  127. {core_framework-0.12.13 → core_framework-0.12.15}/example/app.py +0 -0
  128. {core_framework-0.12.13 → core_framework-0.12.15}/example/auth.py +0 -0
  129. {core_framework-0.12.13 → core_framework-0.12.15}/example/models.py +0 -0
  130. {core_framework-0.12.13 → core_framework-0.12.15}/example/schemas.py +0 -0
  131. {core_framework-0.12.13 → core_framework-0.12.15}/example/views.py +0 -0
  132. {core_framework-0.12.13 → core_framework-0.12.15}/libs/__init__.py +0 -0
  133. {core_framework-0.12.13 → core_framework-0.12.15}/main.py +0 -0
  134. {core_framework-0.12.13 → core_framework-0.12.15}/tests/__init__.py +0 -0
  135. {core_framework-0.12.13 → core_framework-0.12.15}/tests/conftest.py +0 -0
  136. {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_auth_helpers.py +0 -0
  137. {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_imports.py +0 -0
  138. {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_models.py +0 -0
  139. {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_permissions.py +0 -0
  140. {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_querysets.py +0 -0
  141. {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_serializers.py +0 -0
  142. {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: core-framework
3
- Version: 0.12.13
3
+ Version: 0.12.15
4
4
  Summary: Core Framework - Django-inspired, FastAPI-powered. Alta performance, baixo acoplamento, produtividade extrema.
5
5
  Project-URL: Homepage, https://github.com/SorPuti/core-framework
6
6
  Project-URL: Documentation, https://github.com/SorPuti/core-framework#readme
@@ -287,7 +287,7 @@ from core.exceptions import (
287
287
  MissingDependency,
288
288
  )
289
289
 
290
- __version__ = "0.12.13"
290
+ __version__ = "0.12.15"
291
291
  __all__ = [
292
292
  # Models
293
293
  "Model",
@@ -52,6 +52,41 @@ if TYPE_CHECKING:
52
52
  MIGRATIONS_TABLE = "_core_migrations"
53
53
 
54
54
 
55
+ def _detect_extra_imports(ops_code: str) -> list[str]:
56
+ """
57
+ Detect and return extra import statements needed for migration code.
58
+
59
+ Analyzes the generated operation code to find references like:
60
+ - timezone.now -> from core.datetime import timezone
61
+ - AdvancedField.xxx -> from core.fields import AdvancedField
62
+
63
+ Args:
64
+ ops_code: The generated operation code string
65
+
66
+ Returns:
67
+ List of import statements to add to the migration file
68
+ """
69
+ imports = set()
70
+
71
+ # Patterns for short-form callable references
72
+ # These match the output of _serialize_default()
73
+ patterns = [
74
+ # timezone.xxx -> from core.datetime import timezone
75
+ # Match timezone.now but not datetime.timezone.now
76
+ (r'(?<![.\w])timezone\.\w+', 'from core.datetime import timezone'),
77
+ # AdvancedField.xxx -> from core.fields import AdvancedField
78
+ (r'(?<![.\w])AdvancedField\.\w+', 'from core.fields import AdvancedField'),
79
+ # datetime.xxx -> from datetime import datetime (for full module path)
80
+ (r'(?<![.\w])datetime\.\w+', 'from datetime import datetime'),
81
+ ]
82
+
83
+ for pattern, import_stmt in patterns:
84
+ if re.search(pattern, ops_code):
85
+ imports.add(import_stmt)
86
+
87
+ return sorted(imports)
88
+
89
+
55
90
  def _get_migrations_table_sql(dialect: str) -> str:
56
91
  """
57
92
  Returns dialect-specific SQL for creating migrations table.
@@ -442,6 +477,10 @@ class MigrationEngine:
442
477
 
443
478
  ops_str = ",\n".join(ops_code) if ops_code else " # No operations"
444
479
 
480
+ # Detect and add extra imports needed for callable defaults
481
+ extra_imports = _detect_extra_imports(ops_str)
482
+ extra_imports_str = "\n".join(extra_imports) + "\n" if extra_imports else ""
483
+
445
484
  return f'''"""
446
485
  Migration: {name}
447
486
  Generated at: {timezone.now().isoformat()}
@@ -467,7 +506,7 @@ from core.migrations.operations import (
467
506
  DropEnum,
468
507
  AlterEnum,
469
508
  )
470
-
509
+ {extra_imports_str}
471
510
 
472
511
  migration = Migration(
473
512
  name="{name}",
@@ -522,6 +561,10 @@ migration = Migration(
522
561
  return None
523
562
 
524
563
  operations = self._diff_to_operations(diff)
564
+
565
+ if not operations:
566
+ print("No changes detected.")
567
+ return None
525
568
 
526
569
  # Gera nome da migração
527
570
  number = self._get_next_migration_number()
@@ -26,7 +26,7 @@ def _serialize_default(value: Any) -> str:
26
26
  - None -> "None"
27
27
  - Strings -> repr()
28
28
  - Booleans -> "True"/"False"
29
- - Callables -> "module.function" (importable reference)
29
+ - Callables -> short name (e.g., "timezone.now" not "core.datetime.timezone.now")
30
30
  - Other -> repr()
31
31
 
32
32
  Args:
@@ -41,11 +41,21 @@ def _serialize_default(value: Any) -> str:
41
41
  if callable(value):
42
42
  # Get module and function name for proper serialization
43
43
  module = getattr(value, "__module__", "")
44
- name = getattr(value, "__qualname__", "") or getattr(value, "__name__", "")
45
-
46
- if module and name:
47
- # Return importable reference
48
- return f"{module}.{name}"
44
+ qualname = getattr(value, "__qualname__", "") or getattr(value, "__name__", "")
45
+
46
+ if module and qualname:
47
+ # Use short form for known modules to avoid long paths
48
+ # e.g., "core.datetime.timezone.now" -> "timezone.now"
49
+ # The corresponding import will be added to the migration file
50
+ if module == "core.datetime" and qualname.startswith("timezone."):
51
+ return qualname # Returns "timezone.now"
52
+ if module == "core.fields" and qualname.startswith("AdvancedField."):
53
+ return qualname # Returns "AdvancedField.json_field..."
54
+ if module == "datetime":
55
+ return f"datetime.{qualname}" # Returns "datetime.now"
56
+
57
+ # For other modules, return full path
58
+ return f"{module}.{qualname}"
49
59
 
50
60
  # Fallback for lambdas or unnamed functions
51
61
  return "None"
@@ -128,12 +138,58 @@ class ColumnDef:
128
138
 
129
139
  return self.type
130
140
 
141
+ def _format_default_value(self, value: Any, dialect: str) -> str | None:
142
+ """
143
+ Formata um valor para uso em cláusula DEFAULT SQL.
144
+
145
+ Args:
146
+ value: Valor a ser formatado
147
+ dialect: Nome do dialeto SQL
148
+
149
+ Returns:
150
+ String SQL formatada com DEFAULT ou None
151
+ """
152
+ from datetime import datetime, date, time
153
+
154
+ if value is None:
155
+ return None
156
+
157
+ if isinstance(value, bool):
158
+ if dialect == "postgresql":
159
+ return f"DEFAULT {'TRUE' if value else 'FALSE'}"
160
+ return f"DEFAULT {1 if value else 0}"
161
+
162
+ if isinstance(value, datetime):
163
+ # Format datetime as ISO string for SQL
164
+ return f"DEFAULT '{value.isoformat()}'"
165
+
166
+ if isinstance(value, date):
167
+ return f"DEFAULT '{value.isoformat()}'"
168
+
169
+ if isinstance(value, time):
170
+ return f"DEFAULT '{value.isoformat()}'"
171
+
172
+ if isinstance(value, str):
173
+ # Escape single quotes in strings
174
+ escaped = value.replace("'", "''")
175
+ return f"DEFAULT '{escaped}'"
176
+
177
+ if isinstance(value, (int, float)):
178
+ return f"DEFAULT {value}"
179
+
180
+ if isinstance(value, (dict, list)):
181
+ # JSON values - serialize and quote
182
+ import json
183
+ json_str = json.dumps(value).replace("'", "''")
184
+ return f"DEFAULT '{json_str}'"
185
+
186
+ # Fallback: convert to string
187
+ return f"DEFAULT '{str(value)}'"
188
+
131
189
  def _get_default_sql(self, dialect: str) -> str | None:
132
190
  """
133
191
  Gera SQL para valor default considerando o dialeto.
134
192
 
135
- Bug #2: Boolean defaults usando TRUE/FALSE para PostgreSQL
136
-
137
193
  Args:
138
194
  dialect: Nome do dialeto
139
195
 
@@ -143,17 +199,19 @@ class ColumnDef:
143
199
  if self.default is None:
144
200
  return None
145
201
 
146
- if isinstance(self.default, str):
147
- return f"DEFAULT '{self.default}'"
202
+ # Callable defaults: execute and use the result
203
+ # This is the most robust approach - no need to detect function names
204
+ if callable(self.default):
205
+ try:
206
+ result = self.default()
207
+ # Format the result as SQL DEFAULT
208
+ return self._format_default_value(result, dialect)
209
+ except Exception:
210
+ # If callable fails (needs arguments, etc.), skip DEFAULT
211
+ return None
148
212
 
149
- if isinstance(self.default, bool):
150
- # PostgreSQL exige TRUE/FALSE em vez de 1/0
151
- if dialect == "postgresql":
152
- return f"DEFAULT {'TRUE' if self.default else 'FALSE'}"
153
- else:
154
- return f"DEFAULT {1 if self.default else 0}"
155
-
156
- return f"DEFAULT {self.default}"
213
+ # Non-callable values: format directly
214
+ return self._format_default_value(self.default, dialect)
157
215
 
158
216
  def to_sql(self, dialect: str = "sqlite") -> str:
159
217
  """Gera SQL para a coluna, adaptado ao dialeto do banco."""
@@ -446,6 +504,27 @@ class AlterColumn(Operation):
446
504
  if self.set_default:
447
505
  changes.append(f"default to {self.new_default}")
448
506
  return f"Alter column '{self.column_name}' in '{self.table_name}': {', '.join(changes)}"
507
+
508
+ def to_code(self) -> str:
509
+ """Gera código Python para AlterColumn com serialização correta de callables."""
510
+ new_type_str = f"'{self.new_type}'" if self.new_type else "None"
511
+ old_type_str = f"'{self.old_type}'" if self.old_type else "None"
512
+
513
+ # Serializa defaults corretamente (callables, None, strings, etc.)
514
+ new_default_str = _serialize_default(self.new_default)
515
+ old_default_str = _serialize_default(self.old_default)
516
+
517
+ return f"""AlterColumn(
518
+ table_name='{self.table_name}',
519
+ column_name='{self.column_name}',
520
+ new_type={new_type_str},
521
+ new_nullable={self.new_nullable},
522
+ new_default={new_default_str},
523
+ set_default={self.set_default},
524
+ old_type={old_type_str},
525
+ old_nullable={self.old_nullable},
526
+ old_default={old_default_str},
527
+ )"""
449
528
 
450
529
 
451
530
  @dataclass
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "core-framework"
3
- version = "0.12.13"
3
+ version = "0.12.15"
4
4
  description = "Core Framework - Django-inspired, FastAPI-powered. Alta performance, baixo acoplamento, produtividade extrema."
5
5
  requires-python = ">=3.10,<4.0"
6
6
  readme = "README.md"
@@ -251,6 +251,31 @@ class TestIssue5MigrationSerialization:
251
251
  assert "0x" not in code
252
252
  # Should contain proper reference
253
253
  assert "CreateTable" in code
254
+
255
+ def test_alter_column_to_code_with_callable(self):
256
+ """AlterColumn.to_code() should serialize callable defaults correctly."""
257
+ from core.migrations.operations import AlterColumn
258
+ from core.datetime import timezone
259
+
260
+ op = AlterColumn(
261
+ table_name="users",
262
+ column_name="created_at",
263
+ new_type="DATETIME",
264
+ new_default=timezone.now,
265
+ set_default=True,
266
+ old_type="TIMESTAMP",
267
+ old_default=None,
268
+ )
269
+
270
+ code = op.to_code()
271
+
272
+ # Should NOT contain <function ... at 0x...>
273
+ assert "<function" not in code, f"Generated invalid code: {code}"
274
+ assert "0x" not in code, f"Generated memory address: {code}"
275
+ # Should be valid Python
276
+ assert "AlterColumn" in code
277
+ # Should have proper serialization
278
+ assert "core.datetime.timezone.now" in code or "timezone.now" in code
254
279
 
255
280
 
256
281
  class TestIssue6ResetDbCascade: