metaxy 0.0.1.dev3__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.
- metaxy/__init__.py +170 -0
- metaxy/_packaging.py +96 -0
- metaxy/_testing/__init__.py +55 -0
- metaxy/_testing/config.py +43 -0
- metaxy/_testing/metaxy_project.py +780 -0
- metaxy/_testing/models.py +111 -0
- metaxy/_testing/parametric/__init__.py +13 -0
- metaxy/_testing/parametric/metadata.py +664 -0
- metaxy/_testing/pytest_helpers.py +74 -0
- metaxy/_testing/runbook.py +533 -0
- metaxy/_utils.py +35 -0
- metaxy/_version.py +1 -0
- metaxy/cli/app.py +97 -0
- metaxy/cli/console.py +13 -0
- metaxy/cli/context.py +167 -0
- metaxy/cli/graph.py +610 -0
- metaxy/cli/graph_diff.py +290 -0
- metaxy/cli/list.py +46 -0
- metaxy/cli/metadata.py +317 -0
- metaxy/cli/migrations.py +999 -0
- metaxy/cli/utils.py +268 -0
- metaxy/config.py +680 -0
- metaxy/entrypoints.py +296 -0
- metaxy/ext/__init__.py +1 -0
- metaxy/ext/dagster/__init__.py +54 -0
- metaxy/ext/dagster/constants.py +10 -0
- metaxy/ext/dagster/dagster_type.py +156 -0
- metaxy/ext/dagster/io_manager.py +200 -0
- metaxy/ext/dagster/metaxify.py +512 -0
- metaxy/ext/dagster/observable.py +115 -0
- metaxy/ext/dagster/resources.py +27 -0
- metaxy/ext/dagster/selection.py +73 -0
- metaxy/ext/dagster/table_metadata.py +417 -0
- metaxy/ext/dagster/utils.py +462 -0
- metaxy/ext/sqlalchemy/__init__.py +23 -0
- metaxy/ext/sqlalchemy/config.py +29 -0
- metaxy/ext/sqlalchemy/plugin.py +353 -0
- metaxy/ext/sqlmodel/__init__.py +13 -0
- metaxy/ext/sqlmodel/config.py +29 -0
- metaxy/ext/sqlmodel/plugin.py +499 -0
- metaxy/graph/__init__.py +29 -0
- metaxy/graph/describe.py +325 -0
- metaxy/graph/diff/__init__.py +21 -0
- metaxy/graph/diff/diff_models.py +446 -0
- metaxy/graph/diff/differ.py +769 -0
- metaxy/graph/diff/models.py +443 -0
- metaxy/graph/diff/rendering/__init__.py +18 -0
- metaxy/graph/diff/rendering/base.py +323 -0
- metaxy/graph/diff/rendering/cards.py +188 -0
- metaxy/graph/diff/rendering/formatter.py +805 -0
- metaxy/graph/diff/rendering/graphviz.py +246 -0
- metaxy/graph/diff/rendering/mermaid.py +326 -0
- metaxy/graph/diff/rendering/rich.py +169 -0
- metaxy/graph/diff/rendering/theme.py +48 -0
- metaxy/graph/diff/traversal.py +247 -0
- metaxy/graph/status.py +329 -0
- metaxy/graph/utils.py +58 -0
- metaxy/metadata_store/__init__.py +32 -0
- metaxy/metadata_store/_ducklake_support.py +419 -0
- metaxy/metadata_store/base.py +1792 -0
- metaxy/metadata_store/bigquery.py +354 -0
- metaxy/metadata_store/clickhouse.py +184 -0
- metaxy/metadata_store/delta.py +371 -0
- metaxy/metadata_store/duckdb.py +446 -0
- metaxy/metadata_store/exceptions.py +61 -0
- metaxy/metadata_store/ibis.py +542 -0
- metaxy/metadata_store/lancedb.py +391 -0
- metaxy/metadata_store/memory.py +292 -0
- metaxy/metadata_store/system/__init__.py +57 -0
- metaxy/metadata_store/system/events.py +264 -0
- metaxy/metadata_store/system/keys.py +9 -0
- metaxy/metadata_store/system/models.py +129 -0
- metaxy/metadata_store/system/storage.py +957 -0
- metaxy/metadata_store/types.py +10 -0
- metaxy/metadata_store/utils.py +104 -0
- metaxy/metadata_store/warnings.py +36 -0
- metaxy/migrations/__init__.py +32 -0
- metaxy/migrations/detector.py +291 -0
- metaxy/migrations/executor.py +516 -0
- metaxy/migrations/generator.py +319 -0
- metaxy/migrations/loader.py +231 -0
- metaxy/migrations/models.py +528 -0
- metaxy/migrations/ops.py +447 -0
- metaxy/models/__init__.py +0 -0
- metaxy/models/bases.py +12 -0
- metaxy/models/constants.py +139 -0
- metaxy/models/feature.py +1335 -0
- metaxy/models/feature_spec.py +338 -0
- metaxy/models/field.py +263 -0
- metaxy/models/fields_mapping.py +307 -0
- metaxy/models/filter_expression.py +297 -0
- metaxy/models/lineage.py +285 -0
- metaxy/models/plan.py +232 -0
- metaxy/models/types.py +475 -0
- metaxy/py.typed +0 -0
- metaxy/utils/__init__.py +1 -0
- metaxy/utils/constants.py +2 -0
- metaxy/utils/exceptions.py +23 -0
- metaxy/utils/hashing.py +230 -0
- metaxy/versioning/__init__.py +31 -0
- metaxy/versioning/engine.py +656 -0
- metaxy/versioning/feature_dep_transformer.py +151 -0
- metaxy/versioning/ibis.py +249 -0
- metaxy/versioning/lineage_handler.py +205 -0
- metaxy/versioning/polars.py +189 -0
- metaxy/versioning/renamed_df.py +35 -0
- metaxy/versioning/types.py +63 -0
- metaxy-0.0.1.dev3.dist-info/METADATA +96 -0
- metaxy-0.0.1.dev3.dist-info/RECORD +111 -0
- metaxy-0.0.1.dev3.dist-info/WHEEL +4 -0
- metaxy-0.0.1.dev3.dist-info/entry_points.txt +4 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
"""SQLAlchemy integration plugin for metaxy.
|
|
2
|
+
|
|
3
|
+
This module provides SQLAlchemy Table definitions and helpers for metaxy system tables
|
|
4
|
+
and user-defined feature tables. These can be used with migration tools like Alembic.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from sqlalchemy import Column, DateTime, Index, MetaData, String, Table
|
|
12
|
+
|
|
13
|
+
from metaxy.config import MetaxyConfig
|
|
14
|
+
from metaxy.ext.sqlalchemy.config import SQLAlchemyConfig
|
|
15
|
+
from metaxy.metadata_store.system import EVENTS_KEY, FEATURE_VERSIONS_KEY
|
|
16
|
+
from metaxy.models.constants import (
|
|
17
|
+
METAXY_FEATURE_SPEC_VERSION,
|
|
18
|
+
METAXY_FEATURE_VERSION,
|
|
19
|
+
METAXY_FULL_DEFINITION_VERSION,
|
|
20
|
+
METAXY_SNAPSHOT_VERSION,
|
|
21
|
+
)
|
|
22
|
+
from metaxy.models.feature_spec import FeatureSpec
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from metaxy.metadata_store.ibis import IbisMetadataStore
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# System Tables
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def create_system_tables(
|
|
32
|
+
metadata: MetaData,
|
|
33
|
+
table_prefix: str = "",
|
|
34
|
+
) -> tuple[Table, Table]:
|
|
35
|
+
"""Create system table definitions in the given metadata.
|
|
36
|
+
|
|
37
|
+
System tables always include primary key constraints since they are controlled by metaxy.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
metadata: SQLAlchemy MetaData object to add tables to
|
|
41
|
+
table_prefix: Optional prefix to prepend to table names (e.g., "dev_")
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Tuple of (feature_versions_table, events_table)
|
|
45
|
+
"""
|
|
46
|
+
feature_versions_name = (
|
|
47
|
+
f"{table_prefix}{FEATURE_VERSIONS_KEY.table_name}"
|
|
48
|
+
if table_prefix
|
|
49
|
+
else FEATURE_VERSIONS_KEY.table_name
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
feature_versions_table = Table(
|
|
53
|
+
feature_versions_name,
|
|
54
|
+
metadata,
|
|
55
|
+
# Composite primary key
|
|
56
|
+
Column("project", String, primary_key=True, index=True),
|
|
57
|
+
Column("feature_key", String, primary_key=True, index=True),
|
|
58
|
+
Column(
|
|
59
|
+
METAXY_FEATURE_SPEC_VERSION,
|
|
60
|
+
String,
|
|
61
|
+
primary_key=True,
|
|
62
|
+
),
|
|
63
|
+
# Versioning columns
|
|
64
|
+
Column(METAXY_FEATURE_VERSION, String, index=True),
|
|
65
|
+
Column(METAXY_FULL_DEFINITION_VERSION, String, index=True),
|
|
66
|
+
Column(METAXY_SNAPSHOT_VERSION, String, index=True),
|
|
67
|
+
# Metadata columns
|
|
68
|
+
Column("recorded_at", DateTime, index=True),
|
|
69
|
+
Column("feature_schema", String), # JSON string
|
|
70
|
+
Column("tags", String, default="{}"), # JSON string
|
|
71
|
+
# Additional indexes
|
|
72
|
+
Index(
|
|
73
|
+
f"idx_{feature_versions_name}_lookup",
|
|
74
|
+
"project",
|
|
75
|
+
"feature_key",
|
|
76
|
+
METAXY_FEATURE_VERSION,
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
events_name = (
|
|
81
|
+
f"{table_prefix}{EVENTS_KEY.table_name}"
|
|
82
|
+
if table_prefix
|
|
83
|
+
else EVENTS_KEY.table_name
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
events_table = Table(
|
|
87
|
+
events_name,
|
|
88
|
+
metadata,
|
|
89
|
+
# Composite primary key matching Polars append-only storage
|
|
90
|
+
Column("project", String, primary_key=True, index=True),
|
|
91
|
+
Column("execution_id", String, primary_key=True, index=True),
|
|
92
|
+
Column("timestamp", DateTime, primary_key=True),
|
|
93
|
+
# Event fields
|
|
94
|
+
Column("event_type", String, index=True),
|
|
95
|
+
Column("feature_key", String, nullable=True, index=True),
|
|
96
|
+
Column("payload", String, default=""), # JSON string
|
|
97
|
+
# Additional indexes
|
|
98
|
+
Index(
|
|
99
|
+
f"idx_{events_name}_lookup",
|
|
100
|
+
"project",
|
|
101
|
+
"execution_id",
|
|
102
|
+
"event_type",
|
|
103
|
+
),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
return feature_versions_table, events_table
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _get_store_sqlalchemy_url(store: IbisMetadataStore) -> str:
|
|
110
|
+
"""Get SQLAlchemy URL from an IbisMetadataStore instance.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
store: IbisMetadataStore instance
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
SQLAlchemy connection URL string
|
|
117
|
+
|
|
118
|
+
Raises:
|
|
119
|
+
ValueError: If sqlalchemy_url is empty
|
|
120
|
+
"""
|
|
121
|
+
if not store.sqlalchemy_url:
|
|
122
|
+
raise ValueError("IbisMetadataStore has an empty `sqlalchemy_url`.")
|
|
123
|
+
|
|
124
|
+
return store.sqlalchemy_url
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _get_system_metadata(
|
|
128
|
+
table_prefix: str = "",
|
|
129
|
+
) -> MetaData:
|
|
130
|
+
"""Create SQLAlchemy metadata containing system tables.
|
|
131
|
+
|
|
132
|
+
System tables always include primary key constraints.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
table_prefix: Optional prefix to prepend to table names
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
MetaData containing system table definitions
|
|
139
|
+
"""
|
|
140
|
+
metadata = MetaData()
|
|
141
|
+
create_system_tables(metadata, table_prefix=table_prefix)
|
|
142
|
+
return metadata
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def get_system_slqa_metadata(
|
|
146
|
+
store: IbisMetadataStore,
|
|
147
|
+
) -> tuple[str, MetaData]:
|
|
148
|
+
"""Get SQLAlchemy URL and Metaxy system tables metadata for a metadata store.
|
|
149
|
+
|
|
150
|
+
This function retrieves both the connection URL and system table metadata
|
|
151
|
+
for a store, with the store's `table_prefix` automatically applied to table names.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
store: IbisMetadataStore instance
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Tuple of (sqlalchemy_url, system_metadata)
|
|
158
|
+
|
|
159
|
+
Raises:
|
|
160
|
+
ValueError: If store's sqlalchemy_url is empty
|
|
161
|
+
"""
|
|
162
|
+
url = _get_store_sqlalchemy_url(store)
|
|
163
|
+
metadata = _get_system_metadata(table_prefix=store._table_prefix)
|
|
164
|
+
return url, metadata
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _get_features_metadata(
|
|
168
|
+
source_metadata: MetaData,
|
|
169
|
+
store: IbisMetadataStore,
|
|
170
|
+
project: str | None = None,
|
|
171
|
+
filter_by_project: bool = True,
|
|
172
|
+
inject_primary_key: bool | None = None,
|
|
173
|
+
inject_index: bool | None = None,
|
|
174
|
+
) -> MetaData:
|
|
175
|
+
"""Filter user-defined feature tables from source metadata by project.
|
|
176
|
+
|
|
177
|
+
This function must be called after init_metaxy() to ensure features are loaded.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
source_metadata: Source SQLAlchemy MetaData to filter (e.g., SQLModel.metadata)
|
|
181
|
+
store: IbisMetadataStore instance (used to get table_prefix)
|
|
182
|
+
project: Project name to filter by. If None, uses MetaxyConfig.get().project
|
|
183
|
+
filter_by_project: If True, only include features for the specified project.
|
|
184
|
+
inject_primary_key: If True, inject composite primary key constraints.
|
|
185
|
+
If False, do not inject. If None, uses config default.
|
|
186
|
+
inject_index: If True, inject composite index.
|
|
187
|
+
If False, do not inject. If None, uses config default.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Filtered SQLAlchemy MetaData containing only project-scoped feature tables
|
|
191
|
+
"""
|
|
192
|
+
from metaxy.models.feature import FeatureGraph
|
|
193
|
+
|
|
194
|
+
config = MetaxyConfig.get()
|
|
195
|
+
|
|
196
|
+
if project is None:
|
|
197
|
+
project = config.project
|
|
198
|
+
|
|
199
|
+
# Check plugin config for defaults
|
|
200
|
+
sqlalchemy_config = config.get_plugin("sqlalchemy", SQLAlchemyConfig)
|
|
201
|
+
if inject_primary_key is None:
|
|
202
|
+
inject_primary_key = sqlalchemy_config.inject_primary_key
|
|
203
|
+
if inject_index is None:
|
|
204
|
+
inject_index = sqlalchemy_config.inject_index
|
|
205
|
+
|
|
206
|
+
# Get the active feature graph
|
|
207
|
+
graph = FeatureGraph.get_active()
|
|
208
|
+
|
|
209
|
+
# Compute expected table names for features in the project
|
|
210
|
+
expected_table_names = set()
|
|
211
|
+
feature_specs_by_table_name = {}
|
|
212
|
+
|
|
213
|
+
for feature_key, feature_cls in graph.features_by_key.items():
|
|
214
|
+
# Filter by project if requested
|
|
215
|
+
if filter_by_project and hasattr(feature_cls, "project"):
|
|
216
|
+
feature_project = getattr(feature_cls, "project")
|
|
217
|
+
if feature_project != project:
|
|
218
|
+
continue
|
|
219
|
+
|
|
220
|
+
table_name = store.get_table_name(feature_key)
|
|
221
|
+
|
|
222
|
+
expected_table_names.add(table_name)
|
|
223
|
+
feature_specs_by_table_name[table_name] = feature_cls.spec()
|
|
224
|
+
|
|
225
|
+
# Filter source metadata to only include expected tables
|
|
226
|
+
filtered_metadata = MetaData()
|
|
227
|
+
|
|
228
|
+
for table_name, table in source_metadata.tables.items():
|
|
229
|
+
if table_name in expected_table_names:
|
|
230
|
+
# Copy table to filtered metadata
|
|
231
|
+
new_table = table.to_metadata(filtered_metadata)
|
|
232
|
+
|
|
233
|
+
# Inject constraints if requested
|
|
234
|
+
spec = feature_specs_by_table_name[table_name]
|
|
235
|
+
_inject_constraints(
|
|
236
|
+
table=new_table,
|
|
237
|
+
spec=spec,
|
|
238
|
+
inject_primary_key=inject_primary_key,
|
|
239
|
+
inject_index=inject_index,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
return filtered_metadata
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _inject_constraints(
|
|
246
|
+
table: Table,
|
|
247
|
+
spec: FeatureSpec,
|
|
248
|
+
inject_primary_key: bool,
|
|
249
|
+
inject_index: bool,
|
|
250
|
+
) -> None:
|
|
251
|
+
"""Inject primary key and/or index constraints on a table.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
table: SQLAlchemy Table to modify
|
|
255
|
+
spec: Feature specification with id_columns
|
|
256
|
+
inject_primary_key: If True, inject composite primary key
|
|
257
|
+
inject_index: If True, inject composite index
|
|
258
|
+
"""
|
|
259
|
+
from sqlalchemy import PrimaryKeyConstraint
|
|
260
|
+
|
|
261
|
+
from metaxy.models.constants import METAXY_CREATED_AT, METAXY_DATA_VERSION
|
|
262
|
+
|
|
263
|
+
# Composite key/index columns: id_columns + metaxy_created_at + metaxy_data_version
|
|
264
|
+
key_columns = list(spec.id_columns) + [METAXY_CREATED_AT, METAXY_DATA_VERSION]
|
|
265
|
+
|
|
266
|
+
if inject_primary_key:
|
|
267
|
+
# Add primary key constraint
|
|
268
|
+
pk_constraint = PrimaryKeyConstraint(*key_columns, name="metaxy_pk")
|
|
269
|
+
table.append_constraint(pk_constraint)
|
|
270
|
+
|
|
271
|
+
if inject_index:
|
|
272
|
+
# Add composite index
|
|
273
|
+
idx = Index(
|
|
274
|
+
"metaxy_idx",
|
|
275
|
+
*key_columns,
|
|
276
|
+
)
|
|
277
|
+
table.append_constraint(idx)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def filter_feature_sqla_metadata(
|
|
281
|
+
store: IbisMetadataStore,
|
|
282
|
+
source_metadata: MetaData,
|
|
283
|
+
project: str | None = None,
|
|
284
|
+
filter_by_project: bool = True,
|
|
285
|
+
inject_primary_key: bool | None = None,
|
|
286
|
+
inject_index: bool | None = None,
|
|
287
|
+
) -> tuple[str, MetaData]:
|
|
288
|
+
"""Get SQLAlchemy URL and feature table metadata for a metadata store.
|
|
289
|
+
|
|
290
|
+
This function filters the source metadata to include only feature tables
|
|
291
|
+
belonging to the specified project, and returns the connection URL for the store.
|
|
292
|
+
|
|
293
|
+
This function must be called after init_metaxy() to ensure features are loaded.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
store: IbisMetadataStore instance
|
|
297
|
+
source_metadata: Source SQLAlchemy MetaData to filter.
|
|
298
|
+
project: Project name to filter by. If None, uses MetaxyConfig.get().project
|
|
299
|
+
filter_by_project: If True, only include features for the specified project.
|
|
300
|
+
If False, include all features.
|
|
301
|
+
inject_primary_key: If True, inject composite primary key constraints.
|
|
302
|
+
If False, do not inject. If None, uses config default.
|
|
303
|
+
inject_index: If True, inject composite index.
|
|
304
|
+
If False, do not inject. If None, uses config default.
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
Tuple of (sqlalchemy_url, filtered_metadata)
|
|
308
|
+
|
|
309
|
+
Raises:
|
|
310
|
+
ValueError: If store's sqlalchemy_url is empty
|
|
311
|
+
ImportError: If source_metadata is None and SQLModel is not installed
|
|
312
|
+
|
|
313
|
+
Example: Basic Usage
|
|
314
|
+
|
|
315
|
+
```py
|
|
316
|
+
from metaxy.ext.sqlalchemy import filter_feature_sqla_metadata
|
|
317
|
+
from metaxy import init_metaxy
|
|
318
|
+
from metaxy.config import MetaxyConfig
|
|
319
|
+
|
|
320
|
+
# Load features first
|
|
321
|
+
init_metaxy()
|
|
322
|
+
|
|
323
|
+
# Get store instance
|
|
324
|
+
config = MetaxyConfig.get()
|
|
325
|
+
store = config.get_store("my_store")
|
|
326
|
+
|
|
327
|
+
# With custom metadata
|
|
328
|
+
from sqlalchemy import MetaData
|
|
329
|
+
my_metadata = MetaData()
|
|
330
|
+
# ... define tables in my_metadata ...
|
|
331
|
+
|
|
332
|
+
# apply the filter function
|
|
333
|
+
url, metadata = filter_feature_sqla_metadata(store, source_metadata=my_metadata)
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Example: With SQLModel
|
|
337
|
+
|
|
338
|
+
```py
|
|
339
|
+
# With SQLModel
|
|
340
|
+
from sqlmodel import SQLModel
|
|
341
|
+
url, metadata = filter_feature_sqla_metadata(store, SQLModel.metadata)
|
|
342
|
+
```
|
|
343
|
+
"""
|
|
344
|
+
url = _get_store_sqlalchemy_url(store)
|
|
345
|
+
metadata = _get_features_metadata(
|
|
346
|
+
source_metadata=source_metadata,
|
|
347
|
+
store=store,
|
|
348
|
+
project=project,
|
|
349
|
+
filter_by_project=filter_by_project,
|
|
350
|
+
inject_primary_key=inject_primary_key,
|
|
351
|
+
inject_index=inject_index,
|
|
352
|
+
)
|
|
353
|
+
return url, metadata
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from metaxy.ext.sqlmodel.config import SQLModelPluginConfig
|
|
2
|
+
from metaxy.ext.sqlmodel.plugin import (
|
|
3
|
+
BaseSQLModelFeature,
|
|
4
|
+
SQLModelFeatureMeta,
|
|
5
|
+
filter_feature_sqlmodel_metadata,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"SQLModelFeatureMeta",
|
|
10
|
+
"BaseSQLModelFeature",
|
|
11
|
+
"SQLModelPluginConfig",
|
|
12
|
+
"filter_feature_sqlmodel_metadata",
|
|
13
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Configuration for SQLModel integration."""
|
|
2
|
+
|
|
3
|
+
from pydantic import Field as PydanticField
|
|
4
|
+
from pydantic_settings import SettingsConfigDict
|
|
5
|
+
|
|
6
|
+
from metaxy.config import PluginConfig
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SQLModelPluginConfig(PluginConfig):
|
|
10
|
+
"""Configuration for SQLModel integration.
|
|
11
|
+
|
|
12
|
+
This plugin enhances SQLModel-based features with automatic table name
|
|
13
|
+
inference and optional primary key injection.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
model_config = SettingsConfigDict(
|
|
17
|
+
env_prefix="METAXY_EXT__SQLMODEL_",
|
|
18
|
+
extra="forbid",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
inject_primary_key: bool = PydanticField(
|
|
22
|
+
default=False,
|
|
23
|
+
description="Automatically inject composite primary key constraints on SQLModel tables. The key is composed of ID columns, `metaxy_created_at`, and `metaxy_data_version`.",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
inject_index: bool = PydanticField(
|
|
27
|
+
default=False,
|
|
28
|
+
description="Automatically inject composite index on SQLModel tables. The index covers ID columns, `metaxy_created_at`, and `metaxy_data_version`.",
|
|
29
|
+
)
|