mainsequence 4.3.0__tar.gz → 4.3.5__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.3.0/mainsequence.egg-info → mainsequence-4.3.5}/PKG-INFO +1 -1
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/migrations.py +25 -8
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/metatables/core.py +35 -9
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/compiled_sql/v1.py +9 -2
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -1
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -1
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/migrations.py +69 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5/mainsequence.egg-info}/PKG-INFO +1 -1
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence.egg-info/entry_points.txt +1 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/pyproject.toml +2 -1
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_cli_migrations.py +102 -1
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_data_access_mixin_dimension_audit.py +0 -2
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_filter_normalization.py +16 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_meta_table_migrations.py +52 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_meta_tables_client_models.py +44 -1
- {mainsequence-4.3.0 → mainsequence-4.3.5}/LICENSE +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/README.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/__init__.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/__main__.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/bootstrap.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/api.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/cli.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/config.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/project_status.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/cli/ui.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/__init__.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/base.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/client.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/__init__.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/app_component.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/connections.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/data_models.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/workspace.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/fastapi/__init__.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/fastapi/auth.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/models_user.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/utils.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/defaults.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/logconf.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/__init__.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/models.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/schema_names.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence.egg-info/SOURCES.txt +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/setup.cfg +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_auth_precedence.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_build_operations_hashing.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_cli.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_client.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_command_center_app_component_models.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_command_center_data_models.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_command_center_models.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_data_node_storage_dimension_queries.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_data_node_update_flow.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_dependency_extras.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_instrumentation.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_logconf.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_project_batch_jobs_from_file.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_run_configuration.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_schema_names.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_secret_client_model.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_update_statistics.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_update_uid_guards.py +0 -0
- {mainsequence-4.3.0 → mainsequence-4.3.5}/tests/test_workspace_snapshot.py +0 -0
|
@@ -122,12 +122,21 @@ def _load_alembic_command(command_name: str) -> Any:
|
|
|
122
122
|
return command
|
|
123
123
|
|
|
124
124
|
|
|
125
|
+
def _coerce_alembic_version_path(version_path: str) -> str:
|
|
126
|
+
try:
|
|
127
|
+
from alembic import util
|
|
128
|
+
except ImportError as exc:
|
|
129
|
+
raise typer.BadParameter("Alembic is required for migration commands.") from exc
|
|
130
|
+
return str(util.coerce_resource_to_filename(version_path))
|
|
131
|
+
|
|
132
|
+
|
|
125
133
|
def _emit_alembic_script_context(
|
|
126
134
|
config: Any,
|
|
127
135
|
*,
|
|
128
136
|
target_revision: str | None = None,
|
|
129
137
|
) -> None:
|
|
130
138
|
script_location = config.get_main_option("script_location")
|
|
139
|
+
version_locations = config.get_main_option("version_locations")
|
|
131
140
|
version_table = config.get_main_option("version_table")
|
|
132
141
|
version_table_schema = config.get_main_option("version_table_schema")
|
|
133
142
|
version_table_label = (
|
|
@@ -135,9 +144,14 @@ def _emit_alembic_script_context(
|
|
|
135
144
|
if version_table_schema not in (None, "")
|
|
136
145
|
else version_table
|
|
137
146
|
)
|
|
147
|
+
version_locations_label = (
|
|
148
|
+
version_locations.replace("\n", ", ") if version_locations else "<default>"
|
|
149
|
+
)
|
|
138
150
|
_emit_status(
|
|
139
151
|
"Alembic script context "
|
|
140
|
-
f"script_location={script_location}
|
|
152
|
+
f"script_location={script_location} "
|
|
153
|
+
f"version_locations={version_locations_label} "
|
|
154
|
+
f"version_table={version_table_label}"
|
|
141
155
|
)
|
|
142
156
|
try:
|
|
143
157
|
from alembic.script import ScriptDirectory
|
|
@@ -745,14 +759,17 @@ def revision(
|
|
|
745
759
|
)
|
|
746
760
|
_emit_alembic_script_context(config, target_revision=head)
|
|
747
761
|
_emit_status(f"Starting Alembic revision now rev_id={resolved_rev_id}...")
|
|
762
|
+
revision_kwargs = {
|
|
763
|
+
"message": resolved_message,
|
|
764
|
+
"autogenerate": autogenerate,
|
|
765
|
+
"rev_id": resolved_rev_id,
|
|
766
|
+
"head": head,
|
|
767
|
+
}
|
|
768
|
+
version_path = migration.resolved_version_path()
|
|
769
|
+
if version_path is not None:
|
|
770
|
+
revision_kwargs["version_path"] = _coerce_alembic_version_path(version_path)
|
|
748
771
|
with _forward_alembic_logging():
|
|
749
|
-
script = command.revision(
|
|
750
|
-
config,
|
|
751
|
-
message=resolved_message,
|
|
752
|
-
autogenerate=autogenerate,
|
|
753
|
-
rev_id=resolved_rev_id,
|
|
754
|
-
head=head,
|
|
755
|
-
)
|
|
772
|
+
script = command.revision(config, **revision_kwargs)
|
|
756
773
|
_emit_status("Alembic revision finished.")
|
|
757
774
|
_emit(
|
|
758
775
|
{
|
|
@@ -452,8 +452,43 @@ class MetaTableOperationScopeTable(BasePydanticModel):
|
|
|
452
452
|
|
|
453
453
|
|
|
454
454
|
class MetaTableOperationScope(BasePydanticModel):
|
|
455
|
+
data_source_uid: str | None = Field(
|
|
456
|
+
default=None,
|
|
457
|
+
min_length=1,
|
|
458
|
+
validation_alias=AliasChoices("data_source_uid", "dataSourceUid"),
|
|
459
|
+
description=(
|
|
460
|
+
"Public UID of the DynamicTableDataSource that owns the compiled "
|
|
461
|
+
"SQL execution connection. If omitted, the SDK resolves the "
|
|
462
|
+
"configured project/session default data source. Scoped MetaTables "
|
|
463
|
+
"are the permission contract, not the source of execution routing."
|
|
464
|
+
),
|
|
465
|
+
)
|
|
455
466
|
tables: list[MetaTableOperationScopeTable] = Field(..., min_length=1)
|
|
456
467
|
|
|
468
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
469
|
+
|
|
470
|
+
@model_validator(mode="after")
|
|
471
|
+
def resolve_default_data_source_uid(self):
|
|
472
|
+
if self.data_source_uid not in (None, ""):
|
|
473
|
+
self.data_source_uid = str(self.data_source_uid)
|
|
474
|
+
return self
|
|
475
|
+
|
|
476
|
+
try:
|
|
477
|
+
data_source = get_session_data_source()
|
|
478
|
+
except Exception as exc:
|
|
479
|
+
raise ValueError(
|
|
480
|
+
"MetaTable compiled SQL scope requires data_source_uid or a "
|
|
481
|
+
"configured project/session default data source."
|
|
482
|
+
) from exc
|
|
483
|
+
|
|
484
|
+
uid = getattr(data_source, "uid", None) or getattr(data_source, "data_source_uid", None)
|
|
485
|
+
if uid in (None, ""):
|
|
486
|
+
raise ValueError(
|
|
487
|
+
"Configured project/session default data source does not expose a uid."
|
|
488
|
+
)
|
|
489
|
+
self.data_source_uid = str(uid)
|
|
490
|
+
return self
|
|
491
|
+
|
|
457
492
|
|
|
458
493
|
class MetaTableOperationLimits(BasePydanticModel):
|
|
459
494
|
max_rows: int | None = Field(default=None, ge=1)
|
|
@@ -2928,15 +2963,6 @@ class TimeIndexMetaTable(MetaTable):
|
|
|
2928
2963
|
"labels__in": "str",
|
|
2929
2964
|
"labels__contains": "str",
|
|
2930
2965
|
}
|
|
2931
|
-
READ_QUERY_PARAMS: ClassVar[dict[str, str]] = {
|
|
2932
|
-
"include_relations_detail": "bool",
|
|
2933
|
-
}
|
|
2934
|
-
READ_QUERY_PARAM_DESCRIPTIONS: ClassVar[dict[str, str]] = {
|
|
2935
|
-
"include_relations_detail": (
|
|
2936
|
-
"Expand related objects in the serializer response. "
|
|
2937
|
-
"This changes response detail only and does not change which rows are returned."
|
|
2938
|
-
),
|
|
2939
|
-
}
|
|
2940
2966
|
build_configuration_json_schema: dict[str, Any] | None = Field(
|
|
2941
2967
|
None,
|
|
2942
2968
|
description="JSON schema describing the DataNode update build configuration.",
|
|
@@ -34,8 +34,8 @@ def build_operation(
|
|
|
34
34
|
Build and validate the TS Manager compiled-sql.v1 operation contract.
|
|
35
35
|
|
|
36
36
|
This is the client-side protocol object. It is intentionally plain SQL plus
|
|
37
|
-
bound parameters
|
|
38
|
-
object.
|
|
37
|
+
bound parameters, an explicit execution data source, and declared MetaTable
|
|
38
|
+
scope, not a serialized SQLAlchemy object.
|
|
39
39
|
"""
|
|
40
40
|
|
|
41
41
|
if parameters is None:
|
|
@@ -72,6 +72,7 @@ def compile_sqlalchemy_statement(
|
|
|
72
72
|
statement: Any,
|
|
73
73
|
*,
|
|
74
74
|
operation: MetaTableOperation,
|
|
75
|
+
data_source_uid: str | None = None,
|
|
75
76
|
scope_tables: Sequence[MetaTableOperationScopeTable | Mapping[str, Any]],
|
|
76
77
|
limits: MetaTableOperationLimits | Mapping[str, Any] | None = None,
|
|
77
78
|
dialect: MetaTableCompiledSQLDialect = "postgresql",
|
|
@@ -80,6 +81,11 @@ def compile_sqlalchemy_statement(
|
|
|
80
81
|
) -> MetaTableCompiledSQLOperation:
|
|
81
82
|
"""
|
|
82
83
|
Compile a SQLAlchemy/Core statement into the TS Manager compiled-sql.v1 payload.
|
|
84
|
+
|
|
85
|
+
``data_source_uid`` selects the DynamicTableDataSource connection. If it is
|
|
86
|
+
omitted, the SDK resolves the configured project/session default data source.
|
|
87
|
+
``scope_tables`` remains the declared MetaTable permission scope for the
|
|
88
|
+
operation.
|
|
83
89
|
"""
|
|
84
90
|
|
|
85
91
|
if dialect != "postgresql":
|
|
@@ -97,6 +103,7 @@ def compile_sqlalchemy_statement(
|
|
|
97
103
|
)
|
|
98
104
|
parameter_types = _compiled_sqlalchemy_parameter_types(compiled)
|
|
99
105
|
scope = MetaTableOperationScope(
|
|
106
|
+
data_source_uid=data_source_uid,
|
|
100
107
|
tables=[
|
|
101
108
|
(
|
|
102
109
|
table
|
|
@@ -211,7 +211,6 @@ class APIDataNode(DataAccessMixin):
|
|
|
211
211
|
def build_from_table_name(cls, table_name: str) -> "APIDataNode":
|
|
212
212
|
storage_table = TimeIndexMetaTable.get_or_none(
|
|
213
213
|
physical_table_name=table_name,
|
|
214
|
-
include_relations_detail=True,
|
|
215
214
|
)
|
|
216
215
|
if storage_table is None:
|
|
217
216
|
raise DoesNotExist(
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/persist_managers.py
RENAMED
|
@@ -567,7 +567,6 @@ class APIPersistManager:
|
|
|
567
567
|
result = TimeIndexMetaTable.get_or_none(
|
|
568
568
|
physical_table_name=self.storage_hash,
|
|
569
569
|
data_source__uid=self.data_source_uid,
|
|
570
|
-
include_relations_detail=True,
|
|
571
570
|
)
|
|
572
571
|
self._storage_table_future.set_result(result)
|
|
573
572
|
except Exception as exc:
|
|
@@ -101,6 +101,49 @@ class AlembicProviderPhysicalStateError(RuntimeError):
|
|
|
101
101
|
)
|
|
102
102
|
|
|
103
103
|
|
|
104
|
+
def _normalize_optional_alembic_location(value: Any, field_name: str) -> str | None:
|
|
105
|
+
if value is None:
|
|
106
|
+
return None
|
|
107
|
+
text = str(value).strip()
|
|
108
|
+
if not text:
|
|
109
|
+
raise ValueError(f"AlembicMetaTableMigration {field_name} cannot be empty.")
|
|
110
|
+
return text
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _normalize_alembic_version_locations(value: str | Sequence[str] | None) -> tuple[str, ...]:
|
|
114
|
+
if value is None:
|
|
115
|
+
return ()
|
|
116
|
+
raw_locations: Sequence[str]
|
|
117
|
+
if isinstance(value, str):
|
|
118
|
+
raw_locations = [value]
|
|
119
|
+
else:
|
|
120
|
+
raw_locations = value
|
|
121
|
+
|
|
122
|
+
locations: list[str] = []
|
|
123
|
+
seen: set[str] = set()
|
|
124
|
+
for raw_location in raw_locations:
|
|
125
|
+
location = _normalize_optional_alembic_location(raw_location, "version_locations")
|
|
126
|
+
if location is None or location in seen:
|
|
127
|
+
continue
|
|
128
|
+
seen.add(location)
|
|
129
|
+
locations.append(location)
|
|
130
|
+
if not locations:
|
|
131
|
+
raise ValueError("AlembicMetaTableMigration version_locations cannot be empty.")
|
|
132
|
+
return tuple(locations)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _configure_alembic_version_locations(
|
|
136
|
+
config: Any,
|
|
137
|
+
version_locations: str | Sequence[str] | None,
|
|
138
|
+
) -> tuple[str, ...]:
|
|
139
|
+
locations = _normalize_alembic_version_locations(version_locations)
|
|
140
|
+
if not locations:
|
|
141
|
+
return ()
|
|
142
|
+
config.set_main_option("version_locations", "\n".join(locations))
|
|
143
|
+
config.set_main_option("path_separator", "newline")
|
|
144
|
+
return locations
|
|
145
|
+
|
|
146
|
+
|
|
104
147
|
class AlembicVersionMetaTable:
|
|
105
148
|
"""MetaTable catalog binding for Alembic's version table.
|
|
106
149
|
|
|
@@ -255,6 +298,8 @@ class AlembicMetaTableMigration:
|
|
|
255
298
|
script_location: str
|
|
256
299
|
target_metadata: Any
|
|
257
300
|
alembic_registry: type[AlembicVersionMetaTable]
|
|
301
|
+
version_locations: str | Sequence[str] | None = None
|
|
302
|
+
version_path: str | None = None
|
|
258
303
|
metatable_models: Sequence[type[Any]] = field(default_factory=tuple)
|
|
259
304
|
after_register_metatables: Callable[[AlembicMetaTableCatalogRefreshContext], Any] | None = None
|
|
260
305
|
include_name_hook: Any | None = None
|
|
@@ -273,6 +318,8 @@ class AlembicMetaTableMigration:
|
|
|
273
318
|
self.after_register_metatables
|
|
274
319
|
):
|
|
275
320
|
raise TypeError("after_register_metatables must be callable when provided.")
|
|
321
|
+
self.resolved_version_locations()
|
|
322
|
+
self.resolved_version_path()
|
|
276
323
|
_normalize_provider_default_schemas(self.metatable_models)
|
|
277
324
|
_ensure_provider_time_index_grain_indexes(self.metatable_models)
|
|
278
325
|
|
|
@@ -292,6 +339,23 @@ class AlembicMetaTableMigration:
|
|
|
292
339
|
def version_table_schema(self) -> str | None:
|
|
293
340
|
return self.alembic_registry.__alembic_version_schema__
|
|
294
341
|
|
|
342
|
+
def resolved_version_locations(self) -> tuple[str, ...]:
|
|
343
|
+
return _normalize_alembic_version_locations(self.version_locations)
|
|
344
|
+
|
|
345
|
+
def resolved_version_path(self) -> str | None:
|
|
346
|
+
explicit = _normalize_optional_alembic_location(self.version_path, "version_path")
|
|
347
|
+
if explicit is not None:
|
|
348
|
+
return explicit
|
|
349
|
+
locations = self.resolved_version_locations()
|
|
350
|
+
if len(locations) == 1:
|
|
351
|
+
return locations[0]
|
|
352
|
+
if len(locations) > 1:
|
|
353
|
+
raise ValueError(
|
|
354
|
+
"AlembicMetaTableMigration with multiple version_locations requires "
|
|
355
|
+
"an explicit version_path for revision generation."
|
|
356
|
+
)
|
|
357
|
+
return None
|
|
358
|
+
|
|
295
359
|
def include_name(self, name: str | None, type_: str, parent_names: dict[str, Any]) -> bool:
|
|
296
360
|
if self.include_name_hook is not None:
|
|
297
361
|
return bool(self.include_name_hook(name, type_, parent_names))
|
|
@@ -750,6 +814,7 @@ def alembic_config_for_provider(
|
|
|
750
814
|
if output_buffer is not None:
|
|
751
815
|
config.output_buffer = output_buffer
|
|
752
816
|
config.set_main_option("script_location", migration.script_location)
|
|
817
|
+
version_locations = _configure_alembic_version_locations(config, migration.version_locations)
|
|
753
818
|
config.set_main_option("sqlalchemy.url", sqlalchemy_url.replace("%", "%%"))
|
|
754
819
|
config.set_main_option("sqlalchemy.echo", "true")
|
|
755
820
|
config.set_main_option("version_table", migration.version_table)
|
|
@@ -764,6 +829,8 @@ def alembic_config_for_provider(
|
|
|
764
829
|
config.attributes["alembic_version_table"] = migration.alembic_version_table
|
|
765
830
|
config.attributes["version_table"] = migration.version_table
|
|
766
831
|
config.attributes["version_table_schema"] = migration.version_table_schema
|
|
832
|
+
config.attributes["version_locations"] = version_locations
|
|
833
|
+
config.attributes["version_path"] = migration.resolved_version_path()
|
|
767
834
|
return config
|
|
768
835
|
|
|
769
836
|
|
|
@@ -805,6 +872,7 @@ def resolve_alembic_revision_metadata(
|
|
|
805
872
|
*,
|
|
806
873
|
script_location: str,
|
|
807
874
|
revision: str,
|
|
875
|
+
version_locations: str | Sequence[str] | None = None,
|
|
808
876
|
) -> tuple[str, str | None]:
|
|
809
877
|
try:
|
|
810
878
|
from alembic.config import Config
|
|
@@ -814,6 +882,7 @@ def resolve_alembic_revision_metadata(
|
|
|
814
882
|
|
|
815
883
|
config = Config()
|
|
816
884
|
config.set_main_option("script_location", script_location)
|
|
885
|
+
_configure_alembic_version_locations(config, version_locations)
|
|
817
886
|
script = ScriptDirectory.from_config(config)
|
|
818
887
|
resolved = script.get_revision(revision)
|
|
819
888
|
if resolved is None:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mainsequence"
|
|
7
|
-
version = "4.3.
|
|
7
|
+
version = "4.3.5"
|
|
8
8
|
description = "Main Sequence SDK "
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -60,6 +60,7 @@ Homepage = "https://github.com/mainsequence-sdk/mainsequence-sdk"
|
|
|
60
60
|
Issues = "https://github.com/mainsequence-sdk/mainsequence-sdk/issues"
|
|
61
61
|
[project.scripts]
|
|
62
62
|
mainsequence = "mainsequence.cli.cli:app"
|
|
63
|
+
ms = "mainsequence.cli.cli:app"
|
|
63
64
|
|
|
64
65
|
# PEP 621 dependencies (published requirements)
|
|
65
66
|
[dependency-groups]
|
|
@@ -5,6 +5,7 @@ import logging
|
|
|
5
5
|
import pathlib
|
|
6
6
|
import sys
|
|
7
7
|
import types
|
|
8
|
+
from collections.abc import Sequence
|
|
8
9
|
|
|
9
10
|
import pytest
|
|
10
11
|
from sqlalchemy import MetaData
|
|
@@ -37,7 +38,11 @@ def _load_cli_module():
|
|
|
37
38
|
return importlib.import_module("mainsequence.cli.cli")
|
|
38
39
|
|
|
39
40
|
|
|
40
|
-
def _migration(
|
|
41
|
+
def _migration(
|
|
42
|
+
*,
|
|
43
|
+
version_locations: str | Sequence[str] | None = None,
|
|
44
|
+
version_path: str | None = None,
|
|
45
|
+
) -> AlembicMetaTableMigration:
|
|
41
46
|
class Registry(AlembicVersionMetaTable):
|
|
42
47
|
__metatable_uid__ = "registry-meta-table-uid"
|
|
43
48
|
__metatable_data_source_uid__ = "data-source-uid"
|
|
@@ -54,6 +59,8 @@ def _migration() -> AlembicMetaTableMigration:
|
|
|
54
59
|
script_location="msm:alembic",
|
|
55
60
|
target_metadata=MetaData(),
|
|
56
61
|
alembic_registry=Registry,
|
|
62
|
+
version_locations=version_locations,
|
|
63
|
+
version_path=version_path,
|
|
57
64
|
)
|
|
58
65
|
|
|
59
66
|
|
|
@@ -360,6 +367,100 @@ def test_migrations_revision_forwards_alembic_logs_and_scans_revision_id(monkeyp
|
|
|
360
367
|
assert "Preparing platform-managed MetaTable reservations" not in output
|
|
361
368
|
|
|
362
369
|
|
|
370
|
+
def test_coerce_alembic_version_path_resolves_package_resource(tmp_path, monkeypatch):
|
|
371
|
+
migration_cli = importlib.import_module("mainsequence.cli.migrations")
|
|
372
|
+
package_root = tmp_path / "sample_provider"
|
|
373
|
+
namespace_versions = package_root / "migrations" / "versions" / "mainsequence_examples"
|
|
374
|
+
namespace_versions.mkdir(parents=True)
|
|
375
|
+
(package_root / "__init__.py").write_text("", encoding="utf-8")
|
|
376
|
+
(package_root / "migrations" / "__init__.py").write_text("", encoding="utf-8")
|
|
377
|
+
monkeypatch.syspath_prepend(str(tmp_path))
|
|
378
|
+
|
|
379
|
+
assert migration_cli._coerce_alembic_version_path(
|
|
380
|
+
"sample_provider:migrations/versions/mainsequence_examples"
|
|
381
|
+
) == str(namespace_versions)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def test_migrations_revision_passes_provider_version_path(monkeypatch):
|
|
385
|
+
cli_mod = _load_cli_module()
|
|
386
|
+
runner = CliRunner()
|
|
387
|
+
migration_cli = importlib.import_module("mainsequence.cli.migrations")
|
|
388
|
+
version_location = "msm:alembic/versions/mainsequence_examples"
|
|
389
|
+
resolved_version_path = "/tmp/msm/alembic/versions/mainsequence_examples"
|
|
390
|
+
migration = _migration(version_locations=[version_location], version_path=version_location)
|
|
391
|
+
captured = {}
|
|
392
|
+
monkeypatch.setattr(migration_cli, "_load_migration", lambda provider: migration)
|
|
393
|
+
|
|
394
|
+
def fake_coerce_version_path(path):
|
|
395
|
+
captured["unresolved_version_path"] = path
|
|
396
|
+
return resolved_version_path
|
|
397
|
+
|
|
398
|
+
monkeypatch.setattr(migration_cli, "_coerce_alembic_version_path", fake_coerce_version_path)
|
|
399
|
+
|
|
400
|
+
def fail_backend_call(*args, **kwargs):
|
|
401
|
+
raise AssertionError("revision must not provision MetaTables")
|
|
402
|
+
|
|
403
|
+
monkeypatch.setattr(
|
|
404
|
+
AlembicMetaTableMigration,
|
|
405
|
+
"ensure_alembic_registry",
|
|
406
|
+
fail_backend_call,
|
|
407
|
+
)
|
|
408
|
+
monkeypatch.setattr(
|
|
409
|
+
AlembicMetaTableMigration,
|
|
410
|
+
"prepare_for_alembic",
|
|
411
|
+
fail_backend_call,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
from alembic import command
|
|
415
|
+
from alembic.script import ScriptDirectory
|
|
416
|
+
|
|
417
|
+
class FakeScriptDirectory:
|
|
418
|
+
def get_heads(self):
|
|
419
|
+
return ["0004"]
|
|
420
|
+
|
|
421
|
+
def fake_script_directory_from_config(config):
|
|
422
|
+
captured["script_context_version_locations"] = config.get_main_option(
|
|
423
|
+
"version_locations"
|
|
424
|
+
)
|
|
425
|
+
captured["script_context_path_separator"] = config.get_main_option("path_separator")
|
|
426
|
+
return FakeScriptDirectory()
|
|
427
|
+
|
|
428
|
+
monkeypatch.setattr(
|
|
429
|
+
ScriptDirectory,
|
|
430
|
+
"from_config",
|
|
431
|
+
staticmethod(fake_script_directory_from_config),
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
def fake_revision(config, **kwargs):
|
|
435
|
+
captured["revision_version_locations"] = config.get_main_option("version_locations")
|
|
436
|
+
captured["revision_path_separator"] = config.get_main_option("path_separator")
|
|
437
|
+
captured["version_path"] = kwargs["version_path"]
|
|
438
|
+
return types.SimpleNamespace(revision=kwargs["rev_id"], path="/tmp/0005_migration.py")
|
|
439
|
+
|
|
440
|
+
monkeypatch.setattr(command, "revision", fake_revision)
|
|
441
|
+
|
|
442
|
+
result = runner.invoke(
|
|
443
|
+
cli_mod.app,
|
|
444
|
+
[
|
|
445
|
+
"migrations",
|
|
446
|
+
"revision",
|
|
447
|
+
"--provider",
|
|
448
|
+
"ignored:migration",
|
|
449
|
+
"--rev-id",
|
|
450
|
+
"0005",
|
|
451
|
+
"--no-autogenerate",
|
|
452
|
+
],
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
assert result.exit_code == 0
|
|
456
|
+
assert captured["script_context_version_locations"] == version_location
|
|
457
|
+
assert captured["script_context_path_separator"] == "newline"
|
|
458
|
+
assert captured["revision_version_locations"] == version_location
|
|
459
|
+
assert captured["revision_path_separator"] == "newline"
|
|
460
|
+
assert captured["unresolved_version_path"] == version_location
|
|
461
|
+
assert captured["version_path"] == resolved_version_path
|
|
462
|
+
|
|
463
|
+
|
|
363
464
|
def test_migrations_revision_default_autogenerates_without_metatable_provisioning(monkeypatch):
|
|
364
465
|
cli_mod = _load_cli_module()
|
|
365
466
|
runner = CliRunner()
|
|
@@ -142,7 +142,6 @@ def test_api_data_node_build_from_table_name_uses_physical_table_name(monkeypatc
|
|
|
142
142
|
|
|
143
143
|
assert captured == {
|
|
144
144
|
"physical_table_name": "prices_table",
|
|
145
|
-
"include_relations_detail": True,
|
|
146
145
|
}
|
|
147
146
|
assert node.storage_hash == "prices_table"
|
|
148
147
|
assert node.data_source_uid == "data-source-uid"
|
|
@@ -182,7 +181,6 @@ def test_api_persist_manager_resolves_storage_by_table_name_and_data_source(monk
|
|
|
182
181
|
assert captured == {
|
|
183
182
|
"physical_table_name": "prices_table",
|
|
184
183
|
"data_source__uid": "data-source-uid",
|
|
185
|
-
"include_relations_detail": True,
|
|
186
184
|
}
|
|
187
185
|
|
|
188
186
|
|
|
@@ -287,6 +287,22 @@ def test_data_node_storage_rejects_data_source_id_filter():
|
|
|
287
287
|
TimeIndexMetaTable._normalize_filter_kwargs({"data_source__id": {"id": 7}})
|
|
288
288
|
|
|
289
289
|
|
|
290
|
+
def test_include_relations_detail_is_only_data_node_update_read_param():
|
|
291
|
+
from mainsequence.client.metatables import DataNodeUpdate, TimeIndexMetaTable
|
|
292
|
+
|
|
293
|
+
assert "include_relations_detail" in DataNodeUpdate.READ_QUERY_PARAMS
|
|
294
|
+
assert "include_relations_detail" not in (TimeIndexMetaTable.READ_QUERY_PARAMS or {})
|
|
295
|
+
|
|
296
|
+
filter_kwargs, read_query_kwargs = TimeIndexMetaTable._split_filter_and_read_query_kwargs(
|
|
297
|
+
{"include_relations_detail": True}
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
assert read_query_kwargs == {}
|
|
301
|
+
assert filter_kwargs == {"include_relations_detail": True}
|
|
302
|
+
with pytest.raises(ValueError, match="Unsupported TimeIndexMetaTable filter"):
|
|
303
|
+
TimeIndexMetaTable._normalize_filter_kwargs(filter_kwargs)
|
|
304
|
+
|
|
305
|
+
|
|
290
306
|
def test_data_node_storage_delete_after_date_posts_tail_delete(monkeypatch):
|
|
291
307
|
from mainsequence.client import metatables as models_metatables
|
|
292
308
|
|
|
@@ -34,6 +34,7 @@ from mainsequence.meta_tables.migrations import (
|
|
|
34
34
|
alembic_config_for_provider,
|
|
35
35
|
apply_mainsequence_migration_role,
|
|
36
36
|
load_alembic_metatable_migration_provider,
|
|
37
|
+
resolve_alembic_revision_metadata,
|
|
37
38
|
)
|
|
38
39
|
|
|
39
40
|
|
|
@@ -659,6 +660,57 @@ def test_alembic_config_for_provider_uses_scoped_url_and_owner_role():
|
|
|
659
660
|
)
|
|
660
661
|
|
|
661
662
|
|
|
663
|
+
def test_alembic_config_for_provider_supports_namespace_version_locations(tmp_path):
|
|
664
|
+
script_root = tmp_path / "migrations"
|
|
665
|
+
namespace_versions = script_root / "versions" / "mainsequence_examples"
|
|
666
|
+
namespace_versions.mkdir(parents=True)
|
|
667
|
+
(script_root / "env.py").write_text("", encoding="utf-8")
|
|
668
|
+
(namespace_versions / "0003_migration.py").write_text(
|
|
669
|
+
"revision = '0003'\n"
|
|
670
|
+
"down_revision = None\n"
|
|
671
|
+
"branch_labels = None\n"
|
|
672
|
+
"depends_on = None\n",
|
|
673
|
+
encoding="utf-8",
|
|
674
|
+
)
|
|
675
|
+
(namespace_versions / "0004_migration.py").write_text(
|
|
676
|
+
"revision = '0004'\n"
|
|
677
|
+
"down_revision = '0003'\n"
|
|
678
|
+
"branch_labels = None\n"
|
|
679
|
+
"depends_on = None\n",
|
|
680
|
+
encoding="utf-8",
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
class ProjectAlembicVersion(AlembicVersionMetaTable):
|
|
684
|
+
__metatable_data_source_uid__ = "data-source-uid"
|
|
685
|
+
|
|
686
|
+
migration = AlembicMetaTableMigration(
|
|
687
|
+
package="sample",
|
|
688
|
+
migration_namespace="markets",
|
|
689
|
+
script_location=str(script_root),
|
|
690
|
+
version_locations=[str(namespace_versions)],
|
|
691
|
+
version_path=str(namespace_versions),
|
|
692
|
+
target_metadata=MetaData(),
|
|
693
|
+
alembic_registry=ProjectAlembicVersion,
|
|
694
|
+
)
|
|
695
|
+
|
|
696
|
+
config = alembic_config_for_provider(migration, sqlalchemy_url="postgresql://example")
|
|
697
|
+
|
|
698
|
+
assert config.get_main_option("version_locations") == str(namespace_versions)
|
|
699
|
+
assert config.get_main_option("path_separator") == "newline"
|
|
700
|
+
assert config.attributes["version_locations"] == (str(namespace_versions),)
|
|
701
|
+
assert config.attributes["version_path"] == str(namespace_versions)
|
|
702
|
+
|
|
703
|
+
from alembic.script import ScriptDirectory
|
|
704
|
+
|
|
705
|
+
script = ScriptDirectory.from_config(config)
|
|
706
|
+
assert script.get_revision("0004").revision == "0004"
|
|
707
|
+
assert resolve_alembic_revision_metadata(
|
|
708
|
+
script_location=str(script_root),
|
|
709
|
+
version_locations=[str(namespace_versions)],
|
|
710
|
+
revision="0004",
|
|
711
|
+
) == ("0004", "0003")
|
|
712
|
+
|
|
713
|
+
|
|
662
714
|
def test_apply_mainsequence_migration_role_executes_quoted_set_role():
|
|
663
715
|
class FakeConnection:
|
|
664
716
|
def __init__(self):
|
|
@@ -350,6 +350,7 @@ def test_meta_table_execute_operation_serializes_scope_uid(monkeypatch):
|
|
|
350
350
|
parameters={"symbol_1": "%BTC%"},
|
|
351
351
|
),
|
|
352
352
|
scope=meta_table_models.MetaTableOperationScope(
|
|
353
|
+
data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
353
354
|
tables=[
|
|
354
355
|
meta_table_models.MetaTableOperationScopeTable(
|
|
355
356
|
metaTableUid="aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
@@ -367,6 +368,10 @@ def test_meta_table_execute_operation_serializes_scope_uid(monkeypatch):
|
|
|
367
368
|
assert result["ok"] is True
|
|
368
369
|
assert captured["r_type"] == "POST"
|
|
369
370
|
assert captured["url"].endswith("/ts_manager/meta_table/execute-operation/")
|
|
371
|
+
assert (
|
|
372
|
+
captured["payload"]["json"]["scope"]["data_source_uid"]
|
|
373
|
+
== "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
|
|
374
|
+
)
|
|
370
375
|
assert captured["payload"]["json"]["scope"]["tables"][0] == {
|
|
371
376
|
"meta_table_uid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
372
377
|
"alias": "asset",
|
|
@@ -697,6 +702,7 @@ def test_compiled_sql_v1_protocol_is_validated_by_pydantic():
|
|
|
697
702
|
sql="SELECT asset.symbol FROM public.asset AS asset",
|
|
698
703
|
parameters={"symbol_1": "%BTC%"},
|
|
699
704
|
scope={
|
|
705
|
+
"dataSourceUid": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
700
706
|
"tables": [
|
|
701
707
|
{
|
|
702
708
|
"metaTableUid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
@@ -712,6 +718,7 @@ def test_compiled_sql_v1_protocol_is_validated_by_pydantic():
|
|
|
712
718
|
assert operation.version == "compiled-sql.v1"
|
|
713
719
|
assert operation.dialect == "postgresql"
|
|
714
720
|
assert operation.statement.paramstyle == "pyformat"
|
|
721
|
+
assert operation.scope.data_source_uid == "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
|
|
715
722
|
assert operation.scope.tables[0].meta_table_uid == "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"
|
|
716
723
|
assert operation.scope.tables[0].reserved_policy == "reconcile"
|
|
717
724
|
assert (
|
|
@@ -725,6 +732,7 @@ def test_compiled_sql_v1_protocol_is_validated_by_pydantic():
|
|
|
725
732
|
version="compiled-sql.v2",
|
|
726
733
|
statement=meta_table_models.MetaTableStatementPayload(sql="SELECT 1"),
|
|
727
734
|
scope=meta_table_models.MetaTableOperationScope(
|
|
735
|
+
data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
728
736
|
tables=[
|
|
729
737
|
meta_table_models.MetaTableOperationScopeTable(
|
|
730
738
|
metaTableUid="aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
@@ -737,10 +745,40 @@ def test_compiled_sql_v1_protocol_is_validated_by_pydantic():
|
|
|
737
745
|
meta_table_models.MetaTableCompiledSQLOperation(
|
|
738
746
|
operation="select",
|
|
739
747
|
statement=meta_table_models.MetaTableStatementPayload(sql="SELECT 1"),
|
|
740
|
-
scope=meta_table_models.MetaTableOperationScope(
|
|
748
|
+
scope=meta_table_models.MetaTableOperationScope(
|
|
749
|
+
data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
750
|
+
tables=[],
|
|
751
|
+
),
|
|
741
752
|
)
|
|
742
753
|
|
|
743
754
|
|
|
755
|
+
def test_compiled_sql_v1_scope_resolves_session_data_source(monkeypatch):
|
|
756
|
+
monkeypatch.setattr(
|
|
757
|
+
meta_table_models,
|
|
758
|
+
"get_session_data_source",
|
|
759
|
+
lambda: SimpleNamespace(uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd"),
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
operation = build_operation(
|
|
763
|
+
operation="select",
|
|
764
|
+
sql="SELECT asset.symbol FROM public.asset AS asset",
|
|
765
|
+
scope={
|
|
766
|
+
"tables": [
|
|
767
|
+
{
|
|
768
|
+
"metaTableUid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
769
|
+
"alias": "asset",
|
|
770
|
+
}
|
|
771
|
+
]
|
|
772
|
+
},
|
|
773
|
+
)
|
|
774
|
+
|
|
775
|
+
assert operation.scope.data_source_uid == "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
|
|
776
|
+
assert (
|
|
777
|
+
operation.model_dump(mode="json", by_alias=True)["scope"]["data_source_uid"]
|
|
778
|
+
== "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
|
|
779
|
+
)
|
|
780
|
+
|
|
781
|
+
|
|
744
782
|
def test_compiled_sql_v1_serializes_typed_temporal_parameters():
|
|
745
783
|
operation = build_operation(
|
|
746
784
|
operation="select",
|
|
@@ -754,6 +792,7 @@ def test_compiled_sql_v1_serializes_typed_temporal_parameters():
|
|
|
754
792
|
"seen_at": "datetime64[ns, UTC]",
|
|
755
793
|
},
|
|
756
794
|
scope={
|
|
795
|
+
"data_source_uid": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
757
796
|
"tables": [
|
|
758
797
|
{
|
|
759
798
|
"metaTableUid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
@@ -780,6 +819,7 @@ def test_compiled_sql_v1_rejects_untyped_temporal_parameters():
|
|
|
780
819
|
sql="SELECT * FROM asset WHERE as_of = %(as_of)s",
|
|
781
820
|
parameters={"as_of": datetime.date(2026, 5, 28)},
|
|
782
821
|
scope={
|
|
822
|
+
"data_source_uid": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
783
823
|
"tables": [
|
|
784
824
|
{
|
|
785
825
|
"metaTableUid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
@@ -804,6 +844,7 @@ def test_compile_sqlalchemy_statement_emits_temporal_parameter_types():
|
|
|
804
844
|
operation = compile_sqlalchemy_statement(
|
|
805
845
|
statement,
|
|
806
846
|
operation="select",
|
|
847
|
+
data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
807
848
|
scope_tables=[
|
|
808
849
|
{
|
|
809
850
|
"metaTableUid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
@@ -815,6 +856,7 @@ def test_compile_sqlalchemy_statement_emits_temporal_parameter_types():
|
|
|
815
856
|
assert operation.statement.parameter_types == {
|
|
816
857
|
"seen_at": "timestamp with time zone",
|
|
817
858
|
}
|
|
859
|
+
assert operation.scope.data_source_uid == "dddddddd-dddd-4ddd-8ddd-dddddddddddd"
|
|
818
860
|
assert operation.statement.parameters["seen_at"] == "2026-05-28T12:30:00Z"
|
|
819
861
|
|
|
820
862
|
|
|
@@ -833,6 +875,7 @@ def test_compile_sqlalchemy_statement_rejects_naive_datetime_bind_types():
|
|
|
833
875
|
compile_sqlalchemy_statement(
|
|
834
876
|
statement,
|
|
835
877
|
operation="select",
|
|
878
|
+
data_source_uid="dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
836
879
|
scope_tables=[
|
|
837
880
|
{
|
|
838
881
|
"metaTableUid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
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.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/command_center/connections/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/dashboards/streamlit/SKILL.md
RENAMED
|
File without changes
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/data_access/exploration/SKILL.md
RENAMED
|
File without changes
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md
RENAMED
|
File without changes
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md
RENAMED
|
File without changes
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/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
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/app_component.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/command_center/workspace_snapshot.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/data_sources_interfaces/__init__.py
RENAMED
|
File without changes
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/data_sources_interfaces/duckdb.py
RENAMED
|
File without changes
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/client/data_sources_interfaces/local_paths.py
RENAMED
|
File without changes
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/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
|
|
File without changes
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/build_operations.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/mainsequence/meta_tables/data_nodes/namespacing.py
RENAMED
|
File without changes
|
{mainsequence-4.3.0 → mainsequence-4.3.5}/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
|
|
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
|