pyrmute 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.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.
- pyrmute/__init__.py +11 -11
- pyrmute/_migration_manager.py +7 -7
- pyrmute/_schema_manager.py +17 -7
- pyrmute/_version.py +2 -2
- pyrmute/migration_testing.py +4 -4
- pyrmute/model_manager.py +45 -66
- pyrmute/types.py +11 -4
- {pyrmute-0.2.0.dist-info → pyrmute-0.3.0.dist-info}/METADATA +13 -8
- pyrmute-0.3.0.dist-info/RECORD +17 -0
- pyrmute-0.2.0.dist-info/RECORD +0 -17
- {pyrmute-0.2.0.dist-info → pyrmute-0.3.0.dist-info}/WHEEL +0 -0
- {pyrmute-0.2.0.dist-info → pyrmute-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {pyrmute-0.2.0.dist-info → pyrmute-0.3.0.dist-info}/top_level.txt +0 -0
pyrmute/__init__.py
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
"""pyrmute - versioned Pydantic models and schemas with migrations.
|
2
|
-
|
3
|
-
A package for managing versioned Pydantic models with automatic migrations
|
4
|
-
and schema management.
|
5
|
-
"""
|
1
|
+
"""pyrmute - versioned Pydantic models and schemas with migrations."""
|
6
2
|
|
3
|
+
from ._migration_manager import MigrationManager
|
4
|
+
from ._registry import Registry
|
5
|
+
from ._schema_manager import SchemaManager
|
7
6
|
from ._version import __version__
|
8
7
|
from .exceptions import (
|
9
8
|
InvalidVersionError,
|
@@ -21,27 +20,28 @@ from .model_manager import ModelManager
|
|
21
20
|
from .model_version import ModelVersion
|
22
21
|
from .types import (
|
23
22
|
JsonSchema,
|
24
|
-
JsonValue,
|
25
|
-
MigrationData,
|
26
23
|
MigrationFunc,
|
27
|
-
|
24
|
+
ModelData,
|
25
|
+
NestedModelInfo,
|
28
26
|
)
|
29
27
|
|
30
28
|
__all__ = [
|
31
29
|
"InvalidVersionError",
|
32
30
|
"JsonSchema",
|
33
|
-
"JsonValue",
|
34
|
-
"MigrationData",
|
35
31
|
"MigrationError",
|
36
32
|
"MigrationFunc",
|
33
|
+
"MigrationManager",
|
37
34
|
"MigrationTestCase",
|
38
35
|
"MigrationTestResult",
|
39
36
|
"MigrationTestResults",
|
37
|
+
"ModelData",
|
40
38
|
"ModelDiff",
|
41
39
|
"ModelManager",
|
42
|
-
"ModelMetadata",
|
43
40
|
"ModelNotFoundError",
|
44
41
|
"ModelVersion",
|
42
|
+
"NestedModelInfo",
|
43
|
+
"Registry",
|
44
|
+
"SchemaManager",
|
45
45
|
"VersionedModelError",
|
46
46
|
"__version__",
|
47
47
|
]
|
pyrmute/_migration_manager.py
CHANGED
@@ -11,7 +11,7 @@ from pydantic_core import PydanticUndefined
|
|
11
11
|
from ._registry import Registry
|
12
12
|
from .exceptions import MigrationError, ModelNotFoundError
|
13
13
|
from .model_version import ModelVersion
|
14
|
-
from .types import
|
14
|
+
from .types import MigrationFunc, ModelData, ModelName
|
15
15
|
|
16
16
|
|
17
17
|
class MigrationManager:
|
@@ -73,11 +73,11 @@ class MigrationManager:
|
|
73
73
|
|
74
74
|
def migrate(
|
75
75
|
self: Self,
|
76
|
-
data:
|
76
|
+
data: ModelData,
|
77
77
|
name: ModelName,
|
78
78
|
from_version: str | ModelVersion,
|
79
79
|
to_version: str | ModelVersion,
|
80
|
-
) ->
|
80
|
+
) -> ModelData:
|
81
81
|
"""Migrate data from one version to another.
|
82
82
|
|
83
83
|
Args:
|
@@ -224,11 +224,11 @@ class MigrationManager:
|
|
224
224
|
|
225
225
|
def _auto_migrate(
|
226
226
|
self: Self,
|
227
|
-
data:
|
227
|
+
data: ModelData,
|
228
228
|
name: ModelName,
|
229
229
|
from_ver: ModelVersion,
|
230
230
|
to_ver: ModelVersion,
|
231
|
-
) ->
|
231
|
+
) -> ModelData:
|
232
232
|
"""Automatically migrate data when no explicit migration exists.
|
233
233
|
|
234
234
|
This method handles nested Pydantic models recursively, migrating them to their
|
@@ -249,7 +249,7 @@ class MigrationManager:
|
|
249
249
|
from_fields = from_model.model_fields
|
250
250
|
to_fields = to_model.model_fields
|
251
251
|
|
252
|
-
result:
|
252
|
+
result: ModelData = {}
|
253
253
|
|
254
254
|
for field_name, to_field_info in to_fields.items():
|
255
255
|
# Field exists in data, migrate it
|
@@ -313,7 +313,7 @@ class MigrationManager:
|
|
313
313
|
|
314
314
|
def _extract_nested_model_info(
|
315
315
|
self: Self,
|
316
|
-
value:
|
316
|
+
value: ModelData,
|
317
317
|
from_field: FieldInfo | None,
|
318
318
|
to_field: FieldInfo,
|
319
319
|
) -> tuple[ModelName, ModelVersion, ModelVersion] | None:
|
pyrmute/_schema_manager.py
CHANGED
@@ -16,6 +16,7 @@ from .types import (
|
|
16
16
|
JsonValue,
|
17
17
|
ModelMetadata,
|
18
18
|
ModelName,
|
19
|
+
NestedModelInfo,
|
19
20
|
)
|
20
21
|
|
21
22
|
|
@@ -285,7 +286,7 @@ class SchemaManager:
|
|
285
286
|
self: Self,
|
286
287
|
name: ModelName,
|
287
288
|
version: str | ModelVersion,
|
288
|
-
) -> list[
|
289
|
+
) -> list[NestedModelInfo]:
|
289
290
|
"""Get all nested models referenced by a model.
|
290
291
|
|
291
292
|
Args:
|
@@ -293,19 +294,28 @@ class SchemaManager:
|
|
293
294
|
version: Semantic version.
|
294
295
|
|
295
296
|
Returns:
|
296
|
-
List of
|
297
|
+
List of NestedModelInfo.
|
297
298
|
"""
|
298
299
|
ver = ModelVersion.parse(version) if isinstance(version, str) else version
|
299
300
|
model = self.registry.get_model(name, ver)
|
300
301
|
|
301
|
-
nested: list[
|
302
|
+
nested: list[NestedModelInfo] = []
|
302
303
|
|
303
304
|
for field_info in model.model_fields.values():
|
304
305
|
model_type = self._get_model_type_from_field(field_info)
|
305
|
-
if model_type:
|
306
|
-
|
307
|
-
|
308
|
-
|
306
|
+
if not model_type:
|
307
|
+
continue
|
308
|
+
|
309
|
+
model_info = self.registry.get_model_info(model_type)
|
310
|
+
|
311
|
+
if not model_info:
|
312
|
+
continue
|
313
|
+
|
314
|
+
name_, version_ = model_info
|
315
|
+
nested_model_info = NestedModelInfo(name=name_, version=version_)
|
316
|
+
|
317
|
+
if nested_model_info not in nested:
|
318
|
+
nested.append(nested_model_info)
|
309
319
|
|
310
320
|
return nested
|
311
321
|
|
pyrmute/_version.py
CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
28
28
|
commit_id: COMMIT_ID
|
29
29
|
__commit_id__: COMMIT_ID
|
30
30
|
|
31
|
-
__version__ = version = '0.
|
32
|
-
__version_tuple__ = version_tuple = (0,
|
31
|
+
__version__ = version = '0.3.0'
|
32
|
+
__version_tuple__ = version_tuple = (0, 3, 0)
|
33
33
|
|
34
34
|
__commit_id__ = commit_id = None
|
pyrmute/migration_testing.py
CHANGED
@@ -4,7 +4,7 @@ from collections.abc import Iterator
|
|
4
4
|
from dataclasses import dataclass
|
5
5
|
from typing import Self
|
6
6
|
|
7
|
-
from .types import
|
7
|
+
from .types import ModelData
|
8
8
|
|
9
9
|
|
10
10
|
@dataclass
|
@@ -28,8 +28,8 @@ class MigrationTestCase:
|
|
28
28
|
... )
|
29
29
|
"""
|
30
30
|
|
31
|
-
source:
|
32
|
-
target:
|
31
|
+
source: ModelData
|
32
|
+
target: ModelData | None = None
|
33
33
|
description: str = ""
|
34
34
|
|
35
35
|
|
@@ -55,7 +55,7 @@ class MigrationTestResult:
|
|
55
55
|
"""
|
56
56
|
|
57
57
|
test_case: MigrationTestCase
|
58
|
-
actual:
|
58
|
+
actual: ModelData
|
59
59
|
passed: bool
|
60
60
|
error: str | None = None
|
61
61
|
|
pyrmute/model_manager.py
CHANGED
@@ -22,9 +22,9 @@ from .types import (
|
|
22
22
|
DecoratedBaseModel,
|
23
23
|
JsonSchema,
|
24
24
|
JsonSchemaGenerator,
|
25
|
-
MigrationData,
|
26
25
|
MigrationFunc,
|
27
|
-
|
26
|
+
ModelData,
|
27
|
+
NestedModelInfo,
|
28
28
|
)
|
29
29
|
|
30
30
|
|
@@ -55,7 +55,7 @@ class ModelManager:
|
|
55
55
|
>>>
|
56
56
|
>>> # Define migration between versions
|
57
57
|
>>> @manager.migration("User", "1.0.0", "2.0.0")
|
58
|
-
... def migrate(data:
|
58
|
+
... def migrate(data: ModelData) -> ModelData:
|
59
59
|
... return {**data, "email": "unknown@example.com"}
|
60
60
|
>>>
|
61
61
|
>>> # Migrate legacy data
|
@@ -91,9 +91,9 @@ class ModelManager:
|
|
91
91
|
|
92
92
|
def __init__(self: Self) -> None:
|
93
93
|
"""Initialize the versioned model manager."""
|
94
|
-
self.
|
95
|
-
self.
|
96
|
-
self.
|
94
|
+
self._registry = Registry()
|
95
|
+
self._migration_manager = MigrationManager(self._registry)
|
96
|
+
self._schema_manager = SchemaManager(self._registry)
|
97
97
|
|
98
98
|
def model(
|
99
99
|
self: Self,
|
@@ -129,7 +129,7 @@ class ModelManager:
|
|
129
129
|
... class CityV1(BaseModel):
|
130
130
|
... city: City
|
131
131
|
"""
|
132
|
-
return self.
|
132
|
+
return self._registry.register(
|
133
133
|
name, version, schema_generator, enable_ref, backward_compatible
|
134
134
|
)
|
135
135
|
|
@@ -149,11 +149,11 @@ class ModelManager:
|
|
149
149
|
Returns:
|
150
150
|
Decorator function for migration function.
|
151
151
|
"""
|
152
|
-
return self.
|
152
|
+
return self._migration_manager.register_migration(
|
153
|
+
name, from_version, to_version
|
154
|
+
)
|
153
155
|
|
154
|
-
def get(
|
155
|
-
self: Self, name: str, version: str | ModelVersion | None = None
|
156
|
-
) -> type[BaseModel]:
|
156
|
+
def get(self: Self, name: str, version: str | ModelVersion) -> type[BaseModel]:
|
157
157
|
"""Get a model by name and version.
|
158
158
|
|
159
159
|
Args:
|
@@ -163,9 +163,18 @@ class ModelManager:
|
|
163
163
|
Returns:
|
164
164
|
Model class.
|
165
165
|
"""
|
166
|
-
|
167
|
-
|
168
|
-
|
166
|
+
return self._registry.get_model(name, version)
|
167
|
+
|
168
|
+
def get_latest(self: Self, name: str) -> type[BaseModel]:
|
169
|
+
"""Get the latest version of a model by name.
|
170
|
+
|
171
|
+
Args:
|
172
|
+
name: Name of the model.
|
173
|
+
|
174
|
+
Returns:
|
175
|
+
Model class.
|
176
|
+
"""
|
177
|
+
return self._registry.get_latest(name)
|
169
178
|
|
170
179
|
def has_migration_path(
|
171
180
|
self: Self,
|
@@ -200,14 +209,14 @@ class ModelManager:
|
|
200
209
|
else to_version
|
201
210
|
)
|
202
211
|
try:
|
203
|
-
self.
|
212
|
+
self._migration_manager.validate_migration_path(name, from_ver, to_ver)
|
204
213
|
return True
|
205
214
|
except (KeyError, ModelNotFoundError, MigrationError):
|
206
215
|
return False
|
207
216
|
|
208
217
|
def validate_data(
|
209
218
|
self: Self,
|
210
|
-
data:
|
219
|
+
data: ModelData,
|
211
220
|
name: str,
|
212
221
|
version: str | ModelVersion,
|
213
222
|
) -> bool:
|
@@ -241,7 +250,7 @@ class ModelManager:
|
|
241
250
|
|
242
251
|
def migrate(
|
243
252
|
self: Self,
|
244
|
-
data:
|
253
|
+
data: ModelData,
|
245
254
|
name: str,
|
246
255
|
from_version: str | ModelVersion,
|
247
256
|
to_version: str | ModelVersion,
|
@@ -263,11 +272,11 @@ class ModelManager:
|
|
263
272
|
|
264
273
|
def migrate_data(
|
265
274
|
self: Self,
|
266
|
-
data:
|
275
|
+
data: ModelData,
|
267
276
|
name: str,
|
268
277
|
from_version: str | ModelVersion,
|
269
278
|
to_version: str | ModelVersion,
|
270
|
-
) ->
|
279
|
+
) -> ModelData:
|
271
280
|
"""Migrate data between versions.
|
272
281
|
|
273
282
|
Args:
|
@@ -279,11 +288,11 @@ class ModelManager:
|
|
279
288
|
Returns:
|
280
289
|
Raw migrated dictionary.
|
281
290
|
"""
|
282
|
-
return self.
|
291
|
+
return self._migration_manager.migrate(data, name, from_version, to_version)
|
283
292
|
|
284
293
|
def migrate_batch( # noqa: PLR0913
|
285
294
|
self: Self,
|
286
|
-
data_list: Iterable[
|
295
|
+
data_list: Iterable[ModelData],
|
287
296
|
name: str,
|
288
297
|
from_version: str | ModelVersion,
|
289
298
|
to_version: str | ModelVersion,
|
@@ -341,14 +350,14 @@ class ModelManager:
|
|
341
350
|
|
342
351
|
def migrate_batch_data( # noqa: PLR0913
|
343
352
|
self: Self,
|
344
|
-
data_list: Iterable[
|
353
|
+
data_list: Iterable[ModelData],
|
345
354
|
name: str,
|
346
355
|
from_version: str | ModelVersion,
|
347
356
|
to_version: str | ModelVersion,
|
348
357
|
parallel: bool = False,
|
349
358
|
max_workers: int | None = None,
|
350
359
|
use_processes: bool = False,
|
351
|
-
) -> list[
|
360
|
+
) -> list[ModelData]:
|
352
361
|
"""Migrate multiple data items between versions, returning raw dictionaries.
|
353
362
|
|
354
363
|
Args:
|
@@ -393,7 +402,7 @@ class ModelManager:
|
|
393
402
|
|
394
403
|
def migrate_batch_streaming(
|
395
404
|
self: Self,
|
396
|
-
data_list: Iterable[
|
405
|
+
data_list: Iterable[ModelData],
|
397
406
|
name: str,
|
398
407
|
from_version: str | ModelVersion,
|
399
408
|
to_version: str | ModelVersion,
|
@@ -439,12 +448,12 @@ class ModelManager:
|
|
439
448
|
|
440
449
|
def migrate_batch_data_streaming(
|
441
450
|
self: Self,
|
442
|
-
data_list: Iterable[
|
451
|
+
data_list: Iterable[ModelData],
|
443
452
|
name: str,
|
444
453
|
from_version: str | ModelVersion,
|
445
454
|
to_version: str | ModelVersion,
|
446
455
|
chunk_size: int = 100,
|
447
|
-
) -> Iterable[
|
456
|
+
) -> Iterable[ModelData]:
|
448
457
|
"""Migrate data in chunks, yielding raw dictionaries as they complete.
|
449
458
|
|
450
459
|
Useful for large datasets where you want to start processing results before all
|
@@ -548,7 +557,7 @@ class ModelManager:
|
|
548
557
|
Returns:
|
549
558
|
JSON schema dictionary.
|
550
559
|
"""
|
551
|
-
return self.
|
560
|
+
return self._schema_manager.get_schema(name, version, **kwargs)
|
552
561
|
|
553
562
|
def list_models(self: Self) -> list[str]:
|
554
563
|
"""Get list of all registered models.
|
@@ -556,7 +565,7 @@ class ModelManager:
|
|
556
565
|
Returns:
|
557
566
|
List of model names.
|
558
567
|
"""
|
559
|
-
return self.
|
568
|
+
return self._registry.list_models()
|
560
569
|
|
561
570
|
def list_versions(self: Self, name: str) -> list[ModelVersion]:
|
562
571
|
"""Get all versions for a model.
|
@@ -567,7 +576,7 @@ class ModelManager:
|
|
567
576
|
Returns:
|
568
577
|
Sorted list of versions.
|
569
578
|
"""
|
570
|
-
return self.
|
579
|
+
return self._registry.get_versions(name)
|
571
580
|
|
572
581
|
def dump_schemas(
|
573
582
|
self: Self,
|
@@ -582,7 +591,8 @@ class ModelManager:
|
|
582
591
|
output_dir: Directory path for output.
|
583
592
|
indent: JSON indentation level.
|
584
593
|
separate_definitions: If True, create separate schema files for nested
|
585
|
-
models and use $ref to reference them.
|
594
|
+
models and use $ref to reference them. Only applies to models with
|
595
|
+
'enable_ref=True'.
|
586
596
|
ref_template: Template for $ref URLs when separate_definitions=True.
|
587
597
|
Defaults to relative file references if not provided.
|
588
598
|
|
@@ -600,46 +610,15 @@ class ModelManager:
|
|
600
610
|
... ref_template="https://example.com/schemas/{model}_v{version}.json"
|
601
611
|
... )
|
602
612
|
"""
|
603
|
-
self.
|
613
|
+
self._schema_manager.dump_schemas(
|
604
614
|
output_dir, indent, separate_definitions, ref_template
|
605
615
|
)
|
606
616
|
|
607
|
-
def dump_schemas_with_refs(
|
608
|
-
self: Self,
|
609
|
-
output_dir: str | Path,
|
610
|
-
ref_template: str | None = None,
|
611
|
-
indent: int = 2,
|
612
|
-
) -> None:
|
613
|
-
"""Export schemas with separate files for nested models.
|
614
|
-
|
615
|
-
This is a convenience method that calls dump_schemas with
|
616
|
-
separate_definitions=True.
|
617
|
-
|
618
|
-
Args:
|
619
|
-
output_dir: Directory path for output.
|
620
|
-
ref_template: Template for $ref URLs. Supports {model} and {version}
|
621
|
-
placeholders. Defaults to relative file refs.
|
622
|
-
indent: JSON indentation level.
|
623
|
-
|
624
|
-
Example:
|
625
|
-
>>> # Relative file references (default)
|
626
|
-
>>> manager.dump_schemas_with_refs("schemas/")
|
627
|
-
>>>
|
628
|
-
>>> # Absolute URL references
|
629
|
-
>>> manager.dump_schemas_with_refs(
|
630
|
-
... "schemas/",
|
631
|
-
... ref_template="https://example.com/schemas/{model}_v{version}.json"
|
632
|
-
... )
|
633
|
-
"""
|
634
|
-
self.schema_manager.dump_schemas(
|
635
|
-
output_dir, indent, separate_definitions=True, ref_template=ref_template
|
636
|
-
)
|
637
|
-
|
638
617
|
def get_nested_models(
|
639
618
|
self: Self,
|
640
619
|
name: str,
|
641
620
|
version: str | ModelVersion,
|
642
|
-
) -> list[
|
621
|
+
) -> list[NestedModelInfo]:
|
643
622
|
"""Get all nested models used by a model.
|
644
623
|
|
645
624
|
Args:
|
@@ -647,16 +626,16 @@ class ModelManager:
|
|
647
626
|
version: Semantic version.
|
648
627
|
|
649
628
|
Returns:
|
650
|
-
List of
|
629
|
+
List of NestedModelInfo.
|
651
630
|
"""
|
652
|
-
return self.
|
631
|
+
return self._schema_manager.get_nested_models(name, version)
|
653
632
|
|
654
633
|
def test_migration(
|
655
634
|
self: Self,
|
656
635
|
name: str,
|
657
636
|
from_version: str | ModelVersion,
|
658
637
|
to_version: str | ModelVersion,
|
659
|
-
test_cases: list[tuple[
|
638
|
+
test_cases: list[tuple[ModelData, ModelData] | MigrationTestCase],
|
660
639
|
) -> MigrationTestResults:
|
661
640
|
"""Test a migration with multiple test cases.
|
662
641
|
|
pyrmute/types.py
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
from collections.abc import Callable
|
6
|
+
from dataclasses import dataclass
|
6
7
|
from typing import Any, TypeAlias, TypeVar
|
7
8
|
|
8
9
|
from pydantic import BaseModel
|
@@ -19,13 +20,19 @@ JsonSchemaDefinitions: TypeAlias = dict[str, JsonValue]
|
|
19
20
|
JsonSchemaGenerator: TypeAlias = Callable[[type[BaseModel]], JsonSchema]
|
20
21
|
SchemaGenerators: TypeAlias = dict[ModelVersion, JsonSchemaGenerator]
|
21
22
|
|
22
|
-
|
23
|
-
MigrationFunc: TypeAlias = Callable[[
|
24
|
-
|
25
|
-
|
23
|
+
ModelData: TypeAlias = dict[str, Any]
|
24
|
+
MigrationFunc: TypeAlias = Callable[[ModelData], ModelData]
|
26
25
|
MigrationKey: TypeAlias = tuple[ModelVersion, ModelVersion]
|
27
26
|
MigrationMap: TypeAlias = dict[MigrationKey, MigrationFunc]
|
28
27
|
|
29
28
|
ModelName: TypeAlias = str
|
30
29
|
ModelMetadata: TypeAlias = tuple[ModelName, ModelVersion]
|
31
30
|
VersionedModels: TypeAlias = dict[ModelVersion, type[BaseModel]]
|
31
|
+
|
32
|
+
|
33
|
+
@dataclass
|
34
|
+
class NestedModelInfo:
|
35
|
+
"""Contains information about nested models."""
|
36
|
+
|
37
|
+
name: str
|
38
|
+
version: ModelVersion
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: pyrmute
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.3.0
|
4
4
|
Summary: Pydantic model migrations and schemas
|
5
5
|
Author-email: Matt Ferrera <mattferrera@gmail.com>
|
6
6
|
License: MIT
|
@@ -14,6 +14,7 @@ Classifier: Operating System :: POSIX :: Linux
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
17
18
|
Classifier: Natural Language :: English
|
18
19
|
Requires-Python: >=3.11
|
19
20
|
Description-Content-Type: text/markdown
|
@@ -70,7 +71,7 @@ pip install pyrmute
|
|
70
71
|
|
71
72
|
```python
|
72
73
|
from pydantic import BaseModel
|
73
|
-
from pyrmute import ModelManager,
|
74
|
+
from pyrmute import ModelManager, ModelData
|
74
75
|
|
75
76
|
manager = ModelManager()
|
76
77
|
|
@@ -101,7 +102,7 @@ class UserV3(BaseModel):
|
|
101
102
|
|
102
103
|
# Define how to migrate between versions
|
103
104
|
@manager.migration("User", "1.0.0", "2.0.0")
|
104
|
-
def split_name(data:
|
105
|
+
def split_name(data: ModelData) -> ModelData:
|
105
106
|
parts = data["name"].split(" ", 1)
|
106
107
|
return {
|
107
108
|
"first_name": parts[0],
|
@@ -111,7 +112,7 @@ def split_name(data: MigrationData) -> MigrationData:
|
|
111
112
|
|
112
113
|
|
113
114
|
@manager.migration("User", "2.0.0", "3.0.0")
|
114
|
-
def add_email(data:
|
115
|
+
def add_email(data: ModelData) -> ModelData:
|
115
116
|
return {
|
116
117
|
**data,
|
117
118
|
"email": f"{data['first_name'].lower()}@example.com"
|
@@ -133,6 +134,9 @@ print(current_user)
|
|
133
134
|
```python
|
134
135
|
# See exactly what changed between versions
|
135
136
|
diff = manager.diff("User", "1.0.0", "3.0.0")
|
137
|
+
print(f"Added: {diff.added_fields}")
|
138
|
+
print(f"Removed: {diff.removed_fields}")
|
139
|
+
# Render a changelog to Markdown
|
136
140
|
print(diff.to_markdown(header_depth=4))
|
137
141
|
```
|
138
142
|
|
@@ -236,8 +240,9 @@ manager.dump_schemas("schemas/")
|
|
236
240
|
# Creates: User_v1.0.0.json, User_v2.0.0.json, User_v3.0.0.json
|
237
241
|
|
238
242
|
# Use separate files with $ref for nested models with 'enable_ref=True'.
|
239
|
-
manager.
|
243
|
+
manager.dump_schemas(
|
240
244
|
"schemas/",
|
245
|
+
separate_definitions=True,
|
241
246
|
ref_template="https://api.example.com/schemas/{model}_v{version}.json"
|
242
247
|
)
|
243
248
|
```
|
@@ -267,7 +272,7 @@ config = manager.migrate({"timeout": 60}, "Config", "1.0.0", "2.0.0")
|
|
267
272
|
```python
|
268
273
|
from datetime import datetime
|
269
274
|
from pydantic import BaseModel, EmailStr
|
270
|
-
from pyrmute import ModelManager,
|
275
|
+
from pyrmute import ModelManager, ModelData
|
271
276
|
|
272
277
|
manager = ModelManager()
|
273
278
|
|
@@ -308,12 +313,12 @@ class OrderV3(BaseModel):
|
|
308
313
|
|
309
314
|
# Define migrations
|
310
315
|
@manager.migration("Order", "1.0.0", "2.0.0")
|
311
|
-
def add_customer_email(data:
|
316
|
+
def add_customer_email(data: ModelData) -> ModelData:
|
312
317
|
return {**data, "customer_email": "customer@example.com"}
|
313
318
|
|
314
319
|
|
315
320
|
@manager.migration("Order", "2.0.0", "3.0.0")
|
316
|
-
def structure_items(data:
|
321
|
+
def structure_items(data: ModelData) -> ModelData:
|
317
322
|
# Convert simple strings to structured items
|
318
323
|
structured_items = [
|
319
324
|
{
|
@@ -0,0 +1,17 @@
|
|
1
|
+
pyrmute/__init__.py,sha256=vB0WBe3CukMBDnK0XP4qqehbMM4z_TUQMcTVPCUyt6Q,1082
|
2
|
+
pyrmute/_migration_manager.py,sha256=KregnRUKqF1TC9XIpGAHpQvFlnRTEnp2X6Q2sAay8D4,12489
|
3
|
+
pyrmute/_registry.py,sha256=iUjMPd6CYgyvWT8PxZqHWBZnsHrX25fOPDi_-k_QDJs,6124
|
4
|
+
pyrmute/_schema_manager.py,sha256=eun8PTL9Gv1XAMVKmE3tYmjdrcf701-IapUXjb6WDL0,12122
|
5
|
+
pyrmute/_version.py,sha256=5zTqm8rgXsWYBpB2M3Zw_K1D-aV8wP7NsBLrmMKkrAQ,704
|
6
|
+
pyrmute/exceptions.py,sha256=Q57cUuzzMdkIl5Q0_VyLobpdB0WcrE0ggfC-LBoX2Uo,1681
|
7
|
+
pyrmute/migration_testing.py,sha256=dOR8BDzmz4mFAI4hFtDUCEMS8Qc8qqD_iOV0qRai-qM,4967
|
8
|
+
pyrmute/model_diff.py,sha256=vMa2NTYFqt9E7UYDZH4PQmLcoxQw5Sj-nPpUHB_53Ig,9594
|
9
|
+
pyrmute/model_manager.py,sha256=e3UKFo79pkseCUFXIzW2_onu3GYjAnY1FR4JR_QF-Gc,24596
|
10
|
+
pyrmute/model_version.py,sha256=ftNDuJlN3S5ZKQK8DKqqwfBDRiz4rGCYn-aJ3n6Zmqk,2025
|
11
|
+
pyrmute/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
+
pyrmute/types.py,sha256=56IH8Rl9AmVh_w3V6PbSSEwaPrBSfc4pYrtcxodvlT0,1187
|
13
|
+
pyrmute-0.3.0.dist-info/licenses/LICENSE,sha256=otWInySiZeGwhHqQQ7n7nxM5QBSBe2CzeGEmQDZEz8Q,1119
|
14
|
+
pyrmute-0.3.0.dist-info/METADATA,sha256=jnRgO76ovFaktYKqq5BMf4tpUc_DYO_drET7c1hPVk0,9580
|
15
|
+
pyrmute-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
16
|
+
pyrmute-0.3.0.dist-info/top_level.txt,sha256=C8QtzqE6yBHkeewSp1QewvsyeHj_VQLYjSa5HLtMiow,8
|
17
|
+
pyrmute-0.3.0.dist-info/RECORD,,
|
pyrmute-0.2.0.dist-info/RECORD
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
pyrmute/__init__.py,sha256=5XCoTvVxwlDF15tgv6pNGYsqRlZ8-t4VvxkobImiY54,1033
|
2
|
-
pyrmute/_migration_manager.py,sha256=rdE8QS4LWO-BWEE11TW64whZHsdTEutkBPpKlKY6gS8,12517
|
3
|
-
pyrmute/_registry.py,sha256=iUjMPd6CYgyvWT8PxZqHWBZnsHrX25fOPDi_-k_QDJs,6124
|
4
|
-
pyrmute/_schema_manager.py,sha256=OFqq96D0A9Wmwmne8QEtzgBYWeqOBGiGecfxFpRVVQ8,11939
|
5
|
-
pyrmute/_version.py,sha256=Dg8AmJomLVpjKL6prJylOONZAPRtB86LOce7dorQS_A,704
|
6
|
-
pyrmute/exceptions.py,sha256=Q57cUuzzMdkIl5Q0_VyLobpdB0WcrE0ggfC-LBoX2Uo,1681
|
7
|
-
pyrmute/migration_testing.py,sha256=ZofbVAgqSIm1WvOovYE6QLdorEtjcU9bYeUZU_9mqlk,4983
|
8
|
-
pyrmute/model_diff.py,sha256=vMa2NTYFqt9E7UYDZH4PQmLcoxQw5Sj-nPpUHB_53Ig,9594
|
9
|
-
pyrmute/model_manager.py,sha256=BVa8yznbvdOlrHPpBA7PLdDyOUnnwFHxp9O_zUOgjMk,25526
|
10
|
-
pyrmute/model_version.py,sha256=ftNDuJlN3S5ZKQK8DKqqwfBDRiz4rGCYn-aJ3n6Zmqk,2025
|
11
|
-
pyrmute/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
-
pyrmute/types.py,sha256=1C5PrgvxUAtGjbsmcYpgOq0BrLr4BgEwWDe4Nl6JhRc,1038
|
13
|
-
pyrmute-0.2.0.dist-info/licenses/LICENSE,sha256=otWInySiZeGwhHqQQ7n7nxM5QBSBe2CzeGEmQDZEz8Q,1119
|
14
|
-
pyrmute-0.2.0.dist-info/METADATA,sha256=V6qStLPkl95Uuitb4UlFJEFKzxM76mTopmhGlvkKmTs,9437
|
15
|
-
pyrmute-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
16
|
-
pyrmute-0.2.0.dist-info/top_level.txt,sha256=C8QtzqE6yBHkeewSp1QewvsyeHj_VQLYjSa5HLtMiow,8
|
17
|
-
pyrmute-0.2.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|