mainsequence 3.16.0__tar.gz → 3.17.0__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.
Files changed (152) hide show
  1. {mainsequence-3.16.0 → mainsequence-3.17.0}/PKG-INFO +1 -1
  2. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/cli/api.py +83 -0
  3. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/cli/cli.py +170 -0
  4. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/client/__init__.py +1 -1
  5. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/client/base.py +54 -8
  6. mainsequence-3.17.0/mainsequence/client/models_simple_tables.py +1107 -0
  7. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/client/models_tdag.py +144 -126
  8. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/client/models_user.py +0 -1
  9. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/client/utils.py +38 -16
  10. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/logconf.py +11 -7
  11. mainsequence-3.17.0/mainsequence/tdag/__init__.py +38 -0
  12. mainsequence-3.17.0/mainsequence/tdag/base_persist_managers.py +545 -0
  13. mainsequence-3.17.0/mainsequence/tdag/configuration_models.py +23 -0
  14. mainsequence-3.17.0/mainsequence/tdag/data_nodes/__init__.py +31 -0
  15. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/tdag/data_nodes/build_operations.py +23 -105
  16. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/tdag/data_nodes/data_nodes.py +143 -161
  17. mainsequence-3.17.0/mainsequence/tdag/data_nodes/filters.py +72 -0
  18. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/tdag/data_nodes/models.py +3 -11
  19. mainsequence-3.17.0/mainsequence/tdag/data_nodes/persist_managers.py +114 -0
  20. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/tdag/data_nodes/run_operations.py +74 -79
  21. mainsequence-3.17.0/mainsequence/tdag/filters.py +201 -0
  22. mainsequence-3.17.0/mainsequence/tdag/pydantic_metadata.py +122 -0
  23. mainsequence-3.17.0/mainsequence/tdag/simple_tables/__init__.py +15 -0
  24. mainsequence-3.17.0/mainsequence/tdag/simple_tables/filters.py +329 -0
  25. mainsequence-3.17.0/mainsequence/tdag/simple_tables/models.py +115 -0
  26. mainsequence-3.17.0/mainsequence/tdag/simple_tables/persist_managers.py +184 -0
  27. mainsequence-3.17.0/mainsequence/tdag/simple_tables/schema.py +259 -0
  28. mainsequence-3.17.0/mainsequence/tdag/simple_tables/table_nodes.py +479 -0
  29. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence.egg-info/PKG-INFO +1 -1
  30. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence.egg-info/SOURCES.txt +17 -2
  31. {mainsequence-3.16.0 → mainsequence-3.17.0}/pyproject.toml +1 -1
  32. mainsequence-3.17.0/tests/test_auth_precedence.py +106 -0
  33. {mainsequence-3.16.0 → mainsequence-3.17.0}/tests/test_cli.py +131 -0
  34. {mainsequence-3.16.0 → mainsequence-3.17.0}/tests/test_filter_normalization.py +92 -1
  35. mainsequence-3.17.0/tests/test_run_configuration.py +34 -0
  36. mainsequence-3.17.0/tests/test_simple_tables_configuration_hashing.py +145 -0
  37. mainsequence-3.17.0/tests/test_simple_tables_persistence.py +1327 -0
  38. mainsequence-3.16.0/mainsequence/client/data_filters.py +0 -298
  39. mainsequence-3.16.0/mainsequence/tdag/__init__.py +0 -12
  40. mainsequence-3.16.0/mainsequence/tdag/data_nodes/__init__.py +0 -8
  41. mainsequence-3.16.0/mainsequence/tdag/data_nodes/persist_managers.py +0 -775
  42. {mainsequence-3.16.0 → mainsequence-3.17.0}/LICENSE +0 -0
  43. {mainsequence-3.16.0 → mainsequence-3.17.0}/README.md +0 -0
  44. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/__init__.py +0 -0
  45. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/__main__.py +0 -0
  46. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/bootstrap.py +0 -0
  47. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/cli/__init__.py +0 -0
  48. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/cli/config.py +0 -0
  49. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/cli/docker_utils.py +0 -0
  50. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/cli/doctor.py +0 -0
  51. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/cli/local_ops.py +0 -0
  52. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/cli/model_filters.py +0 -0
  53. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/cli/project_status.py +0 -0
  54. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/cli/pydantic_cli.py +0 -0
  55. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/cli/sdk_utils.py +0 -0
  56. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/cli/ssh_utils.py +0 -0
  57. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/cli/ui.py +0 -0
  58. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/client/agent.py +0 -0
  59. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/client/client.py +0 -0
  60. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  61. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
  62. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/client/data_sources_interfaces/timescale.py +0 -0
  63. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/client/exceptions.py +0 -0
  64. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/client/models_helpers.py +0 -0
  65. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/client/models_vam.py +0 -0
  66. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/compute_validation.py +0 -0
  67. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/custom_api/__init__.py +0 -0
  68. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/custom_api/base.py +0 -0
  69. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/custom_api/cli.py +0 -0
  70. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/__init__.py +0 -0
  71. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/__init__.py +0 -0
  72. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/assets/config.toml +0 -0
  73. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/assets/favicon.png +0 -0
  74. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/assets/image_1_base64.txt +0 -0
  75. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/assets/image_2_base64.txt +0 -0
  76. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/assets/image_3_base64.txt +0 -0
  77. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/assets/image_4_base64.txt +0 -0
  78. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/assets/image_5_base64.txt +0 -0
  79. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/assets/logo.png +0 -0
  80. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/components/__init__.py +0 -0
  81. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/components/asset_select.py +0 -0
  82. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/components/date_settings.py +0 -0
  83. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/components/logged_user.py +0 -0
  84. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/core/__init__.py +0 -0
  85. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/core/theme.py +0 -0
  86. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/instruments/__init__.py +0 -0
  87. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/instruments/streamlit_form_factory.py +0 -0
  88. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/pages/__init__.py +0 -0
  89. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/dashboards/streamlit/scaffold.py +0 -0
  90. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instrumentation/__init__.py +0 -0
  91. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instrumentation/utils.py +0 -0
  92. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/__init__.py +0 -0
  93. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/data_interface/__init__.py +0 -0
  94. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/data_interface/data_interface.py +0 -0
  95. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/instruments/__init__.py +0 -0
  96. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/instruments/base_instrument.py +0 -0
  97. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/instruments/bond.py +0 -0
  98. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/instruments/callability.py +0 -0
  99. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/instruments/interest_rate_swap.py +0 -0
  100. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/instruments/json_codec.py +0 -0
  101. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/instruments/position.py +0 -0
  102. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/instruments/ql_fields.py +0 -0
  103. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/interest_rates/__init__.py +0 -0
  104. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/interest_rates/etl/__init__.py +0 -0
  105. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/interest_rates/etl/curve_codec.py +0 -0
  106. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/interest_rates/etl/nodes.py +0 -0
  107. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/interest_rates/etl/registry.py +0 -0
  108. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/pricing_models/__init__.py +0 -0
  109. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/pricing_models/bond_pricer.py +0 -0
  110. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/pricing_models/indices.py +0 -0
  111. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/pricing_models/indices_builders.py +0 -0
  112. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/pricing_models/swap_pricer.py +0 -0
  113. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/settings.py +0 -0
  114. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/instruments/utils.py +0 -0
  115. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/runtime_flags.py +0 -0
  116. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/tdag/__main__.py +0 -0
  117. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/tdag/config.py +0 -0
  118. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/tdag/data_nodes/namespacing.py +0 -0
  119. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/tdag/data_nodes/utils.py +0 -0
  120. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/tdag/future_registry.py +0 -0
  121. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/tdag/utils.py +0 -0
  122. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/__init__.py +0 -0
  123. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/contrib/__init__.py +0 -0
  124. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/contrib/data_nodes/__init__.py +0 -0
  125. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/contrib/data_nodes/external_weights.py +0 -0
  126. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/contrib/data_nodes/intraday_trend.py +0 -0
  127. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/contrib/data_nodes/market_cap.py +0 -0
  128. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/contrib/data_nodes/mock_signal.py +0 -0
  129. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/contrib/data_nodes/portfolio_replicator.py +0 -0
  130. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/contrib/prices/__init__.py +0 -0
  131. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/contrib/prices/data_nodes.py +0 -0
  132. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/contrib/prices/utils.py +0 -0
  133. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/contrib/rebalance_strategies/__init__.py +0 -0
  134. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/contrib/rebalance_strategies/rebalance_strategies.py +0 -0
  135. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/enums.py +0 -0
  136. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/models.py +0 -0
  137. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/portfolio_nodes.py +0 -0
  138. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/resource_factory/__init__.py +0 -0
  139. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/resource_factory/rebalance_factory.py +0 -0
  140. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/resource_factory/signal_factory.py +0 -0
  141. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence/virtualfundbuilder/utils.py +0 -0
  142. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence.egg-info/dependency_links.txt +0 -0
  143. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence.egg-info/entry_points.txt +0 -0
  144. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence.egg-info/requires.txt +0 -0
  145. {mainsequence-3.16.0 → mainsequence-3.17.0}/mainsequence.egg-info/top_level.txt +0 -0
  146. {mainsequence-3.16.0 → mainsequence-3.17.0}/setup.cfg +0 -0
  147. {mainsequence-3.16.0 → mainsequence-3.17.0}/tests/test_build_operations_hashing.py +0 -0
  148. {mainsequence-3.16.0 → mainsequence-3.17.0}/tests/test_client.py +0 -0
  149. {mainsequence-3.16.0 → mainsequence-3.17.0}/tests/test_instruments.py +0 -0
  150. {mainsequence-3.16.0 → mainsequence-3.17.0}/tests/test_logconf.py +0 -0
  151. {mainsequence-3.16.0 → mainsequence-3.17.0}/tests/test_logged_user_components.py +0 -0
  152. {mainsequence-3.16.0 → mainsequence-3.17.0}/tests/test_project_batch_jobs_from_file.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 3.16.0
3
+ Version: 3.17.0
4
4
  Summary: Main Sequence SDK
5
5
  Author-email: Main Sequence GmbH <dev@main-sequence.io>
6
6
  License: MainSequence GmbH SDK License Agreement
@@ -1958,6 +1958,89 @@ def list_data_node_storages(
1958
1958
  raise ApiError(f"Data node storages fetch failed: {e}")
1959
1959
 
1960
1960
 
1961
+ def list_simple_table_storages(
1962
+ *,
1963
+ filters: dict[str, Any] | None = None,
1964
+ timeout: int | None = None,
1965
+ ) -> list[dict[str, Any]]:
1966
+ """
1967
+ List simple table storages via SDK client model.
1968
+
1969
+ Single source of truth:
1970
+ - delegates filtering and payload parsing to `SimpleTableStorage.filter()`
1971
+ """
1972
+ try:
1973
+ storages = _run_sdk_model_operation(
1974
+ module_name="mainsequence.client.models_simple_tables",
1975
+ class_name="SimpleTableStorage",
1976
+ operation=lambda ClientSimpleTableStorage: ClientSimpleTableStorage.filter(
1977
+ timeout=timeout,
1978
+ **dict(filters or {}),
1979
+ ),
1980
+ )
1981
+ return [_sdk_object_to_dict(storage) for storage in storages]
1982
+ except Exception as e:
1983
+ if isinstance(e, (ApiError, NotLoggedIn)):
1984
+ raise
1985
+ raise ApiError(f"Simple table storages fetch failed: {e}")
1986
+
1987
+
1988
+ def get_simple_table_storage(
1989
+ storage_id: int | str,
1990
+ *,
1991
+ timeout: int | None = None,
1992
+ ) -> dict[str, Any]:
1993
+ """
1994
+ Retrieve one simple table storage via SDK client model.
1995
+ """
1996
+ try:
1997
+ storage = _run_sdk_model_operation(
1998
+ module_name="mainsequence.client.models_simple_tables",
1999
+ class_name="SimpleTableStorage",
2000
+ operation=lambda ClientSimpleTableStorage: ClientSimpleTableStorage.get(
2001
+ pk=int(storage_id),
2002
+ timeout=timeout,
2003
+ ),
2004
+ )
2005
+ return _sdk_object_to_dict(storage)
2006
+ except Exception as e:
2007
+ err_name = type(e).__name__
2008
+ if err_name == "NotFoundError":
2009
+ raise ApiError(f"Simple table storage not found: {storage_id}")
2010
+ if isinstance(e, (ApiError, NotLoggedIn)):
2011
+ raise
2012
+ raise ApiError(f"Simple table storage fetch failed: {e}")
2013
+
2014
+
2015
+ def delete_simple_table_storage(
2016
+ storage_id: int | str,
2017
+ *,
2018
+ timeout: int | None = None,
2019
+ ) -> dict[str, Any]:
2020
+ """
2021
+ Delete one simple table storage via SDK client model.
2022
+ """
2023
+ try:
2024
+ def _delete(ClientSimpleTableStorage):
2025
+ storage = ClientSimpleTableStorage.get(pk=int(storage_id), timeout=timeout)
2026
+ payload = _sdk_object_to_dict(storage)
2027
+ storage.delete(timeout=timeout)
2028
+ return payload
2029
+
2030
+ return _run_sdk_model_operation(
2031
+ module_name="mainsequence.client.models_simple_tables",
2032
+ class_name="SimpleTableStorage",
2033
+ operation=_delete,
2034
+ )
2035
+ except Exception as e:
2036
+ err_name = type(e).__name__
2037
+ if err_name == "NotFoundError":
2038
+ raise ApiError(f"Simple table storage not found: {storage_id}")
2039
+ if isinstance(e, (ApiError, NotLoggedIn)):
2040
+ raise
2041
+ raise ApiError(f"Simple table storage deletion failed: {e}")
2042
+
2043
+
1961
2044
  def list_data_node_org_unique_identifiers(
1962
2045
  *,
1963
2046
  timeout: int | None = None,
@@ -79,6 +79,7 @@ from .api import (
79
79
  delete_project_image,
80
80
  delete_resource_release,
81
81
  delete_secret,
82
+ delete_simple_table_storage,
82
83
  fetch_project_env_text,
83
84
  get_constant,
84
85
  get_current_user_profile,
@@ -93,6 +94,7 @@ from .api import (
93
94
  get_projects,
94
95
  get_resource_release,
95
96
  get_secret,
97
+ get_simple_table_storage,
96
98
  list_constant_users_can_edit,
97
99
  list_constant_users_can_view,
98
100
  list_constants,
@@ -116,6 +118,7 @@ from .api import (
116
118
  list_secret_users_can_edit,
117
119
  list_secret_users_can_view,
118
120
  list_secrets,
121
+ list_simple_table_storages,
119
122
  list_team_users_can_edit,
120
123
  list_team_users_can_view,
121
124
  prime_sync_project_after_commit_sdk,
@@ -185,6 +188,7 @@ app = typer.Typer(help="MainSequence CLI (login + project operations)")
185
188
 
186
189
  constants = typer.Typer(help="Constant commands")
187
190
  secrets = typer.Typer(help="Secret commands")
191
+ simple_table = typer.Typer(help="Simple table commands")
188
192
  organization = typer.Typer(help="Organization commands")
189
193
  organization_teams_group = typer.Typer(help="Organization team commands")
190
194
  markets = typer.Typer(help="Markets commands")
@@ -203,6 +207,10 @@ sdk = typer.Typer(help="SDK utilities (latest version, status)")
203
207
 
204
208
  app.add_typer(constants, name="constants")
205
209
  app.add_typer(secrets, name="secrets")
210
+ app.add_typer(simple_table, name="simple_table")
211
+ app.add_typer(simple_table, name="simple-table", hidden=True)
212
+ app.add_typer(simple_table, name="simple_tables", hidden=True)
213
+ app.add_typer(simple_table, name="simple-tables", hidden=True)
206
214
  app.add_typer(organization, name="organization")
207
215
  app.add_typer(markets, name="markets")
208
216
  app.add_typer(data_node_storage_group, name="data-node")
@@ -236,6 +244,7 @@ PROJECT_RESOURCE_MODEL_REF = "mainsequence.client.models_helpers.ProjectResource
236
244
  DATA_NODE_STORAGE_MODEL_REF = "mainsequence.client.models_tdag.DataNodeStorage"
237
245
  CONSTANT_MODEL_REF = "mainsequence.client.models_tdag.Constant"
238
246
  SECRET_MODEL_REF = "mainsequence.client.models_tdag.Secret"
247
+ SIMPLE_TABLE_STORAGE_MODEL_REF = "mainsequence.client.models_simple_tables.SimpleTableStorage"
239
248
  TEAM_MODEL_REF = "mainsequence.client.models_user.Team"
240
249
  PORTFOLIO_MODEL_REF = "mainsequence.client.models_vam.Portfolio"
241
250
  ASSET_TRANSLATION_TABLE_MODEL_REF = "mainsequence.client.models_vam.AssetTranslationTable"
@@ -2916,6 +2925,129 @@ def _secrets_delete_impl(
2916
2925
  print_kv("Deleted Secret", _format_secret_preview(deleted))
2917
2926
 
2918
2927
 
2928
+ def _format_simple_table_storage_preview(storage: dict[str, object]) -> list[tuple[str, str]]:
2929
+ columns = storage.get("columns")
2930
+ foreign_keys = storage.get("foreign_keys")
2931
+ incoming_fks = storage.get("incoming_fks")
2932
+ indexes_meta = storage.get("indexes_meta")
2933
+ return [
2934
+ ("ID", str(storage.get("id") or "-")),
2935
+ ("Source Class", str(storage.get("source_class_name") or "-")),
2936
+ ("Data Source", _format_data_node_storage_data_source(storage.get("data_source"))),
2937
+ ("Columns", str(len(columns)) if isinstance(columns, list) else "-"),
2938
+ ("Foreign Keys", str(len(foreign_keys)) if isinstance(foreign_keys, list) else "-"),
2939
+ ("Incoming FKs", str(len(incoming_fks)) if isinstance(incoming_fks, list) else "-"),
2940
+ ("Indexes", str(len(indexes_meta)) if isinstance(indexes_meta, list) else "-"),
2941
+ ("Open For Everyone", str(storage.get("open_for_everyone"))),
2942
+ ("Creation Date", str(storage.get("creation_date") or "-")),
2943
+ ]
2944
+
2945
+
2946
+ def _simple_tables_list_impl(
2947
+ timeout: int | None,
2948
+ filter_entries: list[str] | None,
2949
+ show_filters: bool,
2950
+ ) -> None:
2951
+ filters = _resolve_cli_list_filters(
2952
+ model_ref=SIMPLE_TABLE_STORAGE_MODEL_REF,
2953
+ filter_entries=filter_entries,
2954
+ show_filters=show_filters,
2955
+ command_label="Simple Tables",
2956
+ )
2957
+ _require_login()
2958
+
2959
+ try:
2960
+ storages = list_simple_table_storages(timeout=timeout, filters=filters)
2961
+ except ApiError as e:
2962
+ error(f"Simple tables fetch failed: {e}")
2963
+ raise typer.Exit(1)
2964
+
2965
+ rows: list[list[str]] = []
2966
+ for storage in storages:
2967
+ columns = storage.get("columns")
2968
+ rows.append(
2969
+ [
2970
+ str(storage.get("id") or "-"),
2971
+ str(storage.get("source_class_name") or "-"),
2972
+ _format_data_node_storage_data_source(storage.get("data_source")),
2973
+ str(len(columns)) if isinstance(columns, list) else "-",
2974
+ str(storage.get("open_for_everyone")),
2975
+ str(storage.get("creation_date") or "-"),
2976
+ ]
2977
+ )
2978
+
2979
+ if rows:
2980
+ print_table(
2981
+ "Simple Tables",
2982
+ ["ID", "Source Class", "Data Source", "Columns", "Open", "Creation Date"],
2983
+ rows,
2984
+ )
2985
+ else:
2986
+ info("No simple tables.")
2987
+ info(f"Total simple tables: {len(storages)}")
2988
+
2989
+
2990
+ def _simple_tables_detail_impl(
2991
+ *,
2992
+ storage_id: int,
2993
+ timeout: int | None,
2994
+ ) -> None:
2995
+ _require_login()
2996
+
2997
+ try:
2998
+ storage = get_simple_table_storage(storage_id, timeout=timeout)
2999
+ except ApiError as e:
3000
+ error(f"Simple table fetch failed: {e}")
3001
+ raise typer.Exit(1)
3002
+
3003
+ print_kv("Simple Table", _format_simple_table_storage_preview(storage))
3004
+ print_kv(
3005
+ "Simple Table Details",
3006
+ [
3007
+ ("Schema", _format_json_value(storage.get("schema") or storage.get("simple_table_schema"))),
3008
+ ("Build Configuration", _format_json_value(storage.get("build_configuration"))),
3009
+ ("Source Code Git Hash", str(storage.get("time_serie_source_code_git_hash") or "-")),
3010
+ ("Organization Owner", str(storage.get("organization_owner") or "-")),
3011
+ ("Created By User", str(storage.get("created_by_user") or "-")),
3012
+ ("Columns Payload", _format_json_value(storage.get("columns"))),
3013
+ ("Foreign Keys Payload", _format_json_value(storage.get("foreign_keys"))),
3014
+ ("Incoming FKs Payload", _format_json_value(storage.get("incoming_fks"))),
3015
+ ("Indexes Payload", _format_json_value(storage.get("indexes_meta"))),
3016
+ ],
3017
+ )
3018
+
3019
+
3020
+ def _simple_tables_delete_impl(
3021
+ *,
3022
+ storage_id: int,
3023
+ timeout: int | None,
3024
+ ) -> None:
3025
+ _require_login()
3026
+
3027
+ try:
3028
+ storage = get_simple_table_storage(storage_id, timeout=timeout)
3029
+ except ApiError as e:
3030
+ error(f"Simple table fetch failed: {e}")
3031
+ raise typer.Exit(1)
3032
+
3033
+ verification_value = str(storage.get("source_class_name") or storage.get("id") or storage_id)
3034
+ _require_delete_verification(
3035
+ preview_title="Simple Table Delete Preview",
3036
+ preview_items=_format_simple_table_storage_preview(storage),
3037
+ verification_value=verification_value,
3038
+ verification_label="source class name" if storage.get("source_class_name") else "simple table id",
3039
+ )
3040
+
3041
+ try:
3042
+ deleted = delete_simple_table_storage(storage_id, timeout=timeout)
3043
+ except ApiError as e:
3044
+ error(f"Simple table deletion failed: {e}")
3045
+ raise typer.Exit(1)
3046
+
3047
+ success(f"Simple table deleted: id={storage_id}")
3048
+ print_kv("Deleted Simple Table", _format_simple_table_storage_preview(deleted))
3049
+
3050
+
2919
3051
  def _data_node_storage_list_impl(
2920
3052
  timeout: int | None,
2921
3053
  filter_entries: list[str] | None,
@@ -3086,6 +3218,44 @@ def _print_data_node_storage_search_section(
3086
3218
  return len(storages)
3087
3219
 
3088
3220
 
3221
+ @simple_table.command("list")
3222
+ def simple_tables_list_cmd(
3223
+ filter_entries: list[str] | None = typer.Option(None, "--filter", help=LIST_FILTER_OPTION_HELP),
3224
+ show_filters: bool = typer.Option(False, "--show-filters", help="Show the filters supported by this list command and exit."),
3225
+ timeout: int | None = typer.Option(None, "--timeout", help="Request timeout in seconds"),
3226
+ ):
3227
+ """
3228
+ List simple table storages visible to the authenticated user.
3229
+ """
3230
+ _simple_tables_list_impl(
3231
+ timeout=timeout,
3232
+ filter_entries=filter_entries,
3233
+ show_filters=show_filters,
3234
+ )
3235
+
3236
+
3237
+ @simple_table.command("detail")
3238
+ def simple_tables_detail_cmd(
3239
+ storage_id: int = typer.Argument(..., help="Simple table storage ID."),
3240
+ timeout: int | None = typer.Option(None, "--timeout", help="Request timeout in seconds"),
3241
+ ):
3242
+ """
3243
+ Show one simple table storage in detail.
3244
+ """
3245
+ _simple_tables_detail_impl(storage_id=storage_id, timeout=timeout)
3246
+
3247
+
3248
+ @simple_table.command("delete")
3249
+ def simple_tables_delete_cmd(
3250
+ storage_id: int = typer.Argument(..., help="Simple table storage ID."),
3251
+ timeout: int | None = typer.Option(None, "--timeout", help="Request timeout in seconds"),
3252
+ ):
3253
+ """
3254
+ Delete one simple table storage.
3255
+ """
3256
+ _simple_tables_delete_impl(storage_id=storage_id, timeout=timeout)
3257
+
3258
+
3089
3259
  @constants.command("list")
3090
3260
  def constants_list_cmd(
3091
3261
  filter_entries: list[str] | None = typer.Option(None, "--filter", help=LIST_FILTER_OPTION_HELP),
@@ -1,8 +1,8 @@
1
1
  from mainsequence.logconf import logger
2
2
 
3
3
  from .agent import *
4
- from .data_filters import *
5
4
  from .models_helpers import *
5
+ from .models_simple_tables import *
6
6
  from .models_tdag import *
7
7
  from .models_user import *
8
8
  from .models_vam import *
@@ -6,8 +6,6 @@ from typing import Any, ClassVar
6
6
 
7
7
  from pydantic import BaseModel, ConfigDict
8
8
 
9
- from mainsequence import logger
10
-
11
9
  from .exceptions import ApiError, raise_for_response
12
10
  from .utils import (
13
11
  API_ENDPOINT,
@@ -632,21 +630,69 @@ class BaseObjectOrm:
632
630
 
633
631
  body = r.json()
634
632
 
635
- def recursive_update(obj, update_dict):
633
+ def _iter_field_aliases(field_info) -> set[str]:
634
+ aliases: set[str] = set()
635
+
636
+ for attr_name in ("alias", "serialization_alias"):
637
+ alias_value = getattr(field_info, attr_name, None)
638
+ if isinstance(alias_value, str) and alias_value:
639
+ aliases.add(alias_value)
640
+
641
+ validation_alias = getattr(field_info, "validation_alias", None)
642
+ if isinstance(validation_alias, str) and validation_alias:
643
+ aliases.add(validation_alias)
644
+ elif validation_alias is not None:
645
+ choices = getattr(validation_alias, "choices", None)
646
+ if choices is not None:
647
+ for choice in choices:
648
+ if isinstance(choice, str) and choice:
649
+ aliases.add(choice)
650
+ else:
651
+ path = getattr(validation_alias, "path", None)
652
+ if isinstance(path, (list, tuple)) and len(path) == 1 and isinstance(path[0], str):
653
+ aliases.add(path[0])
654
+
655
+ return aliases
656
+
657
+ def _resolve_model_field_name(obj, raw_key: str) -> str:
658
+ if not isinstance(obj, BaseModel):
659
+ return raw_key
660
+
661
+ model_fields = getattr(type(obj), "model_fields", {})
662
+ if raw_key in model_fields:
663
+ return raw_key
664
+
665
+ for field_name, field_info in model_fields.items():
666
+ if raw_key in _iter_field_aliases(field_info):
667
+ return field_name
668
+
669
+ return raw_key
670
+
671
+ def recursive_update(obj, update_dict, path=()):
636
672
  for k, v in update_dict.items():
673
+ current_path = (*path, k)
674
+ field_name = _resolve_model_field_name(obj, k)
637
675
  # Get the existing nested object, defaulting to None if it doesn't exist
638
- nested_obj = getattr(obj, k, None)
676
+ nested_obj = getattr(obj, field_name, None)
639
677
 
640
678
  # Only recurse if the update value is a dict AND the existing
641
679
  # attribute is an instance of a Pydantic model.
642
680
  if isinstance(v, dict) and isinstance(nested_obj, BaseModel):
643
- recursive_update(nested_obj, v)
681
+ recursive_update(nested_obj, v, path=current_path)
644
682
  else:
645
683
  # Otherwise, just set the value directly.
646
684
  try:
647
- setattr(obj, k, v)
648
- except Exception as e:
649
- logger.exception(e)
685
+ setattr(obj, field_name, v)
686
+ except Exception as exc:
687
+ field_path = ".".join(current_path)
688
+ response_fragment = repr({k: v})
689
+ if len(response_fragment) > 300:
690
+ response_fragment = f"{response_fragment[:297]}..."
691
+ raise ValueError(
692
+ f"Failed to apply PATCH response to {type(obj).__name__} at field "
693
+ f"'{field_path}'. Response fragment: {response_fragment}. "
694
+ f"Original error: {exc}"
695
+ ) from exc
650
696
 
651
697
  return obj
652
698