mainsequence 4.1.7__tar.gz → 4.1.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mainsequence-4.1.7 → mainsequence-4.1.9}/PKG-INFO +1 -1
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +65 -15
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +39 -9
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/__init__.py +1 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -7
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/pydantic_metadata.py +0 -18
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/sqlalchemy_contracts.py +500 -199
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence.egg-info/PKG-INFO +1 -1
- {mainsequence-4.1.7 → mainsequence-4.1.9}/pyproject.toml +1 -1
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_build_operations_hashing.py +0 -40
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_meta_tables_sqlalchemy_contracts.py +302 -72
- {mainsequence-4.1.7 → mainsequence-4.1.9}/LICENSE +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/README.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/__init__.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/__main__.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/bootstrap.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/api.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/cli.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/config.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/project_status.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/cli/ui.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/__init__.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/base.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/client.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/command_center/__init__.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/command_center/app_component.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/command_center/connections.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/command_center/data_models.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/command_center/workspace.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/fastapi/__init__.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/fastapi/auth.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/models_metatables.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/models_user.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/client/utils.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/defaults.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/logconf.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/data_nodes/models.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence.egg-info/SOURCES.txt +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/setup.cfg +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_auth_precedence.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_cli.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_client.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_command_center_app_component_models.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_command_center_data_models.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_command_center_models.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_data_access_mixin_dimension_audit.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_data_node_storage_dimension_queries.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_data_node_update_flow.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_dependency_extras.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_filter_normalization.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_logconf.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_meta_tables_client_models.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_project_batch_jobs_from_file.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_run_configuration.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_secret_client_model.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_update_statistics.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_update_uid_guards.py +0 -0
- {mainsequence-4.1.7 → mainsequence-4.1.9}/tests/test_workspace_snapshot.py +0 -0
{mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md
RENAMED
|
@@ -25,7 +25,7 @@ Canonical workflow:
|
|
|
25
25
|
- modify an existing `DataNode` update process
|
|
26
26
|
- review whether a DataNode change affects update identity or table contract
|
|
27
27
|
- define or refactor `DataNodeConfiguration`
|
|
28
|
-
- classify
|
|
28
|
+
- classify values as hashed config fields or non-config class/runtime values
|
|
29
29
|
- implement or review:
|
|
30
30
|
- `dependencies()`
|
|
31
31
|
- `update()`
|
|
@@ -112,6 +112,16 @@ Every storage class must include `__metatable_description__`. The description
|
|
|
112
112
|
should explain the table's intention, row grain, and downstream use, not only
|
|
113
113
|
list columns or schema mechanics.
|
|
114
114
|
|
|
115
|
+
Use `__metatable_extra_hash_components__` on storage classes when distinct
|
|
116
|
+
DataNode storage tables could otherwise have the same storage-relevant shape.
|
|
117
|
+
For example, two one-index daily tables with one float column need a stable
|
|
118
|
+
component such as `{"storage_name": "daily_random_number"}` versus
|
|
119
|
+
`{"storage_name": "daily_random_addition"}`.
|
|
120
|
+
|
|
121
|
+
This is storage identity. Changing it creates a different storage table. Do not
|
|
122
|
+
use it for labels, descriptions, runtime options, test isolation, backend UIDs,
|
|
123
|
+
data-source UIDs, or updater scope.
|
|
124
|
+
|
|
115
125
|
Do not put those concerns in `DataNodeConfiguration`.
|
|
116
126
|
|
|
117
127
|
Minimal pattern:
|
|
@@ -132,6 +142,7 @@ class Base(DeclarativeBase):
|
|
|
132
142
|
class PricesTable(PlatformTimeIndexMetaData, Base):
|
|
133
143
|
__metatable_namespace__ = "<domain_namespace>"
|
|
134
144
|
__metatable_identifier__ = "<table_identifier>"
|
|
145
|
+
__metatable_extra_hash_components__ = {"storage_name": "<stable_storage_name>"}
|
|
135
146
|
__metatable_description__ = (
|
|
136
147
|
"Daily close prices keyed by asset unique identifier for portfolio and "
|
|
137
148
|
"risk analytics."
|
|
@@ -183,12 +194,21 @@ Do not accept `test_node`. It has been removed. Use explicit
|
|
|
183
194
|
Pattern:
|
|
184
195
|
|
|
185
196
|
```python
|
|
197
|
+
from typing import ClassVar
|
|
198
|
+
|
|
199
|
+
from pydantic import Field
|
|
200
|
+
|
|
186
201
|
from mainsequence.meta_tables import DataNode, DataNodeConfiguration
|
|
187
202
|
from mainsequence.meta_tables import PlatformTimeIndexMetaData
|
|
188
203
|
|
|
189
204
|
|
|
190
205
|
class PricesConfig(DataNodeConfiguration):
|
|
191
|
-
shard_id: str
|
|
206
|
+
shard_id: str = Field(
|
|
207
|
+
...,
|
|
208
|
+
description="Stable updater partition for this price job.",
|
|
209
|
+
examples=["us_equities_daily"],
|
|
210
|
+
)
|
|
211
|
+
reference_dimension: ClassVar[str] = "unique_identifier"
|
|
192
212
|
|
|
193
213
|
|
|
194
214
|
class PricesUpdate(DataNode):
|
|
@@ -216,23 +236,45 @@ class PricesUpdate(DataNode):
|
|
|
216
236
|
### 3. Configuration Is Update-Scoped By Default
|
|
217
237
|
|
|
218
238
|
Every `DataNodeConfiguration` field participates in `update_hash` by default.
|
|
239
|
+
Declare values that change output values, dependencies, source choice, or
|
|
240
|
+
updater scope as normal config fields.
|
|
219
241
|
|
|
220
|
-
|
|
242
|
+
Every config field must be declared with `Field(...)`. Include a clear
|
|
243
|
+
`description` and add `examples=[...]` whenever a realistic example helps. The
|
|
244
|
+
description must explain what the value means for the update process, not repeat
|
|
245
|
+
the Python type.
|
|
221
246
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
247
|
+
Values that must not affect `update_hash` should not be Pydantic config fields.
|
|
248
|
+
Use `ClassVar[...]` for class-level invariants and implementation constants, or
|
|
249
|
+
keep runtime controls in environment/runtime configuration outside the
|
|
250
|
+
DataNode config.
|
|
226
251
|
|
|
227
|
-
|
|
252
|
+
If a value genuinely must remain a Pydantic field while not affecting
|
|
253
|
+
`update_hash`, the only supported field-level opt-out is
|
|
254
|
+
`json_schema_extra={"hash_excluded": True}`:
|
|
228
255
|
|
|
229
256
|
```python
|
|
230
|
-
|
|
257
|
+
from pydantic import Field
|
|
258
|
+
|
|
259
|
+
from mainsequence.meta_tables import DataNodeConfiguration
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class PricesConfig(DataNodeConfiguration):
|
|
263
|
+
shard_id: str = Field(
|
|
264
|
+
...,
|
|
265
|
+
description="Stable updater partition for this price job.",
|
|
266
|
+
examples=["us_equities_daily"],
|
|
267
|
+
)
|
|
268
|
+
display_label: str | None = Field(
|
|
269
|
+
default=None,
|
|
270
|
+
description="Optional human-facing label for UI display only.",
|
|
271
|
+
examples=["US equities daily prices"],
|
|
272
|
+
json_schema_extra={"hash_excluded": True},
|
|
273
|
+
)
|
|
231
274
|
```
|
|
232
275
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
updater scope, it must remain a normal config field.
|
|
276
|
+
Do not invent other metadata-marker exceptions. Scope, dependency, source, and
|
|
277
|
+
output-affecting fields must remain hashed config fields.
|
|
236
278
|
|
|
237
279
|
### 4. `hash_namespace` Is Isolation Only
|
|
238
280
|
|
|
@@ -311,7 +353,16 @@ Do not construct dependency graphs dynamically inside `update()`.
|
|
|
311
353
|
### 8. Foreign Keys Belong To The Storage Contract
|
|
312
354
|
|
|
313
355
|
For new code, model foreign keys on the `PlatformTimeIndexMetaData` storage
|
|
314
|
-
class or route the storage-contract work to the MetaTable skill.
|
|
356
|
+
class or route the storage-contract work to the MetaTable skill. When a
|
|
357
|
+
DataNode storage table needs a platform-managed FK, use
|
|
358
|
+
`MetaTableForeignKey(TargetModel, column=...)` on the storage class. Do not use
|
|
359
|
+
`ForeignKey(Target.__table__.c.uid)`, table fullnames, or explicit target UID
|
|
360
|
+
maps in DataNode examples.
|
|
361
|
+
|
|
362
|
+
Registration of the storage class follows the MetaTable lifecycle:
|
|
363
|
+
`register()` recursively registers unresolved FK target model classes, uses the
|
|
364
|
+
local process registry keyed by `storage_hash`, and writes the target
|
|
365
|
+
`MetaTable.uid` into the FK contract.
|
|
315
366
|
|
|
316
367
|
Do not add DataNode configuration fields just to mutate storage metadata.
|
|
317
368
|
|
|
@@ -330,11 +381,10 @@ When reviewing an existing DataNode, look for:
|
|
|
330
381
|
- missing `__metatable_description__` on the storage table
|
|
331
382
|
- dependency storage table passed as an ad hoc constructor argument
|
|
332
383
|
- schema or published table metadata hidden in DataNode configuration
|
|
333
|
-
- `update_only`, `runtime_only`, or `ignore_from_storage_hash`
|
|
334
384
|
- `test_node=True`
|
|
335
385
|
- missing explicit `storage_table`
|
|
336
386
|
- accidental storage registration inside the DataNode
|
|
337
|
-
- wrong
|
|
387
|
+
- wrong split between hashed config fields and non-config class/runtime values
|
|
338
388
|
- misuse of `hash_namespace`
|
|
339
389
|
- non-incremental `update()` behavior
|
|
340
390
|
- hidden dependency creation inside `update()`
|
{mainsequence-4.1.7 → mainsequence-4.1.9}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md
RENAMED
|
@@ -103,12 +103,23 @@ Always declare `__metatable_description__` on the model. The description must
|
|
|
103
103
|
explain the table's business intention, row grain, and expected use, not only
|
|
104
104
|
the schema. Column-level descriptions stay in `mapped_column(info={...})`.
|
|
105
105
|
|
|
106
|
+
Use `__metatable_extra_hash_components__` when two backend-managed tables could
|
|
107
|
+
otherwise produce the same storage hash because their storage-relevant shape is
|
|
108
|
+
identical or intentionally generic. The value must be stable and deterministic,
|
|
109
|
+
usually a small mapping such as `{"storage_name": "account_holdings"}`.
|
|
110
|
+
|
|
111
|
+
This attribute is part of storage identity. Changing it defines a different
|
|
112
|
+
table. Do not use it for labels, descriptions, runtime options, test isolation,
|
|
113
|
+
backend UIDs, data-source UIDs, or updater scope. Use `hash_namespace` for test
|
|
114
|
+
or experiment isolation.
|
|
115
|
+
|
|
106
116
|
Register through the class API:
|
|
107
117
|
|
|
108
118
|
```python
|
|
109
119
|
class Account(PlatformManagedMetaTable, Base):
|
|
110
120
|
__metatable_namespace__ = "sdk-examples"
|
|
111
121
|
__metatable_identifier__ = "Account"
|
|
122
|
+
__metatable_extra_hash_components__ = {"storage_name": "account"}
|
|
112
123
|
__metatable_description__ = (
|
|
113
124
|
"Customer account master records used to scope balances, holdings, and "
|
|
114
125
|
"account-level limits."
|
|
@@ -141,29 +152,48 @@ Do not add a `MAINSEQUENCE_META_TABLE_REGISTER` toggle in registration examples.
|
|
|
141
152
|
|
|
142
153
|
Foreign-key contracts reference the target `MetaTable` UID.
|
|
143
154
|
|
|
144
|
-
For `PlatformManagedMetaTable`,
|
|
155
|
+
For `PlatformManagedMetaTable`, define foreign keys with
|
|
156
|
+
`MetaTableForeignKey(TargetModel, column=...)`. Do not write raw SQLAlchemy
|
|
157
|
+
table fullnames, `Parent.__table__.c.<column>` targets, or explicit target UID
|
|
158
|
+
maps in the platform-managed path. Registration is the lifecycle path:
|
|
159
|
+
`register()` recursively registers unresolved target model classes, stores each
|
|
160
|
+
returned `MetaTable` in a local process registry keyed by `storage_hash`, and
|
|
161
|
+
uses the target `MetaTable.uid` in the child FK contract.
|
|
162
|
+
|
|
163
|
+
Use this pattern:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
account_uid: Mapped[uuid.UUID] = mapped_column(
|
|
167
|
+
Uuid,
|
|
168
|
+
MetaTableForeignKey(Account, column="uid", ondelete="RESTRICT"),
|
|
169
|
+
nullable=False,
|
|
170
|
+
)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Every participating table must include `__metatable_description__` describing
|
|
174
|
+
both the schema and the table's intention.
|
|
145
175
|
|
|
146
176
|
Example registration order:
|
|
147
177
|
|
|
148
178
|
```python
|
|
149
|
-
account_meta_table = Account.register(...)
|
|
150
179
|
asset_meta_table = Asset.register(...)
|
|
151
180
|
```
|
|
152
181
|
|
|
153
|
-
The child registration
|
|
154
|
-
|
|
155
|
-
|
|
182
|
+
The child registration registers `Account` first if it has not already been
|
|
183
|
+
registered in the current process. The local registry prevents duplicate backend
|
|
184
|
+
registration attempts for the same `storage_hash` and raises a clear error for
|
|
185
|
+
recursive registration cycles.
|
|
156
186
|
|
|
157
|
-
For `external_registered`, there is no platform-managed parent lookup
|
|
187
|
+
For `external_registered`, there is no platform-managed parent lookup. Register
|
|
188
|
+
the parent first, then build the child registration request with
|
|
189
|
+
`target_meta_tables={Account: account_meta_table}`:
|
|
158
190
|
|
|
159
191
|
```python
|
|
160
192
|
account_meta_table = MetaTable.register(account_request)
|
|
161
193
|
asset_request = external_registered_registration_request_from_sqlalchemy_model(
|
|
162
194
|
Asset,
|
|
163
195
|
data_source_uid=data_source_uid,
|
|
164
|
-
|
|
165
|
-
Account.__table__.fullname: account_meta_table.uid,
|
|
166
|
-
},
|
|
196
|
+
target_meta_tables={Account: account_meta_table},
|
|
167
197
|
)
|
|
168
198
|
asset_meta_table = MetaTable.register(asset_request)
|
|
169
199
|
```
|
|
@@ -9,6 +9,7 @@ _LAZY_IMPORTS = {
|
|
|
9
9
|
".sqlalchemy_contracts",
|
|
10
10
|
"DEFAULT_PLATFORM_MANAGED_PROVISIONING",
|
|
11
11
|
),
|
|
12
|
+
"MetaTableForeignKey": (".sqlalchemy_contracts", "MetaTableForeignKey"),
|
|
12
13
|
"PlatformManagedMetaTable": (".sqlalchemy_contracts", "PlatformManagedMetaTable"),
|
|
13
14
|
"PlatformTimeIndexMetaData": (".sqlalchemy_contracts", "PlatformTimeIndexMetaData"),
|
|
14
15
|
"POSTGRES_IDENTIFIER_MAX_LENGTH": (".hashing", "POSTGRES_IDENTIFIER_MAX_LENGTH"),
|
|
@@ -420,13 +420,6 @@ class DataNode(DataAccessMixin, ABC):
|
|
|
420
420
|
"""
|
|
421
421
|
super().__init_subclass__(**kwargs)
|
|
422
422
|
|
|
423
|
-
if "_ARGS_IGNORE_IN_STORAGE_HASH" in cls.__dict__:
|
|
424
|
-
raise TypeError(
|
|
425
|
-
f"{cls.__name__} uses removed class attribute _ARGS_IGNORE_IN_STORAGE_HASH; "
|
|
426
|
-
"move those fields into DataNodeConfiguration. Configuration fields "
|
|
427
|
-
"participate in update hashing by default."
|
|
428
|
-
)
|
|
429
|
-
|
|
430
423
|
# Get the original __init__ from the new subclass
|
|
431
424
|
original_init = cls.__init__
|
|
432
425
|
|
|
@@ -20,24 +20,6 @@ def serialize_pydantic_model(
|
|
|
20
20
|
|
|
21
21
|
for field_name, field_info in value.__class__.model_fields.items():
|
|
22
22
|
extra = field_info.json_schema_extra or {}
|
|
23
|
-
if "ignore_from_storage_hash" in extra:
|
|
24
|
-
raise ValueError(
|
|
25
|
-
f"{value.__class__.__name__}.{field_name} uses removed metadata "
|
|
26
|
-
"'ignore_from_storage_hash'. All configuration fields participate in "
|
|
27
|
-
'update hashing by default; use json_schema_extra={"hash_excluded": True} '
|
|
28
|
-
"only for fields that should not affect update identity."
|
|
29
|
-
)
|
|
30
|
-
if "update_only" in extra:
|
|
31
|
-
raise ValueError(
|
|
32
|
-
f"{value.__class__.__name__}.{field_name} uses removed metadata "
|
|
33
|
-
"'update_only'. All configuration fields are update-scoped by default."
|
|
34
|
-
)
|
|
35
|
-
if "runtime_only" in extra:
|
|
36
|
-
raise ValueError(
|
|
37
|
-
f"{value.__class__.__name__}.{field_name} uses removed metadata "
|
|
38
|
-
"'runtime_only'; use json_schema_extra={\"hash_excluded\": True} instead."
|
|
39
|
-
)
|
|
40
|
-
|
|
41
23
|
is_hash_excluded = extra.get("hash_excluded", False)
|
|
42
24
|
if not isinstance(is_hash_excluded, bool):
|
|
43
25
|
raise ValueError(
|