UncountablePythonSDK 0.0.24__py3-none-any.whl → 0.0.131__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.
Potentially problematic release.
This version of UncountablePythonSDK might be problematic. Click here for more details.
- docs/conf.py +60 -8
- docs/index.md +107 -4
- docs/integration_examples/create_ingredient.md +43 -0
- docs/integration_examples/create_output.md +56 -0
- docs/integration_examples/index.md +6 -0
- docs/justfile +2 -2
- docs/requirements.txt +7 -5
- examples/async_batch.py +5 -6
- examples/basic_auth.py +7 -0
- examples/create_entity.py +4 -6
- examples/create_ingredient_sdk.py +34 -0
- examples/download_files.py +26 -0
- examples/edit_recipe_inputs.py +50 -0
- examples/integration-server/jobs/materials_auto/concurrent_cron.py +11 -0
- examples/integration-server/jobs/materials_auto/example_cron.py +21 -0
- examples/integration-server/jobs/materials_auto/example_http.py +47 -0
- examples/integration-server/jobs/materials_auto/example_instrument.py +100 -0
- examples/integration-server/jobs/materials_auto/example_parse.py +140 -0
- examples/integration-server/jobs/materials_auto/example_predictions.py +61 -0
- examples/integration-server/jobs/materials_auto/example_runsheet_wh.py +39 -0
- examples/integration-server/jobs/materials_auto/example_wh.py +23 -0
- examples/integration-server/jobs/materials_auto/profile.yaml +104 -0
- examples/integration-server/pyproject.toml +224 -0
- examples/invoke_uploader.py +26 -0
- examples/oauth.py +7 -0
- examples/set_recipe_metadata_file.py +40 -0
- examples/set_recipe_output_file_sdk.py +26 -0
- examples/upload_files.py +2 -3
- pkgs/argument_parser/__init__.py +9 -0
- pkgs/argument_parser/_is_namedtuple.py +3 -0
- pkgs/argument_parser/argument_parser.py +295 -74
- pkgs/argument_parser/case_convert.py +4 -3
- pkgs/filesystem_utils/__init__.py +20 -0
- pkgs/filesystem_utils/_blob_session.py +144 -0
- pkgs/filesystem_utils/_gdrive_session.py +309 -0
- pkgs/filesystem_utils/_local_session.py +69 -0
- pkgs/filesystem_utils/_s3_session.py +118 -0
- pkgs/filesystem_utils/_sftp_session.py +151 -0
- pkgs/filesystem_utils/file_type_utils.py +91 -0
- pkgs/filesystem_utils/filesystem_session.py +39 -0
- pkgs/py.typed +0 -0
- pkgs/serialization/__init__.py +8 -1
- pkgs/serialization/annotation.py +64 -0
- pkgs/serialization/missing_sentry.py +1 -1
- pkgs/serialization/opaque_key.py +1 -1
- pkgs/serialization/serial_alias.py +47 -0
- pkgs/serialization/serial_class.py +69 -54
- pkgs/serialization/serial_generic.py +16 -0
- pkgs/serialization/serial_union.py +84 -0
- pkgs/serialization/yaml.py +57 -0
- pkgs/serialization_util/__init__.py +7 -7
- pkgs/serialization_util/convert_to_snakecase.py +27 -0
- pkgs/serialization_util/dataclasses.py +14 -0
- pkgs/serialization_util/serialization_helpers.py +117 -71
- pkgs/type_spec/actions_registry/__main__.py +0 -4
- pkgs/type_spec/actions_registry/emit_typescript.py +5 -5
- pkgs/type_spec/builder.py +438 -109
- pkgs/type_spec/builder_types.py +9 -0
- pkgs/type_spec/config.py +52 -24
- pkgs/type_spec/cross_output_links.py +99 -0
- pkgs/type_spec/emit_io_ts.py +1 -1
- pkgs/type_spec/emit_open_api.py +160 -41
- pkgs/type_spec/emit_open_api_util.py +13 -7
- pkgs/type_spec/emit_python.py +450 -136
- pkgs/type_spec/emit_typescript.py +117 -250
- pkgs/type_spec/emit_typescript_util.py +293 -4
- pkgs/type_spec/load_types.py +20 -5
- pkgs/type_spec/non_discriminated_union_exceptions.py +14 -0
- pkgs/type_spec/open_api_util.py +29 -4
- pkgs/type_spec/parts/base.py.prepart +13 -10
- pkgs/type_spec/parts/base.ts.prepart +4 -0
- pkgs/type_spec/type_info/__main__.py +3 -1
- pkgs/type_spec/type_info/emit_type_info.py +161 -32
- pkgs/type_spec/ui_entry_actions/__init__.py +4 -0
- pkgs/type_spec/ui_entry_actions/generate_ui_entry_actions.py +308 -0
- pkgs/type_spec/util.py +4 -4
- pkgs/type_spec/value_spec/__main__.py +27 -10
- pkgs/type_spec/value_spec/convert_type.py +21 -1
- pkgs/type_spec/value_spec/emit_python.py +25 -7
- pkgs/type_spec/value_spec/types.py +1 -1
- uncountable/__init__.py +1 -2
- uncountable/core/__init__.py +11 -3
- uncountable/core/async_batch.py +16 -1
- uncountable/core/client.py +247 -52
- uncountable/core/environment.py +41 -0
- uncountable/core/file_upload.py +67 -22
- uncountable/core/types.py +8 -13
- uncountable/integration/cli.py +142 -0
- uncountable/integration/construct_client.py +43 -27
- uncountable/integration/cron.py +12 -11
- uncountable/integration/db/connect.py +12 -2
- uncountable/integration/db/session.py +25 -0
- uncountable/integration/entrypoint.py +4 -34
- uncountable/integration/executors/executors.py +147 -0
- uncountable/integration/executors/generic_upload_executor.py +336 -0
- uncountable/integration/executors/script_executor.py +15 -9
- uncountable/integration/http_server/__init__.py +5 -0
- uncountable/integration/http_server/types.py +69 -0
- uncountable/integration/job.py +246 -19
- uncountable/integration/queue_runner/__init__.py +0 -0
- uncountable/integration/queue_runner/command_server/__init__.py +28 -0
- uncountable/integration/queue_runner/command_server/command_client.py +133 -0
- uncountable/integration/queue_runner/command_server/command_server.py +142 -0
- uncountable/integration/queue_runner/command_server/constants.py +4 -0
- uncountable/integration/queue_runner/command_server/protocol/__init__.py +0 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server.proto +58 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.py +57 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.pyi +114 -0
- uncountable/integration/queue_runner/command_server/protocol/command_server_pb2_grpc.py +264 -0
- uncountable/integration/queue_runner/command_server/types.py +75 -0
- uncountable/integration/queue_runner/datastore/__init__.py +3 -0
- uncountable/integration/queue_runner/datastore/datastore_sqlite.py +250 -0
- uncountable/integration/queue_runner/datastore/interface.py +29 -0
- uncountable/integration/queue_runner/datastore/model.py +24 -0
- uncountable/integration/queue_runner/job_scheduler.py +200 -0
- uncountable/integration/queue_runner/queue_runner.py +34 -0
- uncountable/integration/queue_runner/types.py +7 -0
- uncountable/integration/queue_runner/worker.py +116 -0
- uncountable/integration/scan_profiles.py +67 -0
- uncountable/integration/scheduler.py +199 -0
- uncountable/integration/secret_retrieval/__init__.py +3 -0
- uncountable/integration/secret_retrieval/retrieve_secret.py +93 -0
- uncountable/integration/server.py +103 -54
- uncountable/integration/telemetry.py +251 -0
- uncountable/integration/webhook_server/entrypoint.py +97 -0
- uncountable/types/__init__.py +149 -30
- uncountable/types/api/batch/execute_batch.py +16 -9
- uncountable/types/api/batch/execute_batch_load_async.py +13 -7
- uncountable/types/api/chemical/convert_chemical_formats.py +20 -8
- uncountable/types/api/condition_parameters/__init__.py +1 -0
- uncountable/types/api/condition_parameters/upsert_condition_match.py +72 -0
- uncountable/types/api/entity/create_entities.py +24 -12
- uncountable/types/api/entity/create_entity.py +22 -13
- uncountable/types/api/entity/create_or_update_entity.py +48 -0
- uncountable/types/api/entity/export_entities.py +59 -0
- uncountable/types/api/entity/get_entities_data.py +18 -9
- uncountable/types/api/entity/grant_entity_permissions.py +48 -0
- uncountable/types/api/entity/list_aggregate.py +79 -0
- uncountable/types/api/entity/list_entities.py +53 -14
- uncountable/types/api/entity/lock_entity.py +45 -0
- uncountable/types/api/entity/lookup_entity.py +116 -0
- uncountable/types/api/entity/resolve_entity_ids.py +19 -10
- uncountable/types/api/entity/set_entity_field_values.py +44 -0
- uncountable/types/api/entity/set_values.py +15 -8
- uncountable/types/api/entity/transition_entity_phase.py +27 -12
- uncountable/types/api/entity/unlock_entity.py +44 -0
- uncountable/types/api/equipment/__init__.py +1 -0
- uncountable/types/api/equipment/associate_equipment_input.py +43 -0
- uncountable/types/api/field_options/__init__.py +1 -0
- uncountable/types/api/field_options/upsert_field_options.py +55 -0
- uncountable/types/api/files/__init__.py +1 -0
- uncountable/types/api/files/download_file.py +77 -0
- uncountable/types/api/id_source/list_id_source.py +20 -11
- uncountable/types/api/id_source/match_id_source.py +15 -10
- uncountable/types/api/input_groups/get_input_group_names.py +16 -7
- uncountable/types/api/inputs/create_inputs.py +28 -14
- uncountable/types/api/inputs/get_input_data.py +34 -16
- uncountable/types/api/inputs/get_input_names.py +19 -10
- uncountable/types/api/inputs/get_inputs_data.py +29 -11
- uncountable/types/api/inputs/set_input_attribute_values.py +16 -10
- uncountable/types/api/inputs/set_input_category.py +44 -0
- uncountable/types/api/inputs/set_input_subcategories.py +45 -0
- uncountable/types/api/inputs/set_intermediate_type.py +50 -0
- uncountable/types/api/integrations/__init__.py +1 -0
- uncountable/types/api/integrations/publish_realtime_data.py +41 -0
- uncountable/types/api/integrations/push_notification.py +49 -0
- uncountable/types/api/integrations/register_sockets_token.py +41 -0
- uncountable/types/api/listing/__init__.py +1 -0
- uncountable/types/api/listing/fetch_listing.py +58 -0
- uncountable/types/api/material_families/__init__.py +1 -0
- uncountable/types/api/material_families/update_entity_material_families.py +47 -0
- uncountable/types/api/notebooks/__init__.py +1 -0
- uncountable/types/api/notebooks/add_notebook_content.py +119 -0
- uncountable/types/api/outputs/get_output_data.py +32 -17
- uncountable/types/api/outputs/get_output_names.py +18 -9
- uncountable/types/api/outputs/get_output_organization.py +173 -0
- uncountable/types/api/outputs/resolve_output_conditions.py +23 -11
- uncountable/types/api/permissions/set_core_permissions.py +31 -15
- uncountable/types/api/project/get_projects.py +20 -11
- uncountable/types/api/project/get_projects_data.py +23 -14
- uncountable/types/api/recipe_links/create_recipe_link.py +17 -10
- uncountable/types/api/recipe_links/remove_recipe_link.py +45 -0
- uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +19 -10
- uncountable/types/api/recipes/add_recipe_to_project.py +42 -0
- uncountable/types/api/recipes/add_time_series_data.py +64 -0
- uncountable/types/api/recipes/archive_recipes.py +14 -7
- uncountable/types/api/recipes/associate_recipe_as_input.py +16 -8
- uncountable/types/api/recipes/associate_recipe_as_lot.py +14 -7
- uncountable/types/api/recipes/clear_recipe_outputs.py +42 -0
- uncountable/types/api/recipes/create_mix_order.py +44 -0
- uncountable/types/api/recipes/create_recipe.py +21 -14
- uncountable/types/api/recipes/create_recipes.py +25 -13
- uncountable/types/api/recipes/disassociate_recipe_as_input.py +14 -7
- uncountable/types/api/recipes/edit_recipe_inputs.py +208 -19
- uncountable/types/api/recipes/get_column_calculation_values.py +57 -0
- uncountable/types/api/recipes/get_curve.py +15 -9
- uncountable/types/api/recipes/get_recipe_calculations.py +17 -11
- uncountable/types/api/recipes/get_recipe_links.py +14 -8
- uncountable/types/api/recipes/get_recipe_names.py +16 -7
- uncountable/types/api/recipes/get_recipe_output_metadata.py +16 -10
- uncountable/types/api/recipes/get_recipes_data.py +96 -45
- uncountable/types/api/recipes/lock_recipes.py +64 -0
- uncountable/types/api/recipes/remove_recipe_from_project.py +42 -0
- uncountable/types/api/recipes/set_recipe_inputs.py +19 -13
- uncountable/types/api/recipes/set_recipe_metadata.py +14 -7
- uncountable/types/api/recipes/set_recipe_output_annotations.py +114 -0
- uncountable/types/api/recipes/set_recipe_output_file.py +55 -0
- uncountable/types/api/recipes/set_recipe_outputs.py +40 -15
- uncountable/types/api/recipes/set_recipe_tags.py +30 -13
- uncountable/types/api/recipes/set_recipe_total.py +59 -0
- uncountable/types/api/recipes/unarchive_recipes.py +41 -0
- uncountable/types/api/recipes/unlock_recipes.py +51 -0
- uncountable/types/api/runsheet/__init__.py +1 -0
- uncountable/types/api/runsheet/complete_async_upload.py +41 -0
- uncountable/types/api/triggers/run_trigger.py +15 -8
- uncountable/types/api/uploader/__init__.py +1 -0
- uncountable/types/api/uploader/complete_async_parse.py +46 -0
- uncountable/types/api/uploader/invoke_uploader.py +46 -0
- uncountable/types/api/user/__init__.py +1 -0
- uncountable/types/api/user/get_current_user_info.py +40 -0
- uncountable/types/async_batch.py +8 -52
- uncountable/types/async_batch_processor.py +694 -18
- uncountable/types/async_batch_t.py +108 -0
- uncountable/types/async_jobs.py +8 -0
- uncountable/types/async_jobs_t.py +52 -0
- uncountable/types/auth_retrieval.py +11 -0
- uncountable/types/auth_retrieval_t.py +75 -0
- uncountable/types/base.py +5 -80
- uncountable/types/base_t.py +87 -0
- uncountable/types/calculations.py +3 -19
- uncountable/types/calculations_t.py +26 -0
- uncountable/types/chemical_structure.py +3 -23
- uncountable/types/chemical_structure_t.py +28 -0
- uncountable/types/client_base.py +1170 -88
- uncountable/types/client_config.py +8 -0
- uncountable/types/client_config_t.py +36 -0
- uncountable/types/curves.py +5 -43
- uncountable/types/curves_t.py +50 -0
- uncountable/types/data.py +12 -0
- uncountable/types/data_t.py +103 -0
- uncountable/types/entity.py +8 -270
- uncountable/types/entity_t.py +446 -0
- uncountable/types/experiment_groups.py +3 -19
- uncountable/types/experiment_groups_t.py +26 -0
- uncountable/types/exports.py +8 -0
- uncountable/types/exports_t.py +34 -0
- uncountable/types/field_values.py +25 -61
- uncountable/types/field_values_t.py +302 -0
- uncountable/types/fields.py +3 -20
- uncountable/types/fields_t.py +27 -0
- uncountable/types/generic_upload.py +14 -0
- uncountable/types/generic_upload_t.py +119 -0
- uncountable/types/id_source.py +7 -45
- uncountable/types/id_source_t.py +68 -0
- uncountable/types/identifier.py +6 -50
- uncountable/types/identifier_t.py +62 -0
- uncountable/types/input_attributes.py +3 -25
- uncountable/types/input_attributes_t.py +29 -0
- uncountable/types/inputs.py +6 -57
- uncountable/types/inputs_t.py +82 -0
- uncountable/types/integration_server.py +8 -0
- uncountable/types/integration_server_t.py +46 -0
- uncountable/types/integration_session.py +10 -0
- uncountable/types/integration_session_t.py +60 -0
- uncountable/types/integrations.py +10 -0
- uncountable/types/integrations_t.py +62 -0
- uncountable/types/job_definition.py +28 -0
- uncountable/types/job_definition_t.py +285 -0
- uncountable/types/listing.py +9 -0
- uncountable/types/listing_t.py +51 -0
- uncountable/types/notices.py +8 -0
- uncountable/types/notices_t.py +37 -0
- uncountable/types/notifications.py +11 -0
- uncountable/types/notifications_t.py +74 -0
- uncountable/types/outputs.py +3 -22
- uncountable/types/outputs_t.py +29 -0
- uncountable/types/overrides.py +9 -0
- uncountable/types/overrides_t.py +49 -0
- uncountable/types/permissions.py +3 -42
- uncountable/types/permissions_t.py +45 -0
- uncountable/types/phases.py +3 -19
- uncountable/types/phases_t.py +26 -0
- uncountable/types/post_base.py +3 -26
- uncountable/types/post_base_t.py +29 -0
- uncountable/types/queued_job.py +17 -0
- uncountable/types/queued_job_t.py +140 -0
- uncountable/types/recipe_identifiers.py +7 -58
- uncountable/types/recipe_identifiers_t.py +75 -0
- uncountable/types/recipe_inputs.py +4 -26
- uncountable/types/recipe_inputs_t.py +29 -0
- uncountable/types/recipe_links.py +4 -46
- uncountable/types/recipe_links_t.py +53 -0
- uncountable/types/recipe_metadata.py +5 -48
- uncountable/types/recipe_metadata_t.py +57 -0
- uncountable/types/recipe_output_metadata.py +3 -20
- uncountable/types/recipe_output_metadata_t.py +27 -0
- uncountable/types/recipe_tags.py +3 -19
- uncountable/types/recipe_tags_t.py +26 -0
- uncountable/types/recipe_workflow_steps.py +9 -73
- uncountable/types/recipe_workflow_steps_t.py +95 -0
- uncountable/types/recipes.py +7 -0
- uncountable/types/recipes_t.py +25 -0
- uncountable/types/response.py +3 -21
- uncountable/types/response_t.py +26 -0
- uncountable/types/secret_retrieval.py +11 -0
- uncountable/types/secret_retrieval_t.py +75 -0
- uncountable/types/sockets.py +20 -0
- uncountable/types/sockets_t.py +169 -0
- uncountable/types/structured_filters.py +25 -0
- uncountable/types/structured_filters_t.py +248 -0
- uncountable/types/units.py +3 -19
- uncountable/types/units_t.py +26 -0
- uncountable/types/uploader.py +24 -0
- uncountable/types/uploader_t.py +222 -0
- uncountable/types/users.py +3 -20
- uncountable/types/users_t.py +27 -0
- uncountable/types/webhook_job.py +9 -0
- uncountable/types/webhook_job_t.py +48 -0
- uncountable/types/workflows.py +4 -28
- uncountable/types/workflows_t.py +38 -0
- uncountablepythonsdk-0.0.131.dist-info/METADATA +64 -0
- uncountablepythonsdk-0.0.131.dist-info/RECORD +363 -0
- {UncountablePythonSDK-0.0.24.dist-info → uncountablepythonsdk-0.0.131.dist-info}/WHEEL +1 -1
- {UncountablePythonSDK-0.0.24.dist-info → uncountablepythonsdk-0.0.131.dist-info}/top_level.txt +0 -1
- UncountablePythonSDK-0.0.24.dist-info/METADATA +0 -47
- UncountablePythonSDK-0.0.24.dist-info/RECORD +0 -216
- docs/quickstart.md +0 -19
- examples/recipe-import/importer.py +0 -39
- type_spec/external/api/batch/execute_batch.yaml +0 -56
- type_spec/external/api/batch/execute_batch_load_async.yaml +0 -18
- type_spec/external/api/chemical/convert_chemical_formats.yaml +0 -33
- type_spec/external/api/entity/create_entities.yaml +0 -45
- type_spec/external/api/entity/create_entity.yaml +0 -51
- type_spec/external/api/entity/get_entities_data.yaml +0 -29
- type_spec/external/api/entity/list_entities.yaml +0 -52
- type_spec/external/api/entity/resolve_entity_ids.yaml +0 -29
- type_spec/external/api/entity/set_values.yaml +0 -18
- type_spec/external/api/entity/transition_entity_phase.yaml +0 -44
- type_spec/external/api/id_source/list_id_source.yaml +0 -35
- type_spec/external/api/id_source/match_id_source.yaml +0 -32
- type_spec/external/api/input_groups/get_input_group_names.yaml +0 -29
- type_spec/external/api/inputs/create_inputs.yaml +0 -48
- type_spec/external/api/inputs/get_input_data.yaml +0 -95
- type_spec/external/api/inputs/get_input_names.yaml +0 -38
- type_spec/external/api/inputs/get_inputs_data.yaml +0 -82
- type_spec/external/api/inputs/set_input_attribute_values.yaml +0 -33
- type_spec/external/api/outputs/get_output_data.yaml +0 -92
- type_spec/external/api/outputs/get_output_names.yaml +0 -35
- type_spec/external/api/outputs/resolve_output_conditions.yaml +0 -50
- type_spec/external/api/permissions/set_core_permissions.yaml +0 -69
- type_spec/external/api/project/get_projects.yaml +0 -42
- type_spec/external/api/project/get_projects_data.yaml +0 -50
- type_spec/external/api/recipe_links/create_recipe_link.yaml +0 -25
- type_spec/external/api/recipe_metadata/get_recipe_metadata_data.yaml +0 -41
- type_spec/external/api/recipes/archive_recipes.yaml +0 -20
- type_spec/external/api/recipes/associate_recipe_as_input.yaml +0 -19
- type_spec/external/api/recipes/associate_recipe_as_lot.yaml +0 -19
- type_spec/external/api/recipes/create_recipe.yaml +0 -39
- type_spec/external/api/recipes/create_recipes.yaml +0 -47
- type_spec/external/api/recipes/disassociate_recipe_as_input.yaml +0 -16
- type_spec/external/api/recipes/edit_recipe_inputs.yaml +0 -85
- type_spec/external/api/recipes/get_curve.yaml +0 -21
- type_spec/external/api/recipes/get_recipe_calculations.yaml +0 -39
- type_spec/external/api/recipes/get_recipe_links.yaml +0 -26
- type_spec/external/api/recipes/get_recipe_names.yaml +0 -29
- type_spec/external/api/recipes/get_recipe_output_metadata.yaml +0 -36
- type_spec/external/api/recipes/get_recipes_data.yaml +0 -244
- type_spec/external/api/recipes/set_recipe_inputs.yaml +0 -42
- type_spec/external/api/recipes/set_recipe_metadata.yaml +0 -20
- type_spec/external/api/recipes/set_recipe_outputs.yaml +0 -52
- type_spec/external/api/recipes/set_recipe_tags.yaml +0 -62
- type_spec/external/api/triggers/run_trigger.yaml +0 -18
- uncountable/integration/types.py +0 -89
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from dateutil import tz
|
|
4
|
+
from opentelemetry.trace import get_current_span
|
|
5
|
+
from tabulate import tabulate
|
|
6
|
+
|
|
7
|
+
from uncountable.core.environment import get_local_admin_server_port
|
|
8
|
+
from uncountable.integration.queue_runner.command_server.command_client import (
|
|
9
|
+
send_job_queue_message,
|
|
10
|
+
send_list_queued_jobs_message,
|
|
11
|
+
send_retry_job_message,
|
|
12
|
+
)
|
|
13
|
+
from uncountable.integration.telemetry import Logger
|
|
14
|
+
from uncountable.types import queued_job_t
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def register_enqueue_job_parser(
|
|
18
|
+
sub_parser_manager: argparse._SubParsersAction,
|
|
19
|
+
parents: list[argparse.ArgumentParser],
|
|
20
|
+
) -> None:
|
|
21
|
+
run_parser = sub_parser_manager.add_parser(
|
|
22
|
+
"run",
|
|
23
|
+
parents=parents,
|
|
24
|
+
help="Process a job with a given host and job ID",
|
|
25
|
+
description="Process a job with a given host and job ID",
|
|
26
|
+
)
|
|
27
|
+
run_parser.add_argument("job_id", type=str, help="The ID of the job to process")
|
|
28
|
+
|
|
29
|
+
def _handle_enqueue_job(args: argparse.Namespace) -> None:
|
|
30
|
+
send_job_queue_message(
|
|
31
|
+
job_ref_name=args.job_id,
|
|
32
|
+
payload=queued_job_t.QueuedJobPayload(
|
|
33
|
+
invocation_context=queued_job_t.InvocationContextManual()
|
|
34
|
+
),
|
|
35
|
+
host=args.host,
|
|
36
|
+
port=get_local_admin_server_port(),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
run_parser.set_defaults(func=_handle_enqueue_job)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def register_list_queued_jobs(
|
|
43
|
+
sub_parser_manager: argparse._SubParsersAction,
|
|
44
|
+
parents: list[argparse.ArgumentParser],
|
|
45
|
+
) -> None:
|
|
46
|
+
list_queued_jobs_parser = sub_parser_manager.add_parser(
|
|
47
|
+
"list-queued-jobs",
|
|
48
|
+
parents=parents,
|
|
49
|
+
help="List all jobs queued on the integration server",
|
|
50
|
+
description="List all jobs queued on the integration server",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
list_queued_jobs_parser.add_argument(
|
|
54
|
+
"--offset",
|
|
55
|
+
type=int,
|
|
56
|
+
default=0,
|
|
57
|
+
help="Number of jobs to skip. Should be non-negative.",
|
|
58
|
+
)
|
|
59
|
+
list_queued_jobs_parser.add_argument(
|
|
60
|
+
"--limit",
|
|
61
|
+
type=int,
|
|
62
|
+
default=100,
|
|
63
|
+
help="A number between 1 and 100 specifying the number of jobs to return in the result set.",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def _handle_list_queued_jobs(args: argparse.Namespace) -> None:
|
|
67
|
+
queued_jobs = send_list_queued_jobs_message(
|
|
68
|
+
offset=args.offset,
|
|
69
|
+
limit=args.limit,
|
|
70
|
+
host=args.host,
|
|
71
|
+
port=get_local_admin_server_port(),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
headers = ["UUID", "Job Ref Name", "Attempts", "Status", "Submitted At"]
|
|
75
|
+
rows = [
|
|
76
|
+
[
|
|
77
|
+
job.uuid,
|
|
78
|
+
job.job_ref_name,
|
|
79
|
+
job.num_attempts,
|
|
80
|
+
job.status,
|
|
81
|
+
job.submitted_at.ToDatetime(tz.UTC).astimezone(tz.tzlocal()),
|
|
82
|
+
]
|
|
83
|
+
for job in queued_jobs
|
|
84
|
+
]
|
|
85
|
+
print(tabulate(rows, headers=headers, tablefmt="grid"))
|
|
86
|
+
|
|
87
|
+
list_queued_jobs_parser.set_defaults(func=_handle_list_queued_jobs)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def register_retry_job_parser(
|
|
91
|
+
sub_parser_manager: argparse._SubParsersAction,
|
|
92
|
+
parents: list[argparse.ArgumentParser],
|
|
93
|
+
) -> None:
|
|
94
|
+
retry_failed_jobs_parser = sub_parser_manager.add_parser(
|
|
95
|
+
"retry-job",
|
|
96
|
+
parents=parents,
|
|
97
|
+
help="Retry failed job on the integration server",
|
|
98
|
+
description="Retry failed job on the integration server",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
retry_failed_jobs_parser.add_argument(
|
|
102
|
+
"job_uuid", type=str, help="The uuid of the job to retry"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def _handle_retry_job(args: argparse.Namespace) -> None:
|
|
106
|
+
send_retry_job_message(
|
|
107
|
+
job_uuid=args.job_uuid,
|
|
108
|
+
host=args.host,
|
|
109
|
+
port=get_local_admin_server_port(),
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
retry_failed_jobs_parser.set_defaults(func=_handle_retry_job)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def main() -> None:
|
|
116
|
+
logger = Logger(get_current_span())
|
|
117
|
+
|
|
118
|
+
main_parser = argparse.ArgumentParser(
|
|
119
|
+
description="Execute a given integrations server command."
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
base_parser = argparse.ArgumentParser(add_help=False)
|
|
123
|
+
base_parser.add_argument(
|
|
124
|
+
"--host", type=str, default="localhost", nargs="?", help="The host to run on"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
subparser_action = main_parser.add_subparsers(
|
|
128
|
+
dest="command",
|
|
129
|
+
required=True,
|
|
130
|
+
help="The command to execute (e.g., 'run')",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
register_enqueue_job_parser(subparser_action, parents=[base_parser])
|
|
134
|
+
register_retry_job_parser(subparser_action, parents=[base_parser])
|
|
135
|
+
register_list_queued_jobs(subparser_action, parents=[base_parser])
|
|
136
|
+
|
|
137
|
+
args = main_parser.parse_args()
|
|
138
|
+
with logger.push_scope(args.command):
|
|
139
|
+
args.func(args)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
main()
|
|
@@ -1,35 +1,51 @@
|
|
|
1
|
-
import
|
|
2
|
-
from
|
|
3
|
-
|
|
4
|
-
from uncountable.
|
|
5
|
-
from uncountable.integration.
|
|
6
|
-
|
|
1
|
+
from uncountable.core import AuthDetailsApiKey, Client
|
|
2
|
+
from uncountable.core.client import ClientConfig
|
|
3
|
+
from uncountable.core.types import AuthDetailsAll, AuthDetailsOAuth
|
|
4
|
+
from uncountable.integration.secret_retrieval.retrieve_secret import retrieve_secret
|
|
5
|
+
from uncountable.integration.telemetry import JobLogger
|
|
6
|
+
from uncountable.types import auth_retrieval_t
|
|
7
|
+
from uncountable.types.job_definition_t import (
|
|
7
8
|
ProfileMetadata,
|
|
8
9
|
)
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
def
|
|
12
|
-
value = os.getenv(name)
|
|
13
|
-
if value is None:
|
|
14
|
-
raise Exception(f"environment variable {name} is missing")
|
|
15
|
-
return value
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def construct_uncountable_client(profile_meta: ProfileMetadata) -> Client:
|
|
12
|
+
def _construct_auth_details(profile_meta: ProfileMetadata) -> AuthDetailsAll:
|
|
19
13
|
match profile_meta.auth_retrieval:
|
|
20
|
-
case
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
case auth_retrieval_t.AuthRetrievalOAuth():
|
|
15
|
+
refresh_token = retrieve_secret(
|
|
16
|
+
profile_meta.auth_retrieval.refresh_token_secret,
|
|
17
|
+
profile_metadata=profile_meta,
|
|
18
|
+
)
|
|
19
|
+
return AuthDetailsOAuth(refresh_token=refresh_token)
|
|
20
|
+
case auth_retrieval_t.AuthRetrievalBasic():
|
|
21
|
+
api_id = retrieve_secret(
|
|
22
|
+
profile_meta.auth_retrieval.api_id_secret, profile_metadata=profile_meta
|
|
23
|
+
)
|
|
24
|
+
api_key = retrieve_secret(
|
|
25
|
+
profile_meta.auth_retrieval.api_key_secret,
|
|
26
|
+
profile_metadata=profile_meta,
|
|
24
27
|
)
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
assert api_secret_key is not None
|
|
29
|
+
return AuthDetailsApiKey(api_id=api_id, api_secret_key=api_key)
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
|
|
32
|
+
def _construct_client_config(
|
|
33
|
+
profile_meta: ProfileMetadata, job_logger: JobLogger
|
|
34
|
+
) -> ClientConfig | None:
|
|
35
|
+
if profile_meta.client_options is None:
|
|
36
|
+
return None
|
|
37
|
+
return ClientConfig(
|
|
38
|
+
allow_insecure_tls=profile_meta.client_options.allow_insecure_tls,
|
|
39
|
+
extra_headers=profile_meta.client_options.extra_headers,
|
|
40
|
+
logger=job_logger,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def construct_uncountable_client(
|
|
45
|
+
profile_meta: ProfileMetadata, logger: JobLogger
|
|
46
|
+
) -> Client:
|
|
47
|
+
return Client(
|
|
48
|
+
base_url=profile_meta.base_url,
|
|
49
|
+
auth_details=_construct_auth_details(profile_meta),
|
|
50
|
+
config=_construct_client_config(profile_meta, logger),
|
|
51
|
+
)
|
uncountable/integration/cron.py
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
|
|
3
3
|
from pkgs.argument_parser import CachedParser
|
|
4
|
-
from uncountable.
|
|
5
|
-
from uncountable.integration.
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
from uncountable.core.environment import get_local_admin_server_port
|
|
5
|
+
from uncountable.integration.queue_runner.command_server.command_client import (
|
|
6
|
+
send_job_queue_message,
|
|
7
|
+
)
|
|
8
|
+
from uncountable.types import queued_job_t
|
|
9
|
+
from uncountable.types.job_definition_t import JobDefinition, ProfileMetadata
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
@dataclass
|
|
@@ -18,11 +20,10 @@ cron_args_parser = CachedParser(CronJobArgs)
|
|
|
18
20
|
|
|
19
21
|
def cron_job_executor(**kwargs: dict) -> None:
|
|
20
22
|
args_passed = cron_args_parser.parse_storage(kwargs)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
send_job_queue_message(
|
|
24
|
+
job_ref_name=args_passed.definition.id,
|
|
25
|
+
payload=queued_job_t.QueuedJobPayload(
|
|
26
|
+
invocation_context=queued_job_t.InvocationContextCron()
|
|
27
|
+
),
|
|
28
|
+
port=get_local_admin_server_port(),
|
|
24
29
|
)
|
|
25
|
-
|
|
26
|
-
job = resolve_script_executor(args_passed.definition.executor, args_passed.profile_metadata)
|
|
27
|
-
|
|
28
|
-
job.run(args)
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from enum import StrEnum
|
|
3
|
+
|
|
2
4
|
from sqlalchemy import create_engine
|
|
3
5
|
from sqlalchemy.engine.base import Engine
|
|
4
6
|
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
class IntegrationDBService(StrEnum):
|
|
9
|
+
CRON = "cron"
|
|
10
|
+
RUNNER = "runner"
|
|
11
|
+
|
|
8
12
|
|
|
13
|
+
def create_db_engine(service: IntegrationDBService) -> Engine:
|
|
14
|
+
match service:
|
|
15
|
+
case IntegrationDBService.CRON:
|
|
16
|
+
return create_engine(os.environ["UNC_CRON_SQLITE_URI"])
|
|
17
|
+
case IntegrationDBService.RUNNER:
|
|
18
|
+
return create_engine(os.environ["UNC_RUNNER_SQLITE_URI"])
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from contextlib import _GeneratorContextManager, contextmanager
|
|
2
|
+
from typing import Callable, Generator
|
|
3
|
+
|
|
4
|
+
from sqlalchemy.engine import Engine
|
|
5
|
+
from sqlalchemy.orm import Session, sessionmaker
|
|
6
|
+
|
|
7
|
+
DBSessionMaker = Callable[[], _GeneratorContextManager[Session]]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_session_maker(engine: Engine) -> DBSessionMaker:
|
|
11
|
+
session_maker = sessionmaker(bind=engine)
|
|
12
|
+
|
|
13
|
+
@contextmanager
|
|
14
|
+
def session_manager() -> Generator[Session, None, None]:
|
|
15
|
+
session = session_maker()
|
|
16
|
+
try:
|
|
17
|
+
yield session
|
|
18
|
+
session.commit()
|
|
19
|
+
except Exception:
|
|
20
|
+
session.rollback()
|
|
21
|
+
raise
|
|
22
|
+
finally:
|
|
23
|
+
session.close()
|
|
24
|
+
|
|
25
|
+
return session_manager
|
|
@@ -1,41 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
from
|
|
3
|
-
|
|
1
|
+
from uncountable.integration.db.connect import IntegrationDBService, create_db_engine
|
|
2
|
+
from uncountable.integration.scan_profiles import load_profiles
|
|
4
3
|
from uncountable.integration.server import IntegrationServer
|
|
5
|
-
from uncountable.integration.types import ProfileDefinition
|
|
6
|
-
from pkgs.argument_parser import CachedParser
|
|
7
|
-
from uncountable.integration.db.connect import create_db_engine
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
profile_parser = CachedParser(ProfileDefinition)
|
|
11
4
|
|
|
12
5
|
|
|
13
6
|
def main() -> None:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
# TODO: Loop through all job spec yaml files and call server.add_job
|
|
17
|
-
profiles = [
|
|
18
|
-
entry
|
|
19
|
-
for entry in resources.files(profiles_module).iterdir()
|
|
20
|
-
if entry.is_dir()
|
|
21
|
-
]
|
|
22
|
-
for profile in profiles:
|
|
23
|
-
profile_name = profile.name
|
|
24
|
-
try:
|
|
25
|
-
profile = profile_parser.parse_yaml_resource(
|
|
26
|
-
package=".".join([profiles_module, profile_name]),
|
|
27
|
-
resource="profile.yaml",
|
|
28
|
-
)
|
|
29
|
-
except FileNotFoundError as e:
|
|
30
|
-
print("WARN: profile.yaml not found", e)
|
|
31
|
-
continue
|
|
32
|
-
server.register_profile(
|
|
33
|
-
profile_name=profile_name,
|
|
34
|
-
base_url=profile.base_url,
|
|
35
|
-
auth_retrieval=profile.auth_retrieval,
|
|
36
|
-
jobs=profile.jobs,
|
|
37
|
-
)
|
|
38
|
-
|
|
7
|
+
with IntegrationServer(create_db_engine(IntegrationDBService.CRON)) as server:
|
|
8
|
+
server.register_jobs(load_profiles())
|
|
39
9
|
server.serve_forever()
|
|
40
10
|
|
|
41
11
|
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from typing import assert_never
|
|
2
|
+
|
|
3
|
+
from uncountable.core.client import Client
|
|
4
|
+
from uncountable.integration.executors.generic_upload_executor import GenericUploadJob
|
|
5
|
+
from uncountable.integration.executors.script_executor import resolve_script_executor
|
|
6
|
+
from uncountable.integration.job import Job, JobArguments
|
|
7
|
+
from uncountable.types import (
|
|
8
|
+
async_jobs_t,
|
|
9
|
+
entity_t,
|
|
10
|
+
field_values_t,
|
|
11
|
+
identifier_t,
|
|
12
|
+
integration_server_t,
|
|
13
|
+
job_definition_t,
|
|
14
|
+
transition_entity_phase_t,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def resolve_executor(
|
|
19
|
+
job_executor: job_definition_t.JobExecutor,
|
|
20
|
+
profile_metadata: job_definition_t.ProfileMetadata,
|
|
21
|
+
) -> Job:
|
|
22
|
+
match job_executor:
|
|
23
|
+
case job_definition_t.JobExecutorScript():
|
|
24
|
+
return resolve_script_executor(
|
|
25
|
+
job_executor, profile_metadata=profile_metadata
|
|
26
|
+
)
|
|
27
|
+
case job_definition_t.JobExecutorGenericUpload():
|
|
28
|
+
return GenericUploadJob(
|
|
29
|
+
remote_directories=job_executor.remote_directories,
|
|
30
|
+
upload_strategy=job_executor.upload_strategy,
|
|
31
|
+
data_source=job_executor.data_source,
|
|
32
|
+
)
|
|
33
|
+
assert_never(job_executor)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _create_run_entity(
|
|
37
|
+
*,
|
|
38
|
+
client: Client,
|
|
39
|
+
logging_settings: job_definition_t.JobLoggingSettings,
|
|
40
|
+
job_uuid: str,
|
|
41
|
+
) -> entity_t.Entity:
|
|
42
|
+
run_entity = client.create_entity(
|
|
43
|
+
entity_type=entity_t.EntityType.ASYNC_JOB,
|
|
44
|
+
definition_key=identifier_t.IdentifierKeyRefName(
|
|
45
|
+
ref_name="unc_integration_server_run_definition"
|
|
46
|
+
),
|
|
47
|
+
field_values=[
|
|
48
|
+
field_values_t.FieldRefNameValue(
|
|
49
|
+
field_ref_name=async_jobs_t.ASYNC_JOB_TYPE_FIELD_REF_NAME,
|
|
50
|
+
value=async_jobs_t.AsyncJobType.INTEGRATION_SERVER_RUN,
|
|
51
|
+
),
|
|
52
|
+
field_values_t.FieldRefNameValue(
|
|
53
|
+
field_ref_name=async_jobs_t.ASYNC_JOB_STATUS_FIELD_REF_NAME,
|
|
54
|
+
value=async_jobs_t.AsyncJobStatus.IN_PROGRESS,
|
|
55
|
+
),
|
|
56
|
+
field_values_t.FieldRefNameValue(
|
|
57
|
+
field_ref_name=integration_server_t.INTEGRATION_SERVER_RUN_UUID_FIELD_REF_NAME,
|
|
58
|
+
value=job_uuid,
|
|
59
|
+
),
|
|
60
|
+
],
|
|
61
|
+
).entity
|
|
62
|
+
client.transition_entity_phase(
|
|
63
|
+
entity=run_entity,
|
|
64
|
+
transition=transition_entity_phase_t.TransitionIdentifierPhases(
|
|
65
|
+
phase_from_key=identifier_t.IdentifierKeyRefName(
|
|
66
|
+
ref_name="unc_integration_server_run__queued"
|
|
67
|
+
),
|
|
68
|
+
phase_to_key=identifier_t.IdentifierKeyRefName(
|
|
69
|
+
ref_name="unc_integration_server_run__started"
|
|
70
|
+
),
|
|
71
|
+
),
|
|
72
|
+
)
|
|
73
|
+
if logging_settings.share_with_user_groups is not None:
|
|
74
|
+
client.grant_entity_permissions(
|
|
75
|
+
entity_type=entity_t.EntityType.ASYNC_JOB,
|
|
76
|
+
entity_key=identifier_t.IdentifierKeyId(id=run_entity.id),
|
|
77
|
+
permission_types=[
|
|
78
|
+
entity_t.EntityPermissionType.READ,
|
|
79
|
+
entity_t.EntityPermissionType.WRITE,
|
|
80
|
+
],
|
|
81
|
+
user_group_keys=logging_settings.share_with_user_groups,
|
|
82
|
+
)
|
|
83
|
+
return run_entity
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def execute_job(
|
|
87
|
+
*,
|
|
88
|
+
job_definition: job_definition_t.JobDefinition,
|
|
89
|
+
profile_metadata: job_definition_t.ProfileMetadata,
|
|
90
|
+
args: JobArguments,
|
|
91
|
+
) -> job_definition_t.JobResult:
|
|
92
|
+
with args.logger.push_scope(job_definition.name) as job_logger:
|
|
93
|
+
job = resolve_executor(job_definition.executor, profile_metadata)
|
|
94
|
+
|
|
95
|
+
job_logger.log_info("running job")
|
|
96
|
+
|
|
97
|
+
run_entity: entity_t.Entity | None = None
|
|
98
|
+
try:
|
|
99
|
+
if (
|
|
100
|
+
job_definition.logging_settings is not None
|
|
101
|
+
and job_definition.logging_settings.enabled
|
|
102
|
+
):
|
|
103
|
+
run_entity = _create_run_entity(
|
|
104
|
+
client=args.client,
|
|
105
|
+
logging_settings=job_definition.logging_settings,
|
|
106
|
+
job_uuid=args.job_uuid,
|
|
107
|
+
)
|
|
108
|
+
result = job.run_outer(args=args)
|
|
109
|
+
except Exception as e:
|
|
110
|
+
job_logger.log_exception(e)
|
|
111
|
+
if run_entity is not None:
|
|
112
|
+
args.client.set_values(
|
|
113
|
+
entity=run_entity,
|
|
114
|
+
values=[
|
|
115
|
+
field_values_t.ArgumentValueRefName(
|
|
116
|
+
field_ref_name=async_jobs_t.ASYNC_JOB_STATUS_FIELD_REF_NAME,
|
|
117
|
+
value=async_jobs_t.AsyncJobStatus.ERROR,
|
|
118
|
+
),
|
|
119
|
+
],
|
|
120
|
+
)
|
|
121
|
+
return job_definition_t.JobResult(success=False)
|
|
122
|
+
|
|
123
|
+
if args.batch_processor.current_queue_size() != 0:
|
|
124
|
+
args.batch_processor.send()
|
|
125
|
+
|
|
126
|
+
submitted_batch_job_ids = args.batch_processor.get_submitted_job_ids()
|
|
127
|
+
job_logger.log_info(
|
|
128
|
+
"completed job",
|
|
129
|
+
attributes={
|
|
130
|
+
"submitted_batch_job_ids": submitted_batch_job_ids,
|
|
131
|
+
"success": result.success,
|
|
132
|
+
},
|
|
133
|
+
)
|
|
134
|
+
if run_entity is not None:
|
|
135
|
+
args.client.set_values(
|
|
136
|
+
entity=run_entity,
|
|
137
|
+
values=[
|
|
138
|
+
field_values_t.ArgumentValueRefName(
|
|
139
|
+
field_ref_name=async_jobs_t.ASYNC_JOB_STATUS_FIELD_REF_NAME,
|
|
140
|
+
value=async_jobs_t.AsyncJobStatus.COMPLETED
|
|
141
|
+
if result.success
|
|
142
|
+
else async_jobs_t.AsyncJobStatus.ERROR,
|
|
143
|
+
),
|
|
144
|
+
],
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
return result
|