simstack 2.0.1__py3-none-any.whl
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.
- simstack/__init__.py +0 -0
- simstack/_version.py +24 -0
- simstack/core/__init__.py +0 -0
- simstack/core/artifacts.py +406 -0
- simstack/core/asnyc_helper.py +14 -0
- simstack/core/config_file.py +60 -0
- simstack/core/context.py +283 -0
- simstack/core/definitions.py +22 -0
- simstack/core/engine.py +115 -0
- simstack/core/find_simstack_modules.py +41 -0
- simstack/core/hash.py +179 -0
- simstack/core/node.py +1137 -0
- simstack/core/node_claim.py +31 -0
- simstack/core/node_runner.py +362 -0
- simstack/core/process_results.py +102 -0
- simstack/core/recompute_artifacts.py +117 -0
- simstack/core/reset_db.py +12 -0
- simstack/core/resource_assignment.py +222 -0
- simstack/core/resources.py +90 -0
- simstack/core/route_table.py +24 -0
- simstack/core/run_docker.py +109 -0
- simstack/core/run_node.py +71 -0
- simstack/core/runner.py +169 -0
- simstack/core/services/__init__.py +11 -0
- simstack/core/services/base_service.py +191 -0
- simstack/core/services/file_transfer_service.py +117 -0
- simstack/core/services/git_restart_service.py +12 -0
- simstack/core/services/git_uv_update_service.py +105 -0
- simstack/core/services/node_execution_service.py +182 -0
- simstack/core/services/resource_branch_monitor_service.py +71 -0
- simstack/core/services/runner_cleanup_service.py +149 -0
- simstack/core/services/runner_manager.py +163 -0
- simstack/core/services/runner_status_service.py +10 -0
- simstack/core/services/slurm_status_service.py +67 -0
- simstack/core/services/stop_check_service.py +27 -0
- simstack/core/services/timeout_restart_service.py +26 -0
- simstack/core/simstack_result.py +39 -0
- simstack/core/submit_node.py +217 -0
- simstack/core/task_id.py +22 -0
- simstack/methods/__init__.py +0 -0
- simstack/methods/archive_files.py +292 -0
- simstack/methods/fire_and_forget_runner.py +113 -0
- simstack/methods/generate_test.py +125 -0
- simstack/methods/get_file.py +26 -0
- simstack/methods/mass_runner.py +154 -0
- simstack/methods/multiple_executor.py +307 -0
- simstack/methods/switch_git.py +16 -0
- simstack/methods/upload_helpers.py +81 -0
- simstack/models/__init__.py +74 -0
- simstack/models/array_list.py +21 -0
- simstack/models/array_storage.py +83 -0
- simstack/models/artifact_models.py +127 -0
- simstack/models/base_lists.py +51 -0
- simstack/models/base_types.py +209 -0
- simstack/models/charts_artifact.py +690 -0
- simstack/models/dataset.py +451 -0
- simstack/models/dataset_metadata.py +390 -0
- simstack/models/datasettuple.py +587 -0
- simstack/models/file_instance.py +150 -0
- simstack/models/file_list.py +45 -0
- simstack/models/files.py +424 -0
- simstack/models/fire_and_forget_result.py +19 -0
- simstack/models/images2d.py +113 -0
- simstack/models/log_entry_model.py +37 -0
- simstack/models/models.py +52 -0
- simstack/models/named_data_reference.py +36 -0
- simstack/models/node_registry.py +96 -0
- simstack/models/pandas_model.py +239 -0
- simstack/models/parameters.py +402 -0
- simstack/models/pickle_models.py +300 -0
- simstack/models/project.py +13 -0
- simstack/models/resource_assignment.py +170 -0
- simstack/models/resource_definition.py +125 -0
- simstack/models/runner_model.py +39 -0
- simstack/models/simple_table.py +79 -0
- simstack/models/simstack_model.py +97 -0
- simstack/models/slurm_info.py +17 -0
- simstack/models/table_artifact.py +87 -0
- simstack/models/tag.py +12 -0
- simstack/models/unused_test_file.py +5 -0
- simstack/simstack_marker.com +1 -0
- simstack/static/docs/.buildinfo +4 -0
- simstack/static/docs/_images/GUI Concept.webp +0 -0
- simstack/static/docs/_modules/index.html +434 -0
- simstack/static/docs/_modules/simstack/core/artifacts.html +743 -0
- simstack/static/docs/_modules/simstack/core/asnyc_helper.html +330 -0
- simstack/static/docs/_modules/simstack/core/config_file.html +379 -0
- simstack/static/docs/_modules/simstack/core/definitions.html +341 -0
- simstack/static/docs/_modules/simstack/core/engine.html +437 -0
- simstack/static/docs/_modules/simstack/core/find_simstack_modules.html +360 -0
- simstack/static/docs/_modules/simstack/core/hash.html +528 -0
- simstack/static/docs/_modules/simstack/core/node.html +1392 -0
- simstack/static/docs/_modules/simstack/core/node_claim.html +347 -0
- simstack/static/docs/_modules/simstack/core/node_runner.html +711 -0
- simstack/static/docs/_modules/simstack/core/process_results.html +418 -0
- simstack/static/docs/_modules/simstack/core/recompute_artifacts.html +433 -0
- simstack/static/docs/_modules/simstack/core/reset_db.html +328 -0
- simstack/static/docs/_modules/simstack/core/resource_assignment.html +553 -0
- simstack/static/docs/_modules/simstack/core/resources.html +424 -0
- simstack/static/docs/_modules/simstack/core/route_table.html +349 -0
- simstack/static/docs/_modules/simstack/core/run_docker.html +425 -0
- simstack/static/docs/_modules/simstack/core/run_node.html +390 -0
- simstack/static/docs/_modules/simstack/core/runner.html +491 -0
- simstack/static/docs/_modules/simstack/core/services/base_service.html +522 -0
- simstack/static/docs/_modules/simstack/core/services/file_transfer_service.html +436 -0
- simstack/static/docs/_modules/simstack/core/services/git_restart_service.html +331 -0
- simstack/static/docs/_modules/simstack/core/services/git_uv_update_service.html +427 -0
- simstack/static/docs/_modules/simstack/core/services/node_execution_service.html +507 -0
- simstack/static/docs/_modules/simstack/core/services/resource_branch_monitor_service.html +390 -0
- simstack/static/docs/_modules/simstack/core/services/runner_cleanup_service.html +468 -0
- simstack/static/docs/_modules/simstack/core/services/runner_manager.html +485 -0
- simstack/static/docs/_modules/simstack/core/services/runner_status_service.html +329 -0
- simstack/static/docs/_modules/simstack/core/services/slurm_status_service.html +386 -0
- simstack/static/docs/_modules/simstack/core/services/stop_check_service.html +346 -0
- simstack/static/docs/_modules/simstack/core/services/timeout_restart_service.html +345 -0
- simstack/static/docs/_modules/simstack/core/simstack_result.html +355 -0
- simstack/static/docs/_modules/simstack/core/submit_node.html +536 -0
- simstack/static/docs/_modules/simstack/core/task_id.html +344 -0
- simstack/static/docs/_modules/simstack/methods/archive_files.html +598 -0
- simstack/static/docs/_modules/simstack/methods/fire_and_forget_runner.html +432 -0
- simstack/static/docs/_modules/simstack/methods/generate_test.html +443 -0
- simstack/static/docs/_modules/simstack/methods/get_file.html +342 -0
- simstack/static/docs/_modules/simstack/methods/mass_runner.html +476 -0
- simstack/static/docs/_modules/simstack/methods/multiple_executor.html +674 -0
- simstack/static/docs/_modules/simstack/methods/switch_git.html +332 -0
- simstack/static/docs/_modules/simstack/methods/upload_helpers.html +403 -0
- simstack/static/docs/_modules/simstack/models/array_list.html +337 -0
- simstack/static/docs/_modules/simstack/models/array_storage.html +417 -0
- simstack/static/docs/_modules/simstack/models/base_lists.html +374 -0
- simstack/static/docs/_modules/simstack/models/base_types.html +588 -0
- simstack/static/docs/_modules/simstack/models/charts_artifact.html +1035 -0
- simstack/static/docs/_modules/simstack/models/dataset.html +866 -0
- simstack/static/docs/_modules/simstack/models/dataset_metadata.html +711 -0
- simstack/static/docs/_modules/simstack/models/datasettuple.html +1005 -0
- simstack/static/docs/_modules/simstack/models/file_instance.html +472 -0
- simstack/static/docs/_modules/simstack/models/file_list.html +367 -0
- simstack/static/docs/_modules/simstack/models/files.html +773 -0
- simstack/static/docs/_modules/simstack/models/fire_and_forget_result.html +335 -0
- simstack/static/docs/_modules/simstack/models/images2d.html +447 -0
- simstack/static/docs/_modules/simstack/models/log_entry_model.html +356 -0
- simstack/static/docs/_modules/simstack/models/models.html +374 -0
- simstack/static/docs/_modules/simstack/models/named_data_reference.html +355 -0
- simstack/static/docs/_modules/simstack/models/node_registry.html +415 -0
- simstack/static/docs/_modules/simstack/models/pandas_model.html +570 -0
- simstack/static/docs/_modules/simstack/models/parameters.html +739 -0
- simstack/static/docs/_modules/simstack/models/pickle_models.html +631 -0
- simstack/static/docs/_modules/simstack/models/project.html +329 -0
- simstack/static/docs/_modules/simstack/models/resource_assignment.html +501 -0
- simstack/static/docs/_modules/simstack/models/resource_definition.html +477 -0
- simstack/static/docs/_modules/simstack/models/runner_model.html +361 -0
- simstack/static/docs/_modules/simstack/models/simple_table.html +413 -0
- simstack/static/docs/_modules/simstack/models/simstack_model.html +416 -0
- simstack/static/docs/_modules/simstack/models/slurm_info.html +333 -0
- simstack/static/docs/_modules/simstack/models/table_artifact.html +406 -0
- simstack/static/docs/_modules/simstack/models/tag.html +328 -0
- simstack/static/docs/_modules/simstack/tables/model_table.html +519 -0
- simstack/static/docs/_modules/simstack/tables/node_children.html +538 -0
- simstack/static/docs/_modules/simstack/tables/node_table.html +854 -0
- simstack/static/docs/_modules/simstack/tables/table_builder.html +642 -0
- simstack/static/docs/_modules/simstack/util/async_zip_utils.html +337 -0
- simstack/static/docs/_modules/simstack/util/b64mixin.html +339 -0
- simstack/static/docs/_modules/simstack/util/cleaned_json_schema.html +336 -0
- simstack/static/docs/_modules/simstack/util/config_reader.html +498 -0
- simstack/static/docs/_modules/simstack/util/custom_model_dump.html +391 -0
- simstack/static/docs/_modules/simstack/util/database_information.html +488 -0
- simstack/static/docs/_modules/simstack/util/db.html +1392 -0
- simstack/static/docs/_modules/simstack/util/db_logger.html +448 -0
- simstack/static/docs/_modules/simstack/util/default_from_dict.html +376 -0
- simstack/static/docs/_modules/simstack/util/directory_iterator.html +553 -0
- simstack/static/docs/_modules/simstack/util/docstring_parser.html +526 -0
- simstack/static/docs/_modules/simstack/util/file_hashing.html +394 -0
- simstack/static/docs/_modules/simstack/util/file_transfer_client.html +791 -0
- simstack/static/docs/_modules/simstack/util/files.html +733 -0
- simstack/static/docs/_modules/simstack/util/find_python_files.html +349 -0
- simstack/static/docs/_modules/simstack/util/generate_ui_schema.html +396 -0
- simstack/static/docs/_modules/simstack/util/generic_list_mixin.html +487 -0
- simstack/static/docs/_modules/simstack/util/get_module_path.html +336 -0
- simstack/static/docs/_modules/simstack/util/git_pull.html +381 -0
- simstack/static/docs/_modules/simstack/util/git_repository_status.html +411 -0
- simstack/static/docs/_modules/simstack/util/import_module.html +377 -0
- simstack/static/docs/_modules/simstack/util/importer.html +637 -0
- simstack/static/docs/_modules/simstack/util/init_data_source.html +357 -0
- simstack/static/docs/_modules/simstack/util/make_graph.html +423 -0
- simstack/static/docs/_modules/simstack/util/make_table.html +1171 -0
- simstack/static/docs/_modules/simstack/util/mappings.html +378 -0
- simstack/static/docs/_modules/simstack/util/minimal_route_finder.html +388 -0
- simstack/static/docs/_modules/simstack/util/mock_db.html +472 -0
- simstack/static/docs/_modules/simstack/util/module_path_checker.html +354 -0
- simstack/static/docs/_modules/simstack/util/object_list_mixin.html +649 -0
- simstack/static/docs/_modules/simstack/util/path_manager.html +566 -0
- simstack/static/docs/_modules/simstack/util/project_root_finder.html +357 -0
- simstack/static/docs/_modules/simstack/util/queue_watcher.html +593 -0
- simstack/static/docs/_modules/simstack/util/resource_config.html +532 -0
- simstack/static/docs/_modules/simstack/util/route_finder.html +390 -0
- simstack/static/docs/_modules/simstack/util/runner_util.html +487 -0
- simstack/static/docs/_modules/simstack/util/runner_utils.html +468 -0
- simstack/static/docs/_modules/simstack/util/safe_code_executor.html +510 -0
- simstack/static/docs/_modules/simstack/util/sample_config.html +414 -0
- simstack/static/docs/_modules/simstack/util/sensitive_content_filter.html +364 -0
- simstack/static/docs/_modules/simstack/util/setup_logging.html +365 -0
- simstack/static/docs/_modules/simstack/util/submit_to_watchdog.html +425 -0
- simstack/static/docs/_modules/simstack/util/toml_reader.html +482 -0
- simstack/static/docs/_modules/simstack/util/transform_file_name.html +357 -0
- simstack/static/docs/_modules/simstack/util/ui_tools.html +541 -0
- simstack/static/docs/_sources/architecture.rst.txt +25 -0
- simstack/static/docs/_sources/array_lists.rst.txt +60 -0
- simstack/static/docs/_sources/ci_pipeline.rst.txt +34 -0
- simstack/static/docs/_sources/data.rst.txt +175 -0
- simstack/static/docs/_sources/database_migrations.rst.txt +310 -0
- simstack/static/docs/_sources/datasets.rst.txt +93 -0
- simstack/static/docs/_sources/developer_guide.rst.txt +25 -0
- simstack/static/docs/_sources/docker_nodes.rst.txt +149 -0
- simstack/static/docs/_sources/files.rst.txt +159 -0
- simstack/static/docs/_sources/fire_and_forget_runner.rst.txt +67 -0
- simstack/static/docs/_sources/index.rst.txt +25 -0
- simstack/static/docs/_sources/installation.rst.txt +213 -0
- simstack/static/docs/_sources/lists.rst.txt +111 -0
- simstack/static/docs/_sources/mass_runner.rst.txt +91 -0
- simstack/static/docs/_sources/misc.rst.txt +106 -0
- simstack/static/docs/_sources/overview.rst.txt +91 -0
- simstack/static/docs/_sources/recovery.rst.txt +38 -0
- simstack/static/docs/_sources/resource_config.rst.txt +141 -0
- simstack/static/docs/_sources/simstack/modules/modules.rst.txt +7 -0
- simstack/static/docs/_sources/simstack/modules/simstack.core.rst.txt +205 -0
- simstack/static/docs/_sources/simstack/modules/simstack.core.services.rst.txt +109 -0
- simstack/static/docs/_sources/simstack/modules/simstack.methods.rst.txt +77 -0
- simstack/static/docs/_sources/simstack/modules/simstack.models.rst.txt +261 -0
- simstack/static/docs/_sources/simstack/modules/simstack.rst.txt +22 -0
- simstack/static/docs/_sources/simstack/modules/simstack.tables.rst.txt +45 -0
- simstack/static/docs/_sources/simstack/modules/simstack.util.rst.txt +381 -0
- simstack/static/docs/_sources/submit.rst.txt +141 -0
- simstack/static/docs/_sources/tables.rst.txt +50 -0
- simstack/static/docs/_sources/testing_complex_nodes.rst.txt +92 -0
- simstack/static/docs/_sources/ui_documentation.rst.txt +272 -0
- simstack/static/docs/_sources/workflows.rst.txt +446 -0
- simstack/static/docs/_static/base-stemmer.js +476 -0
- simstack/static/docs/_static/basic.css +906 -0
- simstack/static/docs/_static/cobalt2-theme.css +1 -0
- simstack/static/docs/_static/debug.css +69 -0
- simstack/static/docs/_static/docs-build-badge.css +51 -0
- simstack/static/docs/_static/docs-build-badge.js +51 -0
- simstack/static/docs/_static/doctools.js +150 -0
- simstack/static/docs/_static/documentation_options.js +13 -0
- simstack/static/docs/_static/english-stemmer.js +1066 -0
- simstack/static/docs/_static/file.png +0 -0
- simstack/static/docs/_static/language_data.js +13 -0
- simstack/static/docs/_static/minus.png +0 -0
- simstack/static/docs/_static/plus.png +0 -0
- simstack/static/docs/_static/pygments.css +250 -0
- simstack/static/docs/_static/scripts/furo-extensions.js +0 -0
- simstack/static/docs/_static/scripts/furo.js +3 -0
- simstack/static/docs/_static/scripts/furo.js.LICENSE.txt +7 -0
- simstack/static/docs/_static/scripts/furo.js.map +1 -0
- simstack/static/docs/_static/searchtools.js +693 -0
- simstack/static/docs/_static/skeleton.css +296 -0
- simstack/static/docs/_static/sphinx_highlight.js +159 -0
- simstack/static/docs/_static/styles/furo-extensions.css +2 -0
- simstack/static/docs/_static/styles/furo-extensions.css.map +1 -0
- simstack/static/docs/_static/styles/furo.css +2 -0
- simstack/static/docs/_static/styles/furo.css.map +1 -0
- simstack/static/docs/architecture.html +340 -0
- simstack/static/docs/array_lists.html +678 -0
- simstack/static/docs/ci_pipeline.html +385 -0
- simstack/static/docs/data.html +545 -0
- simstack/static/docs/database_migrations.html +652 -0
- simstack/static/docs/datasets.html +1268 -0
- simstack/static/docs/developer_guide.html +359 -0
- simstack/static/docs/docker_nodes.html +503 -0
- simstack/static/docs/files.html +718 -0
- simstack/static/docs/fire_and_forget_runner.html +641 -0
- simstack/static/docs/genindex.html +6390 -0
- simstack/static/docs/index.html +516 -0
- simstack/static/docs/installation.html +553 -0
- simstack/static/docs/lists.html +478 -0
- simstack/static/docs/mass_runner.html +481 -0
- simstack/static/docs/misc.html +454 -0
- simstack/static/docs/objects.inv +0 -0
- simstack/static/docs/overview.html +469 -0
- simstack/static/docs/py-modindex.html +1111 -0
- simstack/static/docs/recovery.html +371 -0
- simstack/static/docs/resource_config.html +590 -0
- simstack/static/docs/search.html +327 -0
- simstack/static/docs/searchindex.js +1 -0
- simstack/static/docs/simstack/modules/modules.html +489 -0
- simstack/static/docs/simstack/modules/simstack.core.html +1883 -0
- simstack/static/docs/simstack/modules/simstack.core.services.html +698 -0
- simstack/static/docs/simstack/modules/simstack.html +3060 -0
- simstack/static/docs/simstack/modules/simstack.methods.html +1150 -0
- simstack/static/docs/simstack/modules/simstack.models.html +13113 -0
- simstack/static/docs/simstack/modules/simstack.tables.html +635 -0
- simstack/static/docs/simstack/modules/simstack.util.html +3320 -0
- simstack/static/docs/submit.html +487 -0
- simstack/static/docs/tables.html +539 -0
- simstack/static/docs/testing_complex_nodes.html +458 -0
- simstack/static/docs/ui_documentation.html +788 -0
- simstack/static/docs/workflows.html +766 -0
- simstack/tables/__init__.py +0 -0
- simstack/tables/model_table.py +191 -0
- simstack/tables/node_children.py +219 -0
- simstack/tables/node_table.py +517 -0
- simstack/tables/table_builder.py +314 -0
- simstack/util/__init__.py +0 -0
- simstack/util/async_zip_utils.py +18 -0
- simstack/util/b64mixin.py +23 -0
- simstack/util/cleaned_json_schema.py +20 -0
- simstack/util/config_reader.py +179 -0
- simstack/util/custom_model_dump.py +75 -0
- simstack/util/database_information.py +160 -0
- simstack/util/db.py +1013 -0
- simstack/util/db_logger.py +117 -0
- simstack/util/default_from_dict.py +57 -0
- simstack/util/directory_iterator.py +216 -0
- simstack/util/docstring_parser.py +186 -0
- simstack/util/file_hashing.py +72 -0
- simstack/util/file_transfer_client.py +427 -0
- simstack/util/files.py +396 -0
- simstack/util/find_python_files.py +33 -0
- simstack/util/generate_ui_schema.py +80 -0
- simstack/util/generic_list_mixin.py +120 -0
- simstack/util/geometry_utils.py +118 -0
- simstack/util/get_module_path.py +20 -0
- simstack/util/git_pull.py +65 -0
- simstack/util/git_repository_status.py +95 -0
- simstack/util/import_module.py +63 -0
- simstack/util/importer.py +312 -0
- simstack/util/init_data_source.py +41 -0
- simstack/util/make_graph.py +107 -0
- simstack/util/make_table.py +837 -0
- simstack/util/mappings.py +41 -0
- simstack/util/minimal_route_finder.py +72 -0
- simstack/util/mock_db.py +144 -0
- simstack/util/module_path_checker.py +38 -0
- simstack/util/object_list_mixin.py +282 -0
- simstack/util/path_manager.py +223 -0
- simstack/util/project_root_finder.py +41 -0
- simstack/util/queue_watcher.py +262 -0
- simstack/util/resource_config.py +198 -0
- simstack/util/route_finder.py +74 -0
- simstack/util/runner_util.py +166 -0
- simstack/util/runner_utils.py +143 -0
- simstack/util/safe_code_executor.py +194 -0
- simstack/util/sample_config.py +98 -0
- simstack/util/sensitive_content_filter.py +48 -0
- simstack/util/setup_logging.py +49 -0
- simstack/util/submit_to_watchdog.py +106 -0
- simstack/util/toml_reader.py +142 -0
- simstack/util/transform_file_name.py +41 -0
- simstack/util/ui_tools.py +213 -0
- simstack-2.0.1.dist-info/METADATA +454 -0
- simstack-2.0.1.dist-info/RECORD +354 -0
- simstack-2.0.1.dist-info/WHEEL +5 -0
- simstack-2.0.1.dist-info/entry_points.txt +12 -0
- simstack-2.0.1.dist-info/licenses/LICENSE +21 -0
- simstack-2.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import asyncio
|
|
3
|
+
import importlib
|
|
4
|
+
import logging
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Iterable, Optional, Type
|
|
8
|
+
import fnmatch # <-- added
|
|
9
|
+
|
|
10
|
+
from simstack.core.context import context
|
|
11
|
+
from simstack.core.find_simstack_modules import find_simstack_modules
|
|
12
|
+
from simstack.util.db import Database
|
|
13
|
+
from simstack.util.import_module import import_module_from_file
|
|
14
|
+
from simstack.util.path_manager import path_manager
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TableBuilderBase(ABC):
|
|
19
|
+
"""
|
|
20
|
+
Shared pipeline for building "tables" by scanning:
|
|
21
|
+
1) installed simstack modules (packages)
|
|
22
|
+
2) configured project paths (python files)
|
|
23
|
+
|
|
24
|
+
Subclasses only implement `_process_module(module, drops)`.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, db: Database, write_schema: bool = False, project_root: Optional[Path] = None):
|
|
28
|
+
self.db = db
|
|
29
|
+
self.write_schema = write_schema
|
|
30
|
+
self._project_root = project_root
|
|
31
|
+
self._processed_modules = set()
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def project_root(self) -> Path:
|
|
35
|
+
if self._project_root:
|
|
36
|
+
return self._project_root
|
|
37
|
+
if context.config:
|
|
38
|
+
return context.config.project_root
|
|
39
|
+
from simstack.util.project_root_finder import find_project_root
|
|
40
|
+
return find_project_root()
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def logger(self) -> logging.Logger:
|
|
45
|
+
raise NotImplementedError
|
|
46
|
+
|
|
47
|
+
async def build(
|
|
48
|
+
self,
|
|
49
|
+
*,
|
|
50
|
+
dirs: Optional[list[Path]] = None,
|
|
51
|
+
drops: str = "",
|
|
52
|
+
exclude: Optional[list[str]] = None,
|
|
53
|
+
clear: bool = False,
|
|
54
|
+
ignore_entrypoints: bool = False,
|
|
55
|
+
) -> None:
|
|
56
|
+
"""
|
|
57
|
+
Build the table.
|
|
58
|
+
|
|
59
|
+
- If `dirs` is None: use configured `path_manager` paths (existing behavior).
|
|
60
|
+
- If `dirs` is a list: scan those directories for Python files and process them.
|
|
61
|
+
|
|
62
|
+
`exclude` entries can match:
|
|
63
|
+
- path parts (e.g. ".venv", "__pycache__")
|
|
64
|
+
- glob patterns (e.g. "*.generated.py")
|
|
65
|
+
- nested relative paths (e.g. "src/simstack/models")
|
|
66
|
+
"""
|
|
67
|
+
await self._ensure_context_initialized()
|
|
68
|
+
self._processed_modules.clear()
|
|
69
|
+
if clear:
|
|
70
|
+
await self.clear_table()
|
|
71
|
+
|
|
72
|
+
if not ignore_entrypoints:
|
|
73
|
+
await self._process_simstack_modules(drops=drops)
|
|
74
|
+
|
|
75
|
+
await self._process_dirs(dirs, drops=drops, exclude=exclude or [])
|
|
76
|
+
|
|
77
|
+
async def _ensure_context_initialized(self) -> None:
|
|
78
|
+
if not context.initialized:
|
|
79
|
+
await context.initialize()
|
|
80
|
+
|
|
81
|
+
async def _process_simstack_modules(self, drops: str) -> None:
|
|
82
|
+
all_modules = set(find_simstack_modules())
|
|
83
|
+
for module_name in all_modules:
|
|
84
|
+
if module_name in self._processed_modules:
|
|
85
|
+
continue
|
|
86
|
+
self.logger.debug("Processing module: %s", module_name)
|
|
87
|
+
module = self._import_package_module(module_name)
|
|
88
|
+
if module is None:
|
|
89
|
+
continue
|
|
90
|
+
await self._process_module(module, drops=drops)
|
|
91
|
+
self._processed_modules.add(module_name)
|
|
92
|
+
|
|
93
|
+
def _import_package_module(self, module_name: str):
|
|
94
|
+
try:
|
|
95
|
+
return importlib.import_module(module_name)
|
|
96
|
+
except Exception as exc:
|
|
97
|
+
self.logger.warning("Failed to import module %s: %s", module_name, exc)
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
async def _process_configured_paths(self) -> None:
|
|
101
|
+
for path_name in path_manager.paths.keys():
|
|
102
|
+
await self._process_path(path_name)
|
|
103
|
+
|
|
104
|
+
async def _process_path(self, path_name: str) -> None:
|
|
105
|
+
path_info = path_manager.get_path(path_name)
|
|
106
|
+
base_path = path_info["path"]
|
|
107
|
+
drops = path_info.get("drops", "")
|
|
108
|
+
|
|
109
|
+
self.logger.info("Processing configured path %s: %s", path_name, base_path)
|
|
110
|
+
|
|
111
|
+
for file_path in path_manager.find_python_files(path_name):
|
|
112
|
+
await self._process_file(file_path, drops)
|
|
113
|
+
|
|
114
|
+
async def _process_dirs(self, dirs: list[Path], *, drops: str, exclude: list[str]) -> None:
|
|
115
|
+
for base_dir in dirs:
|
|
116
|
+
self.logger.info("Processing CLI dir: %s", base_dir)
|
|
117
|
+
|
|
118
|
+
base_dir = Path(base_dir)
|
|
119
|
+
|
|
120
|
+
# Accept either absolute paths or paths relative to project root.
|
|
121
|
+
base_dir_path = base_dir if base_dir.is_absolute() else (self.project_root / base_dir)
|
|
122
|
+
|
|
123
|
+
for py_file in self._iter_python_files_under_dir(base_dir_path, exclude=exclude):
|
|
124
|
+
await self._process_file(py_file, drops)
|
|
125
|
+
|
|
126
|
+
def _iter_python_files_under_dir(self, base_dir: Path, *, exclude: list[str]) -> Iterable[Path]:
|
|
127
|
+
default_exclude_parts = {
|
|
128
|
+
".git",
|
|
129
|
+
".hg",
|
|
130
|
+
".svn",
|
|
131
|
+
".venv",
|
|
132
|
+
"venv",
|
|
133
|
+
"__init__.py"
|
|
134
|
+
"__pycache__",
|
|
135
|
+
".mypy_cache",
|
|
136
|
+
".pytest_cache",
|
|
137
|
+
".ipynb_checkpoints"
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
def _should_exclude(p: Path) -> bool:
|
|
141
|
+
# Fast path: ignore common tooling/cache dirs anywhere in the path.
|
|
142
|
+
if any(part in default_exclude_parts for part in p.parts):
|
|
143
|
+
return True
|
|
144
|
+
|
|
145
|
+
# If the caller didn't pass any excludes, we're done.
|
|
146
|
+
if not exclude:
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
# Match excludes against path parts and relative path (for nested patterns).
|
|
150
|
+
try:
|
|
151
|
+
rel = p.relative_to(base_dir)
|
|
152
|
+
except ValueError:
|
|
153
|
+
rel = p
|
|
154
|
+
|
|
155
|
+
rel_posix = rel.as_posix()
|
|
156
|
+
|
|
157
|
+
for ex in exclude:
|
|
158
|
+
ex = (ex or "").strip()
|
|
159
|
+
if not ex:
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
# 1) Exact match against any path segment (works for "build", ".tox", etc.)
|
|
163
|
+
if ex in p.parts or ex in rel.parts:
|
|
164
|
+
return True
|
|
165
|
+
|
|
166
|
+
# 2) Glob match against the relative posix path (supports nested paths + file globs)
|
|
167
|
+
# Examples:
|
|
168
|
+
# --exclude "src/simstack/models"
|
|
169
|
+
# --exclude "**/generated/**"
|
|
170
|
+
# --exclude "*.generated.py"
|
|
171
|
+
if fnmatch.fnmatch(rel_posix, ex) or fnmatch.fnmatch(rel_posix, ex.rstrip("/") + "/*"):
|
|
172
|
+
return True
|
|
173
|
+
|
|
174
|
+
# 3) Also allow Windows-ish inputs like "a\\b\\c" by normalizing to posix.
|
|
175
|
+
ex_posix = ex.replace("\\", "/")
|
|
176
|
+
if ex_posix != ex and (
|
|
177
|
+
fnmatch.fnmatch(rel_posix, ex_posix) or fnmatch.fnmatch(rel_posix, ex_posix.rstrip("/") + "/*")
|
|
178
|
+
):
|
|
179
|
+
return True
|
|
180
|
+
|
|
181
|
+
return False
|
|
182
|
+
|
|
183
|
+
if base_dir.is_file() and base_dir.suffix == ".py":
|
|
184
|
+
if not _should_exclude(base_dir):
|
|
185
|
+
yield base_dir
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
if not base_dir.exists():
|
|
189
|
+
self.logger.warning("Skipping non-existent path: %s", base_dir)
|
|
190
|
+
return
|
|
191
|
+
|
|
192
|
+
if not base_dir.is_dir():
|
|
193
|
+
self.logger.warning("Skipping non-directory path: %s", base_dir)
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
for p in base_dir.rglob("*.py"):
|
|
197
|
+
if _should_exclude(p):
|
|
198
|
+
continue
|
|
199
|
+
if p.is_file():
|
|
200
|
+
yield p
|
|
201
|
+
|
|
202
|
+
async def _process_file(self, file_path: Path, drops: str) -> None:
|
|
203
|
+
self.logger.debug("Processing file: %s", file_path)
|
|
204
|
+
module = import_module_from_file(file_path, self.project_root)
|
|
205
|
+
if not module:
|
|
206
|
+
self.logger.debug("Skipping %s because module import returned None", file_path)
|
|
207
|
+
return
|
|
208
|
+
if module.__name__ in self._processed_modules:
|
|
209
|
+
self.logger.debug("Skipping %s because module %s was already processed", file_path, module.__name__)
|
|
210
|
+
return
|
|
211
|
+
await self._process_module(module, drops)
|
|
212
|
+
self._processed_modules.add(module.__name__)
|
|
213
|
+
|
|
214
|
+
@abstractmethod
|
|
215
|
+
async def _process_module(self, module, drops: str) -> None:
|
|
216
|
+
"""Subclass hook: scan/register whatever you need from `module`."""
|
|
217
|
+
raise NotImplementedError
|
|
218
|
+
|
|
219
|
+
async def second_stage(self, drops: str) -> None:
|
|
220
|
+
""" an optional hook to run after all modules have been processed"""
|
|
221
|
+
pass
|
|
222
|
+
|
|
223
|
+
async def clear_table(self) -> None:
|
|
224
|
+
""" an optional hook to clear the table before building """
|
|
225
|
+
pass
|
|
226
|
+
|
|
227
|
+
@classmethod
|
|
228
|
+
def cli_main(cls, builder_cls: Type["TableBuilderBase"], write_schema: bool = False) -> None:
|
|
229
|
+
"""
|
|
230
|
+
Reusable CLI entry point.
|
|
231
|
+
|
|
232
|
+
Options:
|
|
233
|
+
--dir RELPATH (repeatable) Directories (relative to CWD) to scan for *.py files.
|
|
234
|
+
If omitted, defaults to scanning the current working directory.
|
|
235
|
+
--drops STRING Drops prefix (string) applied while processing these CLI dirs.
|
|
236
|
+
-v/--verbose Increase logging verbosity.
|
|
237
|
+
"""
|
|
238
|
+
parser = argparse.ArgumentParser()
|
|
239
|
+
parser.add_argument("-v", "--verbose", action="count", default=0)
|
|
240
|
+
parser.add_argument(
|
|
241
|
+
"--dir",
|
|
242
|
+
dest="dirs",
|
|
243
|
+
action="append",
|
|
244
|
+
default=[],
|
|
245
|
+
help="Path to scan (repeatable). If omitted, CWD is used.",
|
|
246
|
+
)
|
|
247
|
+
parser.add_argument(
|
|
248
|
+
"--exclude",
|
|
249
|
+
dest="exclude",
|
|
250
|
+
action="append",
|
|
251
|
+
default=[],
|
|
252
|
+
help=(
|
|
253
|
+
"Exclude a directory/name/glob from scanning (repeatable). "
|
|
254
|
+
|
|
255
|
+
"Examples: --exclude .venv --exclude __pycache__ --exclude src/simstack/models --exclude '*.generated.py'"
|
|
256
|
+
),
|
|
257
|
+
)
|
|
258
|
+
parser.add_argument(
|
|
259
|
+
"--drops",
|
|
260
|
+
dest="drops",
|
|
261
|
+
default="",
|
|
262
|
+
help="Drops prefix used when processing CLI dirs (string).",
|
|
263
|
+
)
|
|
264
|
+
parser.add_argument(
|
|
265
|
+
"--write-schema",
|
|
266
|
+
dest="write_schema",
|
|
267
|
+
action="store_true",
|
|
268
|
+
help="Enable schema writing.",
|
|
269
|
+
)
|
|
270
|
+
parser.add_argument(
|
|
271
|
+
"--ignore-entrypoints",
|
|
272
|
+
dest="ignore_entrypoints",
|
|
273
|
+
action="store_true",
|
|
274
|
+
help="Do not scan for modules registered via entrypoints.",
|
|
275
|
+
)
|
|
276
|
+
parser.add_argument(
|
|
277
|
+
"--clear",
|
|
278
|
+
dest="clear",
|
|
279
|
+
action="store_true",
|
|
280
|
+
help="Clear the table before building.",
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
args = parser.parse_args()
|
|
284
|
+
|
|
285
|
+
level = logging.WARNING
|
|
286
|
+
if args.verbose == 1:
|
|
287
|
+
level = logging.INFO
|
|
288
|
+
elif args.verbose >= 2:
|
|
289
|
+
level = logging.DEBUG
|
|
290
|
+
|
|
291
|
+
# Resolve dirs relative to the current working directory
|
|
292
|
+
if args.dirs:
|
|
293
|
+
dirs = [Path.cwd() / d for d in args.dirs]
|
|
294
|
+
else:
|
|
295
|
+
dirs = [Path.cwd()]
|
|
296
|
+
|
|
297
|
+
loop = asyncio.new_event_loop()
|
|
298
|
+
asyncio.set_event_loop(loop)
|
|
299
|
+
|
|
300
|
+
logging.getLogger("pymongo").setLevel(logging.INFO)
|
|
301
|
+
|
|
302
|
+
async def _run() -> None:
|
|
303
|
+
await context.initialize(log_level=level, resource="self")
|
|
304
|
+
builder = builder_cls(context.db, write_schema=args.write_schema)
|
|
305
|
+
await builder.build(
|
|
306
|
+
dirs=dirs,
|
|
307
|
+
drops=args.drops,
|
|
308
|
+
exclude=args.exclude,
|
|
309
|
+
clear=args.clear,
|
|
310
|
+
ignore_entrypoints=args.ignore_entrypoints,
|
|
311
|
+
)
|
|
312
|
+
await builder.second_stage(args.drops)
|
|
313
|
+
loop.run_until_complete(_run())
|
|
314
|
+
loop.close()
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
async def async_zip(async_gen1, async_gen2):
|
|
2
|
+
"""Async version of zip for two async generators"""
|
|
3
|
+
async for item1, item2 in async_zip_multiple(async_gen1, async_gen2):
|
|
4
|
+
yield item1, item2
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
async def async_zip_multiple(*async_generators):
|
|
8
|
+
"""Async version of zip for multiple async generators"""
|
|
9
|
+
iterators = [aiter(ag) for ag in async_generators]
|
|
10
|
+
|
|
11
|
+
while True:
|
|
12
|
+
try:
|
|
13
|
+
values = []
|
|
14
|
+
for it in iterators:
|
|
15
|
+
values.append(await anext(it))
|
|
16
|
+
yield tuple(values)
|
|
17
|
+
except StopAsyncIteration:
|
|
18
|
+
break
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import zlib
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BytesB64Mixin:
|
|
6
|
+
"""
|
|
7
|
+
Mixin that teaches Pydantic/ODMantic to serialise *bytes* fields as
|
|
8
|
+
base-64-encoded ASCII strings when exporting to JSON (dict / response).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
model_config = {
|
|
12
|
+
"json_encoders": {bytes: lambda b: base64.b64encode(b).decode("ascii")}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def _compress_bytes(self, data: bytes) -> str:
|
|
16
|
+
"""Compress bytes and encode to base64 string"""
|
|
17
|
+
compressed = zlib.compress(data)
|
|
18
|
+
return base64.b64encode(compressed).decode('utf-8')
|
|
19
|
+
|
|
20
|
+
def _decompress_bytes(self, data: str) -> bytes:
|
|
21
|
+
"""Decode base64 string and decompress to bytes"""
|
|
22
|
+
compressed = base64.b64decode(data.encode('utf-8'))
|
|
23
|
+
return zlib.decompress(compressed)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
def cleaned_json_schema(cls):
|
|
2
|
+
"""
|
|
3
|
+
|
|
4
|
+
Generates a JSON schema for the given class and its fields, but eliminates
|
|
5
|
+
all fields which are models, embedded models, or references to models.
|
|
6
|
+
|
|
7
|
+
:param cls:
|
|
8
|
+
:return:
|
|
9
|
+
"""
|
|
10
|
+
schema = cls.model_json_schema()
|
|
11
|
+
schema["title"] = cls.__name__
|
|
12
|
+
|
|
13
|
+
# for field_name, field in cls.model_fields.items():
|
|
14
|
+
# field_type = field.annotation
|
|
15
|
+
# if hasattr(field_type, '_is_simstack_model'):
|
|
16
|
+
# # If the field is a model or embedded model, remove its proprties
|
|
17
|
+
# if field_name in schema['$defs'] and "properties" in schema['$defs']['properties']:
|
|
18
|
+
# del schema['$defs']['properties']
|
|
19
|
+
|
|
20
|
+
return schema
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import List, TYPE_CHECKING, Union
|
|
3
|
+
from simstack.core.resources import allowed_resources
|
|
4
|
+
from simstack.models.parameters import Resource
|
|
5
|
+
from simstack.models.resource_definition import ResourceDefinition, GitRepo
|
|
6
|
+
from simstack.util.transform_file_name import transform_file_name
|
|
7
|
+
from simstack.util.init_data_source import initialize_resource_from_db
|
|
8
|
+
|
|
9
|
+
from simstack.util.toml_reader import TomlReader
|
|
10
|
+
from simstack.util.database_information import DatabaseInformation
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from simstack.util.db import Database
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ConfigReader(DatabaseInformation):
|
|
17
|
+
"""
|
|
18
|
+
Represents a configuration reader that integrates with a database and a resource
|
|
19
|
+
definition system.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
db_info: Union[DatabaseInformation, "Database"],
|
|
25
|
+
resource_definition: ResourceDefinition,
|
|
26
|
+
*,
|
|
27
|
+
project_root: Path,
|
|
28
|
+
git_list: list[GitRepo] | None = None,
|
|
29
|
+
):
|
|
30
|
+
db_temp_info = DatabaseInformation.from_db_info_or_db(db_info)
|
|
31
|
+
super().__init__(*db_temp_info.get_information())
|
|
32
|
+
|
|
33
|
+
self._project_root = project_root
|
|
34
|
+
self._resource_definition = resource_definition
|
|
35
|
+
self._git_list = list(git_list or [])
|
|
36
|
+
self._resource_str = resource_definition.resource_str
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
async def create(
|
|
40
|
+
cls,
|
|
41
|
+
resource_str,
|
|
42
|
+
db: "Database",
|
|
43
|
+
toml_reader: TomlReader,
|
|
44
|
+
project_root: Path,
|
|
45
|
+
**kwargs,
|
|
46
|
+
):
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
the idea here is to attempt to get:
|
|
50
|
+
|
|
51
|
+
1) all required data from the kwargs, if available
|
|
52
|
+
2) if the data is not all available, get it from the database
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
import logging
|
|
56
|
+
|
|
57
|
+
logger = logging.getLogger("config-reader") # do this here because the calling function sets the logger up
|
|
58
|
+
|
|
59
|
+
required_keys = [
|
|
60
|
+
"python_paths",
|
|
61
|
+
"workdir",
|
|
62
|
+
"environment_start",
|
|
63
|
+
]
|
|
64
|
+
init_done = True
|
|
65
|
+
workdir_self = None
|
|
66
|
+
config = {"project_root": project_root}
|
|
67
|
+
for key in required_keys:
|
|
68
|
+
if key in kwargs:
|
|
69
|
+
config[key] = kwargs.get(key)
|
|
70
|
+
if key == "workdir":
|
|
71
|
+
workdir_self = kwargs.get("workdir")
|
|
72
|
+
logger.info(f"Init from kwargs: {key}: {kwargs.get(key)}")
|
|
73
|
+
else:
|
|
74
|
+
init_done = False
|
|
75
|
+
for key in ["server_url", "server_token"]: # these are optional
|
|
76
|
+
if key in kwargs:
|
|
77
|
+
config[key] = kwargs.get(key)
|
|
78
|
+
logger.info(f"Init from kwargs: {key}: {kwargs.get(key)}")
|
|
79
|
+
|
|
80
|
+
git_list = []
|
|
81
|
+
if not init_done:
|
|
82
|
+
if toml_reader is None:
|
|
83
|
+
raise ValueError("ConfigReader: No toml_reader provided.")
|
|
84
|
+
workdir_self = kwargs.get("workdir", None)
|
|
85
|
+
if workdir_self is None:
|
|
86
|
+
workdir_self = toml_reader.get("parameters.general.workdir_self", None)
|
|
87
|
+
if workdir_self is None:
|
|
88
|
+
workdir_self = toml_reader.get("resources.self.workdir", None)
|
|
89
|
+
if workdir_self is None:
|
|
90
|
+
# raise ValueError("No workdir for self specified in config file or keyword arguments.")
|
|
91
|
+
workdir_self = project_root / "simstack" # Default to project_root / simstack if not found
|
|
92
|
+
else:
|
|
93
|
+
workdir_self = Path(workdir_self)
|
|
94
|
+
|
|
95
|
+
resource_definition = await initialize_resource_from_db(resource_str, db, workdir_self)
|
|
96
|
+
# else:
|
|
97
|
+
# raise RuntimeError("initialization from toml file is deprecated")
|
|
98
|
+
# allowed_resources_list = toml_reader.get_allowed_resources()
|
|
99
|
+
# allowed_resources.set_resources(allowed_resources_list)
|
|
100
|
+
# resource_definition = toml_reader.get_resource_definition(resource_str)
|
|
101
|
+
# git_list = toml_reader.get_git_list()
|
|
102
|
+
# toml_reader.build_routes()
|
|
103
|
+
|
|
104
|
+
if resource_definition is None:
|
|
105
|
+
raise ValueError("No valid resource definition found.")
|
|
106
|
+
|
|
107
|
+
# override the values in resource definition with those from the keyword arguments
|
|
108
|
+
for key in resource_definition.__class__.model_fields.keys():
|
|
109
|
+
if key in config:
|
|
110
|
+
logger.info(f"Overriding {key} from kwargs to: {config[key]}")
|
|
111
|
+
resource_definition.__setattr__(key, config[key])
|
|
112
|
+
del config[key]
|
|
113
|
+
del required_keys[required_keys.index(key)]
|
|
114
|
+
|
|
115
|
+
project_root = config.pop("project_root")
|
|
116
|
+
git_list_final = config.pop("git_list", git_list)
|
|
117
|
+
|
|
118
|
+
if config:
|
|
119
|
+
logger.warning(
|
|
120
|
+
f"Ignoring unused ConfigReader init keys: {sorted(config.keys())}"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
log_msg = f"Resource: {resource_definition.resource_str}"
|
|
124
|
+
for key, value in resource_definition.__dict__.items():
|
|
125
|
+
if key in ["workdir", "git-branch", "environment_start"]:
|
|
126
|
+
log_msg += f" {key}: {value}"
|
|
127
|
+
logger.info(log_msg)
|
|
128
|
+
|
|
129
|
+
return cls(
|
|
130
|
+
db, resource_definition, project_root=project_root, git_list=git_list_final
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def server_url(self) -> str:
|
|
135
|
+
return self._server_url
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def server_token(self) -> str:
|
|
139
|
+
return self._server_token
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def git_list(self) -> List[GitRepo]:
|
|
143
|
+
return self._git_list
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def resource(self) -> Resource:
|
|
147
|
+
return Resource(value=self._resource_str)
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def workdir(self) -> Path:
|
|
151
|
+
return transform_file_name(self._resource_definition.workdir, self.project_root)
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def environment_start(self) -> str:
|
|
155
|
+
return self._resource_definition.environment_start
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def python_paths(self) -> List[Path]:
|
|
159
|
+
return [transform_file_name(p) for p in self._resource_definition.python_paths]
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def ssh_key(self) -> Path:
|
|
163
|
+
return transform_file_name(self._resource_definition.ssh_key)
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def hostname(self):
|
|
167
|
+
return self._resource_definition.hostname
|
|
168
|
+
|
|
169
|
+
@resource.setter
|
|
170
|
+
def resource(self, value: str):
|
|
171
|
+
raise ValueError("ConfigReader: Resource cannot be set directly")
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def project_root(self) -> Path:
|
|
175
|
+
return self._project_root
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def docker(self) -> bool:
|
|
179
|
+
return False
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def custom_model_dump(self, **kwargs) -> Dict[str, Any]:
|
|
5
|
+
"""
|
|
6
|
+
Custom model dump method to handle the conversion of model instances to dictionaries.
|
|
7
|
+
This method recursively traverses dictionaries and lists to convert any nested
|
|
8
|
+
model instances to their dictionary representation.
|
|
9
|
+
|
|
10
|
+
:param self: The model instance
|
|
11
|
+
:param kwargs: Additional keyword arguments
|
|
12
|
+
:return: A dictionary representation of the model instance
|
|
13
|
+
"""
|
|
14
|
+
# First get the dictionary from the standard model_dump
|
|
15
|
+
|
|
16
|
+
filtered_kwargs = {k: v for k, v in kwargs.items() if k != "engine"}
|
|
17
|
+
data = self.model_dump(**filtered_kwargs)
|
|
18
|
+
|
|
19
|
+
# Process each field - the key is to process the original values from self
|
|
20
|
+
for field_name, field in self.model_fields.items():
|
|
21
|
+
field_value = getattr(self, field_name)
|
|
22
|
+
|
|
23
|
+
# Skip None values
|
|
24
|
+
if field_value is None:
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
data[field_name] = await _recursive_process(field_value, **kwargs)
|
|
28
|
+
|
|
29
|
+
return data
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
async def _recursive_process(obj, **kwargs):
|
|
33
|
+
"""
|
|
34
|
+
Helper function to recursively process an object that might contain models
|
|
35
|
+
"""
|
|
36
|
+
if hasattr(obj, "custom_model_dump"):
|
|
37
|
+
return await obj.custom_model_dump(**kwargs)
|
|
38
|
+
elif isinstance(obj, dict):
|
|
39
|
+
return {k: await _recursive_process(v, **kwargs) for k, v in obj.items()}
|
|
40
|
+
elif isinstance(obj, list):
|
|
41
|
+
return [await _recursive_process(item, **kwargs) for item in obj]
|
|
42
|
+
else:
|
|
43
|
+
return obj
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
async def _process_container(container, **kwargs):
|
|
47
|
+
"""
|
|
48
|
+
Helper function to recursively process dictionaries and lists,
|
|
49
|
+
converting any model instances to their dictionary representation.
|
|
50
|
+
|
|
51
|
+
:param container: The container to process (dict or list)
|
|
52
|
+
:param kwargs: Additional keyword arguments
|
|
53
|
+
:return: Processed container with model instances converted to dictionaries
|
|
54
|
+
"""
|
|
55
|
+
if isinstance(container, dict):
|
|
56
|
+
result = {}
|
|
57
|
+
for k, v in container.items():
|
|
58
|
+
if hasattr(v, "custom_model_dump"):
|
|
59
|
+
result[k] = await v.custom_model_dump(**kwargs)
|
|
60
|
+
elif isinstance(v, (dict, list)):
|
|
61
|
+
result[k] = await _process_container(v, **kwargs)
|
|
62
|
+
else:
|
|
63
|
+
result[k] = v
|
|
64
|
+
return result
|
|
65
|
+
elif isinstance(container, list):
|
|
66
|
+
result = []
|
|
67
|
+
for item in container:
|
|
68
|
+
if hasattr(item, "custom_model_dump"):
|
|
69
|
+
result.append(await item.custom_model_dump(**kwargs))
|
|
70
|
+
elif isinstance(item, (dict, list)):
|
|
71
|
+
result.append(await _process_container(item, **kwargs))
|
|
72
|
+
else:
|
|
73
|
+
result.append(item)
|
|
74
|
+
return result
|
|
75
|
+
return container
|