pyrmute 0.4.0__py3-none-any.whl → 0.5.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 CHANGED
@@ -19,8 +19,10 @@ from .migration_testing import (
19
19
  from .model_diff import ModelDiff
20
20
  from .model_manager import ModelManager
21
21
  from .model_version import ModelVersion
22
+ from .schema_config import SchemaConfig
22
23
  from .types import (
23
24
  JsonSchema,
25
+ JsonSchemaMode,
24
26
  MigrationFunc,
25
27
  ModelData,
26
28
  NestedModelInfo,
@@ -29,6 +31,7 @@ from .types import (
29
31
  __all__ = [
30
32
  "InvalidVersionError",
31
33
  "JsonSchema",
34
+ "JsonSchemaMode",
32
35
  "MigrationError",
33
36
  "MigrationFunc",
34
37
  "MigrationManager",
@@ -43,6 +46,7 @@ __all__ = [
43
46
  "ModelVersion",
44
47
  "NestedModelInfo",
45
48
  "Registry",
49
+ "SchemaConfig",
46
50
  "SchemaManager",
47
51
  "VersionedModelError",
48
52
  "__version__",
pyrmute/_registry.py CHANGED
@@ -10,11 +10,9 @@ from .exceptions import ModelNotFoundError
10
10
  from .model_version import ModelVersion
11
11
  from .types import (
12
12
  DecoratedBaseModel,
13
- JsonSchemaGenerator,
14
13
  MigrationMap,
15
14
  ModelMetadata,
16
15
  ModelName,
17
- SchemaGenerators,
18
16
  VersionedModels,
19
17
  )
20
18
 
@@ -28,7 +26,6 @@ class Registry:
28
26
  Attributes:
29
27
  _models: Dictionary mapping model names to version-model mappings.
30
28
  _migrations: Dictionary storing migration functions between versions.
31
- _schema_generators: Dictionary storing custom schema generators.
32
29
  _model_metadata: Dictionary mapping model classes to (name, version).
33
30
  _ref_enabled: Dictionary tracking which models have enable_ref=True.
34
31
  """
@@ -37,7 +34,6 @@ class Registry:
37
34
  """Initialize the model registry."""
38
35
  self._models: dict[ModelName, VersionedModels] = defaultdict(dict)
39
36
  self._migrations: dict[ModelName, MigrationMap] = defaultdict(dict)
40
- self._schema_generators: dict[ModelName, SchemaGenerators] = defaultdict(dict)
41
37
  self._model_metadata: dict[type[BaseModel], ModelMetadata] = {}
42
38
  self._ref_enabled: dict[ModelName, set[ModelVersion]] = defaultdict(set)
43
39
  self._backward_compatible_enabled: dict[ModelName, set[ModelVersion]] = (
@@ -48,7 +44,6 @@ class Registry:
48
44
  self: Self,
49
45
  name: ModelName,
50
46
  version: str | ModelVersion,
51
- schema_generator: JsonSchemaGenerator | None = None,
52
47
  enable_ref: bool = False,
53
48
  backward_compatible: bool = False,
54
49
  ) -> Callable[[type[DecoratedBaseModel]], type[DecoratedBaseModel]]:
@@ -57,7 +52,6 @@ class Registry:
57
52
  Args:
58
53
  name: Name of the model.
59
54
  version: Semantic version string or ModelVersion instance.
60
- schema_generator: Optional custom schema generator function.
61
55
  enable_ref: If True, this model can be referenced via $ref in separate
62
56
  schema files. If False, it will always be inlined.
63
57
  backward_compatible: If True, this model does not need a migration function
@@ -78,8 +72,6 @@ class Registry:
78
72
  def decorator(cls: type[DecoratedBaseModel]) -> type[DecoratedBaseModel]:
79
73
  self._models[name][ver] = cls
80
74
  self._model_metadata[cls] = (name, ver)
81
- if schema_generator:
82
- self._schema_generators[name][ver] = schema_generator
83
75
  if enable_ref:
84
76
  self._ref_enabled[name].add(ver)
85
77
  if backward_compatible:
@@ -1,22 +1,27 @@
1
- """Schema manager."""
1
+ """Schema manager with customizable generation and transformers."""
2
2
 
3
3
  import json
4
+ from collections import defaultdict
4
5
  from pathlib import Path
5
- from typing import Any, Self, get_args, get_origin
6
+ from typing import Any, Self, cast, get_args, get_origin
6
7
 
7
8
  from pydantic import BaseModel
8
9
  from pydantic.fields import FieldInfo
10
+ from pydantic.json_schema import GenerateJsonSchema
9
11
 
10
12
  from ._registry import Registry
11
13
  from .exceptions import ModelNotFoundError
12
14
  from .model_version import ModelVersion
15
+ from .schema_config import SchemaConfig
13
16
  from .types import (
14
17
  JsonSchema,
15
18
  JsonSchemaDefinitions,
19
+ JsonSchemaGenerator,
16
20
  JsonValue,
17
21
  ModelMetadata,
18
22
  ModelName,
19
23
  NestedModelInfo,
24
+ SchemaTransformer,
20
25
  )
21
26
 
22
27
 
@@ -24,53 +29,217 @@ class SchemaManager:
24
29
  """Manager for JSON schema generation and export.
25
30
 
26
31
  Handles schema generation from Pydantic models with support for custom schema
27
- generators and sub-schema references.
32
+ generators, global configuration, per-call overrides, and schema transformers.
28
33
 
29
34
  Attributes:
30
35
  registry: Reference to the Registry.
36
+ default_config: Default schema generation configuration.
31
37
  """
32
38
 
33
- def __init__(self: Self, registry: Registry) -> None:
39
+ def __init__(
40
+ self: Self, registry: Registry, default_config: SchemaConfig | None = None
41
+ ) -> None:
34
42
  """Initialize the schema manager.
35
43
 
36
44
  Args:
37
45
  registry: Registry instance to use.
46
+ default_config: Default configuration for schema generation.
38
47
  """
39
48
  self.registry = registry
49
+ self.default_config = default_config or SchemaConfig()
50
+ self._transformers: dict[
51
+ tuple[ModelName, ModelVersion], list[SchemaTransformer]
52
+ ] = defaultdict(list)
53
+
54
+ def set_default_schema_generator(
55
+ self: Self, generator: JsonSchemaGenerator | type[GenerateJsonSchema]
56
+ ) -> None:
57
+ """Set the default schema generator for all schemas.
58
+
59
+ Args:
60
+ generator: Custom schema generator - either a callable or GenerateJsonSchema
61
+ class.
62
+
63
+ Example (Callable):
64
+ >>> def custom_gen(model: type[BaseModel]) -> JsonSchema:
65
+ ... schema = model.model_json_schema()
66
+ ... schema["x-custom"] = True
67
+ ... return schema
68
+ >>>
69
+ >>> manager.set_default_schema_generator(custom_gen)
70
+
71
+ Example (Class):
72
+ >>> from pydantic.json_schema import GenerateJsonSchema
73
+ >>>
74
+ >>> class CustomGenerator(GenerateJsonSchema):
75
+ ... def generate(
76
+ ... self,
77
+ ... schema: Mapping[str, Any],
78
+ ... mode: JsonSchemaMode = "validation"
79
+ ... ) -> JsonSchema:
80
+ ... json_schema = super().generate(schema, mode=mode)
81
+ ... json_schema["x-custom"] = True
82
+ ... return json_schema
83
+ >>>
84
+ >>> manager.set_default_schema_generator(CustomGenerator)
85
+ """
86
+ self.default_config.schema_generator = generator
87
+
88
+ def register_transformer(
89
+ self: Self,
90
+ name: ModelName,
91
+ version: str | ModelVersion,
92
+ transformer: SchemaTransformer,
93
+ ) -> None:
94
+ """Register a schema transformer for a specific model version.
95
+
96
+ Transformers are applied after schema generation, allowing simple
97
+ post-processing of schemas without needing to customize the generation process
98
+ itself.
99
+
100
+ Args:
101
+ name: Name of the model.
102
+ version: Model version.
103
+ transformer: Function that takes and returns a JsonSchema.
104
+
105
+ Example:
106
+ >>> def add_examples(schema: JsonSchema) -> JsonSchema:
107
+ ... schema["examples"] = [{"name": "John", "age": 30}]
108
+ ... return schema
109
+ >>>
110
+ >>> manager.register_transformer("User", "1.0.0", add_examples)
111
+ """
112
+ ver = ModelVersion.parse(version) if isinstance(version, str) else version
113
+ key = (name, ver)
114
+ self._transformers[key].append(transformer)
40
115
 
41
116
  def get_schema(
42
117
  self: Self,
43
118
  name: ModelName,
44
119
  version: str | ModelVersion,
120
+ config: SchemaConfig | None = None,
121
+ apply_transformers: bool = True,
45
122
  **schema_kwargs: Any,
46
123
  ) -> JsonSchema:
47
124
  """Get JSON schema for a specific model version.
48
125
 
126
+ Execution order:
127
+ 1. Generate base schema using Pydantic
128
+ 2. Apply custom generator (if configured)
129
+ 3. Apply registered transformers (if any)
130
+
49
131
  Args:
50
132
  name: Name of the model.
51
133
  version: Semantic version.
52
- **schema_kwargs: Additional arguments for schema generation.
134
+ config: Optional schema configuration (overrides defaults).
135
+ apply_transformers: If False, skip transformer application.
136
+ **schema_kwargs: Additional arguments for schema generation (overrides
137
+ config).
53
138
 
54
139
  Returns:
55
140
  JSON schema dictionary.
141
+
142
+ Example:
143
+ >>> # Use default config
144
+ >>> schema = manager.get_schema("User", "1.0.0")
145
+ >>>
146
+ >>> # Override with custom config
147
+ >>> config = SchemaConfig(mode="serialization", by_alias=False)
148
+ >>> schema = manager.get_schema("User", "1.0.0", config=config)
149
+ >>>
150
+ >>> # Quick override with kwargs
151
+ >>> schema = manager.get_schema("User", "1.0.0", mode="serialization")
152
+ >>>
153
+ >>> # Get base schema without transformers
154
+ >>> base_schema = manager.get_schema(
155
+ ... "User", "1.0.0",
156
+ ... apply_transformers=False
157
+ ... )
56
158
  """
57
159
  ver = ModelVersion.parse(version) if isinstance(version, str) else version
58
160
  model = self.registry.get_model(name, ver)
59
161
 
60
- if (
61
- name in self.registry._schema_generators
62
- and ver in self.registry._schema_generators[name]
63
- ):
64
- generator = self.registry._schema_generators[name][ver]
65
- return generator(model)
162
+ # Always use the config-based approach
163
+ final_config = self.default_config
164
+ if config is not None:
165
+ final_config = final_config.merge_with(config)
166
+
167
+ if schema_kwargs:
168
+ kwargs_config = SchemaConfig(extra_kwargs=schema_kwargs)
169
+ final_config = final_config.merge_with(kwargs_config)
170
+
171
+ schema: JsonSchema
172
+ if final_config.is_callable_generator():
173
+ schema = final_config.schema_generator(model) # type: ignore
174
+ else:
175
+ schema = model.model_json_schema(**final_config.to_kwargs())
176
+
177
+ if apply_transformers:
178
+ key = (name, ver)
179
+ if key in self._transformers:
180
+ for transformer in self._transformers[key]:
181
+ schema = transformer(schema)
182
+
183
+ return schema
184
+
185
+ def get_transformers(
186
+ self: Self,
187
+ name: ModelName,
188
+ version: str | ModelVersion,
189
+ ) -> list[SchemaTransformer]:
190
+ """Get all transformers registered for a model version.
191
+
192
+ Args:
193
+ name: Name of the model.
194
+ version: Model version.
195
+
196
+ Returns:
197
+ List of transformer functions.
198
+ """
199
+ ver = ModelVersion.parse(version) if isinstance(version, str) else version
200
+ key = (name, ver)
201
+ return self._transformers.get(key, [])
202
+
203
+ def clear_transformers(
204
+ self: Self,
205
+ name: ModelName | None = None,
206
+ version: str | ModelVersion | None = None,
207
+ ) -> None:
208
+ """Clear registered transformers.
66
209
 
67
- return model.model_json_schema(**schema_kwargs)
210
+ Args:
211
+ name: Optional model name. If None, clears all transformers.
212
+ version: Optional version. If None (but name provided), clears all versions
213
+ of that model.
214
+
215
+ Example:
216
+ >>> # Clear all transformers
217
+ >>> manager.clear_transformers()
218
+ >>>
219
+ >>> # Clear all User transformers
220
+ >>> manager.clear_transformers("User")
221
+ >>>
222
+ >>> # Clear specific version
223
+ >>> manager.clear_transformers("User", "1.0.0")
224
+ """
225
+ if name is None:
226
+ self._transformers.clear()
227
+ elif version is None:
228
+ keys_to_remove = [key for key in self._transformers if key[0] == name]
229
+ for key in keys_to_remove:
230
+ del self._transformers[key]
231
+ else:
232
+ ver = ModelVersion.parse(version) if isinstance(version, str) else version
233
+ key = (name, ver)
234
+ if key in self._transformers:
235
+ del self._transformers[key]
68
236
 
69
237
  def get_schema_with_separate_defs(
70
238
  self: Self,
71
239
  name: ModelName,
72
240
  version: str | ModelVersion,
73
241
  ref_template: str = "{model}_v{version}.json",
242
+ config: SchemaConfig | None = None,
74
243
  **schema_kwargs: Any,
75
244
  ) -> JsonSchema:
76
245
  """Get JSON schema with separate definition files for nested models.
@@ -83,6 +252,7 @@ class SchemaManager:
83
252
  version: Semantic version.
84
253
  ref_template: Template for generating $ref URLs. Supports {model} and
85
254
  {version} placeholders.
255
+ config: Optional schema configuration.
86
256
  **schema_kwargs: Additional arguments for schema generation.
87
257
 
88
258
  Returns:
@@ -91,16 +261,16 @@ class SchemaManager:
91
261
  Example:
92
262
  >>> schema = manager.get_schema_with_separate_defs(
93
263
  ... "User", "2.0.0",
94
- ... ref_template="https://example.com/schemas/{model}_v{version}.json"
264
+ ... ref_template="https://example.com/schemas/{model}_v{version}.json",
265
+ ... mode="serialization"
95
266
  ... )
96
267
  """
97
268
  ver = ModelVersion.parse(version) if isinstance(version, str) else version
98
- schema = self.get_schema(name, ver, **schema_kwargs)
269
+ schema = self.get_schema(name, ver, config=config, **schema_kwargs)
99
270
 
100
- # Extract and replace definitions with external references
101
271
  if "$defs" in schema or "definitions" in schema:
102
272
  defs_key = "$defs" if "$defs" in schema else "definitions"
103
- definitions: JsonSchemaDefinitions = schema.pop(defs_key, {}) # type: ignore[assignment]
273
+ definitions = cast("JsonSchemaDefinitions", schema.pop(defs_key, {}))
104
274
 
105
275
  # Update all $ref in the schema to point to external files
106
276
  schema = self._replace_refs_with_external(schema, definitions, ref_template)
@@ -209,11 +379,14 @@ class SchemaManager:
209
379
  return (name, version)
210
380
  return None
211
381
 
212
- def get_all_schemas(self: Self, name: ModelName) -> dict[ModelVersion, JsonSchema]:
382
+ def get_all_schemas(
383
+ self: Self, name: ModelName, config: SchemaConfig | None = None
384
+ ) -> dict[ModelVersion, JsonSchema]:
213
385
  """Get all schemas for a model across all versions.
214
386
 
215
387
  Args:
216
388
  name: Name of the model.
389
+ config: Optional schema configuration.
217
390
 
218
391
  Returns:
219
392
  Dictionary mapping versions to their schemas.
@@ -225,7 +398,7 @@ class SchemaManager:
225
398
  raise ModelNotFoundError(name)
226
399
 
227
400
  return {
228
- version: self.get_schema(name, version)
401
+ version: self.get_schema(name, version, config=config)
229
402
  for version in self.registry._models[name]
230
403
  }
231
404
 
@@ -235,6 +408,7 @@ class SchemaManager:
235
408
  indent: int = 2,
236
409
  separate_definitions: bool = False,
237
410
  ref_template: str | None = None,
411
+ config: SchemaConfig | None = None,
238
412
  ) -> None:
239
413
  """Dump all schemas to JSON files.
240
414
 
@@ -245,27 +419,24 @@ class SchemaManager:
245
419
  models that have enable_ref=True.
246
420
  ref_template: Template for $ref URLs when separate_definitions=True.
247
421
  Defaults to relative file references if not provided.
422
+ config: Optional schema configuration for all exported schemas.
248
423
 
249
424
  Example:
250
- >>> # Inline definitions (default)
251
- >>> manager.dump_schemas("schemas/")
252
- >>>
253
- >>> # Separate sub-schemas with relative refs (when enable_ref=True models)
254
- >>> manager.dump_schemas("schemas/", separate_definitions=True)
255
- >>>
256
- >>> # Separate sub-schemas with absolute URLs
257
- >>> manager.dump_schemas(
258
- ... "schemas/",
259
- ... separate_definitions=True,
260
- ... ref_template="https://example.com/schemas/{model}_v{version}.json"
425
+ >>> # Export with custom schema generator
426
+ >>> config = SchemaConfig(
427
+ ... schema_generator=CustomGenerator,
428
+ ... mode="serialization"
261
429
  ... )
430
+ >>> manager.dump_schemas("schemas/", config=config)
262
431
  """
263
432
  output_path = Path(output_dir)
264
433
  output_path.mkdir(parents=True, exist_ok=True)
265
434
 
266
435
  if not separate_definitions:
267
436
  for name in self.registry._models:
268
- for version, schema in self.get_all_schemas(name).items():
437
+ for version, schema in self.get_all_schemas(
438
+ name, config=config
439
+ ).items():
269
440
  file_path = output_path / f"{name}_v{version}.json"
270
441
  with open(file_path, "w", encoding="utf-8") as f:
271
442
  json.dump(schema, f, indent=indent)
@@ -276,7 +447,7 @@ class SchemaManager:
276
447
  for name in self.registry._models:
277
448
  for version in self.registry._models[name]:
278
449
  schema = self.get_schema_with_separate_defs(
279
- name, version, ref_template
450
+ name, version, ref_template, config=config
280
451
  )
281
452
  file_path = output_path / f"{name}_v{version}.json"
282
453
  with open(file_path, "w", encoding="utf-8") as f:
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.4.0'
32
- __version_tuple__ = version_tuple = (0, 4, 0)
31
+ __version__ = version = '0.5.0'
32
+ __version_tuple__ = version_tuple = (0, 5, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
pyrmute/model_manager.py CHANGED
@@ -1,4 +1,4 @@
1
- """Model manager."""
1
+ """ModelManager class."""
2
2
 
3
3
  from collections.abc import Callable, Iterable
4
4
  from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
@@ -6,6 +6,7 @@ from pathlib import Path
6
6
  from typing import Any, Self
7
7
 
8
8
  from pydantic import BaseModel
9
+ from pydantic.json_schema import GenerateJsonSchema
9
10
 
10
11
  from ._migration_manager import MigrationManager
11
12
  from ._registry import Registry
@@ -19,6 +20,7 @@ from .migration_testing import (
19
20
  )
20
21
  from .model_diff import ModelDiff
21
22
  from .model_version import ModelVersion
23
+ from .schema_config import SchemaConfig
22
24
  from .types import (
23
25
  DecoratedBaseModel,
24
26
  JsonSchema,
@@ -26,20 +28,16 @@ from .types import (
26
28
  MigrationFunc,
27
29
  ModelData,
28
30
  NestedModelInfo,
31
+ SchemaTransformer,
29
32
  )
30
33
 
31
34
 
32
35
  class ModelManager:
33
- """High-level interface for versioned model management.
36
+ """High-level interface for versioned model management and schema generation.
34
37
 
35
38
  ModelManager provides a unified API for managing schema evolution across different
36
39
  versions of Pydantic models. It handles model registration, automatic migration
37
- between versions, schema generation, and batch processing operations.
38
-
39
- Attributes:
40
- registry: Registry instance managing all registered model versions.
41
- migration_manager: MigrationManager instance handling migration logic and paths.
42
- schema_manager: SchemaManager instance for JSON schema generation and export.
40
+ between versions, customizable schema generation, and batch processing operations.
43
41
 
44
42
  Basic Usage:
45
43
  >>> manager = ModelManager()
@@ -64,6 +62,56 @@ class ModelManager:
64
62
  >>> user = manager.migrate(old_data, "User", "1.0.0", "2.0.0")
65
63
  >>> # Result: UserV2(name="Alice", email="unknown@example.com")
66
64
 
65
+ Custom Schema Generation:
66
+ >>> from pydantic.json_schema import GenerateJsonSchema
67
+ >>>
68
+ >>> class CustomSchemaGenerator(GenerateJsonSchema):
69
+ ... '''Add custom metadata to all schemas.'''
70
+ ... def generate(
71
+ ... self,
72
+ ... schema: Mapping[str, Any],
73
+ ... mode: JsonSchemaMode = "validation"
74
+ ... ) -> JsonSchema:
75
+ ... json_schema = super().generate(schema, mode=mode)
76
+ ... json_schema["x-company"] = "Acme"
77
+ ... json_schema["$schema"] = self.schema_dialect
78
+ ... return json_schema
79
+ >>>
80
+ >>> # Set at manager level (applies to all schemas)
81
+ >>> manager = ModelManager(
82
+ ... default_schema_config=SchemaConfig(
83
+ ... schema_generator=CustomSchemaGenerator,
84
+ ... mode="validation",
85
+ ... by_alias=True
86
+ ... )
87
+ ... )
88
+ >>>
89
+ >>> @manager.model("User", "1.0.0")
90
+ ... class User(BaseModel):
91
+ ... name: str = Field(title="Full Name")
92
+ ... email: str
93
+ >>>
94
+ >>> # Get schema with default config
95
+ >>> schema = manager.get_schema("User", "1.0.0")
96
+ >>> # Will include x-company: 'Acme'
97
+
98
+ Schema Transformers:
99
+ >>> manager = ModelManager()
100
+ >>>
101
+ >>> @manager.model("Product", "1.0.0")
102
+ ... class Product(BaseModel):
103
+ ... name: str
104
+ ... price: float
105
+ >>>
106
+ >>> # Add transformer for specific model
107
+ >>> @manager.schema_transformer("Product", "1.0.0")
108
+ ... def add_examples(schema: JsonSchema) -> JsonSchema:
109
+ ... schema["examples"] = [{"name": "Widget", "price": 9.99}]
110
+ ... return schema
111
+ >>>
112
+ >>> schema = manager.get_schema("Product", "1.0.0")
113
+ >>> # Will include examples
114
+
67
115
  Advanced Features:
68
116
  >>> # Batch migration with parallel processing
69
117
  >>> users = manager.migrate_batch(
@@ -72,7 +120,9 @@ class ModelManager:
72
120
  ... )
73
121
  >>>
74
122
  >>> # Stream large datasets efficiently
75
- >>> for user in manager.migrate_batch_streaming(large_dataset, "User", "1.0.0", "2.0.0"):
123
+ >>> for user in manager.migrate_batch_streaming(
124
+ ... large_dataset, "User", "1.0.0", "2.0.0"
125
+ ... ):
76
126
  ... save_to_database(user)
77
127
  >>>
78
128
  >>> # Compare versions and export schemas
@@ -84,23 +134,32 @@ class ModelManager:
84
134
  >>> results = manager.test_migration(
85
135
  ... "User", "1.0.0", "2.0.0",
86
136
  ... test_cases=[
87
- ... ({"name": "Alice"}, {"name": "Alice", "email": "unknown@example.com"})
137
+ ... (
138
+ ... {"name": "Alice"},
139
+ ... {"name": "Alice", "email": "unknown@example.com"}
140
+ ... )
88
141
  ... ]
89
142
  ... )
90
143
  >>> results.assert_all_passed()
91
- """ # noqa: E501
144
+ """
145
+
146
+ def __init__(self: Self, default_schema_config: SchemaConfig | None = None) -> None:
147
+ """Initialize the versioned model manager.
92
148
 
93
- def __init__(self: Self) -> None:
94
- """Initialize the versioned model manager."""
149
+ Args:
150
+ default_schema_config: Default configuration for schema generation
151
+ applied to all schema operations unless overridden.
152
+ """
95
153
  self._registry = Registry()
96
154
  self._migration_manager = MigrationManager(self._registry)
97
- self._schema_manager = SchemaManager(self._registry)
155
+ self._schema_manager = SchemaManager(
156
+ self._registry, default_config=default_schema_config
157
+ )
98
158
 
99
159
  def model(
100
160
  self: Self,
101
161
  name: str,
102
162
  version: str | ModelVersion,
103
- schema_generator: JsonSchemaGenerator | None = None,
104
163
  enable_ref: bool = False,
105
164
  backward_compatible: bool = False,
106
165
  ) -> Callable[[type[DecoratedBaseModel]], type[DecoratedBaseModel]]:
@@ -109,7 +168,6 @@ class ModelManager:
109
168
  Args:
110
169
  name: Name of the model.
111
170
  version: Semantic version.
112
- schema_generator: Optional custom schema generator.
113
171
  enable_ref: If True, this model can be referenced via $ref in separate
114
172
  schema files. If False, it will always be inlined.
115
173
  backward_compatible: If True, this model does not need a migration function
@@ -130,9 +188,7 @@ class ModelManager:
130
188
  ... class CityV1(BaseModel):
131
189
  ... city: City
132
190
  """
133
- return self._registry.register(
134
- name, version, schema_generator, enable_ref, backward_compatible
135
- )
191
+ return self._registry.register(name, version, enable_ref, backward_compatible)
136
192
 
137
193
  def migration(
138
194
  self: Self,
@@ -309,27 +365,12 @@ class ModelManager:
309
365
  from_version: Source version.
310
366
  to_version: Target version.
311
367
  parallel: If True, use parallel processing.
312
- max_workers: Maximum number of workers for parallel processing. Defaults to
313
- None (uses executor default).
368
+ max_workers: Maximum number of workers for parallel processing.
314
369
  use_processes: If True, use ProcessPoolExecutor instead of
315
- ThreadPoolExecutor. Useful for CPU-intensive migrations.
370
+ ThreadPoolExecutor.
316
371
 
317
372
  Returns:
318
373
  List of migrated BaseModel instances.
319
-
320
- Example:
321
- >>> legacy_users = [
322
- ... {"name": "Alice"},
323
- ... {"name": "Bob"},
324
- ... {"name": "Charlie"}
325
- ... ]
326
- >>> users = manager.migrate_batch(
327
- ... legacy_users,
328
- ... "User",
329
- ... from_version="1.0.0",
330
- ... to_version="3.0.0",
331
- ... parallel=True
332
- ... )
333
374
  """
334
375
  data_list = list(data_list)
335
376
 
@@ -372,15 +413,6 @@ class ModelManager:
372
413
 
373
414
  Returns:
374
415
  List of raw migrated dictionaries.
375
-
376
- Example:
377
- >>> legacy_data = [{"name": "Alice"}, {"name": "Bob"}]
378
- >>> migrated_data = manager.migrate_batch_data(
379
- ... legacy_data,
380
- ... "User",
381
- ... from_version="1.0.0",
382
- ... to_version="2.0.0"
383
- ... )
384
416
  """
385
417
  data_list = list(data_list)
386
418
 
@@ -411,9 +443,6 @@ class ModelManager:
411
443
  ) -> Iterable[BaseModel]:
412
444
  """Migrate data in chunks, yielding results as they complete.
413
445
 
414
- Useful for large datasets where you want to start processing results before all
415
- migrations complete.
416
-
417
446
  Args:
418
447
  data_list: Iterable of data dictionaries to migrate.
419
448
  name: Name of the model.
@@ -423,17 +452,6 @@ class ModelManager:
423
452
 
424
453
  Yields:
425
454
  Migrated BaseModel instances.
426
-
427
- Example:
428
- >>> legacy_users = load_large_dataset()
429
- >>> for user in manager.migrate_batch_streaming(
430
- ... legacy_users,
431
- ... "User",
432
- ... from_version="1.0.0",
433
- ... to_version="3.0.0"
434
- ... ):
435
- ... # Process each user as it's migrated
436
- ... save_to_database(user)
437
455
  """
438
456
  chunk = []
439
457
 
@@ -457,9 +475,6 @@ class ModelManager:
457
475
  ) -> Iterable[ModelData]:
458
476
  """Migrate data in chunks, yielding raw dictionaries as they complete.
459
477
 
460
- Useful for large datasets where you want to start processing results before all
461
- migrations complete, without the validation overhead.
462
-
463
478
  Args:
464
479
  data_list: Iterable of data dictionaries to migrate.
465
480
  name: Name of the model.
@@ -469,17 +484,6 @@ class ModelManager:
469
484
 
470
485
  Yields:
471
486
  Raw migrated dictionaries.
472
-
473
- Example:
474
- >>> legacy_data = load_large_dataset()
475
- >>> for data in manager.migrate_batch_data_streaming(
476
- ... legacy_data,
477
- ... "User",
478
- ... from_version="1.0.0",
479
- ... to_version="3.0.0"
480
- ... ):
481
- ... # Process raw data as it's migrated
482
- ... bulk_insert_to_database(data)
483
487
  """
484
488
  chunk = []
485
489
 
@@ -503,9 +507,6 @@ class ModelManager:
503
507
  ) -> ModelDiff:
504
508
  """Get a detailed diff between two model versions.
505
509
 
506
- Compares field names, types, requirements, and default values to provide a
507
- comprehensive view of what changed between versions.
508
-
509
510
  Args:
510
511
  name: Name of the model.
511
512
  from_version: Source version.
@@ -513,12 +514,6 @@ class ModelManager:
513
514
 
514
515
  Returns:
515
516
  ModelDiff with detailed change information.
516
-
517
- Example:
518
- >>> diff = manager.diff("User", "1.0.0", "2.0.0")
519
- >>> print(diff.to_markdown())
520
- >>> print(f"Added: {diff.added_fields}")
521
- >>> print(f"Removed: {diff.removed_fields}")
522
517
  """
523
518
  from_ver_str = str(
524
519
  ModelVersion.parse(from_version)
@@ -542,10 +537,136 @@ class ModelManager:
542
537
  to_version=to_ver_str,
543
538
  )
544
539
 
540
+ def set_default_schema_generator(
541
+ self: Self, generator: JsonSchemaGenerator | type[GenerateJsonSchema]
542
+ ) -> None:
543
+ """Set the default schema generator for all schemas.
544
+
545
+ This is a convenience method that updates the default schema configuration.
546
+
547
+ Args:
548
+ generator: Custom schema generator - either a callable or GenerateJsonSchema
549
+ class.
550
+
551
+ Example (Callable):
552
+ >>> def my_generator(model: type[BaseModel]) -> JsonSchema:
553
+ ... schema = model.model_json_schema()
554
+ ... schema["x-custom"] = True
555
+ ... return schema
556
+ >>>
557
+ >>> manager = ModelManager()
558
+ >>> manager.set_default_schema_generator(my_generator)
559
+
560
+ Example (Class - Recommended):
561
+ >>> from pydantic.json_schema import GenerateJsonSchema
562
+ >>>
563
+ >>> class MyGenerator(GenerateJsonSchema):
564
+ ... def generate(
565
+ ... self,
566
+ ... schema: Mapping[str, Any],
567
+ ... mode: JsonSchemaMode = "validation"
568
+ ... ) -> JsonSchema:
569
+ ... json_schema = super().generate(schema, mode=mode)
570
+ ... json_schema["x-custom"] = True
571
+ ... json_schema["$schema"] = self.schema_dialect
572
+ ... return json_schema
573
+ >>>
574
+ >>> manager = ModelManager()
575
+ >>> manager.set_default_schema_generator(MyGenerator)
576
+ >>>
577
+ >>> # All subsequent schema calls will use MyGenerator
578
+ >>> schema = manager.get_schema("User", "1.0.0")
579
+ """
580
+ self._schema_manager.set_default_schema_generator(generator)
581
+
582
+ def schema_transformer(
583
+ self: Self,
584
+ name: str,
585
+ version: str | ModelVersion,
586
+ ) -> Callable[[SchemaTransformer], SchemaTransformer]:
587
+ """Decorator to register a schema transformer for a model version.
588
+
589
+ Transformers are simple functions that modify a schema after generation.
590
+ They're useful for model-specific customizations that don't require deep
591
+ integration with Pydantic's generation process.
592
+
593
+ Args:
594
+ name: Name of the model.
595
+ version: Model version.
596
+
597
+ Returns:
598
+ Decorator function.
599
+
600
+ Example:
601
+ >>> @manager.schema_transformer("User", "1.0.0")
602
+ ... def add_auth_metadata(schema: JsonSchema) -> JsonSchema:
603
+ ... schema["x-requires-auth"] = True
604
+ ... schema["x-auth-level"] = 'admin'
605
+ ... return schema
606
+ >>>
607
+ >>> @manager.schema_transformer("Product", "2.0.0")
608
+ ... def add_product_examples(schema: JsonSchema) -> JsonSchema:
609
+ ... schema["examples"] = [
610
+ ... {"name": "Widget", "price": 9.99},
611
+ ... {"name": "Gadget", "price": 19.99}
612
+ ... ]
613
+ ... return schema
614
+ """
615
+
616
+ def decorator(func: SchemaTransformer) -> SchemaTransformer:
617
+ self._schema_manager.register_transformer(name, version, func)
618
+ return func
619
+
620
+ return decorator
621
+
622
+ def get_schema_transformers(
623
+ self: Self,
624
+ name: str,
625
+ version: str | ModelVersion,
626
+ ) -> list[SchemaTransformer]:
627
+ """Get all transformers for a model version.
628
+
629
+ Args:
630
+ name: Name of the model.
631
+ version: Model version.
632
+
633
+ Returns:
634
+ List of transformer functions.
635
+
636
+ Example:
637
+ >>> transformers = manager.get_schema_transformers("User", "1.0.0")
638
+ >>> print(f"Found {len(transformers)} transformers")
639
+ """
640
+ return self._schema_manager.get_transformers(name, version)
641
+
642
+ def clear_schema_transformers(
643
+ self: Self,
644
+ name: str | None = None,
645
+ version: str | ModelVersion | None = None,
646
+ ) -> None:
647
+ """Clear schema transformers.
648
+
649
+ Args:
650
+ name: Optional model name. If None, clears all.
651
+ version: Optional version. If None, clears all versions of model.
652
+
653
+ Example:
654
+ >>> # Clear all transformers
655
+ >>> manager.clear_schema_transformers()
656
+ >>>
657
+ >>> # Clear User transformers
658
+ >>> manager.clear_schema_transformers("User")
659
+ >>>
660
+ >>> # Clear specific version
661
+ >>> manager.clear_schema_transformers("User", "1.0.0")
662
+ """
663
+ self._schema_manager.clear_transformers(name, version)
664
+
545
665
  def get_schema(
546
666
  self: Self,
547
667
  name: str,
548
668
  version: str | ModelVersion,
669
+ config: SchemaConfig | None = None,
549
670
  **kwargs: Any,
550
671
  ) -> JsonSchema:
551
672
  """Get JSON schema for a specific version.
@@ -553,12 +674,25 @@ class ModelManager:
553
674
  Args:
554
675
  name: Name of the model.
555
676
  version: Semantic version.
556
- **kwargs: Additional schema generation arguments.
677
+ config: Optional schema configuration (overrides default).
678
+ **kwargs: Additional schema generation arguments (e.g.,
679
+ mode="serialization").
557
680
 
558
681
  Returns:
559
682
  JSON schema dictionary.
683
+
684
+ Example:
685
+ >>> # Use default config
686
+ >>> schema = manager.get_schema("User", "1.0.0")
687
+ >>>
688
+ >>> # Override with custom config
689
+ >>> config = SchemaConfig(mode="serialization")
690
+ >>> schema = manager.get_schema("User", "1.0.0", config=config)
691
+ >>>
692
+ >>> # Quick override with kwargs
693
+ >>> schema = manager.get_schema("User", "1.0.0", mode="serialization")
560
694
  """
561
- return self._schema_manager.get_schema(name, version, **kwargs)
695
+ return self._schema_manager.get_schema(name, version, config=config, **kwargs)
562
696
 
563
697
  def list_models(self: Self) -> list[str]:
564
698
  """Get list of all registered models.
@@ -585,6 +719,7 @@ class ModelManager:
585
719
  indent: int = 2,
586
720
  separate_definitions: bool = False,
587
721
  ref_template: str | None = None,
722
+ config: SchemaConfig | None = None,
588
723
  ) -> None:
589
724
  """Export all schemas to JSON files.
590
725
 
@@ -592,27 +727,30 @@ class ModelManager:
592
727
  output_dir: Directory path for output.
593
728
  indent: JSON indentation level.
594
729
  separate_definitions: If True, create separate schema files for nested
595
- models and use $ref to reference them. Only applies to models with
596
- 'enable_ref=True'.
730
+ models and use $ref to reference them.
597
731
  ref_template: Template for $ref URLs when separate_definitions=True.
598
- Defaults to relative file references if not provided.
732
+ config: Optional schema configuration for all exported schemas.
599
733
 
600
734
  Example:
601
- >>> # Inline definitions (default)
602
- >>> manager.dump_schemas("schemas/")
603
- >>>
604
- >>> # Separate sub-schemas with relative refs
605
- >>> manager.dump_schemas("schemas/", separate_definitions=True)
735
+ >>> # Export with custom generator
736
+ >>> config = SchemaConfig(
737
+ ... schema_generator=CustomGenerator,
738
+ ... mode="validation"
739
+ ... )
740
+ >>> manager.dump_schemas("schemas/", config=config)
606
741
  >>>
607
- >>> # Separate sub-schemas with absolute URLs
742
+ >>> # Export validation and serialization schemas separately
608
743
  >>> manager.dump_schemas(
609
- ... "schemas/",
610
- ... separate_definitions=True,
611
- ... ref_template="https://example.com/schemas/{model}_v{version}.json"
744
+ ... "schemas/validation/",
745
+ ... config=SchemaConfig(mode="validation")
746
+ ... )
747
+ >>> manager.dump_schemas(
748
+ ... "schemas/serialization/",
749
+ ... config=SchemaConfig(mode="serialization")
612
750
  ... )
613
751
  """
614
752
  self._schema_manager.dump_schemas(
615
- output_dir, indent, separate_definitions, ref_template
753
+ output_dir, indent, separate_definitions, ref_template, config=config
616
754
  )
617
755
 
618
756
  def get_nested_models(
@@ -640,54 +778,15 @@ class ModelManager:
640
778
  ) -> MigrationTestResults:
641
779
  """Test a migration with multiple test cases.
642
780
 
643
- Executes a migration on multiple test inputs and validates the outputs match
644
- expected values. Useful for regression testing and validating migration logic.
645
-
646
781
  Args:
647
782
  name: Name of the model.
648
783
  from_version: Source version to migrate from.
649
784
  to_version: Target version to migrate to.
650
- test_cases: List of test cases, either as (source, target) tuples or
651
- MigrationTestCase objects. If target is None, only verifies the
652
- migration completes without errors.
785
+ test_cases: List of test cases.
653
786
 
654
787
  Returns:
655
788
  MigrationTestResults containing individual results for each test case.
656
-
657
- Example:
658
- >>> # Using tuples (source, target)
659
- >>> results = manager.test_migration(
660
- ... "User", "1.0.0", "2.0.0",
661
- ... test_cases=[
662
- ... ({"name": "Alice"}, {"name": "Alice", "email": "alice@example.com"}),
663
- ... ({"name": "Bob"}, {"name": "Bob", "email": "bob@example.com"})
664
- ... ]
665
- ... )
666
- >>> assert results.all_passed
667
- >>>
668
- >>> # Using MigrationTestCase objects
669
- >>> results = manager.test_migration(
670
- ... "User", "1.0.0", "2.0.0",
671
- ... test_cases=[
672
- ... MigrationTestCase(
673
- ... source={"name": "Alice"},
674
- ... target={"name": "Alice", "email": "alice@example.com"},
675
- ... description="Standard user migration"
676
- ... )
677
- ... ]
678
- ... )
679
- >>>
680
- >>> # Use in pytest
681
- >>> def test_user_migration():
682
- ... results = manager.test_migration("User", "1.0.0", "2.0.0", test_cases)
683
- ... results.assert_all_passed() # Raises AssertionError with details if failed
684
- >>>
685
- >>> # Inspect failures
686
- >>> if not results.all_passed:
687
- ... for failure in results.failures:
688
- ... print(f"Failed: {failure.test_case.description}")
689
- ... print(f" Error: {failure.error}")
690
- """ # noqa: E501
789
+ """
691
790
  results = []
692
791
 
693
792
  for test_case_input in test_cases:
@@ -0,0 +1,130 @@
1
+ """Schema configuration for customized schema generation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import TYPE_CHECKING, Any, Self
7
+
8
+ from pydantic.json_schema import GenerateJsonSchema
9
+
10
+ if TYPE_CHECKING:
11
+ from .types import JsonSchemaGenerator, JsonSchemaMode
12
+
13
+
14
+ @dataclass
15
+ class SchemaConfig:
16
+ """Configuration for JSON schema generation.
17
+
18
+ This class provides fine-grained control over how Pydantic generates JSON schemas,
19
+ supporting both callable generators and Pydantic's GenerateJsonSchema classes.
20
+
21
+ Attributes:
22
+ schema_generator: Custom schema generator. Can be either:
23
+ - A callable taking (type[BaseModel]) -> JsonSchema
24
+ - A subclass of pydantic.json_schema.GenerateJsonSchema
25
+ mode: Schema generation mode - 'validation' for input validation or
26
+ 'serialization' for output serialization.
27
+ by_alias: Whether to use field aliases in the schema.
28
+ ref_template: Template for JSON schema $ref URIs.
29
+ extra_kwargs: Additional arguments to pass to model_json_schema().
30
+
31
+ Example (Callable Generator):
32
+ >>> def custom_generator(model: type[BaseModel]) -> JsonSchema:
33
+ ... schema = model.model_json_schema()
34
+ ... schema["x-custom"] = "metadata"
35
+ ... return schema
36
+ >>>
37
+ >>> config = SchemaConfig(
38
+ ... schema_generator=custom_generator,
39
+ ... mode="validation"
40
+ ... )
41
+
42
+ Example (GenerateJsonSchema Class):
43
+ >>> from pydantic.json_schema import GenerateJsonSchema
44
+ >>>
45
+ >>> class CustomSchemaGenerator(GenerateJsonSchema):
46
+ ... def generate(
47
+ ... self,
48
+ ... schema: Mapping[str, Any],
49
+ ... mode: JsonSchemaMode = "validation"
50
+ ... ) -> JsonSchema:
51
+ ... json_schema = super().generate(schema, mode=mode)
52
+ ... json_schema["x-custom"] = "metadata"
53
+ ... return json_schema
54
+ >>>
55
+ >>> config = SchemaConfig(
56
+ ... schema_generator=CustomSchemaGenerator,
57
+ ... mode="validation",
58
+ ... by_alias=True
59
+ ... )
60
+ """
61
+
62
+ schema_generator: JsonSchemaGenerator | type[GenerateJsonSchema] | None = None
63
+ mode: JsonSchemaMode = "validation"
64
+ by_alias: bool = True
65
+ ref_template: str = "#/$defs/{model}"
66
+ extra_kwargs: dict[str, Any] = field(default_factory=dict)
67
+
68
+ def merge_with(self: Self, other: SchemaConfig | None) -> SchemaConfig:
69
+ """Merge this config with another, with other taking precedence.
70
+
71
+ Args:
72
+ other: Configuration to merge with (overrides this config).
73
+
74
+ Returns:
75
+ New SchemaConfig with merged values.
76
+ """
77
+ if other is None:
78
+ return self
79
+
80
+ return SchemaConfig(
81
+ schema_generator=other.schema_generator or self.schema_generator,
82
+ mode=other.mode if other.mode != "validation" else self.mode,
83
+ by_alias=other.by_alias if not other.by_alias else self.by_alias,
84
+ ref_template=other.ref_template
85
+ if other.ref_template != "#/$defs/{model}"
86
+ else self.ref_template,
87
+ extra_kwargs={**self.extra_kwargs, **other.extra_kwargs},
88
+ )
89
+
90
+ def to_kwargs(self: Self) -> dict[str, Any]:
91
+ """Convert config to kwargs for model_json_schema().
92
+
93
+ Note: If schema_generator is a callable (JsonSchemaGenerator type), it cannot be
94
+ passed to model_json_schema() and must be handled separately by calling it
95
+ directly.
96
+
97
+ Returns:
98
+ Dictionary of arguments for Pydantic's model_json_schema(). If
99
+ schema_generator is a callable, it will NOT be included.
100
+ """
101
+ kwargs = {
102
+ "mode": self.mode,
103
+ "by_alias": self.by_alias,
104
+ "ref_template": self.ref_template,
105
+ **self.extra_kwargs,
106
+ }
107
+
108
+ # Only add schema_generator if it's a GenerateJsonSchema class
109
+ # Callable generators are handled separately
110
+ if (
111
+ self.schema_generator is not None
112
+ and isinstance(self.schema_generator, type)
113
+ and issubclass(self.schema_generator, GenerateJsonSchema)
114
+ ):
115
+ kwargs["schema_generator"] = self.schema_generator
116
+
117
+ return kwargs
118
+
119
+ def is_callable_generator(self: Self) -> bool:
120
+ """Check if schema_generator is a callable function.
121
+
122
+ Returns:
123
+ True if schema_generator is a callable (not a class).
124
+ """
125
+ if self.schema_generator is None:
126
+ return False
127
+
128
+ return callable(self.schema_generator) and not isinstance(
129
+ self.schema_generator, type
130
+ )
pyrmute/types.py CHANGED
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from collections.abc import Callable
6
6
  from dataclasses import dataclass
7
- from typing import Any, TypeAlias, TypeVar
7
+ from typing import Any, Literal, TypeAlias, TypeVar
8
8
 
9
9
  from pydantic import BaseModel
10
10
 
@@ -16,9 +16,10 @@ JsonValue: TypeAlias = (
16
16
  int | float | str | bool | None | list["JsonValue"] | dict[str, "JsonValue"]
17
17
  )
18
18
  JsonSchema: TypeAlias = dict[str, JsonValue]
19
+ JsonSchemaMode = Literal["validation", "serialization"]
19
20
  JsonSchemaDefinitions: TypeAlias = dict[str, JsonValue]
20
21
  JsonSchemaGenerator: TypeAlias = Callable[[type[BaseModel]], JsonSchema]
21
- SchemaGenerators: TypeAlias = dict[ModelVersion, JsonSchemaGenerator]
22
+ SchemaTransformer = Callable[[JsonSchema], JsonSchema]
22
23
 
23
24
  ModelData: TypeAlias = dict[str, Any]
24
25
  MigrationFunc: TypeAlias = Callable[[ModelData], ModelData]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrmute
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Pydantic model migrations and schemas
5
5
  Author-email: Matt Ferrera <mattferrera@gmail.com>
6
6
  License: MIT
@@ -58,7 +58,7 @@ through multiple versions.
58
58
 
59
59
  ## When to Use pyrmute
60
60
 
61
- pyrmute excels at handling schema evolution in production systems:
61
+ pyrmute is useful for handling schema evolution in production systems:
62
62
 
63
63
  - **Configuration files** - Upgrade user config files as your CLI/desktop app
64
64
  evolves (`.apprc`, `config.json`, `settings.yaml`)
@@ -78,6 +78,21 @@ pyrmute excels at handling schema evolution in production systems:
78
78
  See the [examples/](examples/) directory for complete, runnable code
79
79
  demonstrating these patterns.
80
80
 
81
+ ## When Not to Use
82
+
83
+ pyrmute may not be the right choice if you have:
84
+
85
+ - **High-throughput systems** - Runtime migration adds latency to hot paths.
86
+ Use upfront batch migrations instead.
87
+ - **Multi-language services** - Python-only. Use Protobuf, Avro, or JSON
88
+ Schema for polyglot architectures.
89
+ - **Existing schema registries** - Already using Confluent/AWS Glue? Stick
90
+ with them for compatibility enforcement and governance.
91
+ - **Stable schemas** - Models rarely change? Traditional migration tools are
92
+ simpler and more maintainable.
93
+ - **Database DDL changes** - pyrmute transforms data, not database schemas.
94
+ Alembic/Flyway or other ORMs may still be needed to alter tables.
95
+
81
96
  ## Help
82
97
 
83
98
  See [documentation](https://mferrera.github.io/pyrmute/) for complete guides
@@ -0,0 +1,18 @@
1
+ pyrmute/__init__.py,sha256=vgq5e3jmr2FHmSnnnf-Kvl4Y6oGra3BDl5p_CaHDYLQ,1234
2
+ pyrmute/_migration_manager.py,sha256=TFws66RsEdKLpvjDDQB1pKgeeyUe5WoutacTaeDsZoE,26154
3
+ pyrmute/_registry.py,sha256=eEhMagcpUeZSPNZlIAuTzXcCkPGHTxfaIfEYEoEiFU8,5680
4
+ pyrmute/_schema_manager.py,sha256=9NmyooG92gcOMOYNLRuC8isGUvivIf_Hp8kdVC8lmu8,18622
5
+ pyrmute/_version.py,sha256=fvHpBU3KZKRinkriKdtAt3crenOyysELF-M9y3ozg3U,704
6
+ pyrmute/exceptions.py,sha256=Q57cUuzzMdkIl5Q0_VyLobpdB0WcrE0ggfC-LBoX2Uo,1681
7
+ pyrmute/migration_testing.py,sha256=fpKT2u7pgPRpswb4PUvbd-fQ3W76svNWvEVYVDmb3Dg,5066
8
+ pyrmute/model_diff.py,sha256=vMa2NTYFqt9E7UYDZH4PQmLcoxQw5Sj-nPpUHB_53Ig,9594
9
+ pyrmute/model_manager.py,sha256=EC3xN4Jve77Fk3xJjC3KrU_sY3Eoae-ucJE8wQ3bO3M,27778
10
+ pyrmute/model_version.py,sha256=ftNDuJlN3S5ZKQK8DKqqwfBDRiz4rGCYn-aJ3n6Zmqk,2025
11
+ pyrmute/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ pyrmute/schema_config.py,sha256=ttM4a1-EUUlxmsol6L4rN_Mse-77Y-yidn0ezojy0uY,4767
13
+ pyrmute/types.py,sha256=rzRxOYfh4WPVR1KoNT3vC3UjuBlTarMnNL6Z1Y5icrw,1237
14
+ pyrmute-0.5.0.dist-info/licenses/LICENSE,sha256=otWInySiZeGwhHqQQ7n7nxM5QBSBe2CzeGEmQDZEz8Q,1119
15
+ pyrmute-0.5.0.dist-info/METADATA,sha256=QOv8NMAZ_sw-k2bFOHXM79XGnPUBArt4PRELq5cUdMY,15169
16
+ pyrmute-0.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
+ pyrmute-0.5.0.dist-info/top_level.txt,sha256=C8QtzqE6yBHkeewSp1QewvsyeHj_VQLYjSa5HLtMiow,8
18
+ pyrmute-0.5.0.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- pyrmute/__init__.py,sha256=j2pbMYswL0xR8FZwZDg-qAw6HwFpA3KPhY06_2yc_U0,1132
2
- pyrmute/_migration_manager.py,sha256=TFws66RsEdKLpvjDDQB1pKgeeyUe5WoutacTaeDsZoE,26154
3
- pyrmute/_registry.py,sha256=iUjMPd6CYgyvWT8PxZqHWBZnsHrX25fOPDi_-k_QDJs,6124
4
- pyrmute/_schema_manager.py,sha256=eun8PTL9Gv1XAMVKmE3tYmjdrcf701-IapUXjb6WDL0,12122
5
- pyrmute/_version.py,sha256=2_0GUP7yBCXRus-qiJKxQD62z172WSs1sQ6DVpPsbmM,704
6
- pyrmute/exceptions.py,sha256=Q57cUuzzMdkIl5Q0_VyLobpdB0WcrE0ggfC-LBoX2Uo,1681
7
- pyrmute/migration_testing.py,sha256=fpKT2u7pgPRpswb4PUvbd-fQ3W76svNWvEVYVDmb3Dg,5066
8
- pyrmute/model_diff.py,sha256=vMa2NTYFqt9E7UYDZH4PQmLcoxQw5Sj-nPpUHB_53Ig,9594
9
- pyrmute/model_manager.py,sha256=a6ecd-lZ3iliP3lqgCAi7xLeFlBh50kBA-m6gLGKRx4,24585
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.4.0.dist-info/licenses/LICENSE,sha256=otWInySiZeGwhHqQQ7n7nxM5QBSBe2CzeGEmQDZEz8Q,1119
14
- pyrmute-0.4.0.dist-info/METADATA,sha256=-KbIQN_INu7ZGfyEO65qmSGsWAXiTHQXR_7F5f-enOQ,14480
15
- pyrmute-0.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- pyrmute-0.4.0.dist-info/top_level.txt,sha256=C8QtzqE6yBHkeewSp1QewvsyeHj_VQLYjSa5HLtMiow,8
17
- pyrmute-0.4.0.dist-info/RECORD,,