model-generator-kit 0.1.2__tar.gz → 0.1.4__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.
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/PKG-INFO +2 -2
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/README.md +1 -1
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/pyproject.toml +1 -1
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/__init__.py +1 -1
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/api/request.py.j2 +22 -22
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/api/response.py.j2 +14 -2
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/validators.py.j2 +17 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/migrations/env.py.j2 +61 -1
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator_kit.egg-info/PKG-INFO +2 -2
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/tests/test_generators.py +292 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/LICENSE +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/setup.cfg +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/generate.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/generators/__init__.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/generators/api.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/generators/constraints.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/generators/database.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/generators/enums.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/generators/infrastructure.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/generators/migrations.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/py.typed +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/schema/model.schema.json +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/config.yaml +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/_shared/_base.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/_shared/_entity.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/_shared/_examples.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/_shared/_fields.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/_shared/_tests.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/api/init.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/api/pagination.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/api/route.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/database/constraints.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/database/enums.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/database/factory.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/database/init.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/database/model.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/auth_router.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/base.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/csrf.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/database_init.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/encrypted_bytes.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/engine.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/errors.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/gitignore.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/main.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/pyproject.toml.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/rate_limit.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/request_limit.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/types.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/infrastructure/utils.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/migrations/ini.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/migrations/script.py.mako.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/tests/conftest_root.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/stacks/python-fastapi/templates/tests/contract.py.j2 +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/utils/__init__.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/utils/conftest_generator.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/utils/constants.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/utils/loaders.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/utils/parser.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/utils/quality.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/utils/templates.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/validate.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/wizard/__init__.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/wizard/actions/__init__.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/wizard/actions/clean.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/wizard/actions/generate.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/wizard/actions/project_setup.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/wizard/actions/test_runner.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/wizard/menu.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/wizard/prompts.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator_kit.egg-info/SOURCES.txt +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator_kit.egg-info/dependency_links.txt +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator_kit.egg-info/entry_points.txt +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator_kit.egg-info/requires.txt +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator_kit.egg-info/top_level.txt +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/tests/test_cleanup.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/tests/test_cli.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/tests/test_edge_cases.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/tests/test_enum_examples.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/tests/test_full_generation.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/tests/test_integration.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/tests/test_template_utils.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/tests/test_utils.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/tests/test_validate.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/tests/test_validation.py +0 -0
- {model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/tests/test_wizard.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: model-generator-kit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: One-shot bootstrap generator for database models, API models, routes, and tests
|
|
5
5
|
Author-email: nuncaeslupus <imarcos@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -140,4 +140,4 @@ Define specifications once in JSON. Generate production-ready scaffolds. Then ma
|
|
|
140
140
|
|
|
141
141
|
---
|
|
142
142
|
|
|
143
|
-
**Model Generator** | Bootstrap Tool for API Backends | v0.1.
|
|
143
|
+
**Model Generator** | Bootstrap Tool for API Backends | v0.1.4
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "model-generator-kit"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.4"
|
|
8
8
|
description = "One-shot bootstrap generator for database models, API models, routes, and tests"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -57,11 +57,13 @@ This module contains:
|
|
|
57
57
|
{% endif %}
|
|
58
58
|
{#- Check for financial/counter fields with constraints needing validators -#}
|
|
59
59
|
{% if not field.api_exclude_create | default(false) or not field.api_exclude_update | default(false) %}
|
|
60
|
-
{% if field.type == 'financial'
|
|
61
|
-
{
|
|
62
|
-
{%
|
|
63
|
-
{% if
|
|
64
|
-
{%
|
|
60
|
+
{% if field.type == 'financial' %}
|
|
61
|
+
{#- Choose validator by declared constraint: non-negative when constrained, signed otherwise -#}
|
|
62
|
+
{% set ctypes = (field.constraints | map(attribute='type') | list) if (field.constraints is defined and field.constraints) else [] %}
|
|
63
|
+
{% if 'non_negative' in ctypes or 'non_negative_or_null' in ctypes %}{% set fin_validator = 'validate_non_negative_decimal' %}
|
|
64
|
+
{% elif 'positive' in ctypes or 'positive_or_null' in ctypes %}{% set fin_validator = 'validate_positive_decimal' %}
|
|
65
|
+
{% else %}{% set fin_validator = 'validate_decimal' %}{% endif %}
|
|
66
|
+
{% if fin_validator not in ns.needs_validators %}{% set _ = ns.needs_validators.append(fin_validator) %}{% endif %}
|
|
65
67
|
{% elif field.type == 'percentage' %}
|
|
66
68
|
{% if 'validate_percentage' not in ns.needs_validators %}{% set _ = ns.needs_validators.append('validate_percentage') %}{% endif %}
|
|
67
69
|
{% elif field.type == 'counter' and field.constraints is defined %}
|
|
@@ -208,7 +210,7 @@ class Create{{ entity_name }}Request(BaseModel):
|
|
|
208
210
|
{% elif field.type == 'text' and db_name == 'username' %}
|
|
209
211
|
"{{ field_name }}": "john_doe",
|
|
210
212
|
{% elif field.type == 'text' and 'password' in db_name.lower() %}
|
|
211
|
-
"{{ field_name }}": "
|
|
213
|
+
"{{ field_name }}": "your-password-here",
|
|
212
214
|
{% elif field.type == 'text' and 'name' in db_name.lower() %}
|
|
213
215
|
"{{ field_name }}": "{{ db_name.replace('_', ' ').title() }}",
|
|
214
216
|
{% elif field.type == 'text' %}
|
|
@@ -271,14 +273,13 @@ class Create{{ entity_name }}Request(BaseModel):
|
|
|
271
273
|
{#- Collect constraint-driven validators (financial, percentage, counter) -#}
|
|
272
274
|
{% for item in create_fields %}
|
|
273
275
|
{% set field = item.field %}
|
|
274
|
-
{% if field.type == 'financial'
|
|
275
|
-
{
|
|
276
|
-
{%
|
|
277
|
-
{%
|
|
278
|
-
{% elif
|
|
279
|
-
{% set
|
|
280
|
-
{%
|
|
281
|
-
{% endfor %}
|
|
276
|
+
{% if field.type == 'financial' %}
|
|
277
|
+
{#- Choose validator by declared constraint: non-negative when constrained, signed otherwise -#}
|
|
278
|
+
{% set ctypes = (field.constraints | map(attribute='type') | list) if (field.constraints is defined and field.constraints) else [] %}
|
|
279
|
+
{% if 'non_negative' in ctypes or 'non_negative_or_null' in ctypes %}{% set fin_validator = 'validate_non_negative_decimal' %}
|
|
280
|
+
{% elif 'positive' in ctypes or 'positive_or_null' in ctypes %}{% set fin_validator = 'validate_positive_decimal' %}
|
|
281
|
+
{% else %}{% set fin_validator = 'validate_decimal' %}{% endif %}
|
|
282
|
+
{% set _ = field_validators.append({'name': item.name, 'validator': fin_validator}) %}
|
|
282
283
|
{% elif field.type == 'percentage' %}
|
|
283
284
|
{% set _ = field_validators.append({'name': item.name, 'validator': 'validate_percentage'}) %}
|
|
284
285
|
{% elif field.type == 'counter' and field.constraints is defined %}
|
|
@@ -416,14 +417,13 @@ class Update{{ entity_name }}Request(BaseModel):
|
|
|
416
417
|
{% set update_validators = [] %}
|
|
417
418
|
{% for item in update_fields %}
|
|
418
419
|
{% set field = item.field %}
|
|
419
|
-
{% if field.type == 'financial'
|
|
420
|
-
{
|
|
421
|
-
{%
|
|
422
|
-
{%
|
|
423
|
-
{% elif
|
|
424
|
-
{% set
|
|
425
|
-
{%
|
|
426
|
-
{% endfor %}
|
|
420
|
+
{% if field.type == 'financial' %}
|
|
421
|
+
{#- Choose validator by declared constraint: non-negative when constrained, signed otherwise -#}
|
|
422
|
+
{% set ctypes = (field.constraints | map(attribute='type') | list) if (field.constraints is defined and field.constraints) else [] %}
|
|
423
|
+
{% if 'non_negative' in ctypes or 'non_negative_or_null' in ctypes %}{% set fin_validator = 'validate_non_negative_decimal' %}
|
|
424
|
+
{% elif 'positive' in ctypes or 'positive_or_null' in ctypes %}{% set fin_validator = 'validate_positive_decimal' %}
|
|
425
|
+
{% else %}{% set fin_validator = 'validate_decimal' %}{% endif %}
|
|
426
|
+
{% set _ = update_validators.append({'name': item.name, 'validator': fin_validator}) %}
|
|
427
427
|
{% elif field.type == 'percentage' %}
|
|
428
428
|
{% set _ = update_validators.append({'name': item.name, 'validator': 'validate_percentage'}) %}
|
|
429
429
|
{% elif field.type == 'counter' and field.constraints is defined %}
|
|
@@ -26,7 +26,14 @@ This module contains:
|
|
|
26
26
|
{% if not field.api_exclude_response | default(false) %}
|
|
27
27
|
{% if field.type in ['json_object', 'json_array'] %}{% set ns.has_any = true %}{% endif %}
|
|
28
28
|
{% if field.type == 'enum' and field.enum_name not in ns.enum_imports %}{% set _ = ns.enum_imports.append(field.enum_name) %}{% endif %}
|
|
29
|
-
{% if field.type == 'financial'
|
|
29
|
+
{% if field.type == 'financial' %}
|
|
30
|
+
{#- Choose validator by declared constraint: non-negative when constrained, signed otherwise -#}
|
|
31
|
+
{% set ctypes = (field.constraints | map(attribute='type') | list) if (field.constraints is defined and field.constraints) else [] %}
|
|
32
|
+
{% if 'non_negative' in ctypes or 'non_negative_or_null' in ctypes %}{% set fin_validator = 'validate_non_negative_decimal' %}
|
|
33
|
+
{% elif 'positive' in ctypes or 'positive_or_null' in ctypes %}{% set fin_validator = 'validate_positive_decimal' %}
|
|
34
|
+
{% else %}{% set fin_validator = 'validate_decimal' %}{% endif %}
|
|
35
|
+
{% if fin_validator not in ns.needs_validators %}{% set _ = ns.needs_validators.append(fin_validator) %}{% endif %}
|
|
36
|
+
{% endif %}
|
|
30
37
|
{% if field.type == 'percentage' and 'validate_percentage' not in ns.needs_validators %}{% set _ = ns.needs_validators.append('validate_percentage') %}{% endif %}
|
|
31
38
|
{% if field.type == 'counter' and 'validate_non_negative_int' not in ns.needs_validators %}{% set _ = ns.needs_validators.append('validate_non_negative_int') %}{% endif %}
|
|
32
39
|
{% endif %}
|
|
@@ -173,7 +180,12 @@ class {{ entity_name }}Response(BaseModel):
|
|
|
173
180
|
{% for field_name, field in entity.fields.items() %}
|
|
174
181
|
{% if not field.api_exclude_response | default(false) %}
|
|
175
182
|
{% if field.type == 'financial' %}
|
|
176
|
-
{
|
|
183
|
+
{#- Choose validator by declared constraint: non-negative when constrained, signed otherwise -#}
|
|
184
|
+
{% set ctypes = (field.constraints | map(attribute='type') | list) if (field.constraints is defined and field.constraints) else [] %}
|
|
185
|
+
{% if 'non_negative' in ctypes or 'non_negative_or_null' in ctypes %}{% set fin_validator = 'validate_non_negative_decimal' %}
|
|
186
|
+
{% elif 'positive' in ctypes or 'positive_or_null' in ctypes %}{% set fin_validator = 'validate_positive_decimal' %}
|
|
187
|
+
{% else %}{% set fin_validator = 'validate_decimal' %}{% endif %}
|
|
188
|
+
{% set _ = validated_fields.append({'name': field_name, 'validator': fin_validator}) %}
|
|
177
189
|
{% elif field.type == 'percentage' %}
|
|
178
190
|
{% set _ = validated_fields.append({'name': field_name, 'validator': 'validate_percentage'}) %}
|
|
179
191
|
{% elif field.type == 'counter' %}
|
|
@@ -23,6 +23,23 @@ from typing import Any
|
|
|
23
23
|
{{ section_divider('DECIMAL VALIDATORS') }}
|
|
24
24
|
|
|
25
25
|
|
|
26
|
+
def validate_decimal(value: Any) -> str | None:
|
|
27
|
+
"""Validate that a value is a well-formed decimal (sign-agnostic).
|
|
28
|
+
|
|
29
|
+
Accepts negative, zero, and positive values. Use this for signed
|
|
30
|
+
financial fields (e.g. PnL, returns) that may legitimately be negative.
|
|
31
|
+
"""
|
|
32
|
+
if value is None:
|
|
33
|
+
return value
|
|
34
|
+
try:
|
|
35
|
+
decimal_value = Decimal(str(value))
|
|
36
|
+
except InvalidOperation:
|
|
37
|
+
raise ValueError("Invalid decimal value") from None
|
|
38
|
+
if not decimal_value.is_finite():
|
|
39
|
+
raise ValueError("Value must be a finite decimal")
|
|
40
|
+
return str(value)
|
|
41
|
+
|
|
42
|
+
|
|
26
43
|
def validate_non_negative_decimal(value: Any) -> str | None:
|
|
27
44
|
"""Validate that a decimal value is non-negative (>= 0)."""
|
|
28
45
|
if value is None:
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
{{ generated_marker() }}
|
|
11
11
|
"""Alembic environment configuration."""
|
|
12
12
|
from logging.config import fileConfig
|
|
13
|
+
from typing import Any
|
|
13
14
|
|
|
14
15
|
from sqlalchemy import engine_from_config
|
|
15
16
|
from sqlalchemy import pool
|
|
@@ -32,6 +33,63 @@ if config.config_file_name is not None:
|
|
|
32
33
|
from {{ config.paths.database_models | path_to_import }}.base import Base
|
|
33
34
|
target_metadata = Base.metadata
|
|
34
35
|
|
|
36
|
+
# Project custom SQLAlchemy types live here. Autogenerate must import them into
|
|
37
|
+
# the migrations it writes (see render_item below), otherwise the generated
|
|
38
|
+
# migration references e.g. PortableUuid() without an import and fails to load.
|
|
39
|
+
_CUSTOM_TYPE_MODULE = "{{ config.paths.database_models.rsplit('/', 1)[0] | path_to_import }}.types"
|
|
40
|
+
_CUSTOM_TYPE_NAMES = ("PortableNumeric", "PortableUuid")
|
|
41
|
+
|
|
42
|
+
# Async drivers map to their sync equivalents. Alembic runs migrations
|
|
43
|
+
# synchronously, so an async DATABASE_URL (e.g. postgresql+asyncpg://) must be
|
|
44
|
+
# coerced to a sync driver before an Engine is built.
|
|
45
|
+
_ASYNC_TO_SYNC_DRIVERS = {
|
|
46
|
+
"+asyncpg": "+psycopg2",
|
|
47
|
+
"+aiosqlite": "",
|
|
48
|
+
"+aiomysql": "+pymysql",
|
|
49
|
+
"+asyncmy": "+pymysql",
|
|
50
|
+
"+aioodbc": "+pyodbc",
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _coerce_sync_driver(url: str) -> str:
|
|
55
|
+
"""Return ``url`` with any known async driver in the *scheme* swapped for a
|
|
56
|
+
sync one. Only the scheme (before "://") is touched, so a credential or
|
|
57
|
+
database name that happens to contain a driver substring is never mangled.
|
|
58
|
+
"""
|
|
59
|
+
scheme, sep, rest = url.partition("://")
|
|
60
|
+
if not sep:
|
|
61
|
+
return url
|
|
62
|
+
for async_driver, sync_driver in _ASYNC_TO_SYNC_DRIVERS.items():
|
|
63
|
+
if scheme.endswith(async_driver):
|
|
64
|
+
scheme = scheme[: -len(async_driver)] + sync_driver
|
|
65
|
+
break
|
|
66
|
+
return scheme + sep + rest
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def render_item(type_: str, obj: Any, autogen_context: Any) -> str | bool:
|
|
70
|
+
"""Render the project's custom column types with their import.
|
|
71
|
+
|
|
72
|
+
Without this, autogenerate emits ``PortableUuid()`` / ``PortableNumeric()``
|
|
73
|
+
in the migration body but never imports them, so the migration raises
|
|
74
|
+
``NameError`` on load. Returning ``False`` falls back to Alembic's default
|
|
75
|
+
rendering for everything else.
|
|
76
|
+
"""
|
|
77
|
+
if type_ == "type" and type(obj).__name__ in _CUSTOM_TYPE_NAMES:
|
|
78
|
+
cls = type(obj).__name__
|
|
79
|
+
autogen_context.imports.add(f"from {_CUSTOM_TYPE_MODULE} import {cls}")
|
|
80
|
+
if cls == "PortableNumeric":
|
|
81
|
+
args = [
|
|
82
|
+
f"{name}={value}"
|
|
83
|
+
for name, value in (
|
|
84
|
+
("precision", obj.precision),
|
|
85
|
+
("scale", obj.scale),
|
|
86
|
+
)
|
|
87
|
+
if value is not None
|
|
88
|
+
]
|
|
89
|
+
return f"{cls}({', '.join(args)})"
|
|
90
|
+
return f"{cls}()"
|
|
91
|
+
return False
|
|
92
|
+
|
|
35
93
|
# other values from the config, defined by the needs of env.py,
|
|
36
94
|
# can be acquired:
|
|
37
95
|
# my_important_option = config.get_main_option("my_important_option")
|
|
@@ -60,7 +118,7 @@ def get_url() -> str:
|
|
|
60
118
|
"alembic.ini, must be set"
|
|
61
119
|
)
|
|
62
120
|
raise RuntimeError(msg)
|
|
63
|
-
return url
|
|
121
|
+
return _coerce_sync_driver(url)
|
|
64
122
|
|
|
65
123
|
|
|
66
124
|
def run_migrations_offline() -> None:
|
|
@@ -83,6 +141,7 @@ def run_migrations_offline() -> None:
|
|
|
83
141
|
dialect_opts={"paramstyle": "named"},
|
|
84
142
|
compare_type=True,
|
|
85
143
|
compare_server_default=True,
|
|
144
|
+
render_item=render_item,
|
|
86
145
|
)
|
|
87
146
|
|
|
88
147
|
with context.begin_transaction():
|
|
@@ -113,6 +172,7 @@ def run_migrations_online() -> None:
|
|
|
113
172
|
target_metadata=target_metadata,
|
|
114
173
|
compare_type=True,
|
|
115
174
|
compare_server_default=True,
|
|
175
|
+
render_item=render_item,
|
|
116
176
|
)
|
|
117
177
|
|
|
118
178
|
with context.begin_transaction():
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator_kit.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: model-generator-kit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: One-shot bootstrap generator for database models, API models, routes, and tests
|
|
5
5
|
Author-email: nuncaeslupus <imarcos@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -140,4 +140,4 @@ Define specifications once in JSON. Generate production-ready scaffolds. Then ma
|
|
|
140
140
|
|
|
141
141
|
---
|
|
142
142
|
|
|
143
|
-
**Model Generator** | Bootstrap Tool for API Backends | v0.1.
|
|
143
|
+
**Model Generator** | Bootstrap Tool for API Backends | v0.1.4
|
|
@@ -1105,6 +1105,223 @@ class TestApiModelsGeneratorPerEntity:
|
|
|
1105
1105
|
assert "class UpdateAuthorRequest(BaseModel):" in author_req
|
|
1106
1106
|
assert "CreatePostRequest" not in author_req
|
|
1107
1107
|
|
|
1108
|
+
def test_password_example_is_not_a_secret(
|
|
1109
|
+
self, project_env_per_entity: Any
|
|
1110
|
+
) -> None:
|
|
1111
|
+
"""The OpenAPI example for a password field must be a non-secret
|
|
1112
|
+
placeholder so secret scanners (GitGuardian) don't flag generated repos."""
|
|
1113
|
+
project_root, config, env = project_env_per_entity
|
|
1114
|
+
model = {
|
|
1115
|
+
"domain": "accounts",
|
|
1116
|
+
"entities": {
|
|
1117
|
+
"User": {
|
|
1118
|
+
"table": "users",
|
|
1119
|
+
"fields": {
|
|
1120
|
+
"id": {
|
|
1121
|
+
"type": "uuid",
|
|
1122
|
+
"primary_key": True,
|
|
1123
|
+
"auto_generate": True,
|
|
1124
|
+
},
|
|
1125
|
+
"password": {"type": "text", "required": True},
|
|
1126
|
+
},
|
|
1127
|
+
}
|
|
1128
|
+
},
|
|
1129
|
+
}
|
|
1130
|
+
result = generate_api_models(model, config, env, project_root)
|
|
1131
|
+
assert isinstance(result, list)
|
|
1132
|
+
content = next(
|
|
1133
|
+
r["content"] for r in result if r["path"].name == "user_requests.py"
|
|
1134
|
+
)
|
|
1135
|
+
assert "SecureP@ssw0rd!" not in content
|
|
1136
|
+
assert '"password": "your-password-here"' in content
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
@pytest.fixture
|
|
1140
|
+
def financial_model() -> dict[str, Any]:
|
|
1141
|
+
"""Single entity exercising signed vs constrained financial fields.
|
|
1142
|
+
|
|
1143
|
+
- unrealized_pnl: no constraint -> signed -> validate_decimal
|
|
1144
|
+
- balance: non_negative -> validate_non_negative_decimal
|
|
1145
|
+
- deposit: positive -> validate_positive_decimal
|
|
1146
|
+
"""
|
|
1147
|
+
return {
|
|
1148
|
+
"domain": "ledger",
|
|
1149
|
+
"description": "Ledger domain with signed and constrained money",
|
|
1150
|
+
"entities": {
|
|
1151
|
+
"Account": {
|
|
1152
|
+
"table": "accounts",
|
|
1153
|
+
"description": "Trading account",
|
|
1154
|
+
"fields": {
|
|
1155
|
+
"id": {
|
|
1156
|
+
"type": "uuid",
|
|
1157
|
+
"primary_key": True,
|
|
1158
|
+
"auto_generate": True,
|
|
1159
|
+
},
|
|
1160
|
+
"unrealized_pnl": {
|
|
1161
|
+
"type": "financial",
|
|
1162
|
+
"required": True,
|
|
1163
|
+
},
|
|
1164
|
+
"balance": {
|
|
1165
|
+
"type": "financial",
|
|
1166
|
+
"required": True,
|
|
1167
|
+
"constraints": [{"type": "non_negative"}],
|
|
1168
|
+
},
|
|
1169
|
+
"deposit": {
|
|
1170
|
+
"type": "financial",
|
|
1171
|
+
"required": True,
|
|
1172
|
+
"constraints": [{"type": "positive"}],
|
|
1173
|
+
},
|
|
1174
|
+
},
|
|
1175
|
+
"timestamps": {"created": True, "updated": True},
|
|
1176
|
+
},
|
|
1177
|
+
},
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
|
|
1181
|
+
class TestFinancialValidatorSelection:
|
|
1182
|
+
"""Signed financial fields must not be forced non-negative.
|
|
1183
|
+
|
|
1184
|
+
Regression: the response/request templates hardcoded
|
|
1185
|
+
validate_non_negative_decimal for every financial field, so any
|
|
1186
|
+
legitimately negative value (PnL, returns) failed validation.
|
|
1187
|
+
"""
|
|
1188
|
+
|
|
1189
|
+
def _content(self, model: dict[str, Any], env_fixture: Any, suffix: str) -> str:
|
|
1190
|
+
project_root, config, env = env_fixture
|
|
1191
|
+
result = generate_api_models(model, config, env, project_root)
|
|
1192
|
+
assert isinstance(result, list)
|
|
1193
|
+
content = next(r["content"] for r in result if r["path"].name.endswith(suffix))
|
|
1194
|
+
assert isinstance(content, str)
|
|
1195
|
+
return content
|
|
1196
|
+
|
|
1197
|
+
def test_response_signed_financial_uses_validate_decimal(
|
|
1198
|
+
self, financial_model: dict[str, Any], project_env_per_entity: Any
|
|
1199
|
+
) -> None:
|
|
1200
|
+
content = self._content(
|
|
1201
|
+
financial_model, project_env_per_entity, "account_response.py"
|
|
1202
|
+
)
|
|
1203
|
+
# Unconstrained financial -> signed validator
|
|
1204
|
+
assert (
|
|
1205
|
+
'_validate_unrealized_pnl = field_validator("unrealized_pnl")'
|
|
1206
|
+
"(validate_decimal)" in content
|
|
1207
|
+
)
|
|
1208
|
+
# Constrained financial -> stays non-negative / positive
|
|
1209
|
+
assert (
|
|
1210
|
+
'_validate_balance = field_validator("balance")'
|
|
1211
|
+
"(validate_non_negative_decimal)" in content
|
|
1212
|
+
)
|
|
1213
|
+
assert (
|
|
1214
|
+
'_validate_deposit = field_validator("deposit")'
|
|
1215
|
+
"(validate_positive_decimal)" in content
|
|
1216
|
+
)
|
|
1217
|
+
|
|
1218
|
+
def test_response_imports_match_validators_used(
|
|
1219
|
+
self, financial_model: dict[str, Any], project_env_per_entity: Any
|
|
1220
|
+
) -> None:
|
|
1221
|
+
content = self._content(
|
|
1222
|
+
financial_model, project_env_per_entity, "account_response.py"
|
|
1223
|
+
)
|
|
1224
|
+
import_line = next(
|
|
1225
|
+
line
|
|
1226
|
+
for line in content.splitlines()
|
|
1227
|
+
if line.startswith("from") and "validators import" in line
|
|
1228
|
+
)
|
|
1229
|
+
assert "validate_decimal" in import_line
|
|
1230
|
+
assert "validate_non_negative_decimal" in import_line
|
|
1231
|
+
assert "validate_positive_decimal" in import_line
|
|
1232
|
+
|
|
1233
|
+
def test_request_signed_financial_uses_validate_decimal(
|
|
1234
|
+
self, financial_model: dict[str, Any], project_env_per_entity: Any
|
|
1235
|
+
) -> None:
|
|
1236
|
+
content = self._content(
|
|
1237
|
+
financial_model, project_env_per_entity, "account_requests.py"
|
|
1238
|
+
)
|
|
1239
|
+
assert (
|
|
1240
|
+
'_validate_unrealized_pnl = field_validator("unrealized_pnl")'
|
|
1241
|
+
"(validate_decimal)" in content
|
|
1242
|
+
)
|
|
1243
|
+
assert (
|
|
1244
|
+
'_validate_balance = field_validator("balance")'
|
|
1245
|
+
"(validate_non_negative_decimal)" in content
|
|
1246
|
+
)
|
|
1247
|
+
assert (
|
|
1248
|
+
'_validate_deposit = field_validator("deposit")'
|
|
1249
|
+
"(validate_positive_decimal)" in content
|
|
1250
|
+
)
|
|
1251
|
+
|
|
1252
|
+
def test_request_imports_match_validators_used(
|
|
1253
|
+
self, financial_model: dict[str, Any], project_env_per_entity: Any
|
|
1254
|
+
) -> None:
|
|
1255
|
+
content = self._content(
|
|
1256
|
+
financial_model, project_env_per_entity, "account_requests.py"
|
|
1257
|
+
)
|
|
1258
|
+
import_line = next(
|
|
1259
|
+
line
|
|
1260
|
+
for line in content.splitlines()
|
|
1261
|
+
if line.startswith("from") and "validators import" in line
|
|
1262
|
+
)
|
|
1263
|
+
assert "validate_decimal" in import_line
|
|
1264
|
+
assert "validate_non_negative_decimal" in import_line
|
|
1265
|
+
assert "validate_positive_decimal" in import_line
|
|
1266
|
+
|
|
1267
|
+
def test_validators_module_defines_validate_decimal(
|
|
1268
|
+
self, project_env_per_entity: Any
|
|
1269
|
+
) -> None:
|
|
1270
|
+
project_root, config, env = project_env_per_entity
|
|
1271
|
+
result = generate_validators(config, env, project_root)
|
|
1272
|
+
assert result is not None
|
|
1273
|
+
assert "def validate_decimal(value: Any) -> str | None:" in result["content"]
|
|
1274
|
+
|
|
1275
|
+
def test_validate_decimal_rejects_non_finite(
|
|
1276
|
+
self, project_env_per_entity: Any
|
|
1277
|
+
) -> None:
|
|
1278
|
+
"""The emitted validate_decimal must reject NaN/Infinity, not just
|
|
1279
|
+
malformed strings (financial fields cannot store non-finite values)."""
|
|
1280
|
+
project_root, config, env = project_env_per_entity
|
|
1281
|
+
result = generate_validators(config, env, project_root)
|
|
1282
|
+
assert result is not None
|
|
1283
|
+
ns: dict[str, Any] = {}
|
|
1284
|
+
exec(result["content"], ns)
|
|
1285
|
+
validate_decimal = ns["validate_decimal"]
|
|
1286
|
+
# Signed finite values pass.
|
|
1287
|
+
assert validate_decimal("-12.5") == "-12.5"
|
|
1288
|
+
assert validate_decimal(None) is None
|
|
1289
|
+
# Non-finite values are rejected.
|
|
1290
|
+
for bad in ("NaN", "Infinity", "-Infinity", "sNaN"):
|
|
1291
|
+
with pytest.raises(ValueError):
|
|
1292
|
+
validate_decimal(bad)
|
|
1293
|
+
with pytest.raises(ValueError):
|
|
1294
|
+
validate_decimal("not-a-number")
|
|
1295
|
+
|
|
1296
|
+
def test_empty_constraints_fall_back_to_signed(
|
|
1297
|
+
self, project_env_per_entity: Any
|
|
1298
|
+
) -> None:
|
|
1299
|
+
"""A financial field with an empty (falsy) `constraints` list falls
|
|
1300
|
+
back to the signed default rather than crashing on the `map` filter."""
|
|
1301
|
+
model = {
|
|
1302
|
+
"domain": "ledger",
|
|
1303
|
+
"entities": {
|
|
1304
|
+
"Account": {
|
|
1305
|
+
"table": "accounts",
|
|
1306
|
+
"fields": {
|
|
1307
|
+
"id": {
|
|
1308
|
+
"type": "uuid",
|
|
1309
|
+
"primary_key": True,
|
|
1310
|
+
"auto_generate": True,
|
|
1311
|
+
},
|
|
1312
|
+
"pnl": {
|
|
1313
|
+
"type": "financial",
|
|
1314
|
+
"required": True,
|
|
1315
|
+
"constraints": [],
|
|
1316
|
+
},
|
|
1317
|
+
},
|
|
1318
|
+
}
|
|
1319
|
+
},
|
|
1320
|
+
}
|
|
1321
|
+
for suffix in ("account_response.py", "account_requests.py"):
|
|
1322
|
+
content = self._content(model, project_env_per_entity, suffix)
|
|
1323
|
+
assert '_validate_pnl = field_validator("pnl")(validate_decimal)' in content
|
|
1324
|
+
|
|
1108
1325
|
|
|
1109
1326
|
class TestGenerateApiInitPerEntity:
|
|
1110
1327
|
"""Per-entity api __init__.py emission."""
|
|
@@ -2858,6 +3075,81 @@ class TestMigrationGenerator:
|
|
|
2858
3075
|
# config.paths.database_models is used in the import path
|
|
2859
3076
|
assert "src.database.models" in env_py["content"]
|
|
2860
3077
|
|
|
3078
|
+
def _env_py(self, minimal_model: dict[str, Any], project_env: Any) -> str:
|
|
3079
|
+
project_root, config, env = project_env
|
|
3080
|
+
result = generate_migration_init(minimal_model, config, env, project_root)
|
|
3081
|
+
assert isinstance(result, list)
|
|
3082
|
+
env_py = next(
|
|
3083
|
+
r for r in result if r["path"] == project_root / "alembic" / "env.py"
|
|
3084
|
+
)
|
|
3085
|
+
content = env_py["content"]
|
|
3086
|
+
assert isinstance(content, str)
|
|
3087
|
+
return content
|
|
3088
|
+
|
|
3089
|
+
def test_env_py_is_valid_python(
|
|
3090
|
+
self, minimal_model: dict[str, Any], project_env: Any
|
|
3091
|
+
) -> None:
|
|
3092
|
+
"""The rendered env.py must parse — guards the new helper blocks."""
|
|
3093
|
+
import ast
|
|
3094
|
+
|
|
3095
|
+
ast.parse(self._env_py(minimal_model, project_env))
|
|
3096
|
+
|
|
3097
|
+
def test_env_py_coerces_async_drivers_to_sync(
|
|
3098
|
+
self, minimal_model: dict[str, Any], project_env: Any
|
|
3099
|
+
) -> None:
|
|
3100
|
+
"""Alembic runs sync, so get_url() must coerce async drivers."""
|
|
3101
|
+
content = self._env_py(minimal_model, project_env)
|
|
3102
|
+
assert "def _coerce_sync_driver(url: str) -> str:" in content
|
|
3103
|
+
assert '"+asyncpg": "+psycopg2",' in content
|
|
3104
|
+
assert '"+aiosqlite": "",' in content
|
|
3105
|
+
# get_url returns the coerced URL, not the raw one.
|
|
3106
|
+
assert "return _coerce_sync_driver(url)" in content
|
|
3107
|
+
# Only the scheme is rewritten, so credentials/db names are never mangled.
|
|
3108
|
+
assert 'scheme, sep, rest = url.partition("://")' in content
|
|
3109
|
+
assert "scheme.endswith(async_driver)" in content
|
|
3110
|
+
|
|
3111
|
+
def test_coerce_sync_driver_behaviour(
|
|
3112
|
+
self, minimal_model: dict[str, Any], project_env: Any
|
|
3113
|
+
) -> None:
|
|
3114
|
+
"""Exercise the generated _coerce_sync_driver in isolation (executing
|
|
3115
|
+
the whole module would run migrations)."""
|
|
3116
|
+
import ast
|
|
3117
|
+
|
|
3118
|
+
content = self._env_py(minimal_model, project_env)
|
|
3119
|
+
tree = ast.parse(content)
|
|
3120
|
+
wanted = {"_ASYNC_TO_SYNC_DRIVERS", "_coerce_sync_driver"}
|
|
3121
|
+
nodes: list[ast.stmt] = [
|
|
3122
|
+
n
|
|
3123
|
+
for n in tree.body
|
|
3124
|
+
if (isinstance(n, ast.FunctionDef) and n.name in wanted)
|
|
3125
|
+
or (
|
|
3126
|
+
isinstance(n, ast.Assign)
|
|
3127
|
+
and any(getattr(t, "id", None) in wanted for t in n.targets)
|
|
3128
|
+
)
|
|
3129
|
+
]
|
|
3130
|
+
ns: dict[str, Any] = {}
|
|
3131
|
+
exec(compile(ast.Module(body=nodes, type_ignores=[]), "<env>", "exec"), ns)
|
|
3132
|
+
coerce = ns["_coerce_sync_driver"]
|
|
3133
|
+
assert (
|
|
3134
|
+
coerce("postgresql+asyncpg://u:p@h/db") == "postgresql+psycopg2://u:p@h/db"
|
|
3135
|
+
)
|
|
3136
|
+
assert coerce("sqlite+aiosqlite:///x.db") == "sqlite:///x.db"
|
|
3137
|
+
# A driver substring inside credentials must NOT be rewritten.
|
|
3138
|
+
assert coerce("postgresql://u:+asyncpg@h/db") == "postgresql://u:+asyncpg@h/db"
|
|
3139
|
+
# Already-sync URLs pass through untouched.
|
|
3140
|
+
assert coerce("postgresql://u:p@h/db") == "postgresql://u:p@h/db"
|
|
3141
|
+
|
|
3142
|
+
def test_env_py_renders_custom_types_with_import(
|
|
3143
|
+
self, minimal_model: dict[str, Any], project_env: Any
|
|
3144
|
+
) -> None:
|
|
3145
|
+
"""render_item must be wired into both configure() calls and import
|
|
3146
|
+
the project's custom column types into generated migrations."""
|
|
3147
|
+
content = self._env_py(minimal_model, project_env)
|
|
3148
|
+
assert "def render_item(type_: str, obj: Any, autogen_context: Any)" in content
|
|
3149
|
+
assert '_CUSTOM_TYPE_MODULE = "src.database.types"' in content
|
|
3150
|
+
# Wired into offline and online configure() calls.
|
|
3151
|
+
assert content.count("render_item=render_item,") == 2
|
|
3152
|
+
|
|
2861
3153
|
def test_custom_migrations_path(
|
|
2862
3154
|
self, minimal_model: dict[str, Any], project_env: Any
|
|
2863
3155
|
) -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/generators/__init__.py
RENAMED
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/generators/api.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/generators/database.py
RENAMED
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/generators/enums.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/generators/migrations.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/schema/model.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/utils/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/utils/constants.py
RENAMED
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/utils/loaders.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/utils/quality.py
RENAMED
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/utils/templates.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/wizard/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/wizard/actions/clean.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator/wizard/prompts.py
RENAMED
|
File without changes
|
{model_generator_kit-0.1.2 → model_generator_kit-0.1.4}/src/model_generator_kit.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|