UncountablePythonSDK 0.0.58__tar.gz → 0.0.60__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.
Potentially problematic release.
This version of UncountablePythonSDK might be problematic. Click here for more details.
- {uncountablepythonsdk-0.0.58/UncountablePythonSDK.egg-info → uncountablepythonsdk-0.0.60}/PKG-INFO +2 -1
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60/UncountablePythonSDK.egg-info}/PKG-INFO +2 -1
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/UncountablePythonSDK.egg-info/SOURCES.txt +4 -1
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/UncountablePythonSDK.egg-info/requires.txt +1 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pyproject.toml +2 -1
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/core/client.py +1 -1
- uncountablepythonsdk-0.0.60/uncountable/core/environment.py +24 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/cron.py +6 -18
- uncountablepythonsdk-0.0.60/uncountable/integration/executors/executors.py +56 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/executors/generic_upload_executor.py +1 -1
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/job.py +23 -5
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/server.py +4 -1
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/telemetry.py +57 -18
- uncountablepythonsdk-0.0.60/uncountable/integration/webhook_server/entrypoint.py +164 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/__init__.py +2 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/entity_t.py +2 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/job_definition_t.py +2 -1
- uncountablepythonsdk-0.0.60/uncountable/types/webhook_job.py +9 -0
- uncountablepythonsdk-0.0.60/uncountable/types/webhook_job_t.py +33 -0
- uncountablepythonsdk-0.0.58/uncountable/core/version.py +0 -11
- uncountablepythonsdk-0.0.58/uncountable/integration/executors/executors.py +0 -24
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/.github/workflows/documentation.yml +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/.github/workflows/publish.yml +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/.gitignore +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/README.md +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/UncountablePythonSDK.egg-info/dependency_links.txt +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/UncountablePythonSDK.egg-info/top_level.txt +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/.gitignore +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/conf.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/index.md +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/justfile +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/quickstart.md +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/requirements.txt +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/static/favicons/android-chrome-192x192.png +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/static/favicons/android-chrome-512x512.png +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/static/favicons/apple-touch-icon.png +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/static/favicons/browserconfig.xml +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/static/favicons/favicon-16x16.png +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/static/favicons/favicon-32x32.png +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/static/favicons/manifest.json +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/static/favicons/mstile-150x150.png +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/static/favicons/safari-pinned-tab.svg +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/docs/static/logo_blue.png +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/examples/async_batch.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/examples/create_entity.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/examples/edit_recipe_inputs.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/examples/invoke_uploader.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/examples/upload_files.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/argument_parser/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/argument_parser/_is_enum.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/argument_parser/_is_namedtuple.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/argument_parser/argument_parser.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/argument_parser/case_convert.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/filesystem_utils/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/filesystem_utils/_gdrive_session.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/filesystem_utils/_local_session.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/filesystem_utils/_s3_session.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/filesystem_utils/_sftp_session.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/filesystem_utils/file_type_utils.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/filesystem_utils/filesystem_session.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/serialization/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/serialization/missing_sentry.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/serialization/opaque_key.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/serialization/serial_class.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/serialization/serial_union.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/serialization/yaml.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/serialization_util/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/serialization_util/_get_type_for_serialization.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/serialization_util/convert_to_snakecase.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/serialization_util/serialization_helpers.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/strenum_compat/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/strenum_compat/strenum_compat.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/__main__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/actions_registry/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/actions_registry/__main__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/actions_registry/emit_typescript.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/builder.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/config.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/emit_io_ts.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/emit_open_api.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/emit_open_api_util.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/emit_python.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/emit_typescript.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/emit_typescript_util.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/load_types.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/open_api_util.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/parts/base.py.prepart +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/parts/base.ts.prepart +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/test.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/type_info/__main__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/type_info/emit_type_info.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/util.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/value_spec/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/value_spec/__main__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/value_spec/convert_type.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/value_spec/emit_python.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/pkgs/type_spec/value_spec/types.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/setup.cfg +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/core/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/core/async_batch.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/core/file_upload.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/core/types.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/construct_client.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/db/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/db/connect.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/entrypoint.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/executors/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/executors/script_executor.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/scan_profiles.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/secret_retrieval/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/secret_retrieval/retrieve_secret.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/py.typed +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/batch/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/batch/execute_batch.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/batch/execute_batch_load_async.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/chemical/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/chemical/convert_chemical_formats.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/entity/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/entity/create_entities.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/entity/create_entity.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/entity/get_entities_data.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/entity/list_entities.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/entity/lock_entity.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/entity/resolve_entity_ids.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/entity/set_values.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/entity/transition_entity_phase.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/entity/unlock_entity.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/equipment/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/equipment/associate_equipment_input.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/field_options/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/field_options/upsert_field_options.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/id_source/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/id_source/list_id_source.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/id_source/match_id_source.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/input_groups/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/input_groups/get_input_group_names.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/inputs/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/inputs/create_inputs.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/inputs/get_input_data.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/inputs/get_input_names.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/inputs/get_inputs_data.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/inputs/set_input_attribute_values.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/inputs/set_input_category.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/inputs/set_input_subcategories.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/inputs/set_intermediate_type.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/material_families/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/material_families/update_entity_material_families.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/outputs/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/outputs/get_output_data.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/outputs/get_output_names.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/outputs/resolve_output_conditions.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/permissions/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/permissions/set_core_permissions.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/project/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/project/get_projects.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/project/get_projects_data.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipe_links/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipe_links/create_recipe_link.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipe_links/remove_recipe_link.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipe_metadata/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/add_recipe_to_project.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/archive_recipes.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/associate_recipe_as_input.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/associate_recipe_as_lot.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/clear_recipe_outputs.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/create_recipe.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/create_recipes.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/disassociate_recipe_as_input.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/edit_recipe_inputs.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/get_curve.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/get_recipe_calculations.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/get_recipe_links.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/get_recipe_names.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/get_recipe_output_metadata.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/get_recipes_data.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/lock_recipes.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/remove_recipe_from_project.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/set_recipe_inputs.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/set_recipe_metadata.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/set_recipe_output_annotations.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/set_recipe_outputs.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/set_recipe_tags.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/unarchive_recipes.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/recipes/unlock_recipes.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/triggers/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/triggers/run_trigger.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/uploader/__init__.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/api/uploader/invoke_uploader.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/async_batch.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/async_batch_processor.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/async_batch_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/base.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/base_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/calculations.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/calculations_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/chemical_structure.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/chemical_structure_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/client_base.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/client_config.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/client_config_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/curves.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/curves_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/entity.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/experiment_groups.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/experiment_groups_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/field_values.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/field_values_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/fields.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/fields_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/generic_upload.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/generic_upload_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/id_source.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/id_source_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/identifier.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/identifier_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/input_attributes.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/input_attributes_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/inputs.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/inputs_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/job_definition.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/outputs.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/outputs_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/overrides.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/overrides_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/permissions.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/permissions_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/phases.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/phases_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/post_base.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/post_base_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_identifiers.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_identifiers_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_inputs.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_inputs_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_links.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_links_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_metadata.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_metadata_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_output_metadata.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_output_metadata_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_tags.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_tags_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_workflow_steps.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipe_workflow_steps_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipes.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/recipes_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/response.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/response_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/secret_retrieval.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/secret_retrieval_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/units.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/units_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/users.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/users_t.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/workflows.py +0 -0
- {uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/workflows_t.py +0 -0
{uncountablepythonsdk-0.0.58/UncountablePythonSDK.egg-info → uncountablepythonsdk-0.0.60}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: UncountablePythonSDK
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.60
|
|
4
4
|
Summary: Uncountable SDK
|
|
5
5
|
Project-URL: Homepage, https://github.com/uncountableinc/uncountable-python-sdk
|
|
6
6
|
Project-URL: Repository, https://github.com/uncountableinc/uncountable-python-sdk.git
|
|
@@ -33,6 +33,7 @@ Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.*
|
|
|
33
33
|
Requires-Dist: opentelemetry-sdk==1.*
|
|
34
34
|
Requires-Dist: paramiko==3.*
|
|
35
35
|
Requires-Dist: boto3==1.*
|
|
36
|
+
Requires-Dist: flask==3.*
|
|
36
37
|
Provides-Extra: test
|
|
37
38
|
Requires-Dist: mypy==1.*; extra == "test"
|
|
38
39
|
Requires-Dist: ruff==0.*; extra == "test"
|
{uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60/UncountablePythonSDK.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: UncountablePythonSDK
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.60
|
|
4
4
|
Summary: Uncountable SDK
|
|
5
5
|
Project-URL: Homepage, https://github.com/uncountableinc/uncountable-python-sdk
|
|
6
6
|
Project-URL: Repository, https://github.com/uncountableinc/uncountable-python-sdk.git
|
|
@@ -33,6 +33,7 @@ Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.*
|
|
|
33
33
|
Requires-Dist: opentelemetry-sdk==1.*
|
|
34
34
|
Requires-Dist: paramiko==3.*
|
|
35
35
|
Requires-Dist: boto3==1.*
|
|
36
|
+
Requires-Dist: flask==3.*
|
|
36
37
|
Provides-Extra: test
|
|
37
38
|
Requires-Dist: mypy==1.*; extra == "test"
|
|
38
39
|
Requires-Dist: ruff==0.*; extra == "test"
|
|
@@ -85,9 +85,9 @@ uncountable/py.typed
|
|
|
85
85
|
uncountable/core/__init__.py
|
|
86
86
|
uncountable/core/async_batch.py
|
|
87
87
|
uncountable/core/client.py
|
|
88
|
+
uncountable/core/environment.py
|
|
88
89
|
uncountable/core/file_upload.py
|
|
89
90
|
uncountable/core/types.py
|
|
90
|
-
uncountable/core/version.py
|
|
91
91
|
uncountable/integration/__init__.py
|
|
92
92
|
uncountable/integration/construct_client.py
|
|
93
93
|
uncountable/integration/cron.py
|
|
@@ -104,6 +104,7 @@ uncountable/integration/executors/generic_upload_executor.py
|
|
|
104
104
|
uncountable/integration/executors/script_executor.py
|
|
105
105
|
uncountable/integration/secret_retrieval/__init__.py
|
|
106
106
|
uncountable/integration/secret_retrieval/retrieve_secret.py
|
|
107
|
+
uncountable/integration/webhook_server/entrypoint.py
|
|
107
108
|
uncountable/types/__init__.py
|
|
108
109
|
uncountable/types/async_batch.py
|
|
109
110
|
uncountable/types/async_batch_processor.py
|
|
@@ -173,6 +174,8 @@ uncountable/types/units.py
|
|
|
173
174
|
uncountable/types/units_t.py
|
|
174
175
|
uncountable/types/users.py
|
|
175
176
|
uncountable/types/users_t.py
|
|
177
|
+
uncountable/types/webhook_job.py
|
|
178
|
+
uncountable/types/webhook_job_t.py
|
|
176
179
|
uncountable/types/workflows.py
|
|
177
180
|
uncountable/types/workflows_t.py
|
|
178
181
|
uncountable/types/api/__init__.py
|
|
@@ -14,7 +14,7 @@ from requests.exceptions import JSONDecodeError
|
|
|
14
14
|
from pkgs.argument_parser import CachedParser
|
|
15
15
|
from pkgs.serialization_util import serialize_for_api
|
|
16
16
|
from pkgs.serialization_util.serialization_helpers import JsonValue
|
|
17
|
-
from uncountable.core.
|
|
17
|
+
from uncountable.core.environment import get_version
|
|
18
18
|
from uncountable.integration.telemetry import JobLogger
|
|
19
19
|
from uncountable.types.client_base import APIRequest, ClientMethods
|
|
20
20
|
from uncountable.types.client_config import ClientConfigOptions
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import os
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@functools.cache
|
|
7
|
+
def get_version() -> str:
|
|
8
|
+
try:
|
|
9
|
+
version_str = version("UncountablePythonSDK")
|
|
10
|
+
except PackageNotFoundError:
|
|
11
|
+
version_str = "unknown"
|
|
12
|
+
return version_str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_integration_env() -> str | None:
|
|
16
|
+
return os.environ.get("UNC_INTEGRATION_ENV")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_webhook_server_port() -> int:
|
|
20
|
+
return int(os.environ.get("UNC_WEBHOOK_SERVER_PORT") or 5001)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_otel_enabled() -> bool:
|
|
24
|
+
return os.environ.get("UNC_OTEL_ENABLED") == "true"
|
|
@@ -3,7 +3,7 @@ from dataclasses import dataclass
|
|
|
3
3
|
from pkgs.argument_parser import CachedParser
|
|
4
4
|
from uncountable.core.async_batch import AsyncBatchProcessor
|
|
5
5
|
from uncountable.integration.construct_client import construct_uncountable_client
|
|
6
|
-
from uncountable.integration.executors.executors import
|
|
6
|
+
from uncountable.integration.executors.executors import execute_job
|
|
7
7
|
from uncountable.integration.job import CronJobArguments
|
|
8
8
|
from uncountable.integration.telemetry import JobLogger
|
|
9
9
|
from uncountable.types.job_definition_t import JobDefinition, ProfileMetadata
|
|
@@ -36,20 +36,8 @@ def cron_job_executor(**kwargs: dict) -> None:
|
|
|
36
36
|
logger=job_logger,
|
|
37
37
|
)
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
job_logger.log_info("running job")
|
|
45
|
-
|
|
46
|
-
job.run(args=args)
|
|
47
|
-
|
|
48
|
-
if batch_processor.current_queue_size() != 0:
|
|
49
|
-
batch_processor.send()
|
|
50
|
-
|
|
51
|
-
submitted_batch_job_ids = batch_processor.get_submitted_job_ids()
|
|
52
|
-
job_logger.log_info(
|
|
53
|
-
"completed job",
|
|
54
|
-
attributes={"submitted_batch_job_ids": submitted_batch_job_ids},
|
|
55
|
-
)
|
|
39
|
+
execute_job(
|
|
40
|
+
args=args,
|
|
41
|
+
profile_metadata=args_passed.profile_metadata,
|
|
42
|
+
job_definition=args_passed.definition,
|
|
43
|
+
)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from typing import assert_never
|
|
2
|
+
|
|
3
|
+
from uncountable.integration.executors.generic_upload_executor import GenericUploadJob
|
|
4
|
+
from uncountable.integration.executors.script_executor import resolve_script_executor
|
|
5
|
+
from uncountable.integration.job import Job, JobArguments
|
|
6
|
+
from uncountable.types import job_definition_t
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def resolve_executor(
|
|
10
|
+
job_executor: job_definition_t.JobExecutor,
|
|
11
|
+
profile_metadata: job_definition_t.ProfileMetadata,
|
|
12
|
+
) -> Job:
|
|
13
|
+
match job_executor:
|
|
14
|
+
case job_definition_t.JobExecutorScript():
|
|
15
|
+
return resolve_script_executor(
|
|
16
|
+
job_executor, profile_metadata=profile_metadata
|
|
17
|
+
)
|
|
18
|
+
case job_definition_t.JobExecutorGenericUpload():
|
|
19
|
+
return GenericUploadJob(
|
|
20
|
+
remote_directories=job_executor.remote_directories,
|
|
21
|
+
upload_strategy=job_executor.upload_strategy,
|
|
22
|
+
data_source=job_executor.data_source,
|
|
23
|
+
)
|
|
24
|
+
assert_never(job_executor)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def execute_job(
|
|
28
|
+
*,
|
|
29
|
+
job_definition: job_definition_t.JobDefinition,
|
|
30
|
+
profile_metadata: job_definition_t.ProfileMetadata,
|
|
31
|
+
args: JobArguments,
|
|
32
|
+
) -> job_definition_t.JobResult:
|
|
33
|
+
with args.logger.push_scope(job_definition.name) as job_logger:
|
|
34
|
+
job = resolve_executor(job_definition.executor, profile_metadata)
|
|
35
|
+
|
|
36
|
+
job_logger.log_info("running job")
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
result = job.run_outer(args=args)
|
|
40
|
+
except Exception as e:
|
|
41
|
+
job_logger.log_exception(e)
|
|
42
|
+
return job_definition_t.JobResult(success=False)
|
|
43
|
+
|
|
44
|
+
if args.batch_processor.current_queue_size() != 0:
|
|
45
|
+
args.batch_processor.send()
|
|
46
|
+
|
|
47
|
+
submitted_batch_job_ids = args.batch_processor.get_submitted_job_ids()
|
|
48
|
+
job_logger.log_info(
|
|
49
|
+
"completed job",
|
|
50
|
+
attributes={
|
|
51
|
+
"submitted_batch_job_ids": submitted_batch_job_ids,
|
|
52
|
+
"success": result.success,
|
|
53
|
+
},
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return result
|
|
@@ -226,7 +226,7 @@ class GenericUploadJob(Job):
|
|
|
226
226
|
|
|
227
227
|
return S3Session(s3_config=s3_config)
|
|
228
228
|
|
|
229
|
-
def
|
|
229
|
+
def run_outer(self, args: JobArguments) -> JobResult:
|
|
230
230
|
client = args.client
|
|
231
231
|
batch_processor = args.batch_processor
|
|
232
232
|
logger = args.logger
|
|
@@ -4,10 +4,11 @@ from dataclasses import dataclass
|
|
|
4
4
|
from uncountable.core.async_batch import AsyncBatchProcessor
|
|
5
5
|
from uncountable.core.client import Client
|
|
6
6
|
from uncountable.integration.telemetry import JobLogger
|
|
7
|
+
from uncountable.types import webhook_job_t
|
|
7
8
|
from uncountable.types.job_definition_t import JobDefinition, JobResult, ProfileMetadata
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
@dataclass
|
|
11
|
+
@dataclass(kw_only=True)
|
|
11
12
|
class JobArgumentsBase:
|
|
12
13
|
job_definition: JobDefinition
|
|
13
14
|
profile_metadata: ProfileMetadata
|
|
@@ -16,27 +17,44 @@ class JobArgumentsBase:
|
|
|
16
17
|
logger: JobLogger
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
@dataclass
|
|
20
|
+
@dataclass(kw_only=True)
|
|
20
21
|
class CronJobArguments(JobArgumentsBase):
|
|
21
|
-
# can imagine passing additional data such as in the sftp or webhook cases
|
|
22
22
|
pass
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
@dataclass(kw_only=True)
|
|
26
|
+
class WebhookJobArguments(JobArgumentsBase):
|
|
27
|
+
payload: webhook_job_t.WebhookEventBody
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
JobArguments = CronJobArguments | WebhookJobArguments
|
|
26
31
|
|
|
27
32
|
|
|
28
33
|
class Job(ABC):
|
|
29
34
|
_unc_job_registered: bool = False
|
|
30
35
|
|
|
31
36
|
@abstractmethod
|
|
32
|
-
def
|
|
37
|
+
def run_outer(self, args: JobArguments) -> JobResult: ...
|
|
33
38
|
|
|
34
39
|
|
|
35
40
|
class CronJob(Job):
|
|
41
|
+
def run_outer(self, args: JobArguments) -> JobResult:
|
|
42
|
+
assert isinstance(args, CronJobArguments)
|
|
43
|
+
return self.run(args)
|
|
44
|
+
|
|
36
45
|
@abstractmethod
|
|
37
46
|
def run(self, args: CronJobArguments) -> JobResult: ...
|
|
38
47
|
|
|
39
48
|
|
|
49
|
+
class WebhookJob(Job):
|
|
50
|
+
def run_outer(self, args: JobArguments) -> JobResult:
|
|
51
|
+
assert isinstance(args, WebhookJobArguments)
|
|
52
|
+
return self.run(args)
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def run(self, args: WebhookJobArguments) -> JobResult: ...
|
|
56
|
+
|
|
57
|
+
|
|
40
58
|
def register_job(cls: type[Job]) -> type[Job]:
|
|
41
59
|
cls._unc_job_registered = True
|
|
42
60
|
return cls
|
{uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/server.py
RENAMED
|
@@ -19,6 +19,7 @@ from uncountable.types.job_definition_t import (
|
|
|
19
19
|
CronJobDefinition,
|
|
20
20
|
JobDefinition,
|
|
21
21
|
ProfileMetadata,
|
|
22
|
+
WebhookJobDefinition,
|
|
22
23
|
)
|
|
23
24
|
|
|
24
25
|
_MAX_APSCHEDULER_CONCURRENT_JOBS = 1
|
|
@@ -97,8 +98,10 @@ class IntegrationServer:
|
|
|
97
98
|
kwargs=job_kwargs,
|
|
98
99
|
**job_opts,
|
|
99
100
|
)
|
|
101
|
+
case WebhookJobDefinition():
|
|
102
|
+
pass
|
|
100
103
|
case _:
|
|
101
|
-
assert_never(job_defn
|
|
104
|
+
assert_never(job_defn)
|
|
102
105
|
|
|
103
106
|
def serve_forever(self) -> None:
|
|
104
107
|
signal.pause()
|
{uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/integration/telemetry.py
RENAMED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import functools
|
|
2
2
|
import os
|
|
3
3
|
import time
|
|
4
|
+
import typing
|
|
4
5
|
from contextlib import contextmanager
|
|
5
6
|
from enum import StrEnum
|
|
6
7
|
from typing import Generator, assert_never, cast
|
|
@@ -16,9 +17,13 @@ from opentelemetry.sdk.trace import TracerProvider
|
|
|
16
17
|
from opentelemetry.sdk.trace.export import (
|
|
17
18
|
SimpleSpanProcessor,
|
|
18
19
|
)
|
|
19
|
-
from opentelemetry.trace import Tracer
|
|
20
|
+
from opentelemetry.trace import DEFAULT_TRACE_OPTIONS, Span, Tracer
|
|
20
21
|
|
|
21
|
-
from uncountable.core.
|
|
22
|
+
from uncountable.core.environment import (
|
|
23
|
+
get_integration_env,
|
|
24
|
+
get_otel_enabled,
|
|
25
|
+
get_version,
|
|
26
|
+
)
|
|
22
27
|
from uncountable.types import base_t, job_definition_t
|
|
23
28
|
|
|
24
29
|
|
|
@@ -35,7 +40,7 @@ def get_otel_resource() -> Resource:
|
|
|
35
40
|
unc_version = os.environ.get("UNC_VERSION")
|
|
36
41
|
if unc_version is not None:
|
|
37
42
|
attributes["service.version"] = unc_version
|
|
38
|
-
unc_env =
|
|
43
|
+
unc_env = get_integration_env()
|
|
39
44
|
if unc_env is not None:
|
|
40
45
|
attributes["deployment.environment"] = unc_env
|
|
41
46
|
resource = Resource.create(attributes=_cast_attributes(attributes))
|
|
@@ -45,7 +50,8 @@ def get_otel_resource() -> Resource:
|
|
|
45
50
|
@functools.cache
|
|
46
51
|
def get_otel_tracer() -> Tracer:
|
|
47
52
|
provider = TracerProvider(resource=get_otel_resource())
|
|
48
|
-
|
|
53
|
+
if get_otel_enabled():
|
|
54
|
+
provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter()))
|
|
49
55
|
trace.set_tracer_provider(provider)
|
|
50
56
|
return provider.get_tracer("integration.telemetry")
|
|
51
57
|
|
|
@@ -54,7 +60,8 @@ def get_otel_tracer() -> Tracer:
|
|
|
54
60
|
def get_otel_logger() -> OTELLogger:
|
|
55
61
|
provider = LoggerProvider(resource=get_otel_resource())
|
|
56
62
|
provider.add_log_record_processor(BatchLogRecordProcessor(ConsoleLogExporter()))
|
|
57
|
-
|
|
63
|
+
if get_otel_enabled():
|
|
64
|
+
provider.add_log_record_processor(BatchLogRecordProcessor(OTLPLogExporter()))
|
|
58
65
|
_logs.set_logger_provider(provider)
|
|
59
66
|
return provider.get_logger("integration.telemetry")
|
|
60
67
|
|
|
@@ -66,8 +73,19 @@ class LogSeverity(StrEnum):
|
|
|
66
73
|
|
|
67
74
|
|
|
68
75
|
class Logger:
|
|
69
|
-
|
|
70
|
-
|
|
76
|
+
current_span: Span | None = None
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def current_span_id(self) -> int | None:
|
|
80
|
+
if self.current_span is None:
|
|
81
|
+
return None
|
|
82
|
+
return self.current_span.get_span_context().span_id
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def current_trace_id(self) -> int | None:
|
|
86
|
+
if self.current_span is None:
|
|
87
|
+
return None
|
|
88
|
+
return self.current_span.get_span_context().trace_id
|
|
71
89
|
|
|
72
90
|
def _patch_attributes(self, attributes: Attributes | None) -> Attributes:
|
|
73
91
|
return attributes or {}
|
|
@@ -83,6 +101,9 @@ class Logger:
|
|
|
83
101
|
attributes=self._patch_attributes(attributes),
|
|
84
102
|
span_id=self.current_span_id,
|
|
85
103
|
trace_id=self.current_trace_id,
|
|
104
|
+
trace_flags=DEFAULT_TRACE_OPTIONS,
|
|
105
|
+
severity_number=_logs.SeverityNumber.UNSPECIFIED,
|
|
106
|
+
resource=get_otel_resource(),
|
|
86
107
|
)
|
|
87
108
|
otel_logger.emit(log_record)
|
|
88
109
|
|
|
@@ -103,6 +124,33 @@ class Logger:
|
|
|
103
124
|
message=message, severity=LogSeverity.ERROR, attributes=attributes
|
|
104
125
|
)
|
|
105
126
|
|
|
127
|
+
def log_exception(
|
|
128
|
+
self,
|
|
129
|
+
exception: BaseException,
|
|
130
|
+
*,
|
|
131
|
+
message: str | None = None,
|
|
132
|
+
attributes: Attributes | None = None,
|
|
133
|
+
) -> None:
|
|
134
|
+
patched_attributes = self._patch_attributes(attributes)
|
|
135
|
+
if self.current_span is not None:
|
|
136
|
+
self.current_span.record_exception(
|
|
137
|
+
exception=exception, attributes=patched_attributes
|
|
138
|
+
)
|
|
139
|
+
self.log_error(
|
|
140
|
+
message=f"error: {message}\nexception: {exception}",
|
|
141
|
+
attributes=patched_attributes,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
@contextmanager
|
|
145
|
+
def push_scope(
|
|
146
|
+
self, scope_name: str, *, attributes: Attributes | None = None
|
|
147
|
+
) -> Generator[typing.Self, None, None]:
|
|
148
|
+
with get_otel_tracer().start_as_current_span(
|
|
149
|
+
scope_name, attributes=self._patch_attributes(attributes)
|
|
150
|
+
) as span:
|
|
151
|
+
self.current_span = span
|
|
152
|
+
yield self
|
|
153
|
+
|
|
106
154
|
|
|
107
155
|
class JobLogger(Logger):
|
|
108
156
|
def __init__(
|
|
@@ -128,6 +176,8 @@ class JobLogger(Logger):
|
|
|
128
176
|
patched_attributes["job.definition.cron_spec"] = (
|
|
129
177
|
self.job_definition.cron_spec
|
|
130
178
|
)
|
|
179
|
+
case job_definition_t.WebhookJobDefinition():
|
|
180
|
+
pass
|
|
131
181
|
case _:
|
|
132
182
|
assert_never(self.job_definition)
|
|
133
183
|
patched_attributes["job.definition.executor.type"] = (
|
|
@@ -145,14 +195,3 @@ class JobLogger(Logger):
|
|
|
145
195
|
case _:
|
|
146
196
|
assert_never(self.job_definition.executor)
|
|
147
197
|
return _cast_attributes(patched_attributes)
|
|
148
|
-
|
|
149
|
-
@contextmanager
|
|
150
|
-
def push_scope(
|
|
151
|
-
self, scope_name: str, *, attributes: Attributes | None = None
|
|
152
|
-
) -> Generator["JobLogger", None, None]:
|
|
153
|
-
with get_otel_tracer().start_as_current_span(
|
|
154
|
-
scope_name, attributes=self._patch_attributes(attributes)
|
|
155
|
-
) as span:
|
|
156
|
-
self.current_span_id = span.get_span_context().span_id
|
|
157
|
-
self.current_trace_id = span.get_span_context().trace_id
|
|
158
|
-
yield self
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import hmac
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
import flask
|
|
5
|
+
import simplejson
|
|
6
|
+
from flask.typing import ResponseReturnValue
|
|
7
|
+
from flask.wrappers import Response
|
|
8
|
+
from uncountable.core.async_batch import AsyncBatchProcessor
|
|
9
|
+
from uncountable.core.environment import get_integration_env, get_webhook_server_port
|
|
10
|
+
from uncountable.integration.construct_client import construct_uncountable_client
|
|
11
|
+
from uncountable.integration.executors.executors import execute_job
|
|
12
|
+
from uncountable.integration.job import WebhookJobArguments
|
|
13
|
+
from uncountable.integration.scan_profiles import load_profiles
|
|
14
|
+
from uncountable.integration.secret_retrieval.retrieve_secret import retrieve_secret
|
|
15
|
+
from uncountable.integration.telemetry import JobLogger, Logger
|
|
16
|
+
from uncountable.types import job_definition_t, webhook_job_t
|
|
17
|
+
|
|
18
|
+
from pkgs.argument_parser import CachedParser
|
|
19
|
+
|
|
20
|
+
app = flask.Flask(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(kw_only=True)
|
|
24
|
+
class WebhookResponse:
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
webhook_payload_parser = CachedParser(webhook_job_t.WebhookEventBody)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class WebhookException(BaseException):
|
|
32
|
+
error_code: int
|
|
33
|
+
message: str
|
|
34
|
+
|
|
35
|
+
def __init__(self, *, error_code: int, message: str) -> None:
|
|
36
|
+
self.error_code = error_code
|
|
37
|
+
self.message = message
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def payload_failed_signature() -> "WebhookException":
|
|
41
|
+
return WebhookException(
|
|
42
|
+
error_code=401, message="webhook payload did not match signature"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def no_signature_passed() -> "WebhookException":
|
|
47
|
+
return WebhookException(error_code=400, message="missing signature")
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def body_parse_error() -> "WebhookException":
|
|
51
|
+
return WebhookException(error_code=400, message="body parse error")
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def unknown_error() -> "WebhookException":
|
|
55
|
+
return WebhookException(error_code=500, message="internal server error")
|
|
56
|
+
|
|
57
|
+
def __str__(self) -> str:
|
|
58
|
+
return f"[{self.error_code}]: {self.message}"
|
|
59
|
+
|
|
60
|
+
def make_error_response(self) -> Response:
|
|
61
|
+
return Response(
|
|
62
|
+
status=self.error_code, response={"error": {"message": str(self)}}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _parse_webhook_payload(
|
|
67
|
+
*, raw_request_body: bytes, signature_key: str, passed_signature: str
|
|
68
|
+
) -> webhook_job_t.WebhookEventBody:
|
|
69
|
+
request_body_signature = hmac.new(
|
|
70
|
+
signature_key.encode("utf-8"), msg=raw_request_body, digestmod="sha256"
|
|
71
|
+
).hexdigest()
|
|
72
|
+
|
|
73
|
+
if request_body_signature != passed_signature:
|
|
74
|
+
raise WebhookException.payload_failed_signature()
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
request_body = simplejson.loads(raw_request_body.decode())
|
|
78
|
+
return webhook_payload_parser.parse_api(request_body)
|
|
79
|
+
except (simplejson.JSONDecodeError, ValueError) as e:
|
|
80
|
+
raise WebhookException.body_parse_error() from e
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def register_route(
|
|
84
|
+
*,
|
|
85
|
+
server_logger: Logger,
|
|
86
|
+
profile_meta: job_definition_t.ProfileMetadata,
|
|
87
|
+
job: job_definition_t.WebhookJobDefinition,
|
|
88
|
+
) -> None:
|
|
89
|
+
route = f"/{profile_meta.name}/{job.id}"
|
|
90
|
+
|
|
91
|
+
@app.route(route, methods=["POST"])
|
|
92
|
+
def handle_webhook() -> ResponseReturnValue:
|
|
93
|
+
with server_logger.push_scope(route):
|
|
94
|
+
try:
|
|
95
|
+
signature_key = retrieve_secret(
|
|
96
|
+
profile_metadata=profile_meta,
|
|
97
|
+
secret_retrieval=job.signature_key_secret,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
passed_signature = flask.request.headers.get(
|
|
101
|
+
"Uncountable-Webhook-Signature"
|
|
102
|
+
)
|
|
103
|
+
if passed_signature is None:
|
|
104
|
+
raise WebhookException.no_signature_passed()
|
|
105
|
+
|
|
106
|
+
webhook_payload = _parse_webhook_payload(
|
|
107
|
+
raw_request_body=flask.request.data,
|
|
108
|
+
signature_key=signature_key,
|
|
109
|
+
passed_signature=passed_signature,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
job_logger = JobLogger(
|
|
113
|
+
profile_metadata=profile_metadata, job_definition=job
|
|
114
|
+
)
|
|
115
|
+
client = construct_uncountable_client(
|
|
116
|
+
profile_meta=profile_meta, job_logger=job_logger
|
|
117
|
+
)
|
|
118
|
+
execute_job(
|
|
119
|
+
job_definition=job,
|
|
120
|
+
profile_metadata=profile_meta,
|
|
121
|
+
args=WebhookJobArguments(
|
|
122
|
+
job_definition=job,
|
|
123
|
+
profile_metadata=profile_metadata,
|
|
124
|
+
client=client,
|
|
125
|
+
batch_processor=AsyncBatchProcessor(client=client),
|
|
126
|
+
logger=job_logger,
|
|
127
|
+
payload=webhook_payload,
|
|
128
|
+
),
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
return flask.jsonify(WebhookResponse())
|
|
132
|
+
except WebhookException as e:
|
|
133
|
+
server_logger.log_exception(e)
|
|
134
|
+
return e.make_error_response()
|
|
135
|
+
except Exception as e:
|
|
136
|
+
server_logger.log_exception(e)
|
|
137
|
+
return WebhookException.unknown_error().make_error_response()
|
|
138
|
+
|
|
139
|
+
server_logger.log_info(f"job {job.id} webhook registered at: {route}")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
profiles = load_profiles()
|
|
143
|
+
for profile in profiles:
|
|
144
|
+
server_logger = Logger()
|
|
145
|
+
profile_metadata = job_definition_t.ProfileMetadata(
|
|
146
|
+
name=profile.name,
|
|
147
|
+
auth_retrieval=profile.definition.auth_retrieval,
|
|
148
|
+
base_url=profile.definition.base_url,
|
|
149
|
+
client_options=profile.definition.client_options,
|
|
150
|
+
)
|
|
151
|
+
for job in profile.definition.jobs:
|
|
152
|
+
if isinstance(job, job_definition_t.WebhookJobDefinition):
|
|
153
|
+
register_route(
|
|
154
|
+
server_logger=server_logger, profile_meta=profile_metadata, job=job
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
if __name__ == "__main__":
|
|
159
|
+
app.run(
|
|
160
|
+
host="0.0.0.0",
|
|
161
|
+
port=get_webhook_server_port(),
|
|
162
|
+
debug=get_integration_env() == "playground",
|
|
163
|
+
exclude_patterns=[],
|
|
164
|
+
)
|
|
@@ -97,6 +97,7 @@ from .api.recipes import unlock_recipes as unlock_recipes_t
|
|
|
97
97
|
from .api.material_families import update_entity_material_families as update_entity_material_families_t
|
|
98
98
|
from .api.field_options import upsert_field_options as upsert_field_options_t
|
|
99
99
|
from . import users_t as users_t
|
|
100
|
+
from . import webhook_job_t as webhook_job_t
|
|
100
101
|
from . import workflows_t as workflows_t
|
|
101
102
|
|
|
102
103
|
|
|
@@ -195,5 +196,6 @@ __all__: list[str] = [
|
|
|
195
196
|
"update_entity_material_families_t",
|
|
196
197
|
"upsert_field_options_t",
|
|
197
198
|
"users_t",
|
|
199
|
+
"webhook_job_t",
|
|
198
200
|
"workflows_t",
|
|
199
201
|
]
|
|
@@ -25,6 +25,7 @@ __all__: list[str] = [
|
|
|
25
25
|
"analytical_method_category": "Analytical Method Category",
|
|
26
26
|
"annotation_type": "Annotation Type",
|
|
27
27
|
"approval": "Approval",
|
|
28
|
+
"async_job": "Async Job",
|
|
28
29
|
"calculation": "Calculation",
|
|
29
30
|
"calendar": "Calendar",
|
|
30
31
|
"calendar_event": "Calendar Event",
|
|
@@ -176,6 +177,7 @@ class EntityType(StrEnum):
|
|
|
176
177
|
ANALYTICAL_METHOD_CATEGORY = "analytical_method_category"
|
|
177
178
|
ANNOTATION_TYPE = "annotation_type"
|
|
178
179
|
APPROVAL = "approval"
|
|
180
|
+
ASYNC_JOB = "async_job"
|
|
179
181
|
CALCULATION = "calculation"
|
|
180
182
|
CALENDAR = "calendar"
|
|
181
183
|
CALENDAR_EVENT = "calendar_event"
|
{uncountablepythonsdk-0.0.58 → uncountablepythonsdk-0.0.60}/uncountable/types/job_definition_t.py
RENAMED
|
@@ -191,11 +191,12 @@ class WebhookJobDefinition(JobDefinitionBase):
|
|
|
191
191
|
|
|
192
192
|
# DO NOT MODIFY -- This file is generated by type_spec
|
|
193
193
|
JobDefinition = typing.Annotated[
|
|
194
|
-
typing.Union[CronJobDefinition],
|
|
194
|
+
typing.Union[CronJobDefinition, WebhookJobDefinition],
|
|
195
195
|
serial_union_annotation(
|
|
196
196
|
discriminator="type",
|
|
197
197
|
discriminator_map={
|
|
198
198
|
"cron": CronJobDefinition,
|
|
199
|
+
"webhook": WebhookJobDefinition,
|
|
199
200
|
},
|
|
200
201
|
),
|
|
201
202
|
]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# flake8: noqa: F821
|
|
2
|
+
# ruff: noqa: E402 Q003
|
|
3
|
+
# fmt: off
|
|
4
|
+
# isort: skip_file
|
|
5
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|
|
6
|
+
# Kept only for SDK backwards compatibility
|
|
7
|
+
from .webhook_job_t import WebhookEventPayload as WebhookEventPayload
|
|
8
|
+
from .webhook_job_t import WebhookEventBody as WebhookEventBody
|
|
9
|
+
# DO NOT MODIFY -- This file is generated by type_spec
|