mainsequence 4.2.39__tar.gz → 4.2.40__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.2.39/mainsequence.egg-info → mainsequence-4.2.40}/PKG-INFO +1 -1
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +6 -2
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/migrations.py +28 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/sqlalchemy_contracts.py +49 -9
- {mainsequence-4.2.39 → mainsequence-4.2.40/mainsequence.egg-info}/PKG-INFO +1 -1
- {mainsequence-4.2.39 → mainsequence-4.2.40}/pyproject.toml +1 -1
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_meta_table_migrations.py +67 -1
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_meta_tables_sqlalchemy_contracts.py +64 -5
- {mainsequence-4.2.39 → mainsequence-4.2.40}/LICENSE +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/README.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/__init__.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/__main__.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/bootstrap.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/api.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/cli.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/config.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/migrations.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/project_status.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/cli/ui.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/__init__.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/base.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/client.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/command_center/__init__.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/command_center/app_component.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/command_center/connections.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/command_center/data_models.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/command_center/workspace.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/fastapi/__init__.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/fastapi/auth.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/metatables/core.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/models_user.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/utils.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/defaults.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/logconf.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/__init__.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/models.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/schema_names.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence.egg-info/SOURCES.txt +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/setup.cfg +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_auth_precedence.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_build_operations_hashing.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_cli.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_cli_migrations.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_client.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_command_center_app_component_models.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_command_center_data_models.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_command_center_models.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_data_access_mixin_dimension_audit.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_data_node_storage_dimension_queries.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_data_node_update_flow.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_dependency_extras.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_filter_normalization.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_logconf.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_meta_tables_client_models.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_project_batch_jobs_from_file.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_run_configuration.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_schema_names.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_secret_client_model.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_update_statistics.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_update_uid_guards.py +0 -0
- {mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_workspace_snapshot.py +0 -0
|
@@ -137,7 +137,11 @@ first version, use Alembic. Keep the SDK model as a normal
|
|
|
137
137
|
`PlatformManagedMetaTable` or `PlatformTimeIndexMetaData` catalog contract, and
|
|
138
138
|
apply physical schema changes through the Alembic migration workflow.
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
Default-schema tables must leave SQLAlchemy `Table.schema` unset; do not write
|
|
141
|
+
`__table_args__ = {"schema": "public"}` for the default PostgreSQL schema. Set
|
|
142
|
+
schema metadata only for non-default schemas, using `__table_args__ = {"schema":
|
|
143
|
+
"custom_schema"}` or the tuple form ending in `{"schema": ...}`. Do not add a
|
|
144
|
+
separate MetaTable-specific schema attribute.
|
|
141
145
|
|
|
142
146
|
Always declare `__metatable_description__` on the model. The description must
|
|
143
147
|
explain the table's business intention, row grain, and expected use, not only
|
|
@@ -246,7 +250,7 @@ Use this pattern:
|
|
|
246
250
|
```python
|
|
247
251
|
account_uid: Mapped[uuid.UUID] = mapped_column(
|
|
248
252
|
Uuid,
|
|
249
|
-
ForeignKey("
|
|
253
|
+
ForeignKey("sdk_examples__account.uid", ondelete="RESTRICT"),
|
|
250
254
|
nullable=False,
|
|
251
255
|
)
|
|
252
256
|
```
|
|
@@ -27,9 +27,14 @@ from mainsequence.meta_tables.hashing import build_meta_table_storage_hash
|
|
|
27
27
|
from mainsequence.meta_tables.sqlalchemy_contracts import (
|
|
28
28
|
PlatformManagedMetaTable,
|
|
29
29
|
PlatformTimeIndexMetaData,
|
|
30
|
+
_ensure_time_index_unique_grain_index,
|
|
31
|
+
_normalize_table_default_schema,
|
|
30
32
|
_resolve_model_data_source_uid,
|
|
31
33
|
_resolve_table,
|
|
34
|
+
_resolve_time_index_name,
|
|
35
|
+
_resolve_time_index_names,
|
|
32
36
|
_table_name,
|
|
37
|
+
_validate_time_index_contract,
|
|
33
38
|
platform_managed_migration_registration_context,
|
|
34
39
|
)
|
|
35
40
|
|
|
@@ -268,6 +273,8 @@ class AlembicMetaTableMigration:
|
|
|
268
273
|
self.after_register_metatables
|
|
269
274
|
):
|
|
270
275
|
raise TypeError("after_register_metatables must be callable when provided.")
|
|
276
|
+
_normalize_provider_default_schemas(self.metatable_models)
|
|
277
|
+
_ensure_provider_time_index_grain_indexes(self.metatable_models)
|
|
271
278
|
|
|
272
279
|
@property
|
|
273
280
|
def alembic_version_table(self) -> str:
|
|
@@ -956,6 +963,27 @@ def _metadata_table_names(target_metadata: Any) -> list[str]:
|
|
|
956
963
|
return list(dict.fromkeys(names))
|
|
957
964
|
|
|
958
965
|
|
|
966
|
+
def _normalize_provider_default_schemas(models: Sequence[type[Any]]) -> None:
|
|
967
|
+
for model in models:
|
|
968
|
+
if _is_platform_managed_metatable_model(model):
|
|
969
|
+
_normalize_table_default_schema(_resolve_table(model))
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
def _ensure_provider_time_index_grain_indexes(models: Sequence[type[Any]]) -> None:
|
|
973
|
+
for model in models:
|
|
974
|
+
if not _is_platform_time_index_metatable_model(model):
|
|
975
|
+
continue
|
|
976
|
+
table = _resolve_table(model)
|
|
977
|
+
time_index_name = _resolve_time_index_name(model)
|
|
978
|
+
index_names = _resolve_time_index_names(model, time_index_name=time_index_name)
|
|
979
|
+
_validate_time_index_contract(
|
|
980
|
+
columns=list(table.columns),
|
|
981
|
+
time_index_name=time_index_name,
|
|
982
|
+
index_names=index_names,
|
|
983
|
+
)
|
|
984
|
+
_ensure_time_index_unique_grain_index(table=table, index_names=index_names)
|
|
985
|
+
|
|
986
|
+
|
|
959
987
|
def _normalize_down_revision(value: Any) -> str | None:
|
|
960
988
|
if value in (None, ""):
|
|
961
989
|
return None
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/sqlalchemy_contracts.py
RENAMED
|
@@ -11,6 +11,7 @@ from uuid import UUID
|
|
|
11
11
|
from sqlalchemy import Table as _SQLAlchemyTable
|
|
12
12
|
from sqlalchemy import inspect as _sqlalchemy_inspect
|
|
13
13
|
from sqlalchemy.orm import declared_attr as _sqlalchemy_declared_attr
|
|
14
|
+
from sqlalchemy.schema import BLANK_SCHEMA as _SQLALCHEMY_BLANK_SCHEMA
|
|
14
15
|
|
|
15
16
|
from mainsequence.client.dtype_codec import (
|
|
16
17
|
is_temporal_token,
|
|
@@ -35,6 +36,7 @@ DEFAULT_PLATFORM_MANAGED_PROVISIONING = {
|
|
|
35
36
|
"if_not_exists": True,
|
|
36
37
|
}
|
|
37
38
|
SERVER_GENERATED_UUID_DEFAULT = "gen_random_uuid()"
|
|
39
|
+
DEFAULT_POSTGRES_SCHEMA = "public"
|
|
38
40
|
_PLATFORM_MANAGED_MIGRATION_REGISTRATION_CONTEXT: contextvars.ContextVar[bool] = (
|
|
39
41
|
contextvars.ContextVar(
|
|
40
42
|
"mainsequence_platform_managed_migration_registration_context",
|
|
@@ -153,8 +155,14 @@ class PlatformManagedMetaTable:
|
|
|
153
155
|
|
|
154
156
|
name, metadata, *table_items = args
|
|
155
157
|
kwargs = dict(kwargs)
|
|
156
|
-
schema =
|
|
157
|
-
|
|
158
|
+
schema = _normalize_sqlalchemy_schema(
|
|
159
|
+
kwargs.get("schema")
|
|
160
|
+
if "schema" in kwargs
|
|
161
|
+
else _resolve_class_schema(cls, metadata=metadata)
|
|
162
|
+
)
|
|
163
|
+
if schema is None:
|
|
164
|
+
kwargs["schema"] = _SQLALCHEMY_BLANK_SCHEMA
|
|
165
|
+
else:
|
|
158
166
|
kwargs["schema"] = schema
|
|
159
167
|
|
|
160
168
|
from sqlalchemy import Table
|
|
@@ -308,8 +316,14 @@ class PlatformTimeIndexMetaData(PlatformManagedMetaTable):
|
|
|
308
316
|
|
|
309
317
|
name, metadata, *table_items = args
|
|
310
318
|
kwargs = dict(kwargs)
|
|
311
|
-
schema =
|
|
312
|
-
|
|
319
|
+
schema = _normalize_sqlalchemy_schema(
|
|
320
|
+
kwargs.get("schema")
|
|
321
|
+
if "schema" in kwargs
|
|
322
|
+
else _resolve_class_schema(cls, metadata=metadata)
|
|
323
|
+
)
|
|
324
|
+
if schema is None:
|
|
325
|
+
kwargs["schema"] = _SQLALCHEMY_BLANK_SCHEMA
|
|
326
|
+
else:
|
|
313
327
|
kwargs["schema"] = schema
|
|
314
328
|
|
|
315
329
|
columns = [item for item in table_items if _looks_like_column(item)]
|
|
@@ -416,7 +430,7 @@ def table_contract_from_sqlalchemy_model(
|
|
|
416
430
|
include_physical_table_name: bool = True,
|
|
417
431
|
) -> MetaTableContract:
|
|
418
432
|
table = _resolve_table(model_or_table)
|
|
419
|
-
_resolve_schema(table, schema=schema)
|
|
433
|
+
resolved_schema = _resolve_schema(table, schema=schema)
|
|
420
434
|
module, qualname = _resolve_model_path(
|
|
421
435
|
model_or_table,
|
|
422
436
|
table_model_module=table_model_module,
|
|
@@ -433,6 +447,7 @@ def table_contract_from_sqlalchemy_model(
|
|
|
433
447
|
}
|
|
434
448
|
},
|
|
435
449
|
physical=MetaTablePhysicalContract(
|
|
450
|
+
schema_=resolved_schema,
|
|
436
451
|
table_name=_table_name(table) if include_physical_table_name else None,
|
|
437
452
|
),
|
|
438
453
|
columns=[
|
|
@@ -563,7 +578,10 @@ def time_indexed_registration_request_from_sqlalchemy_model(
|
|
|
563
578
|
),
|
|
564
579
|
},
|
|
565
580
|
},
|
|
566
|
-
"physical": {
|
|
581
|
+
"physical": {
|
|
582
|
+
"schema": resolved_schema,
|
|
583
|
+
"table_name": _table_name(table),
|
|
584
|
+
},
|
|
567
585
|
"columns": column_contracts,
|
|
568
586
|
},
|
|
569
587
|
)
|
|
@@ -744,7 +762,7 @@ def _table_name(table: Any) -> str:
|
|
|
744
762
|
def _resolve_schema(table: Any, *, schema: str | None = None) -> str:
|
|
745
763
|
resolved_schema = schema or getattr(table, "schema", None)
|
|
746
764
|
if not resolved_schema:
|
|
747
|
-
|
|
765
|
+
return DEFAULT_POSTGRES_SCHEMA
|
|
748
766
|
return str(resolved_schema)
|
|
749
767
|
|
|
750
768
|
|
|
@@ -755,7 +773,7 @@ def _resolve_class_namespace(cls: type[Any]) -> str:
|
|
|
755
773
|
return str(resolved_namespace)
|
|
756
774
|
|
|
757
775
|
|
|
758
|
-
def _resolve_class_schema(cls: type[Any], *, metadata: Any | None = None) -> str:
|
|
776
|
+
def _resolve_class_schema(cls: type[Any], *, metadata: Any | None = None) -> str | None:
|
|
759
777
|
table_args = getattr(cls, "__table_args__", None)
|
|
760
778
|
if isinstance(table_args, Mapping):
|
|
761
779
|
schema = table_args.get("schema")
|
|
@@ -765,7 +783,29 @@ def _resolve_class_schema(cls: type[Any], *, metadata: Any | None = None) -> str
|
|
|
765
783
|
schema = None
|
|
766
784
|
if schema is None and metadata is not None:
|
|
767
785
|
schema = getattr(metadata, "schema", None)
|
|
768
|
-
return
|
|
786
|
+
return _normalize_sqlalchemy_schema(schema)
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
def _normalize_sqlalchemy_schema(schema: Any | None) -> str | None:
|
|
790
|
+
if schema is None:
|
|
791
|
+
return None
|
|
792
|
+
schema_name = str(schema).strip()
|
|
793
|
+
if schema_name in ("", DEFAULT_POSTGRES_SCHEMA):
|
|
794
|
+
return None
|
|
795
|
+
return schema_name
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
def _normalize_table_default_schema(table: Any) -> None:
|
|
799
|
+
if getattr(table, "schema", None) != DEFAULT_POSTGRES_SCHEMA:
|
|
800
|
+
return
|
|
801
|
+
|
|
802
|
+
metadata = getattr(table, "metadata", None)
|
|
803
|
+
if metadata is not None:
|
|
804
|
+
metadata._remove_table(table.name, table.schema)
|
|
805
|
+
table.schema = None
|
|
806
|
+
table.fullname = _table_name(table)
|
|
807
|
+
if metadata is not None:
|
|
808
|
+
metadata._add_table(table.name, table.schema, table)
|
|
769
809
|
|
|
770
810
|
|
|
771
811
|
def _resolve_data_source_uid(
|
|
@@ -16,7 +16,11 @@ from mainsequence.client.metatables import (
|
|
|
16
16
|
MetaTable,
|
|
17
17
|
TimeIndexMetaData,
|
|
18
18
|
)
|
|
19
|
-
from mainsequence.meta_tables import
|
|
19
|
+
from mainsequence.meta_tables import (
|
|
20
|
+
PlatformManagedMetaTable,
|
|
21
|
+
PlatformTimeIndexMetaData,
|
|
22
|
+
schema_index_name,
|
|
23
|
+
)
|
|
20
24
|
from mainsequence.meta_tables.migrations import (
|
|
21
25
|
DEFAULT_ALEMBIC_VERSION_COLUMN_NAME,
|
|
22
26
|
DEFAULT_ALEMBIC_VERSION_IDENTIFIER,
|
|
@@ -1005,6 +1009,68 @@ def test_prepare_for_alembic_routes_time_indexed_models_to_dynamic_table_bulk_cr
|
|
|
1005
1009
|
assert "schema_management" not in row
|
|
1006
1010
|
|
|
1007
1011
|
|
|
1012
|
+
def test_provider_adds_time_index_grain_index_when_table_cls_is_overridden():
|
|
1013
|
+
class Base(DeclarativeBase):
|
|
1014
|
+
metadata = MetaData(schema="public")
|
|
1015
|
+
|
|
1016
|
+
class CustomTimeIndexMixin(PlatformTimeIndexMetaData):
|
|
1017
|
+
__abstract__ = True
|
|
1018
|
+
|
|
1019
|
+
@classmethod
|
|
1020
|
+
def __table_cls__(cls, *args, **kwargs):
|
|
1021
|
+
name, metadata, *table_items = args
|
|
1022
|
+
return Table(str(name), metadata, *table_items, **kwargs)
|
|
1023
|
+
|
|
1024
|
+
class ProjectAlembicVersion(AlembicVersionMetaTable):
|
|
1025
|
+
__metatable_uid__ = "registry-meta-table-uid"
|
|
1026
|
+
__metatable_data_source_uid__ = "data-source-uid"
|
|
1027
|
+
|
|
1028
|
+
class Prices(CustomTimeIndexMixin, Base):
|
|
1029
|
+
__tablename__ = "example_assets__prices"
|
|
1030
|
+
__metatable_data_source_uid__ = "data-source-uid"
|
|
1031
|
+
__metatable_namespace__ = "example.assets"
|
|
1032
|
+
__metatable_identifier__ = "Prices"
|
|
1033
|
+
__time_index_name__ = "time_index"
|
|
1034
|
+
__index_names__ = ["time_index", "asset_identifier"]
|
|
1035
|
+
|
|
1036
|
+
time_index: Mapped[datetime.datetime] = mapped_column(
|
|
1037
|
+
DateTime(timezone=True),
|
|
1038
|
+
nullable=False,
|
|
1039
|
+
)
|
|
1040
|
+
asset_identifier: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
1041
|
+
close: Mapped[int] = mapped_column(Integer, nullable=True)
|
|
1042
|
+
|
|
1043
|
+
assert not Prices.__table__.indexes
|
|
1044
|
+
assert Prices.__table__.schema == "public"
|
|
1045
|
+
assert "public.example_assets__prices" in Base.metadata.tables
|
|
1046
|
+
|
|
1047
|
+
AlembicMetaTableMigration(
|
|
1048
|
+
package="sample",
|
|
1049
|
+
migration_namespace="markets",
|
|
1050
|
+
script_location="sample:migrations",
|
|
1051
|
+
target_metadata=Base.metadata,
|
|
1052
|
+
alembic_registry=ProjectAlembicVersion,
|
|
1053
|
+
metatable_models=[Prices],
|
|
1054
|
+
)
|
|
1055
|
+
|
|
1056
|
+
assert Prices.__table__.schema is None
|
|
1057
|
+
assert "example_assets__prices" in Base.metadata.tables
|
|
1058
|
+
assert "public.example_assets__prices" not in Base.metadata.tables
|
|
1059
|
+
|
|
1060
|
+
grain_indexes = [
|
|
1061
|
+
index
|
|
1062
|
+
for index in Prices.__table__.indexes
|
|
1063
|
+
if index.unique
|
|
1064
|
+
and [column.name for column in index.columns] == ["time_index", "asset_identifier"]
|
|
1065
|
+
]
|
|
1066
|
+
assert len(grain_indexes) == 1
|
|
1067
|
+
assert grain_indexes[0].name == schema_index_name(
|
|
1068
|
+
"example_assets__prices",
|
|
1069
|
+
["time_index", "asset_identifier"],
|
|
1070
|
+
unique=True,
|
|
1071
|
+
)
|
|
1072
|
+
|
|
1073
|
+
|
|
1008
1074
|
def test_prepare_for_alembic_reserves_existing_table_name_with_provider_identity(monkeypatch):
|
|
1009
1075
|
class Base(DeclarativeBase):
|
|
1010
1076
|
metadata = MetaData()
|
|
@@ -710,6 +710,63 @@ def test_platform_managed_schema_resolves_from_sqlalchemy_table_args_only():
|
|
|
710
710
|
assert sqlalchemy_contracts._resolve_class_schema(Account) == "table_args_schema"
|
|
711
711
|
|
|
712
712
|
|
|
713
|
+
def test_platform_managed_default_public_schema_stays_sqlalchemy_default():
|
|
714
|
+
pytest.importorskip("sqlalchemy")
|
|
715
|
+
|
|
716
|
+
from sqlalchemy import MetaData
|
|
717
|
+
from sqlalchemy import Uuid as SQLAlchemyUuid
|
|
718
|
+
from sqlalchemy.orm import DeclarativeBase, mapped_column
|
|
719
|
+
|
|
720
|
+
class Base(DeclarativeBase):
|
|
721
|
+
metadata = MetaData()
|
|
722
|
+
|
|
723
|
+
class Account(PlatformManagedMetaTable, Base):
|
|
724
|
+
__tablename__ = "example_assets__account"
|
|
725
|
+
__table_args__ = {"schema": "public"}
|
|
726
|
+
__metatable_namespace__ = "example.assets"
|
|
727
|
+
__metatable_identifier__ = "Account"
|
|
728
|
+
|
|
729
|
+
uid: Mapped[uuid.UUID] = mapped_column(SQLAlchemyUuid, primary_key=True)
|
|
730
|
+
|
|
731
|
+
assert Account.__table__.schema is None
|
|
732
|
+
assert sqlalchemy_contracts._resolve_schema(Account.__table__) == "public"
|
|
733
|
+
|
|
734
|
+
request = Account.build_registration_request(
|
|
735
|
+
data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
736
|
+
)
|
|
737
|
+
|
|
738
|
+
assert request.table_contract.physical.schema_ == "public"
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
def test_platform_managed_default_public_metadata_schema_stays_sqlalchemy_default():
|
|
742
|
+
pytest.importorskip("sqlalchemy")
|
|
743
|
+
|
|
744
|
+
from sqlalchemy import MetaData
|
|
745
|
+
from sqlalchemy import Uuid as SQLAlchemyUuid
|
|
746
|
+
from sqlalchemy.orm import DeclarativeBase, mapped_column
|
|
747
|
+
|
|
748
|
+
class Base(DeclarativeBase):
|
|
749
|
+
metadata = MetaData(schema="public")
|
|
750
|
+
|
|
751
|
+
class Account(PlatformManagedMetaTable, Base):
|
|
752
|
+
__tablename__ = "example_assets__account"
|
|
753
|
+
__metatable_namespace__ = "example.assets"
|
|
754
|
+
__metatable_identifier__ = "Account"
|
|
755
|
+
|
|
756
|
+
uid: Mapped[uuid.UUID] = mapped_column(SQLAlchemyUuid, primary_key=True)
|
|
757
|
+
|
|
758
|
+
assert Account.__table__.schema is None
|
|
759
|
+
assert Account.__table__.fullname == "example_assets__account"
|
|
760
|
+
assert "example_assets__account" in Base.metadata.tables
|
|
761
|
+
assert "public.example_assets__account" not in Base.metadata.tables
|
|
762
|
+
|
|
763
|
+
request = Account.build_registration_request(
|
|
764
|
+
data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
assert request.table_contract.physical.schema_ == "public"
|
|
768
|
+
|
|
769
|
+
|
|
713
770
|
def test_time_index_optional_table_info_resolvers_allow_pending_declarative_class():
|
|
714
771
|
class PendingTimeIndexTable:
|
|
715
772
|
__time_index_name__ = "time_index"
|
|
@@ -1663,14 +1720,15 @@ def test_platform_managed_register_preserves_authored_sqlalchemy_table_name(
|
|
|
1663
1720
|
assert Account.get_storage_hash() == storage_hash
|
|
1664
1721
|
assert Account.get_physical_table_name() == "example_assets__account"
|
|
1665
1722
|
assert Account.__table__.name == "example_assets__account"
|
|
1666
|
-
assert Account.__table__.
|
|
1667
|
-
assert
|
|
1723
|
+
assert Account.__table__.schema is None
|
|
1724
|
+
assert Account.__table__.fullname == "example_assets__account"
|
|
1725
|
+
assert Base.metadata.tables["example_assets__account"] is Account.__table__
|
|
1668
1726
|
assert f"public.{physical_table_name}" not in Base.metadata.tables
|
|
1669
1727
|
|
|
1670
1728
|
compiled_sql = str(
|
|
1671
1729
|
select(Account.__table__).compile(dialect=postgresql.dialect(paramstyle="pyformat"))
|
|
1672
1730
|
)
|
|
1673
|
-
assert "FROM
|
|
1731
|
+
assert "FROM example_assets__account" in compiled_sql
|
|
1674
1732
|
assert physical_table_name not in compiled_sql
|
|
1675
1733
|
assert storage_hash not in compiled_sql
|
|
1676
1734
|
|
|
@@ -1702,7 +1760,8 @@ def test_bound_parent_table_foreign_key_stays_sqlalchemy_only():
|
|
|
1702
1760
|
)
|
|
1703
1761
|
)
|
|
1704
1762
|
|
|
1705
|
-
assert Account.__table__.
|
|
1763
|
+
assert Account.__table__.schema is None
|
|
1764
|
+
assert Account.__table__.fullname == "example_assets__account"
|
|
1706
1765
|
|
|
1707
1766
|
class Asset(PlatformManagedMetaTable, Base):
|
|
1708
1767
|
__tablename__ = "example_assets__asset"
|
|
@@ -1712,7 +1771,7 @@ def test_bound_parent_table_foreign_key_stays_sqlalchemy_only():
|
|
|
1712
1771
|
uid: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True)
|
|
1713
1772
|
account_uid: Mapped[uuid.UUID] = mapped_column(
|
|
1714
1773
|
Uuid,
|
|
1715
|
-
ForeignKey("
|
|
1774
|
+
ForeignKey("example_assets__account.uid", ondelete="RESTRICT"),
|
|
1716
1775
|
nullable=False,
|
|
1717
1776
|
)
|
|
1718
1777
|
symbol: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/a2a_communication/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/dashboards/streamlit/SKILL.md
RENAMED
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/data_access/exploration/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/command_center/app_component.py
RENAMED
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/command_center/connections.py
RENAMED
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/command_center/data_models.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/command_center/workspace_snapshot.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/__init__.py
RENAMED
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/duckdb.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/client/data_sources_interfaces/sqlite.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/compiled_sql/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/build_operations.py
RENAMED
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/data_nodes.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/namespacing.py
RENAMED
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/persist_managers.py
RENAMED
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/mainsequence/meta_tables/data_nodes/run_operations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_command_center_app_component_models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.2.39 → mainsequence-4.2.40}/tests/test_data_node_storage_dimension_queries.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|