mafw.db.migration.diff
Schema differ comparing discovered models with the live database.
Peewee can introspect a live schema (playhouse.reflection.Introspector)
and apply individual migration operations (playhouse.migrate), but it has
no built-in schema diff. This module is the MAFw-authored differ: it compares
the discovered MAFwBaseModel subclasses against the
introspected live schema and classifies each detected change as a
supported or an unsupported difference.
The initial supported set is intentionally conservative and additive: new tables and new nullable columns. Everything else the differ detects — renames, drops and other destructive operations, type changes, constraint changes, and required data backfills — is classified as unsupported. Differences the differ cannot even detect are documented as undetectable, which is why an empty diff is only ever reported as scoped synchronization and never as full schema equivalence.
Introspection here is strictly read-only, which is what makes the diff and
generate service operations non-mutating.
Added in version 2.4.
- author:
Bulgheroni Antonio (antonio.bulgheroni@ec.europa.eu)
Module Attributes
The additive-only supported difference kinds: new tables, new nullable columns, new indexes. |
Functions
|
Compute the schema diff between the discovered models and the live database. |
Classes
|
One detected schema difference. |
|
The kind of schema difference detected by the differ. |
|
The structured comparison between the models and the live schema. |
|
Whether a detected difference can be safely expressed as a migration. |
- class mafw.db.migration.diff.Difference(kind: ~mafw.db.migration.diff.DifferenceKind, table: str, support: ~mafw.db.migration.diff.SupportLevel, detail: ~collections.abc.Mapping[str, ~typing.Any] = <factory>, reason: str | None = None)[source]
Bases:
objectOne detected schema difference.
- Parameters:
kind (DifferenceKind) – The kind of difference.
table (str) – The table the difference affects.
support (SupportLevel) – Whether the difference is supported or unsupported.
detail (Mapping[str, Any]) – Extra structured information (column name, type, nullability).
reason (str, Optional) – Why the difference is unsupported, for the report.
Added in version 2.4.
- class mafw.db.migration.diff.DifferenceKind(*values)[source]
Bases:
StrEnumThe kind of schema difference detected by the differ.
Added in version 2.4.
- static _generate_next_value_(name, start, count, last_values)
Return the lower-cased version of the member name.
- ADD_COLUMN = 'add_column'
A model declares a nullable column absent from the live table (supported).
- ADD_INDEX = 'add_index'
A model declares an index absent from the live table (supported).
- ADD_NOT_NULL_COLUMN = 'add_not_null_column'
A model declares a non-nullable column absent from the live table (unsupported).
- ADD_TABLE = 'add_table'
A model has no counterpart table in the live schema (supported).
- ALTER_COLUMN_TYPE = 'alter_column_type'
A column type differs between the model and the live table (unsupported).
- ALTER_NULLABILITY = 'alter_nullability'
A column nullability differs between the model and the live table (unsupported).
- DROP_COLUMN = 'drop_column'
The live table has a column absent from the model (unsupported).
- DROP_INDEX = 'drop_index'
The live table has an index absent from the model (unsupported).
- DROP_TABLE = 'drop_table'
The live schema has a table absent from the models (unsupported).
- class mafw.db.migration.diff.SchemaDiff(supported: tuple[Difference, ...] = (), unsupported: tuple[Difference, ...] = ())[source]
Bases:
objectThe structured comparison between the models and the live schema.
The supported and unsupported lists partition the detected differences: each detected difference appears in exactly one list.
- Parameters:
supported (tuple[Difference, ...]) – The supported differences, in deterministic order.
unsupported (tuple[Difference, ...]) – The unsupported differences, in deterministic order.
Added in version 2.4.
- canonical_key() str[source]
Return a stable string key uniquely identifying the diff content.
Two diffs that describe the same changes produce the same key regardless of discovery order. This underpins deterministic script naming.
- Returns:
A canonical, order-independent key for the whole diff.
- Return type:
str
- property all_differences: tuple[Difference, ...]
The supported and unsupported differences concatenated.
- property has_supported: bool
Whether the diff contains at least one supported difference.
- property is_empty: bool
Whether the diff contains no supported and no unsupported difference.
- class mafw.db.migration.diff.SupportLevel(*values)[source]
Bases:
StrEnumWhether a detected difference can be safely expressed as a migration.
Added in version 2.4.
- static _generate_next_value_(name, start, count, last_values)
Return the lower-cased version of the member name.
- SUPPORTED = 'supported'
The differ can both detect and safely express the difference.
- UNSUPPORTED = 'unsupported'
The difference is detected but cannot be safely expressed automatically.
- class mafw.db.migration.diff._PeeweeIndex(*args, **kwargs)[source]
Bases:
ProtocolDescribe index attributes used by the differ but omitted from Peewee stubs.
- mafw.db.migration.diff._detect_type_change(table: str, column_name: str, field_obj: Field, live_column: Any) Difference | None[source]
Return an
ALTER_COLUMN_TYPEdifference when a column type changed.The comparison uses peewee’s own
field_typetoken on both sides — the model field’s token versus the token of thefield_classthatplayhouse.reflection.Introspectorinferred for the live column — reduced to a coarse type family so that benign within-family differences (for example the SQLiteTEXT/VARCHARequivalence, orINTvsBIGINT) do not cause false positives. Primary-key columns are skipped because reflection reports them asAUTO/INTinconsistently across backends. A detected change is classified as unsupported: altering a column type can be lossy and must be reviewed.- Parameters:
table (str) – The table the column belongs to.
column_name (str) – The database column name.
field_obj (peewee.Field) – The model field.
live_column (Any) – The reflected live column metadata.
- Returns:
An unsupported type-change difference, or
Nonewhen unchanged.- Return type:
Difference | None
- mafw.db.migration.diff._diff_indexes(model: type[MAFwBaseModel], table: str, database: Database, supported: list[Difference], unsupported: list[Difference]) None[source]
Compare a model’s declared indexes against the live table’s indexes.
Adding an index is additive and therefore supported; dropping an index (present in the database but not declared by the model) is unsupported and left for review, since an index may have been added deliberately outside the model.
- Parameters:
model (type[MAFwBaseModel]) – The model being compared.
table (str) – The table name.
database (peewee.Database) – The live database.
supported (list[Difference]) – The list that collects supported differences (mutated).
unsupported (list[Difference]) – The list that collects unsupported differences (mutated).
- mafw.db.migration.diff._diff_sort_key(difference: Difference) tuple[str, str, str][source]
Sort key giving a deterministic, stable ordering of differences.
- Parameters:
difference (Difference) – The difference to produce a sort key for.
- Returns:
A tuple used to order differences deterministically.
- Return type:
tuple[str, str, str]
- mafw.db.migration.diff._field_type_token(field_type: Any) str[source]
Normalise a peewee
field_typetoken for comparison.The token is upper-cased and its trailing length/precision specifier (for example the
(255)inVARCHAR(255)) is dropped, so that equivalent types compare equal across backends and reflection quirks.- Parameters:
field_type (Any) – The
field_typetoken from a model field or a reflected column’sfield_class.- Returns:
The normalised, comparable type token, or
''when the input is not a plain string token (for example a descriptor leaking through).- Return type:
str
- mafw.db.migration.diff._live_index_signatures(database: Database, table: str) set[tuple[tuple[str, ...], bool]][source]
Return the set of
(columns, unique)signatures present on a live table.Primary-key and partial/expression indexes are excluded, mirroring
playhouse.schema_diff.diff_models().- Parameters:
database (peewee.Database) – The live database.
table (str) – The table to inspect.
- Returns:
The set of live
(ordered column names, unique flag)signatures.- Return type:
set[tuple[tuple[str, …], bool]]
- mafw.db.migration.diff._model_columns(model: type[MAFwBaseModel]) dict[str, Field][source]
Return the mapping of column name to field for a model.
- Parameters:
model (type[MAFwBaseModel]) – The model whose columns are requested.
- Returns:
Mapping of database column name to the peewee field.
- Return type:
dict[str, peewee.Field]
- mafw.db.migration.diff._model_index_signatures(model: type[MAFwBaseModel]) set[tuple[tuple[str, ...], bool]][source]
Return the set of
(columns, unique)signatures declared by a model.Only plain column indexes are considered; partial and expression indexes carry no column signature and are left to hand-authored migrations, matching the behaviour of
playhouse.schema_diff.diff_models().- Parameters:
model (type[MAFwBaseModel]) – The model whose declared indexes are inspected.
- Returns:
The set of
(ordered column names, unique flag)signatures.- Return type:
set[tuple[tuple[str, …], bool]]
- mafw.db.migration.diff._type_family(token: str) str[source]
Return the coarse type family for a normalised type token.
- Parameters:
token (str) – A normalised type token from
_field_type_token().- Returns:
The family name, or the token itself when it has no mapped family.
- Return type:
str
- mafw.db.migration.diff.compute_diff(models: Sequence[type[MAFwBaseModel]], database: Database, *, ignore_tables: frozenset[str] = frozenset({})) SchemaDiff[source]
Compute the schema diff between the discovered models and the live database.
The live schema is obtained with
playhouse.reflection.Introspectorin a strictly read-only manner. Detected differences are partitioned into the supported (additive) and unsupported lists.- Parameters:
models (Sequence[type[MAFwBaseModel]]) – The discovered models to compare.
database (peewee.Database) – The live database to introspect.
ignore_tables (frozenset[str]) – Live table names to exclude from drop detection (for example the internal migration-history table).
- Returns:
The computed schema diff.
- Return type:
- mafw.db.migration.diff.SUPPORTED_KINDS: frozenset[DifferenceKind] = frozenset({DifferenceKind.ADD_COLUMN, DifferenceKind.ADD_INDEX, DifferenceKind.ADD_TABLE})
The additive-only supported difference kinds: new tables, new nullable columns, new indexes.
- mafw.db.migration.diff._TYPE_FAMILIES: dict[str, str] = {'BIGINT': 'integer', 'BOOL': 'integer', 'CHAR': 'string', 'DECIMAL': 'real', 'DOUBLE': 'real', 'FLOAT': 'real', 'INT': 'integer', 'INTEGER': 'integer', 'REAL': 'real', 'SMALLINT': 'integer', 'TEXT': 'string', 'VARCHAR': 'string'}
Maps normalised type tokens to a coarse “family”. Two columns are only reported as a type change when they fall into different families, so benign within-family differences — notably the SQLite
TEXT/VARCHAR/CHARequivalence, orINTvsBIGINT— never raise a false positive. A token that is not mapped forms its own singleton family and so compares only to itself.