mainsequence 3.18.0__tar.gz → 3.18.2__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 (178) hide show
  1. {mainsequence-3.18.0 → mainsequence-3.18.2}/PKG-INFO +1 -1
  2. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/models_user.py +130 -8
  3. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence.egg-info/PKG-INFO +1 -1
  4. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence.egg-info/SOURCES.txt +1 -0
  5. {mainsequence-3.18.0 → mainsequence-3.18.2}/pyproject.toml +1 -1
  6. mainsequence-3.18.2/tests/test_models_user_request_bound_auth.py +170 -0
  7. {mainsequence-3.18.0 → mainsequence-3.18.2}/LICENSE +0 -0
  8. {mainsequence-3.18.0 → mainsequence-3.18.2}/README.md +0 -0
  9. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/AGENTS.md +0 -0
  10. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
  11. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
  12. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
  13. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
  14. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
  15. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
  16. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
  17. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
  18. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/data_publishing/simple_tables/SKILL.md +0 -0
  19. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
  20. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/maintenance/local_journal/SKILL.md +0 -0
  21. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/markets_platform/assets_and_translation/SKILL.md +0 -0
  22. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/markets_platform/instruments_and_pricing/SKILL.md +0 -0
  23. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/markets_platform/virtualfundbuilder/SKILL.md +0 -0
  24. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
  25. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
  26. {mainsequence-3.18.0 → mainsequence-3.18.2}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
  27. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/__init__.py +0 -0
  28. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/__main__.py +0 -0
  29. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/bootstrap.py +0 -0
  30. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/__init__.py +0 -0
  31. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/api.py +0 -0
  32. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/browser_auth.py +0 -0
  33. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/cli.py +0 -0
  34. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/config.py +0 -0
  35. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/docker_utils.py +0 -0
  36. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/doctor.py +0 -0
  37. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/local_ops.py +0 -0
  38. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/model_filters.py +0 -0
  39. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/project_status.py +0 -0
  40. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/pydantic_cli.py +0 -0
  41. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/sdk_utils.py +0 -0
  42. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/ssh_utils.py +0 -0
  43. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/cli/ui.py +0 -0
  44. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/__init__.py +0 -0
  45. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/agent.py +0 -0
  46. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/agent_runtime_models.py +0 -0
  47. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/base.py +0 -0
  48. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/client.py +0 -0
  49. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/command_center/__init__.py +0 -0
  50. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/command_center/app_component.py +0 -0
  51. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/command_center/data_models.py +0 -0
  52. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/command_center/workspace.py +0 -0
  53. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
  54. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
  55. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
  56. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/data_sources_interfaces/timescale.py +0 -0
  57. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/exceptions.py +0 -0
  58. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/fastapi/__init__.py +0 -0
  59. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/fastapi/auth.py +0 -0
  60. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/models_helpers.py +0 -0
  61. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/models_simple_tables.py +0 -0
  62. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/models_tdag.py +0 -0
  63. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/models_vam.py +0 -0
  64. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/client/utils.py +0 -0
  65. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/compute_validation.py +0 -0
  66. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/__init__.py +0 -0
  67. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/__init__.py +0 -0
  68. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/assets/config.toml +0 -0
  69. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/assets/favicon.png +0 -0
  70. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/assets/image_1_base64.txt +0 -0
  71. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/assets/image_2_base64.txt +0 -0
  72. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/assets/image_3_base64.txt +0 -0
  73. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/assets/image_4_base64.txt +0 -0
  74. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/assets/image_5_base64.txt +0 -0
  75. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/assets/logo.png +0 -0
  76. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/components/__init__.py +0 -0
  77. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/components/asset_select.py +0 -0
  78. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/components/date_settings.py +0 -0
  79. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/components/logged_user.py +0 -0
  80. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/core/__init__.py +0 -0
  81. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/core/theme.py +0 -0
  82. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/instruments/__init__.py +0 -0
  83. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/instruments/streamlit_form_factory.py +0 -0
  84. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/pages/__init__.py +0 -0
  85. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/dashboards/streamlit/scaffold.py +0 -0
  86. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instrumentation/__init__.py +0 -0
  87. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instrumentation/utils.py +0 -0
  88. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/__init__.py +0 -0
  89. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/data_interface/__init__.py +0 -0
  90. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/data_interface/data_interface.py +0 -0
  91. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/instruments/__init__.py +0 -0
  92. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/instruments/base_instrument.py +0 -0
  93. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/instruments/bond.py +0 -0
  94. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/instruments/callability.py +0 -0
  95. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/instruments/interest_rate_swap.py +0 -0
  96. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/instruments/json_codec.py +0 -0
  97. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/instruments/position.py +0 -0
  98. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/instruments/ql_fields.py +0 -0
  99. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/interest_rates/__init__.py +0 -0
  100. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/interest_rates/etl/__init__.py +0 -0
  101. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/interest_rates/etl/curve_codec.py +0 -0
  102. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/interest_rates/etl/nodes.py +0 -0
  103. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/interest_rates/etl/registry.py +0 -0
  104. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/pricing_models/__init__.py +0 -0
  105. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/pricing_models/bond_pricer.py +0 -0
  106. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/pricing_models/indices.py +0 -0
  107. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/pricing_models/indices_builders.py +0 -0
  108. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/pricing_models/swap_pricer.py +0 -0
  109. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/settings.py +0 -0
  110. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/instruments/utils.py +0 -0
  111. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/logconf.py +0 -0
  112. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/runtime_flags.py +0 -0
  113. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/__init__.py +0 -0
  114. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/__main__.py +0 -0
  115. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/base_persist_managers.py +0 -0
  116. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/config.py +0 -0
  117. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/configuration_models.py +0 -0
  118. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/data_nodes/__init__.py +0 -0
  119. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/data_nodes/build_operations.py +0 -0
  120. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/data_nodes/data_nodes.py +0 -0
  121. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/data_nodes/filters.py +0 -0
  122. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/data_nodes/models.py +0 -0
  123. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/data_nodes/namespacing.py +0 -0
  124. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/data_nodes/persist_managers.py +0 -0
  125. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/data_nodes/run_operations.py +0 -0
  126. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/data_nodes/utils.py +0 -0
  127. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/filters.py +0 -0
  128. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/future_registry.py +0 -0
  129. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/pydantic_metadata.py +0 -0
  130. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/simple_tables/__init__.py +0 -0
  131. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/simple_tables/filters.py +0 -0
  132. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/simple_tables/models.py +0 -0
  133. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/simple_tables/persist_managers.py +0 -0
  134. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/simple_tables/schema.py +0 -0
  135. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/simple_tables/table_nodes.py +0 -0
  136. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/tdag/utils.py +0 -0
  137. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/__init__.py +0 -0
  138. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/contrib/__init__.py +0 -0
  139. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/contrib/data_nodes/__init__.py +0 -0
  140. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/contrib/data_nodes/external_weights.py +0 -0
  141. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/contrib/data_nodes/intraday_trend.py +0 -0
  142. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/contrib/data_nodes/market_cap.py +0 -0
  143. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/contrib/data_nodes/mock_signal.py +0 -0
  144. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/contrib/data_nodes/portfolio_replicator.py +0 -0
  145. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/contrib/prices/__init__.py +0 -0
  146. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/contrib/prices/data_nodes.py +0 -0
  147. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/contrib/prices/utils.py +0 -0
  148. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/contrib/rebalance_strategies/__init__.py +0 -0
  149. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/contrib/rebalance_strategies/rebalance_strategies.py +0 -0
  150. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/enums.py +0 -0
  151. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/models.py +0 -0
  152. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/portfolio_nodes.py +0 -0
  153. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/resource_factory/__init__.py +0 -0
  154. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/resource_factory/rebalance_factory.py +0 -0
  155. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/resource_factory/signal_factory.py +0 -0
  156. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence/virtualfundbuilder/utils.py +0 -0
  157. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence.egg-info/dependency_links.txt +0 -0
  158. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence.egg-info/entry_points.txt +0 -0
  159. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence.egg-info/requires.txt +0 -0
  160. {mainsequence-3.18.0 → mainsequence-3.18.2}/mainsequence.egg-info/top_level.txt +0 -0
  161. {mainsequence-3.18.0 → mainsequence-3.18.2}/setup.cfg +0 -0
  162. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_auth_precedence.py +0 -0
  163. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_build_operations_hashing.py +0 -0
  164. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_cli.py +0 -0
  165. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_cli_browser_auth.py +0 -0
  166. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_client.py +0 -0
  167. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_command_center_app_component_models.py +0 -0
  168. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_command_center_models.py +0 -0
  169. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_dependency_extras.py +0 -0
  170. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_filter_normalization.py +0 -0
  171. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_instruments.py +0 -0
  172. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_logconf.py +0 -0
  173. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_logged_user_components.py +0 -0
  174. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_pod_project_resolution.py +0 -0
  175. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_project_batch_jobs_from_file.py +0 -0
  176. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_run_configuration.py +0 -0
  177. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_simple_tables_configuration_hashing.py +0 -0
  178. {mainsequence-3.18.0 → mainsequence-3.18.2}/tests/test_simple_tables_persistence.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 3.18.0
3
+ Version: 3.18.2
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
@@ -18,6 +18,7 @@ from .base import (
18
18
  )
19
19
  from .exceptions import ApiError, raise_for_response
20
20
  from .utils import (
21
+ DEFAULT_TIMEOUT,
21
22
  make_request,
22
23
  )
23
24
 
@@ -94,6 +95,29 @@ def _logged_user_header_context(
94
95
  "authorization_scheme": authorization_scheme,
95
96
  }
96
97
 
98
+
99
+ def _build_request_bound_outbound_headers(headers: Mapping[str, Any]) -> dict[str, str]:
100
+ excluded = {
101
+ "connection",
102
+ "content-length",
103
+ "host",
104
+ "keep-alive",
105
+ "proxy-authenticate",
106
+ "proxy-authorization",
107
+ "te",
108
+ "trailer",
109
+ "trailers",
110
+ "transfer-encoding",
111
+ "upgrade",
112
+ }
113
+ outbound: dict[str, str] = {}
114
+ for key, value in headers.items():
115
+ key_str = str(key)
116
+ if key_str.lower() in excluded:
117
+ continue
118
+ outbound[key_str] = str(value)
119
+ return outbound
120
+
97
121
  class Organization(BasePydanticModel):
98
122
  id: int = Field(
99
123
  ...,
@@ -952,6 +976,53 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
952
976
  def effective_plan(self) -> Any | None:
953
977
  return self.plan if self.plan is not None else self.active_plan_type
954
978
 
979
+ @classmethod
980
+ def _build_request_bound_identity_user(
981
+ cls,
982
+ *,
983
+ normalized_headers: Mapping[str, Any],
984
+ user_id: int,
985
+ ):
986
+ username = str(
987
+ normalized_headers.get("X-Username")
988
+ or normalized_headers.get("x-username")
989
+ or normalized_headers.get("X-User-Email")
990
+ or normalized_headers.get("x-user-email")
991
+ or f"user-{user_id}"
992
+ ).strip() or f"user-{user_id}"
993
+ email = str(
994
+ normalized_headers.get("X-User-Email")
995
+ or normalized_headers.get("x-user-email")
996
+ or username
997
+ ).strip() or username
998
+
999
+ payload = {
1000
+ "id": user_id,
1001
+ "username": username,
1002
+ "email": email,
1003
+ "date_joined": None,
1004
+ "is_active": True,
1005
+ "last_login": None,
1006
+ "api_request_limit": None,
1007
+ "mfa_enabled": False,
1008
+ "organization": None,
1009
+ "phone_number": None,
1010
+ "plan": None,
1011
+ "active_plan_type": None,
1012
+ "groups": [],
1013
+ "user_permissions": [],
1014
+ "organization_teams": [],
1015
+ "is_verified": None,
1016
+ "blocked_access": None,
1017
+ "requires_password_change": None,
1018
+ "identity_platform_uid": None,
1019
+ }
1020
+
1021
+ model_construct = getattr(cls, "model_construct", None)
1022
+ if callable(model_construct):
1023
+ return model_construct(**payload)
1024
+ return cls.construct(**payload)
1025
+
955
1026
  @classmethod
956
1027
  def get_authenticated_user_details(cls):
957
1028
  """
@@ -980,6 +1051,35 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
980
1051
 
981
1052
  return cls.parse_obj(data)
982
1053
 
1054
+ @classmethod
1055
+ def _get_request_bound_user(
1056
+ cls,
1057
+ *,
1058
+ headers: Mapping[str, Any],
1059
+ user_id: int | None = None,
1060
+ ) -> User:
1061
+ outbound_headers = _build_request_bound_outbound_headers(headers)
1062
+ if user_id is None:
1063
+ url = f"{cls.get_object_url()}/get_user_details/"
1064
+ params = None
1065
+ else:
1066
+ url = f"{cls.get_object_url()}/{user_id}/"
1067
+ params = {"serializer": "full"}
1068
+
1069
+ response = cls.build_session().get(
1070
+ url,
1071
+ headers=outbound_headers,
1072
+ params=params,
1073
+ timeout=DEFAULT_TIMEOUT,
1074
+ )
1075
+ raise_for_response(response)
1076
+
1077
+ data = response.json()
1078
+ if hasattr(cls, "model_validate"):
1079
+ return cls.model_validate(data)
1080
+
1081
+ return cls.parse_obj(data)
1082
+
983
1083
  @classmethod
984
1084
  def get_logged_user(cls) -> User:
985
1085
  """
@@ -1029,6 +1129,15 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
1029
1129
  normalized_headers[key_str] = value
1030
1130
  normalized_headers[key_str.lower()] = value
1031
1131
 
1132
+ authorization_value = (
1133
+ normalized_headers.get("Authorization")
1134
+ or normalized_headers.get("authorization")
1135
+ )
1136
+ has_bearer_authorization = bool(
1137
+ authorization_value
1138
+ and str(authorization_value).split(" ", 1)[0].lower() == "bearer"
1139
+ )
1140
+
1032
1141
  user_id_raw = (
1033
1142
  normalized_headers.get("X-User-ID")
1034
1143
  or normalized_headers.get("x-user-id")
@@ -1037,20 +1146,19 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
1037
1146
  )
1038
1147
 
1039
1148
  if user_id_raw in (None, ""):
1040
- if normalized_headers.get("authorization") and "Bearer" in normalized_headers.get("authorization"):
1149
+ if has_bearer_authorization:
1041
1150
  outgoing_authorization = None
1042
1151
  outgoing_authorization_scheme = None
1043
1152
  try:
1044
- outgoing_headers = cls.LOADERS.auth_headers
1045
- outgoing_authorization = (
1046
- outgoing_headers.get("Authorization")
1047
- or outgoing_headers.get("authorization")
1153
+ outgoing_headers = _build_request_bound_outbound_headers(headers)
1154
+ outgoing_authorization = outgoing_headers.get("Authorization") or outgoing_headers.get(
1155
+ "authorization"
1048
1156
  )
1049
1157
  if outgoing_authorization:
1050
1158
  outgoing_authorization_scheme = str(outgoing_authorization).split(" ", 1)[0]
1051
1159
  except Exception as auth_exc:
1052
1160
  logger.exception(
1053
- "User.get_logged_user could not inspect outgoing auth headers "
1161
+ "User.get_logged_user could not inspect request-bound outgoing auth headers "
1054
1162
  "for /user/api/user/get_user_details/: %s",
1055
1163
  auth_exc,
1056
1164
  )
@@ -1061,7 +1169,7 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
1061
1169
  outgoing_authorization_scheme,
1062
1170
  )
1063
1171
  try:
1064
- user = cls.get_authenticated_user_details()
1172
+ user = cls._get_request_bound_user(headers=headers)
1065
1173
  except Exception:
1066
1174
  context = _logged_user_header_context(headers, header_source=header_source)
1067
1175
  logger.exception(
@@ -1075,6 +1183,7 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
1075
1183
  context["authorization_scheme"],
1076
1184
  )
1077
1185
  raise
1186
+ _CURRENT_USER.set(user)
1078
1187
  logger.info(
1079
1188
  "User.get_logged_user resolved user_id=%s via bearer fallback",
1080
1189
  user.id,
@@ -1110,7 +1219,20 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
1110
1219
  )
1111
1220
  raise RuntimeError(f"Invalid X-User-ID value: {user_id_raw!r}") from exc
1112
1221
 
1113
- user = cls.get(pk=user_id, serializer="full")
1222
+ if not has_bearer_authorization:
1223
+ user = cls._build_request_bound_identity_user(
1224
+ normalized_headers=normalized_headers,
1225
+ user_id=user_id,
1226
+ )
1227
+ _CURRENT_USER.set(user)
1228
+ logger.info(
1229
+ "User.get_logged_user resolved user_id=%s via request identity headers without backend auth",
1230
+ user.id,
1231
+ )
1232
+ return user
1233
+
1234
+ user = cls._get_request_bound_user(headers=headers, user_id=user_id)
1235
+ _CURRENT_USER.set(user)
1114
1236
  logger.info(
1115
1237
  "User.get_logged_user resolved user_id=%s via X-User-ID header",
1116
1238
  user.id,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mainsequence
3
- Version: 3.18.0
3
+ Version: 3.18.2
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
@@ -168,6 +168,7 @@ tests/test_filter_normalization.py
168
168
  tests/test_instruments.py
169
169
  tests/test_logconf.py
170
170
  tests/test_logged_user_components.py
171
+ tests/test_models_user_request_bound_auth.py
171
172
  tests/test_pod_project_resolution.py
172
173
  tests/test_project_batch_jobs_from_file.py
173
174
  tests/test_run_configuration.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mainsequence"
7
- version = "3.18.0"
7
+ version = "3.18.2"
8
8
  description = "Main Sequence SDK "
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -0,0 +1,170 @@
1
+ from __future__ import annotations
2
+
3
+ import mainsequence.client.models_user as models_user_mod
4
+
5
+
6
+ class _FakeResponse:
7
+ def __init__(self, payload: dict):
8
+ self.status_code = 200
9
+ self._payload = payload
10
+
11
+ def json(self):
12
+ return self._payload
13
+
14
+
15
+ def test_get_logged_user_uses_request_bound_headers_for_user_lookup(monkeypatch):
16
+ monkeypatch.delenv("MAINSEQUENCE_ACCESS_TOKEN", raising=False)
17
+ monkeypatch.delenv("MAINSEQUENCE_REFRESH_TOKEN", raising=False)
18
+
19
+ captured: dict[str, object] = {}
20
+
21
+ class _FakeSession:
22
+ def get(self, url, *, headers=None, params=None, timeout=None):
23
+ captured["url"] = url
24
+ captured["headers"] = headers
25
+ captured["params"] = params
26
+ captured["timeout"] = timeout
27
+ return _FakeResponse(
28
+ {
29
+ "id": 4,
30
+ "username": "jose",
31
+ "email": "jose@main-sequence.io",
32
+ "date_joined": "2026-01-01T00:00:00Z",
33
+ "is_active": True,
34
+ "api_request_limit": 10000,
35
+ "mfa_enabled": False,
36
+ "groups": [],
37
+ "user_permissions": [],
38
+ "organization_teams": [],
39
+ }
40
+ )
41
+
42
+ monkeypatch.setattr(
43
+ models_user_mod.User,
44
+ "build_session",
45
+ classmethod(lambda cls: _FakeSession()),
46
+ )
47
+ monkeypatch.setattr(
48
+ models_user_mod.User,
49
+ "get",
50
+ classmethod(lambda cls, *args, **kwargs: (_ for _ in ()).throw(AssertionError("User.get should not be used"))),
51
+ )
52
+
53
+ auth_token = models_user_mod._CURRENT_AUTH_HEADERS.set(
54
+ {
55
+ "X-User-ID": "4",
56
+ "Authorization": "Bearer inbound-token",
57
+ "Cookie": "sessionid=abc",
58
+ "Host": "frontend.test",
59
+ }
60
+ )
61
+ user_token = models_user_mod._CURRENT_USER.set(None)
62
+ try:
63
+ user = models_user_mod.User.get_logged_user()
64
+ finally:
65
+ models_user_mod._CURRENT_USER.reset(user_token)
66
+ models_user_mod._CURRENT_AUTH_HEADERS.reset(auth_token)
67
+
68
+ assert user.id == 4
69
+ assert str(captured["url"]).endswith("/user/api/user/4/")
70
+ assert captured["params"] == {"serializer": "full"}
71
+ assert captured["headers"]["Authorization"] == "Bearer inbound-token"
72
+ assert captured["headers"]["Cookie"] == "sessionid=abc"
73
+ assert "Host" not in captured["headers"]
74
+
75
+
76
+ def test_get_logged_user_returns_header_identity_without_backend_auth(monkeypatch):
77
+ monkeypatch.delenv("MAINSEQUENCE_ACCESS_TOKEN", raising=False)
78
+ monkeypatch.delenv("MAINSEQUENCE_REFRESH_TOKEN", raising=False)
79
+
80
+ class _FakeSession:
81
+ def get(self, url, *, headers=None, params=None, timeout=None):
82
+ raise AssertionError("header-only identity should not trigger backend lookup")
83
+
84
+ monkeypatch.setattr(
85
+ models_user_mod.User,
86
+ "build_session",
87
+ classmethod(lambda cls: _FakeSession()),
88
+ )
89
+
90
+ auth_token = models_user_mod._CURRENT_AUTH_HEADERS.set(
91
+ {
92
+ "X-User-ID": "4",
93
+ "X-Username": "dashboard-user",
94
+ "X-Dashboard-ID": "dashboard-7",
95
+ }
96
+ )
97
+ user_token = models_user_mod._CURRENT_USER.set(None)
98
+ try:
99
+ user = models_user_mod.User.get_logged_user()
100
+ finally:
101
+ models_user_mod._CURRENT_USER.reset(user_token)
102
+ models_user_mod._CURRENT_AUTH_HEADERS.reset(auth_token)
103
+
104
+ assert user.id == 4
105
+ assert user.username == "dashboard-user"
106
+ assert user.email == "dashboard-user"
107
+ assert user.date_joined is None
108
+ assert user.api_request_limit is None
109
+
110
+
111
+ def test_get_logged_user_bearer_fallback_uses_request_bound_headers(monkeypatch):
112
+ monkeypatch.delenv("MAINSEQUENCE_ACCESS_TOKEN", raising=False)
113
+ monkeypatch.delenv("MAINSEQUENCE_REFRESH_TOKEN", raising=False)
114
+
115
+ captured: dict[str, object] = {}
116
+
117
+ class _FakeSession:
118
+ def get(self, url, *, headers=None, params=None, timeout=None):
119
+ captured["url"] = url
120
+ captured["headers"] = headers
121
+ captured["params"] = params
122
+ captured["timeout"] = timeout
123
+ return _FakeResponse(
124
+ {
125
+ "id": 7,
126
+ "username": "streamlit-user",
127
+ "email": "streamlit@main-sequence.io",
128
+ "date_joined": "2026-01-01T00:00:00Z",
129
+ "is_active": True,
130
+ "api_request_limit": 10000,
131
+ "mfa_enabled": False,
132
+ "groups": [],
133
+ "user_permissions": [],
134
+ "organization_teams": [],
135
+ }
136
+ )
137
+
138
+ monkeypatch.setattr(
139
+ models_user_mod.User,
140
+ "build_session",
141
+ classmethod(lambda cls: _FakeSession()),
142
+ )
143
+ monkeypatch.setattr(
144
+ models_user_mod.User,
145
+ "get_authenticated_user_details",
146
+ classmethod(
147
+ lambda cls: (_ for _ in ()).throw(
148
+ AssertionError("get_authenticated_user_details should not be used in request-bound fallback")
149
+ )
150
+ ),
151
+ )
152
+
153
+ auth_token = models_user_mod._CURRENT_AUTH_HEADERS.set(
154
+ {
155
+ "Authorization": "Bearer inbound-token",
156
+ "Cookie": "sessionid=abc",
157
+ }
158
+ )
159
+ user_token = models_user_mod._CURRENT_USER.set(None)
160
+ try:
161
+ user = models_user_mod.User.get_logged_user()
162
+ finally:
163
+ models_user_mod._CURRENT_USER.reset(user_token)
164
+ models_user_mod._CURRENT_AUTH_HEADERS.reset(auth_token)
165
+
166
+ assert user.id == 7
167
+ assert str(captured["url"]).endswith("/user/api/user/get_user_details/")
168
+ assert captured["params"] is None
169
+ assert captured["headers"]["Authorization"] == "Bearer inbound-token"
170
+ assert captured["headers"]["Cookie"] == "sessionid=abc"
File without changes
File without changes
File without changes