UncountablePythonSDK 0.0.31__tar.gz → 0.0.34__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.31/UncountablePythonSDK.egg-info → uncountablepythonsdk-0.0.34}/PKG-INFO +2 -2
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34/UncountablePythonSDK.egg-info}/PKG-INFO +2 -2
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/UncountablePythonSDK.egg-info/SOURCES.txt +2 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/UncountablePythonSDK.egg-info/requires.txt +1 -1
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/argument_parser/argument_parser.py +41 -2
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/serialization/__init__.py +2 -0
- uncountablepythonsdk-0.0.34/pkgs/serialization/serial_union.py +81 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/serialization_util/__init__.py +1 -7
- uncountablepythonsdk-0.0.34/pkgs/serialization_util/convert_to_snakecase.py +27 -0
- uncountablepythonsdk-0.0.34/pkgs/serialization_util/serialization_helpers.py +230 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/builder.py +57 -1
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/emit_open_api.py +6 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/emit_python.py +23 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/emit_typescript.py +6 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/type_info/emit_type_info.py +30 -4
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pyproject.toml +1 -1
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/id_source/match_id_source.py +0 -4
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/edit_recipe_inputs.py +13 -1
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/get_recipes_data.py +0 -3
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/recipe_workflow_steps.py +14 -3
- uncountablepythonsdk-0.0.31/pkgs/serialization_util/serialization_helpers.py +0 -175
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/.github/workflows/documentation.yml +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/.github/workflows/publish.yml +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/.gitignore +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/README.md +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/UncountablePythonSDK.egg-info/dependency_links.txt +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/UncountablePythonSDK.egg-info/top_level.txt +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/.gitignore +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/conf.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/index.md +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/justfile +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/quickstart.md +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/requirements.txt +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/static/favicons/android-chrome-192x192.png +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/static/favicons/android-chrome-512x512.png +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/static/favicons/apple-touch-icon.png +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/static/favicons/browserconfig.xml +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/static/favicons/favicon-16x16.png +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/static/favicons/favicon-32x32.png +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/static/favicons/manifest.json +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/static/favicons/mstile-150x150.png +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/static/favicons/safari-pinned-tab.svg +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/docs/static/logo_blue.png +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/examples/async_batch.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/examples/create_entity.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/examples/upload_files.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/argument_parser/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/argument_parser/_is_enum.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/argument_parser/_is_namedtuple.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/argument_parser/case_convert.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/serialization/missing_sentry.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/serialization/opaque_key.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/serialization/serial_class.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/serialization_util/_get_type_for_serialization.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/strenum_compat/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/strenum_compat/strenum_compat.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/__main__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/actions_registry/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/actions_registry/__main__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/actions_registry/emit_typescript.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/config.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/emit_io_ts.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/emit_open_api_util.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/emit_typescript_util.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/load_types.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/open_api_util.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/parts/base.py.prepart +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/parts/base.ts.prepart +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/test.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/type_info/__main__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/util.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/value_spec/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/value_spec/__main__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/value_spec/convert_type.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/value_spec/emit_python.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/value_spec/types.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/setup.cfg +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/core/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/core/async_batch.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/core/client.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/core/file_upload.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/core/types.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/integration/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/integration/construct_client.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/integration/cron.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/integration/db/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/integration/db/connect.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/integration/entrypoint.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/integration/executors/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/integration/executors/script_executor.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/integration/job.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/integration/server.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/integration/types.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/py.typed +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/batch/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/batch/execute_batch.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/batch/execute_batch_load_async.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/chemical/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/chemical/convert_chemical_formats.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/entity/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/entity/create_entities.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/entity/create_entity.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/entity/get_entities_data.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/entity/list_entities.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/entity/lock_entity.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/entity/resolve_entity_ids.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/entity/set_values.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/entity/transition_entity_phase.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/entity/unlock_entity.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/field_options/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/field_options/upsert_field_options.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/id_source/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/id_source/list_id_source.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/input_groups/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/input_groups/get_input_group_names.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/inputs/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/inputs/create_inputs.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/inputs/get_input_data.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/inputs/get_input_names.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/inputs/get_inputs_data.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/inputs/set_input_attribute_values.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/inputs/set_input_category.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/inputs/set_input_subcategories.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/inputs/set_intermediate_type.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/material_families/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/material_families/update_entity_material_families.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/outputs/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/outputs/get_output_data.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/outputs/get_output_names.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/outputs/resolve_output_conditions.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/permissions/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/permissions/set_core_permissions.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/project/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/project/get_projects.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/project/get_projects_data.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipe_links/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipe_links/create_recipe_link.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipe_metadata/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/add_recipe_to_project.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/archive_recipes.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/associate_recipe_as_input.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/associate_recipe_as_lot.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/create_recipe.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/create_recipes.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/disassociate_recipe_as_input.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/get_curve.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/get_recipe_calculations.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/get_recipe_links.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/get_recipe_names.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/get_recipe_output_metadata.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/remove_recipe_from_project.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/set_recipe_inputs.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/set_recipe_metadata.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/set_recipe_outputs.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/set_recipe_tags.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/recipes/unarchive_recipes.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/triggers/__init__.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/api/triggers/run_trigger.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/async_batch.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/async_batch_processor.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/base.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/calculations.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/chemical_structure.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/client_base.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/curves.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/entity.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/experiment_groups.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/field_values.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/fields.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/id_source.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/identifier.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/input_attributes.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/inputs.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/outputs.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/permissions.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/phases.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/post_base.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/recipe_identifiers.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/recipe_inputs.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/recipe_links.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/recipe_metadata.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/recipe_output_metadata.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/recipe_tags.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/recipes.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/response.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/units.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/users.py +0 -0
- {uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/uncountable/types/workflows.py +0 -0
{uncountablepythonsdk-0.0.31/UncountablePythonSDK.egg-info → uncountablepythonsdk-0.0.34}/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.34
|
|
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
|
|
@@ -19,7 +19,7 @@ Description-Content-Type: text/markdown
|
|
|
19
19
|
Requires-Dist: aiotus==0.*
|
|
20
20
|
Requires-Dist: aiohttp==3.*
|
|
21
21
|
Requires-Dist: requests==2.*
|
|
22
|
-
Requires-Dist: SQLAlchemy
|
|
22
|
+
Requires-Dist: SQLAlchemy>=1.4.0
|
|
23
23
|
Requires-Dist: APScheduler==3.*
|
|
24
24
|
Requires-Dist: python-dateutil==2.*
|
|
25
25
|
Requires-Dist: shelljob==0.*
|
{uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34/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.34
|
|
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
|
|
@@ -19,7 +19,7 @@ Description-Content-Type: text/markdown
|
|
|
19
19
|
Requires-Dist: aiotus==0.*
|
|
20
20
|
Requires-Dist: aiohttp==3.*
|
|
21
21
|
Requires-Dist: requests==2.*
|
|
22
|
-
Requires-Dist: SQLAlchemy
|
|
22
|
+
Requires-Dist: SQLAlchemy>=1.4.0
|
|
23
23
|
Requires-Dist: APScheduler==3.*
|
|
24
24
|
Requires-Dist: python-dateutil==2.*
|
|
25
25
|
Requires-Dist: shelljob==0.*
|
|
@@ -37,8 +37,10 @@ pkgs/serialization/__init__.py
|
|
|
37
37
|
pkgs/serialization/missing_sentry.py
|
|
38
38
|
pkgs/serialization/opaque_key.py
|
|
39
39
|
pkgs/serialization/serial_class.py
|
|
40
|
+
pkgs/serialization/serial_union.py
|
|
40
41
|
pkgs/serialization_util/__init__.py
|
|
41
42
|
pkgs/serialization_util/_get_type_for_serialization.py
|
|
43
|
+
pkgs/serialization_util/convert_to_snakecase.py
|
|
42
44
|
pkgs/serialization_util/serialization_helpers.py
|
|
43
45
|
pkgs/strenum_compat/__init__.py
|
|
44
46
|
pkgs/strenum_compat/strenum_compat.py
|
{uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/argument_parser/argument_parser.py
RENAMED
|
@@ -10,7 +10,12 @@ from importlib import resources
|
|
|
10
10
|
import dateutil.parser
|
|
11
11
|
import yaml
|
|
12
12
|
|
|
13
|
-
from pkgs.serialization import
|
|
13
|
+
from pkgs.serialization import (
|
|
14
|
+
MissingSentryType,
|
|
15
|
+
OpaqueKey,
|
|
16
|
+
get_serial_class_data,
|
|
17
|
+
get_serial_union_data,
|
|
18
|
+
)
|
|
14
19
|
|
|
15
20
|
from ._is_enum import is_string_enum_class
|
|
16
21
|
from ._is_namedtuple import is_namedtuple_type
|
|
@@ -112,6 +117,23 @@ def _invoke_membership_parser(
|
|
|
112
117
|
raise ValueError(f"Expected value from {expected_values} but got value {value}")
|
|
113
118
|
|
|
114
119
|
|
|
120
|
+
def _build_parser_discriminated_union(
|
|
121
|
+
discriminator: str, discriminator_map: dict[str, ParserFunction[T]]
|
|
122
|
+
) -> ParserFunction[T]:
|
|
123
|
+
def parse(value: typing.Any) -> typing.Any:
|
|
124
|
+
discriminant = value.get(discriminator)
|
|
125
|
+
if discriminant is None:
|
|
126
|
+
raise ValueError("missing-union-discriminant")
|
|
127
|
+
if not isinstance(discriminant, str):
|
|
128
|
+
raise ValueError("union-discriminant-is-not-string")
|
|
129
|
+
parser = discriminator_map.get(discriminant)
|
|
130
|
+
if parser is None:
|
|
131
|
+
raise ValueError("missing-type-for-union-discriminant", discriminant)
|
|
132
|
+
return parser(value)
|
|
133
|
+
|
|
134
|
+
return parse
|
|
135
|
+
|
|
136
|
+
|
|
115
137
|
def _build_parser_inner(
|
|
116
138
|
parsed_type: type[T],
|
|
117
139
|
context: ParserContext,
|
|
@@ -130,6 +152,23 @@ def _build_parser_inner(
|
|
|
130
152
|
are cached now, as they don't use the argument, and they're known to be safe.
|
|
131
153
|
This is also enough to support some recursion.
|
|
132
154
|
"""
|
|
155
|
+
|
|
156
|
+
serial_union = get_serial_union_data(parsed_type)
|
|
157
|
+
if serial_union is not None:
|
|
158
|
+
discriminator = serial_union.discriminator
|
|
159
|
+
discriminator_map = serial_union.discriminator_map
|
|
160
|
+
if discriminator is None or discriminator_map is None:
|
|
161
|
+
# fallback to standard union parsing
|
|
162
|
+
parsed_type = serial_union.get_union_underlying()
|
|
163
|
+
else:
|
|
164
|
+
return _build_parser_discriminated_union(
|
|
165
|
+
discriminator,
|
|
166
|
+
{
|
|
167
|
+
key: _build_parser_inner(value, context)
|
|
168
|
+
for key, value in discriminator_map.items()
|
|
169
|
+
},
|
|
170
|
+
)
|
|
171
|
+
|
|
133
172
|
if dataclasses.is_dataclass(parsed_type):
|
|
134
173
|
return _build_parser_dataclass(parsed_type, context) # type: ignore[arg-type]
|
|
135
174
|
|
|
@@ -341,7 +380,7 @@ def _build_parser_dataclass(
|
|
|
341
380
|
|
|
342
381
|
except Exception as e:
|
|
343
382
|
raise ValueError(
|
|
344
|
-
f"unable
|
|
383
|
+
f"unable-to-parse-field:{field.name}", field_raw_value
|
|
345
384
|
) from e
|
|
346
385
|
|
|
347
386
|
if context.options.strict_property_parsing:
|
|
@@ -8,3 +8,5 @@ from .serial_class import get_serial_class_data as get_serial_class_data
|
|
|
8
8
|
from .serial_class import get_serial_string_enum_data as get_serial_string_enum_data
|
|
9
9
|
from .serial_class import serial_class as serial_class
|
|
10
10
|
from .serial_class import serial_string_enum as serial_string_enum
|
|
11
|
+
from .serial_union import serial_union_annotation as serial_union_annotation
|
|
12
|
+
from .serial_union import get_serial_union_data as get_serial_union_data
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
T = typing.TypeVar("T")
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class IdentityHashWrapper(typing.Generic[T]):
|
|
8
|
+
"""This allows unhashable types to be used in the SerialUnion, like dict.
|
|
9
|
+
Since we have only one copy of the types themselves, we rely on
|
|
10
|
+
object identity for the hashing."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, inner: T) -> None:
|
|
13
|
+
self.inner = inner
|
|
14
|
+
|
|
15
|
+
def __hash__(self) -> int:
|
|
16
|
+
return id(self.inner)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclasses.dataclass(kw_only=True, frozen=True, eq=True)
|
|
20
|
+
class _SerialUnion:
|
|
21
|
+
"""
|
|
22
|
+
This class is to be kept private, to provide flexibility in registration/lookup.
|
|
23
|
+
Places that need the data should access it via help classes/methods.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
# If specified, indicates the Union has a discriminator which should be used to
|
|
27
|
+
# determine which type to parse.
|
|
28
|
+
discriminator: typing.Optional[str] = None
|
|
29
|
+
discriminator_map: typing.Optional[IdentityHashWrapper[dict[str, type]]] = None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def serial_union_annotation(
|
|
33
|
+
*,
|
|
34
|
+
discriminator: typing.Optional[str] = None,
|
|
35
|
+
discriminator_map: typing.Optional[dict[str, type]] = None,
|
|
36
|
+
) -> _SerialUnion:
|
|
37
|
+
return _SerialUnion(
|
|
38
|
+
discriminator=discriminator,
|
|
39
|
+
discriminator_map=IdentityHashWrapper(discriminator_map)
|
|
40
|
+
if discriminator_map is not None
|
|
41
|
+
else None,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _get_serial_union(parsed_type: type[T]) -> _SerialUnion | None:
|
|
46
|
+
if not hasattr(parsed_type, "__metadata__"):
|
|
47
|
+
return None
|
|
48
|
+
metadata = parsed_type.__metadata__ # type:ignore[attr-defined]
|
|
49
|
+
if not isinstance(metadata, tuple) or len(metadata) != 1:
|
|
50
|
+
return None
|
|
51
|
+
serial = metadata[0]
|
|
52
|
+
if not isinstance(serial, _SerialUnion):
|
|
53
|
+
return None
|
|
54
|
+
return serial
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class SerialClassInspector(typing.Generic[T]):
|
|
58
|
+
def __init__(self, parsed_type: type[T], serial_union: _SerialUnion) -> None:
|
|
59
|
+
self._parsed_type = parsed_type
|
|
60
|
+
self._serial_union = serial_union
|
|
61
|
+
|
|
62
|
+
def get_union_underlying(self) -> type[T]:
|
|
63
|
+
return typing.get_args(self._parsed_type)[0] # type:ignore[no-any-return]
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def discriminator(self) -> typing.Optional[str]:
|
|
67
|
+
return self._serial_union.discriminator
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def discriminator_map(self) -> typing.Optional[dict[str, type]]:
|
|
71
|
+
if self._serial_union.discriminator_map is None:
|
|
72
|
+
return None
|
|
73
|
+
return self._serial_union.discriminator_map.inner
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_serial_union_data(parsed_type: type[T]) -> SerialClassInspector[T] | None:
|
|
77
|
+
serial = _get_serial_union(parsed_type)
|
|
78
|
+
if serial is None:
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
return SerialClassInspector(parsed_type, serial)
|
{uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/serialization_util/__init__.py
RENAMED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
+
from .convert_to_snakecase import convert_dict_to_snake_case
|
|
1
2
|
from .serialization_helpers import (
|
|
2
|
-
convert_dict_to_snake_case,
|
|
3
|
-
convert_to_camelcase,
|
|
4
|
-
resolve_missing_to_none,
|
|
5
|
-
serialize,
|
|
6
3
|
serialize_for_api,
|
|
7
4
|
serialize_for_storage,
|
|
8
5
|
serialize_for_storage_dict,
|
|
@@ -10,9 +7,6 @@ from .serialization_helpers import (
|
|
|
10
7
|
|
|
11
8
|
__all__: list[str] = [
|
|
12
9
|
"convert_dict_to_snake_case",
|
|
13
|
-
"convert_to_camelcase",
|
|
14
|
-
"resolve_missing_to_none",
|
|
15
|
-
"serialize",
|
|
16
10
|
"serialize_for_api",
|
|
17
11
|
"serialize_for_storage",
|
|
18
12
|
"serialize_for_storage_dict",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from typing import (
|
|
2
|
+
Any,
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
from pkgs.argument_parser import camel_to_snake_case
|
|
6
|
+
from pkgs.serialization import (
|
|
7
|
+
MISSING_SENTRY,
|
|
8
|
+
OpaqueKey,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _key_convert_to_snake_case(o: Any) -> Any:
|
|
13
|
+
if isinstance(o, OpaqueKey):
|
|
14
|
+
return o
|
|
15
|
+
if isinstance(o, str):
|
|
16
|
+
return camel_to_snake_case(o)
|
|
17
|
+
return o
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def convert_dict_to_snake_case(data: Any) -> Any:
|
|
21
|
+
return {
|
|
22
|
+
_key_convert_to_snake_case(k): convert_dict_to_snake_case(v)
|
|
23
|
+
if isinstance(v, dict)
|
|
24
|
+
else v
|
|
25
|
+
for k, v in data.items()
|
|
26
|
+
if v != MISSING_SENTRY
|
|
27
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import datetime
|
|
3
|
+
import enum
|
|
4
|
+
import functools
|
|
5
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
6
|
+
from decimal import Decimal
|
|
7
|
+
from typing import (
|
|
8
|
+
TYPE_CHECKING,
|
|
9
|
+
Any,
|
|
10
|
+
ClassVar,
|
|
11
|
+
Protocol,
|
|
12
|
+
TypeVar,
|
|
13
|
+
Union,
|
|
14
|
+
overload,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
import flask
|
|
19
|
+
except Exception:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
from pkgs.argument_parser import snake_to_camel_case
|
|
23
|
+
from pkgs.serialization import (
|
|
24
|
+
MISSING_SENTRY,
|
|
25
|
+
OpaqueKey,
|
|
26
|
+
get_serial_class_data,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from ._get_type_for_serialization import SerializationType, get_serialization_type
|
|
30
|
+
|
|
31
|
+
# Inlined types which otherwise would import from types/base.py
|
|
32
|
+
JsonScalar = Union[str, float, bool, Decimal, None, datetime.datetime, datetime.date]
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
JsonValue = Union[JsonScalar, Mapping[str, "JsonValue"], Sequence["JsonValue"]]
|
|
35
|
+
else:
|
|
36
|
+
JsonValue = Union[JsonScalar, dict[str, Any], list[Any]]
|
|
37
|
+
|
|
38
|
+
T = TypeVar("T")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Dataclass(Protocol):
|
|
42
|
+
__dataclass_fields__: ClassVar[dict] # type: ignore[type-arg,unused-ignore]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def identity(x: T) -> T:
|
|
46
|
+
return x
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def key_convert_to_camelcase(o: Any) -> str:
|
|
50
|
+
if isinstance(o, OpaqueKey):
|
|
51
|
+
return o
|
|
52
|
+
if isinstance(o, enum.StrEnum):
|
|
53
|
+
return o.value
|
|
54
|
+
if isinstance(o, enum.Enum):
|
|
55
|
+
try:
|
|
56
|
+
print(
|
|
57
|
+
"DEPRECATED_SERIALIZATION--non-string-enum-used-as-key",
|
|
58
|
+
flask.request.path,
|
|
59
|
+
)
|
|
60
|
+
except Exception:
|
|
61
|
+
pass
|
|
62
|
+
return o.value # type: ignore[no-any-return]
|
|
63
|
+
if isinstance(o, str):
|
|
64
|
+
return snake_to_camel_case(o)
|
|
65
|
+
if isinstance(o, int):
|
|
66
|
+
# temporary bypass to maintain behavior
|
|
67
|
+
return o # type: ignore[return-value]
|
|
68
|
+
try:
|
|
69
|
+
print("DEPRECATED_SERIALIZATION--non-string-used-as-key", o, flask.request.path)
|
|
70
|
+
except Exception:
|
|
71
|
+
pass
|
|
72
|
+
return o # type: ignore[no-any-return]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _convert_dict(d: dict[str, Any]) -> dict[str, JsonValue]:
|
|
76
|
+
return {
|
|
77
|
+
key_convert_to_camelcase(k): serialize_for_api(v)
|
|
78
|
+
for k, v in d.items()
|
|
79
|
+
if v != MISSING_SENTRY
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _serialize_dict(d: dict[str, Any]) -> dict[str, JsonValue]:
|
|
84
|
+
return {k: serialize_for_storage(v) for k, v in d.items() if v != MISSING_SENTRY}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _to_string_value(value: Any) -> str:
|
|
88
|
+
assert isinstance(value, (Decimal, int))
|
|
89
|
+
return str(value)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclasses.dataclass(kw_only=True)
|
|
93
|
+
class DataclassConversions:
|
|
94
|
+
key_conversions: dict[str, str]
|
|
95
|
+
value_conversion_functions: dict[str, Callable[[Any], JsonValue]]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@functools.lru_cache(maxsize=10000)
|
|
99
|
+
def _get_dataclass_conversion_lookups(dataclass_type: Any) -> DataclassConversions:
|
|
100
|
+
scd = get_serial_class_data(dataclass_type)
|
|
101
|
+
|
|
102
|
+
key_conversions: dict[str, str] = {}
|
|
103
|
+
value_conversion_functions: dict[str, Callable[[Any], JsonValue]] = {}
|
|
104
|
+
|
|
105
|
+
for field in dataclasses.fields(dataclass_type):
|
|
106
|
+
key = field.name
|
|
107
|
+
if scd.has_unconverted_key(key):
|
|
108
|
+
key_conversions[key] = key
|
|
109
|
+
else:
|
|
110
|
+
key_conversions[key] = key_convert_to_camelcase(key)
|
|
111
|
+
|
|
112
|
+
if scd.has_to_string_value(key):
|
|
113
|
+
value_conversion_functions[key] = _to_string_value
|
|
114
|
+
elif scd.has_unconverted_value(key):
|
|
115
|
+
value_conversion_functions[key] = identity
|
|
116
|
+
else:
|
|
117
|
+
value_conversion_functions[key] = serialize_for_api
|
|
118
|
+
|
|
119
|
+
return DataclassConversions(
|
|
120
|
+
key_conversions=key_conversions,
|
|
121
|
+
value_conversion_functions=value_conversion_functions,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _convert_dataclass(d: Any) -> dict[str, JsonValue]:
|
|
126
|
+
conversions = _get_dataclass_conversion_lookups(type(d)) # type: ignore[arg-type]
|
|
127
|
+
# return {
|
|
128
|
+
# conversions.key_conversions[k]: (
|
|
129
|
+
# conversions.value_conversion_functions[k](v) if v is not None else None
|
|
130
|
+
# )
|
|
131
|
+
# for k, v in d.__dict__.items()
|
|
132
|
+
# if v != MISSING_SENTRY
|
|
133
|
+
# }
|
|
134
|
+
|
|
135
|
+
serialized_data_class: dict[str, JsonValue] = {}
|
|
136
|
+
for k, v in d.__dict__.items():
|
|
137
|
+
if v == MISSING_SENTRY:
|
|
138
|
+
continue
|
|
139
|
+
if k not in conversions.key_conversions:
|
|
140
|
+
try:
|
|
141
|
+
print(
|
|
142
|
+
"DEPRECATED_SERIALIZATION--missing-dataclass-key",
|
|
143
|
+
k,
|
|
144
|
+
flask.request.path,
|
|
145
|
+
)
|
|
146
|
+
except Exception:
|
|
147
|
+
pass
|
|
148
|
+
continue
|
|
149
|
+
serialized_data_class[conversions.key_conversions[k]] = (
|
|
150
|
+
conversions.value_conversion_functions[k](v) if v is not None else None
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return serialized_data_class
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
_SERIALIZATION_FUNCS_STANDARD = {
|
|
157
|
+
SerializationType.ENUM: lambda x: str(x.value),
|
|
158
|
+
SerializationType.DATE: lambda x: x.isoformat(),
|
|
159
|
+
SerializationType.TIMEDELTA: lambda x: x.total_seconds(),
|
|
160
|
+
SerializationType.UNKNOWN: identity,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
_CONVERSION_SERIALIZATION_FUNCS: dict[SerializationType, Callable[[Any], JsonValue]] = {
|
|
164
|
+
**_SERIALIZATION_FUNCS_STANDARD,
|
|
165
|
+
SerializationType.NAMED_TUPLE: lambda x: _convert_dict(x._asdict()),
|
|
166
|
+
SerializationType.ITERABLE: lambda x: [serialize_for_api(v) for v in x],
|
|
167
|
+
SerializationType.DICT: _convert_dict,
|
|
168
|
+
SerializationType.DATACLASS: _convert_dataclass,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@overload
|
|
173
|
+
def serialize_for_api(obj: None) -> None: ...
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@overload
|
|
177
|
+
def serialize_for_api(obj: dict[str, Any]) -> dict[str, JsonValue]: ...
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@overload
|
|
181
|
+
def serialize_for_api(obj: Dataclass) -> dict[str, JsonValue]: ...
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@overload
|
|
185
|
+
def serialize_for_api(obj: Any) -> JsonValue: ...
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def serialize_for_api(obj: Any) -> JsonValue:
|
|
189
|
+
"""
|
|
190
|
+
Serialize to a parsed-JSON format suitably encoded for API output.
|
|
191
|
+
|
|
192
|
+
Use the CachedParser.parse_api to parse this data.
|
|
193
|
+
"""
|
|
194
|
+
serialization_type = get_serialization_type(type(obj)) # type: ignore
|
|
195
|
+
return _CONVERSION_SERIALIZATION_FUNCS[serialization_type](obj)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
_SERIALIZATION_FUNCS_DICT: dict[
|
|
199
|
+
SerializationType, Callable[[Any], dict[str, JsonValue]]
|
|
200
|
+
] = {
|
|
201
|
+
SerializationType.DICT: _serialize_dict,
|
|
202
|
+
SerializationType.DATACLASS: lambda x: _serialize_dict(x.__dict__),
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
_SERIALIZATION_FUNCS: dict[SerializationType, Callable[[Any], JsonValue]] = {
|
|
207
|
+
**_SERIALIZATION_FUNCS_STANDARD,
|
|
208
|
+
**_SERIALIZATION_FUNCS_DICT,
|
|
209
|
+
SerializationType.NAMED_TUPLE: lambda x: _serialize_dict(x._asdict()),
|
|
210
|
+
SerializationType.ITERABLE: lambda x: [serialize_for_storage(v) for v in x],
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def serialize_for_storage(obj: Any) -> JsonValue:
|
|
215
|
+
"""
|
|
216
|
+
Convert a value into the pseudo-JSON form for
|
|
217
|
+
storage in the DB, file, or other non-API use.
|
|
218
|
+
|
|
219
|
+
Use the CachedParser.parse_storage to parse this data.
|
|
220
|
+
"""
|
|
221
|
+
serialization_type = get_serialization_type(type(obj)) # type: ignore
|
|
222
|
+
return _SERIALIZATION_FUNCS[serialization_type](obj)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def serialize_for_storage_dict(obj: dict | Dataclass) -> dict[str, JsonValue]: # type: ignore[type-arg]
|
|
226
|
+
"""
|
|
227
|
+
Same as serialize for storage but guarantees outer object is a dictionary
|
|
228
|
+
"""
|
|
229
|
+
serialization_type = get_serialization_type(type(obj))
|
|
230
|
+
return _SERIALIZATION_FUNCS_DICT[serialization_type](obj)
|
|
@@ -102,6 +102,8 @@ class DefnTypeName(StrEnum):
|
|
|
102
102
|
s_string_enum = "StringEnum"
|
|
103
103
|
# a particular literal value
|
|
104
104
|
s_string_literal = "_StringLiteral"
|
|
105
|
+
# A union of several other types
|
|
106
|
+
s_union = "Union"
|
|
105
107
|
|
|
106
108
|
|
|
107
109
|
base_namespace_name = "base"
|
|
@@ -547,13 +549,65 @@ class SpecTypeDefnAlias(SpecTypeDefn):
|
|
|
547
549
|
super().base_process(builder, data, ["type", "desc", "alias", "discriminator"])
|
|
548
550
|
self.alias = builder.parse_type(self.namespace, data["alias"])
|
|
549
551
|
self.desc = data.get("desc", None)
|
|
550
|
-
# Should be limited to Union type aliases
|
|
551
552
|
self.discriminator = data.get("discriminator", None)
|
|
552
553
|
|
|
553
554
|
def get_referenced_types(self) -> list[SpecType]:
|
|
554
555
|
return [self.alias]
|
|
555
556
|
|
|
556
557
|
|
|
558
|
+
class SpecTypeDefnUnion(SpecTypeDefn):
|
|
559
|
+
def __init__(self, namespace: SpecNamespace, name: str) -> None:
|
|
560
|
+
super().__init__(namespace, name)
|
|
561
|
+
self.discriminator: str | None = None
|
|
562
|
+
self.types: list[SpecType] = []
|
|
563
|
+
self._alias_type: SpecType | None = None
|
|
564
|
+
self.discriminator_map: dict[str, SpecType] | None = None
|
|
565
|
+
self.desc: str | None = None
|
|
566
|
+
|
|
567
|
+
def process(self, builder: SpecBuilder, data: RawDict) -> None:
|
|
568
|
+
super().base_process(builder, data, ["type", "desc", "types", "discriminator"])
|
|
569
|
+
|
|
570
|
+
self.desc = data.get("desc", None)
|
|
571
|
+
self.discriminator = data.get("discriminator", None)
|
|
572
|
+
|
|
573
|
+
for sub_type_str in data["types"]:
|
|
574
|
+
sub_type = builder.parse_type(self.namespace, sub_type_str)
|
|
575
|
+
self.types.append(sub_type)
|
|
576
|
+
|
|
577
|
+
base_type = builder.namespaces[base_namespace_name].types[BaseTypeName.s_union]
|
|
578
|
+
self._backing_type = SpecTypeInstance(base_type, self.types)
|
|
579
|
+
|
|
580
|
+
if self.discriminator is not None:
|
|
581
|
+
self.discriminator_map = {}
|
|
582
|
+
for sub_type in self.types:
|
|
583
|
+
builder.push_where(sub_type.name)
|
|
584
|
+
assert isinstance(
|
|
585
|
+
sub_type, SpecTypeDefnObject
|
|
586
|
+
), "union-type-must-be-object"
|
|
587
|
+
assert sub_type.properties is not None
|
|
588
|
+
discriminator_type = sub_type.properties.get(self.discriminator)
|
|
589
|
+
assert (
|
|
590
|
+
discriminator_type is not None
|
|
591
|
+
), f"missing-discriminator-field: {sub_type}"
|
|
592
|
+
prop_type = unwrap_literal_type(discriminator_type.spec_type)
|
|
593
|
+
assert prop_type is not None
|
|
594
|
+
assert prop_type.is_value_to_string()
|
|
595
|
+
discriminant = str(prop_type.value)
|
|
596
|
+
assert (
|
|
597
|
+
discriminant not in self.discriminator_map
|
|
598
|
+
), f"duplicated-discriminant, {discriminant} in {sub_type}"
|
|
599
|
+
self.discriminator_map[discriminant] = sub_type
|
|
600
|
+
|
|
601
|
+
builder.pop_where()
|
|
602
|
+
|
|
603
|
+
def get_referenced_types(self) -> list[SpecType]:
|
|
604
|
+
return self.types
|
|
605
|
+
|
|
606
|
+
def get_backing_type(self) -> SpecType:
|
|
607
|
+
assert self._backing_type is not None
|
|
608
|
+
return self._backing_type
|
|
609
|
+
|
|
610
|
+
|
|
557
611
|
class SpecTypeDefnExternal(SpecTypeDefn):
|
|
558
612
|
external_map: dict[str, str]
|
|
559
613
|
|
|
@@ -1017,6 +1071,8 @@ class SpecNamespace:
|
|
|
1017
1071
|
spec_type: SpecTypeDefn
|
|
1018
1072
|
if defn_type == DefnTypeName.s_alias:
|
|
1019
1073
|
spec_type = SpecTypeDefnAlias(self, name)
|
|
1074
|
+
elif defn_type == DefnTypeName.s_union:
|
|
1075
|
+
spec_type = SpecTypeDefnUnion(self, name)
|
|
1020
1076
|
elif defn_type == DefnTypeName.s_external:
|
|
1021
1077
|
spec_type = SpecTypeDefnExternal(self, name)
|
|
1022
1078
|
elif defn_type == DefnTypeName.s_string_enum:
|
|
@@ -430,6 +430,12 @@ def _emit_type(
|
|
|
430
430
|
ctx.types[stype.name] = open_api_type(ctx, stype.alias, config=config)
|
|
431
431
|
return
|
|
432
432
|
|
|
433
|
+
if isinstance(stype, builder.SpecTypeDefnUnion):
|
|
434
|
+
ctx.types[stype.name] = open_api_type(
|
|
435
|
+
ctx, stype.get_backing_type(), config=config
|
|
436
|
+
)
|
|
437
|
+
return
|
|
438
|
+
|
|
433
439
|
if isinstance(stype, builder.SpecTypeDefnStringEnum):
|
|
434
440
|
# TODO: check that these are always string enums
|
|
435
441
|
# IMPROVE: reflect the enum names in the description
|
|
@@ -43,6 +43,7 @@ class TrackingContext:
|
|
|
43
43
|
use_serial_string_enum: bool = False
|
|
44
44
|
use_dataclass: bool = False
|
|
45
45
|
use_serial_class: bool = False
|
|
46
|
+
use_serial_union: bool = False
|
|
46
47
|
use_missing: bool = False
|
|
47
48
|
use_opaque_key: bool = False
|
|
48
49
|
|
|
@@ -219,6 +220,8 @@ def _emit_types_imports(*, out: io.StringIO, ctx: Context) -> None:
|
|
|
219
220
|
out.write("from dataclasses import dataclass\n")
|
|
220
221
|
if ctx.use_serial_class:
|
|
221
222
|
out.write("from pkgs.serialization import serial_class\n")
|
|
223
|
+
if ctx.use_serial_union:
|
|
224
|
+
out.write("from pkgs.serialization import serial_union_annotation\n")
|
|
222
225
|
if ctx.use_serial_string_enum:
|
|
223
226
|
out.write("from pkgs.serialization import serial_string_enum\n")
|
|
224
227
|
if ctx.use_missing:
|
|
@@ -727,6 +730,26 @@ def _emit_type(ctx: Context, stype: builder.SpecType) -> None:
|
|
|
727
730
|
ctx.out.write(f"{stype.name} = {refer_to(ctx, stype.alias)}\n")
|
|
728
731
|
return
|
|
729
732
|
|
|
733
|
+
if isinstance(stype, builder.SpecTypeDefnUnion):
|
|
734
|
+
ctx.use_serial_union = True
|
|
735
|
+
ctx.out.write(f"{stype.name} = typing.Annotated[\n")
|
|
736
|
+
ctx.out.write(f"{INDENT}{refer_to(ctx, stype.get_backing_type())},\n")
|
|
737
|
+
ctx.out.write(f"{INDENT}serial_union_annotation(\n")
|
|
738
|
+
if stype.discriminator is not None:
|
|
739
|
+
ctx.out.write(
|
|
740
|
+
f"{INDENT * 2}discriminator={util.encode_common_string(stype.discriminator)},\n"
|
|
741
|
+
)
|
|
742
|
+
if stype.discriminator_map is not None:
|
|
743
|
+
ctx.out.write(f"{INDENT * 2}discriminator_map={{\n")
|
|
744
|
+
for key, value in stype.discriminator_map.items():
|
|
745
|
+
ctx.out.write(
|
|
746
|
+
f"{INDENT * 3}{util.encode_common_string(key)}: {refer_to(ctx, value)},\n"
|
|
747
|
+
)
|
|
748
|
+
ctx.out.write(f"{INDENT * 2}}},\n")
|
|
749
|
+
ctx.out.write(f"{INDENT}),\n")
|
|
750
|
+
ctx.out.write("]\n")
|
|
751
|
+
return
|
|
752
|
+
|
|
730
753
|
if isinstance(stype, builder.SpecTypeDefnStringEnum):
|
|
731
754
|
return _emit_string_enum(ctx, stype)
|
|
732
755
|
|
{uncountablepythonsdk-0.0.31 → uncountablepythonsdk-0.0.34}/pkgs/type_spec/emit_typescript.py
RENAMED
|
@@ -303,6 +303,12 @@ def _emit_type(ctx: EmitTypescriptContext, stype: builder.SpecType) -> None:
|
|
|
303
303
|
ctx.out.write(f"export type {stype.name} = {refer_to(ctx, stype.alias)}\n")
|
|
304
304
|
return
|
|
305
305
|
|
|
306
|
+
if isinstance(stype, builder.SpecTypeDefnUnion):
|
|
307
|
+
ctx.out.write(
|
|
308
|
+
f"export type {stype.name} = {refer_to(ctx, stype.get_backing_type())}\n"
|
|
309
|
+
)
|
|
310
|
+
return
|
|
311
|
+
|
|
306
312
|
if isinstance(stype, builder.SpecTypeDefnStringEnum):
|
|
307
313
|
ctx.out.write(f"export enum {stype.name} {{\n")
|
|
308
314
|
assert stype.values
|