UncountablePythonSDK 0.0.190__tar.gz → 0.0.191__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (462) hide show
  1. {uncountablepythonsdk-0.0.190/UncountablePythonSDK.egg-info → uncountablepythonsdk-0.0.191}/PKG-INFO +1 -1
  2. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191/UncountablePythonSDK.egg-info}/PKG-INFO +1 -1
  3. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/UncountablePythonSDK.egg-info/SOURCES.txt +2 -0
  4. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/UncountablePythonSDK.egg-info/scm_file_list.json +2 -0
  5. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/emit_typescript_util.py +25 -4
  6. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/parts/base.ts.prepart +6 -1
  7. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/cli.py +2 -0
  8. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/job.py +47 -30
  9. uncountablepythonsdk-0.0.191/uncountable/integration/job_queue.py +14 -0
  10. uncountablepythonsdk-0.0.191/uncountable/integration/ownership_lock.py +77 -0
  11. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/command_server/command_client.py +2 -0
  12. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/command_server/command_server.py +2 -0
  13. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/command_server/protocol/command_server.proto +1 -0
  14. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.py +4 -4
  15. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/command_server/protocol/command_server_pb2.pyi +2 -0
  16. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/command_server/types.py +1 -0
  17. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/datastore/datastore_sql_base.py +14 -7
  18. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/datastore/interface.py +6 -1
  19. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/job_scheduler.py +66 -23
  20. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/worker.py +8 -0
  21. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/scheduler.py +86 -57
  22. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/webhook_server/entrypoint.py +17 -5
  23. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/entity_t.py +2 -0
  24. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/.github/workflows/documentation.yml +0 -0
  25. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/.github/workflows/publish.yml +0 -0
  26. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/.gitignore +0 -0
  27. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/README.md +0 -0
  28. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/UncountablePythonSDK.egg-info/dependency_links.txt +0 -0
  29. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/UncountablePythonSDK.egg-info/requires.txt +0 -0
  30. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/UncountablePythonSDK.egg-info/top_level.txt +0 -0
  31. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/.flowmarkignore +0 -0
  32. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/.gitignore +0 -0
  33. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/conf.py +0 -0
  34. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/index.md +0 -0
  35. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/integration_examples/add_file_to_folder.md +0 -0
  36. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/integration_examples/create_ingredient.md +0 -0
  37. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/integration_examples/create_output.md +0 -0
  38. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/integration_examples/index.md +0 -0
  39. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/integration_examples/upload_file.md +0 -0
  40. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/justfile +0 -0
  41. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/requirements.txt +0 -0
  42. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/static/favicons/android-chrome-192x192.png +0 -0
  43. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/static/favicons/android-chrome-512x512.png +0 -0
  44. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/static/favicons/apple-touch-icon.png +0 -0
  45. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/static/favicons/browserconfig.xml +0 -0
  46. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/static/favicons/favicon-16x16.png +0 -0
  47. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/static/favicons/favicon-32x32.png +0 -0
  48. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/static/favicons/manifest.json +0 -0
  49. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/static/favicons/mstile-150x150.png +0 -0
  50. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/static/favicons/safari-pinned-tab.svg +0 -0
  51. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/docs/static/logo_blue.png +0 -0
  52. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/async_batch.py +0 -0
  53. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/async_batch_large_parse.py +0 -0
  54. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/basic_auth.py +0 -0
  55. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/create_entity.py +0 -0
  56. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/create_ingredient_sdk.py +0 -0
  57. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/download_files.py +0 -0
  58. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/edit_recipe_inputs.py +0 -0
  59. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/integration-server/jobs/materials_auto/concurrent_cron.py +0 -0
  60. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/integration-server/jobs/materials_auto/example_cron.py +0 -0
  61. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/integration-server/jobs/materials_auto/example_http.py +0 -0
  62. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/integration-server/jobs/materials_auto/example_http_get.py +0 -0
  63. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/integration-server/jobs/materials_auto/example_instrument.py +0 -0
  64. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/integration-server/jobs/materials_auto/example_parse.py +0 -0
  65. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/integration-server/jobs/materials_auto/example_predictions.py +0 -0
  66. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/integration-server/jobs/materials_auto/example_runsheet_wh.py +0 -0
  67. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/integration-server/jobs/materials_auto/example_scale_connection.py +0 -0
  68. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/integration-server/jobs/materials_auto/example_wh.py +0 -0
  69. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/integration-server/jobs/materials_auto/profile.yaml +0 -0
  70. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/integration-server/pyproject.toml +0 -0
  71. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/invoke_uploader.py +0 -0
  72. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/oauth.py +0 -0
  73. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/set_input_pdf_attribute.py +0 -0
  74. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/set_recipe_metadata_file.py +0 -0
  75. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/set_recipe_output_file_sdk.py +0 -0
  76. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/stream_dataset_measurements.py +0 -0
  77. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/examples/upload_files.py +0 -0
  78. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/__init__.py +0 -0
  79. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/argument_parser/__init__.py +0 -0
  80. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/argument_parser/cached_parser.py +0 -0
  81. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/argument_parser/case_convert.py +0 -0
  82. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/argument_parser/field_name_resolution.py +0 -0
  83. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/argument_parser/parser_base.py +0 -0
  84. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/argument_parser/parser_builder.py +0 -0
  85. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/argument_parser/parser_cache.py +0 -0
  86. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/argument_parser/parser_error.py +0 -0
  87. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/argument_parser/parser_function_type.py +0 -0
  88. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/argument_parser/parser_inner.py +0 -0
  89. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/argument_parser/parser_options.py +0 -0
  90. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/argument_parser/type_predicates.py +0 -0
  91. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/filesystem_utils/__init__.py +0 -0
  92. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/filesystem_utils/_blob_session.py +0 -0
  93. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/filesystem_utils/_file_share_session.py +0 -0
  94. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/filesystem_utils/_gdrive_session.py +0 -0
  95. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/filesystem_utils/_local_session.py +0 -0
  96. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/filesystem_utils/_s3_session.py +0 -0
  97. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/filesystem_utils/_sftp_connection.py +0 -0
  98. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/filesystem_utils/_sftp_session.py +0 -0
  99. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/filesystem_utils/file_type_utils.py +0 -0
  100. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/filesystem_utils/filesystem_session.py +0 -0
  101. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/py.typed +0 -0
  102. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization/__init__.py +0 -0
  103. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization/annotation.py +0 -0
  104. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization/missing_sentry.py +0 -0
  105. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization/opaque_key.py +0 -0
  106. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization/serial_alias.py +0 -0
  107. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization/serial_class.py +0 -0
  108. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization/serial_generic.py +0 -0
  109. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization/serial_union.py +0 -0
  110. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization/unknown.py +0 -0
  111. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization/usage.py +0 -0
  112. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization/yaml.py +0 -0
  113. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization_util/__init__.py +0 -0
  114. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization_util/_get_type_for_serialization.py +0 -0
  115. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization_util/convert_to_snakecase.py +0 -0
  116. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization_util/dataclasses.py +0 -0
  117. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization_util/reuse_containers.py +0 -0
  118. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization_util/serialization_helpers.py +0 -0
  119. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization_util/serialize_for_api_encoding.py +0 -0
  120. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/serialization_util/source_encoding_serializer.py +0 -0
  121. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/strenum_compat/__init__.py +0 -0
  122. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/strenum_compat/strenum_compat.py +0 -0
  123. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/AGENTS.md +0 -0
  124. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/CLAUDE.md +0 -0
  125. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/__init__.py +0 -0
  126. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/__main__.py +0 -0
  127. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/actions_registry/__init__.py +0 -0
  128. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/actions_registry/__main__.py +0 -0
  129. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/actions_registry/emit_python.py +0 -0
  130. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/actions_registry/emit_typescript.py +0 -0
  131. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/builder.py +0 -0
  132. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/builder_types.py +0 -0
  133. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/config.py +0 -0
  134. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/cross_output_links.py +0 -0
  135. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/emit_io_ts.py +0 -0
  136. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/emit_open_api.py +0 -0
  137. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/emit_open_api_util.py +0 -0
  138. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/emit_python.py +0 -0
  139. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/emit_scss.py +0 -0
  140. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/emit_typescript.py +0 -0
  141. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/load_types.py +0 -0
  142. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/non_discriminated_union_exceptions.py +0 -0
  143. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/open_api_util.py +0 -0
  144. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/parts/base.py.prepart +0 -0
  145. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/test.py +0 -0
  146. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/type_info/__main__.py +0 -0
  147. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/type_info/emit_type_info.py +0 -0
  148. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/ui_entry_actions/__init__.py +0 -0
  149. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/ui_entry_actions/generate_ui_entry_actions.py +0 -0
  150. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/util.py +0 -0
  151. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/value_spec/__init__.py +0 -0
  152. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/value_spec/__main__.py +0 -0
  153. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/value_spec/convert_type.py +0 -0
  154. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/value_spec/emit_python.py +0 -0
  155. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pkgs/type_spec/value_spec/types.py +0 -0
  156. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/pyproject.toml +0 -0
  157. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/setup.cfg +0 -0
  158. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/__init__.py +0 -0
  159. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/core/__init__.py +0 -0
  160. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/core/async_batch.py +0 -0
  161. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/core/client.py +0 -0
  162. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/core/environment.py +0 -0
  163. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/core/file_upload.py +0 -0
  164. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/core/query/__init__.py +0 -0
  165. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/core/query/builder.py +0 -0
  166. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/core/query/column.py +0 -0
  167. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/core/query/row.py +0 -0
  168. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/core/query/types.py +0 -0
  169. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/core/types.py +0 -0
  170. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/__init__.py +0 -0
  171. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/construct_client.py +0 -0
  172. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/cron.py +0 -0
  173. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/db/__init__.py +0 -0
  174. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/db/connect.py +0 -0
  175. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/db/session.py +0 -0
  176. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/entrypoint.py +0 -0
  177. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/executors/__init__.py +0 -0
  178. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/executors/executors.py +0 -0
  179. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/executors/generic_upload_executor.py +0 -0
  180. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/executors/script_executor.py +0 -0
  181. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/http_server/__init__.py +0 -0
  182. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/http_server/types.py +0 -0
  183. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/__init__.py +0 -0
  184. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/command_server/__init__.py +0 -0
  185. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/command_server/constants.py +0 -0
  186. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/command_server/protocol/__init__.py +0 -0
  187. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/command_server/protocol/command_server_pb2_grpc.py +0 -0
  188. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/command_server/provision_webhook.py +0 -0
  189. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/datastore/__init__.py +0 -0
  190. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/datastore/datastore_postgres.py +0 -0
  191. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/datastore/datastore_sqlite.py +0 -0
  192. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/datastore/factory.py +0 -0
  193. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/datastore/model.py +0 -0
  194. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/job_process.py +0 -0
  195. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/job_runner.py +0 -0
  196. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/queue_runner.py +0 -0
  197. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/queue_runner/types.py +0 -0
  198. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/request_context.py +0 -0
  199. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/scan_profiles.py +0 -0
  200. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/secret_retrieval/__init__.py +0 -0
  201. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/secret_retrieval/basic_secret.py +0 -0
  202. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/secret_retrieval/retrieve_secret.py +0 -0
  203. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/server.py +0 -0
  204. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/service.py +0 -0
  205. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/service_runner.py +0 -0
  206. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/telemetry.py +0 -0
  207. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/webhook_bootstrap.py +0 -0
  208. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/webhook_entity.py +0 -0
  209. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/integration/webhook_server/gunicorn_config.py +0 -0
  210. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/py.typed +0 -0
  211. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/__init__.py +0 -0
  212. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/__init__.py +0 -0
  213. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/batch/__init__.py +0 -0
  214. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/batch/execute_batch.py +0 -0
  215. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/batch/execute_batch_load_async.py +0 -0
  216. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/chemical/__init__.py +0 -0
  217. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/chemical/convert_chemical_formats.py +0 -0
  218. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/condition_parameters/__init__.py +0 -0
  219. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/condition_parameters/upsert_condition_match.py +0 -0
  220. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/condition_parameters/upsert_condition_matches.py +0 -0
  221. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/__init__.py +0 -0
  222. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/add_entity_comment.py +0 -0
  223. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/create_entities.py +0 -0
  224. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/create_entity.py +0 -0
  225. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/create_or_update_entities.py +0 -0
  226. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/create_or_update_entity.py +0 -0
  227. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/export_entities.py +0 -0
  228. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/get_entities_data.py +0 -0
  229. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/grant_entity_permissions.py +0 -0
  230. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/list_aggregate.py +0 -0
  231. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/list_entities.py +0 -0
  232. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/lock_entity.py +0 -0
  233. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/lookup_entity.py +0 -0
  234. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/resolve_entity_ids.py +0 -0
  235. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/set_barcode.py +0 -0
  236. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/set_entities_field_values.py +0 -0
  237. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/set_entity_field_values.py +0 -0
  238. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/set_values.py +0 -0
  239. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/transition_entity_phase.py +0 -0
  240. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/entity/unlock_entity.py +0 -0
  241. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/equipment/__init__.py +0 -0
  242. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/equipment/associate_equipment_input.py +0 -0
  243. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/field_options/__init__.py +0 -0
  244. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/field_options/upsert_field_options.py +0 -0
  245. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/file_folders/__init__.py +0 -0
  246. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/file_folders/add_file_to_folder.py +0 -0
  247. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/file_folders/modify_file_system.py +0 -0
  248. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/files/__init__.py +0 -0
  249. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/files/download_file.py +0 -0
  250. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/id_source/__init__.py +0 -0
  251. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/id_source/list_id_source.py +0 -0
  252. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/id_source/match_id_source.py +0 -0
  253. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/input_groups/__init__.py +0 -0
  254. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/input_groups/get_input_group_names.py +0 -0
  255. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/inputs/__init__.py +0 -0
  256. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/inputs/create_inputs.py +0 -0
  257. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/inputs/get_input_data.py +0 -0
  258. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/inputs/get_input_names.py +0 -0
  259. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/inputs/get_inputs_data.py +0 -0
  260. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/inputs/set_input_attribute_values.py +0 -0
  261. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/inputs/set_input_category.py +0 -0
  262. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/inputs/set_input_subcategories.py +0 -0
  263. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/inputs/set_intermediate_type.py +0 -0
  264. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/integrations/__init__.py +0 -0
  265. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/integrations/publish_realtime_data.py +0 -0
  266. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/integrations/push_notification.py +0 -0
  267. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/integrations/register_sockets_token.py +0 -0
  268. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/listing/__init__.py +0 -0
  269. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/listing/export_listing.py +0 -0
  270. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/listing/fetch_listing.py +0 -0
  271. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/material_families/__init__.py +0 -0
  272. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/material_families/update_entity_material_families.py +0 -0
  273. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/ml/__init__.py +0 -0
  274. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/ml/get_analyze_results.py +0 -0
  275. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/ml/get_suggest_results.py +0 -0
  276. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/ml/submit_async_job.py +0 -0
  277. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/notebooks/__init__.py +0 -0
  278. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/notebooks/add_notebook_content.py +0 -0
  279. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/notebooks/get_notebook_content.py +0 -0
  280. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/output_parameters/__init__.py +0 -0
  281. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/output_parameters/swap_output_condition_parameters.py +0 -0
  282. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/outputs/__init__.py +0 -0
  283. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/outputs/get_output_data.py +0 -0
  284. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/outputs/get_output_names.py +0 -0
  285. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/outputs/get_output_organization.py +0 -0
  286. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/outputs/resolve_output_conditions.py +0 -0
  287. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/outputs/update_output_condition_parameter.py +0 -0
  288. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/permissions/__init__.py +0 -0
  289. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/permissions/set_core_permissions.py +0 -0
  290. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/permissions/set_entity_permission.py +0 -0
  291. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/project/__init__.py +0 -0
  292. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/project/get_projects.py +0 -0
  293. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/project/get_projects_data.py +0 -0
  294. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipe_links/__init__.py +0 -0
  295. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipe_links/create_recipe_link.py +0 -0
  296. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipe_links/create_recipe_links.py +0 -0
  297. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipe_links/remove_recipe_link.py +0 -0
  298. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipe_metadata/__init__.py +0 -0
  299. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +0 -0
  300. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/__init__.py +0 -0
  301. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/add_recipe_to_project.py +0 -0
  302. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/add_time_series_data.py +0 -0
  303. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/archive_recipes.py +0 -0
  304. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/associate_recipe_as_input.py +0 -0
  305. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/associate_recipe_as_lot.py +0 -0
  306. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/clear_recipe_outputs.py +0 -0
  307. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/create_mix_order.py +0 -0
  308. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/create_recipe.py +0 -0
  309. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/create_recipes.py +0 -0
  310. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/disassociate_recipe_as_input.py +0 -0
  311. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/edit_recipe_inputs.py +0 -0
  312. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/get_column_calculation_values.py +0 -0
  313. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/get_curve.py +0 -0
  314. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/get_recipe_calculations.py +0 -0
  315. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/get_recipe_links.py +0 -0
  316. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/get_recipe_names.py +0 -0
  317. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/get_recipe_output_metadata.py +0 -0
  318. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/get_recipes_data.py +0 -0
  319. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/lock_recipes.py +0 -0
  320. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/remove_recipe_from_project.py +0 -0
  321. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/set_recipe_inputs.py +0 -0
  322. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/set_recipe_metadata.py +0 -0
  323. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/set_recipe_output_annotations.py +0 -0
  324. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/set_recipe_output_file.py +0 -0
  325. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/set_recipe_outputs.py +0 -0
  326. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/set_recipe_tags.py +0 -0
  327. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/set_recipe_total.py +0 -0
  328. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/unarchive_recipes.py +0 -0
  329. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/unlock_recipes.py +0 -0
  330. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/upsert_recipe_workflow_step.py +0 -0
  331. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/recipes/upsert_step_relationships.py +0 -0
  332. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/runsheet/__init__.py +0 -0
  333. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/runsheet/complete_async_upload.py +0 -0
  334. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/runsheet/export_default_runsheet.py +0 -0
  335. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/time_series/__init__.py +0 -0
  336. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/time_series/ingest_dataset_measurements.py +0 -0
  337. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/triggers/__init__.py +0 -0
  338. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/triggers/run_trigger.py +0 -0
  339. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/uploader/__init__.py +0 -0
  340. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/uploader/complete_async_parse.py +0 -0
  341. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/uploader/invoke_uploader.py +0 -0
  342. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/user/__init__.py +0 -0
  343. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/api/user/get_current_user_info.py +0 -0
  344. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/async_batch.py +0 -0
  345. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/async_batch_processor.py +0 -0
  346. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/async_batch_t.py +0 -0
  347. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/async_jobs.py +0 -0
  348. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/async_jobs_t.py +0 -0
  349. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/auth_retrieval.py +0 -0
  350. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/auth_retrieval_t.py +0 -0
  351. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/base.py +0 -0
  352. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/base_t.py +0 -0
  353. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/calculations.py +0 -0
  354. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/calculations_t.py +0 -0
  355. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/chemical_structure.py +0 -0
  356. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/chemical_structure_t.py +0 -0
  357. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/client_base.py +0 -0
  358. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/client_config.py +0 -0
  359. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/client_config_t.py +0 -0
  360. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/condition_match.py +0 -0
  361. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/condition_match_t.py +0 -0
  362. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/curves.py +0 -0
  363. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/curves_t.py +0 -0
  364. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/data.py +0 -0
  365. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/data_t.py +0 -0
  366. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/dataset_measurements.py +0 -0
  367. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/dataset_measurements_t.py +0 -0
  368. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/entity.py +0 -0
  369. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/experiment_groups.py +0 -0
  370. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/experiment_groups_t.py +0 -0
  371. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/exports.py +0 -0
  372. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/exports_t.py +0 -0
  373. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/field_values.py +0 -0
  374. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/field_values_t.py +0 -0
  375. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/fields.py +0 -0
  376. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/fields_t.py +0 -0
  377. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/generic_upload.py +0 -0
  378. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/generic_upload_t.py +0 -0
  379. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/id_source.py +0 -0
  380. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/id_source_t.py +0 -0
  381. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/identifier.py +0 -0
  382. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/identifier_t.py +0 -0
  383. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/input_attributes.py +0 -0
  384. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/input_attributes_t.py +0 -0
  385. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/inputs.py +0 -0
  386. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/inputs_t.py +0 -0
  387. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/integration_server.py +0 -0
  388. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/integration_server_t.py +0 -0
  389. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/integration_session.py +0 -0
  390. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/integration_session_t.py +0 -0
  391. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/integrations.py +0 -0
  392. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/integrations_t.py +0 -0
  393. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/job_definition.py +0 -0
  394. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/job_definition_t.py +0 -0
  395. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/listing.py +0 -0
  396. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/listing_t.py +0 -0
  397. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/ml_reports.py +0 -0
  398. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/ml_reports_t.py +0 -0
  399. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/notices.py +0 -0
  400. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/notices_t.py +0 -0
  401. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/notifications.py +0 -0
  402. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/notifications_t.py +0 -0
  403. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/outputs.py +0 -0
  404. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/outputs_t.py +0 -0
  405. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/overrides.py +0 -0
  406. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/overrides_t.py +0 -0
  407. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/permissions.py +0 -0
  408. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/permissions_t.py +0 -0
  409. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/phases.py +0 -0
  410. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/phases_t.py +0 -0
  411. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/post_base.py +0 -0
  412. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/post_base_t.py +0 -0
  413. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/queued_job.py +0 -0
  414. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/queued_job_t.py +0 -0
  415. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_identifiers.py +0 -0
  416. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_identifiers_t.py +0 -0
  417. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_inputs.py +0 -0
  418. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_inputs_t.py +0 -0
  419. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_links.py +0 -0
  420. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_links_t.py +0 -0
  421. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_metadata.py +0 -0
  422. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_metadata_t.py +0 -0
  423. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_output_metadata.py +0 -0
  424. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_output_metadata_t.py +0 -0
  425. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_step_relationships.py +0 -0
  426. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_step_relationships_t.py +0 -0
  427. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_tags.py +0 -0
  428. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_tags_t.py +0 -0
  429. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_workflow_step_types.py +0 -0
  430. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_workflow_step_types_t.py +0 -0
  431. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_workflow_steps.py +0 -0
  432. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipe_workflow_steps_t.py +0 -0
  433. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipes.py +0 -0
  434. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/recipes_t.py +0 -0
  435. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/request_headers.py +0 -0
  436. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/request_headers_t.py +0 -0
  437. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/response.py +0 -0
  438. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/response_t.py +0 -0
  439. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/runsheet.py +0 -0
  440. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/runsheet_format.py +0 -0
  441. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/runsheet_format_t.py +0 -0
  442. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/runsheet_t.py +0 -0
  443. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/secret_retrieval.py +0 -0
  444. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/secret_retrieval_t.py +0 -0
  445. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/sockets.py +0 -0
  446. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/sockets_t.py +0 -0
  447. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/specs.py +0 -0
  448. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/specs_t.py +0 -0
  449. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/step_relationships.py +0 -0
  450. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/step_relationships_t.py +0 -0
  451. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/units.py +0 -0
  452. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/units_t.py +0 -0
  453. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/uploader.py +0 -0
  454. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/uploader_t.py +0 -0
  455. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/users.py +0 -0
  456. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/users_t.py +0 -0
  457. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/webhook.py +0 -0
  458. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/webhook_job.py +0 -0
  459. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/webhook_job_t.py +0 -0
  460. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/webhook_t.py +0 -0
  461. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/workflows.py +0 -0
  462. {uncountablepythonsdk-0.0.190 → uncountablepythonsdk-0.0.191}/uncountable/types/workflows_t.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: UncountablePythonSDK
3
- Version: 0.0.190
3
+ Version: 0.0.191
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: UncountablePythonSDK
3
- Version: 0.0.190
3
+ Version: 0.0.191
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
@@ -155,6 +155,8 @@ uncountable/integration/construct_client.py
155
155
  uncountable/integration/cron.py
156
156
  uncountable/integration/entrypoint.py
157
157
  uncountable/integration/job.py
158
+ uncountable/integration/job_queue.py
159
+ uncountable/integration/ownership_lock.py
158
160
  uncountable/integration/request_context.py
159
161
  uncountable/integration/scan_profiles.py
160
162
  uncountable/integration/scheduler.py
@@ -159,6 +159,8 @@
159
159
  "uncountable/integration/http_server/__init__.py",
160
160
  "uncountable/integration/http_server/types.py",
161
161
  "uncountable/integration/job.py",
162
+ "uncountable/integration/job_queue.py",
163
+ "uncountable/integration/ownership_lock.py",
162
164
  "uncountable/integration/queue_runner/__init__.py",
163
165
  "uncountable/integration/queue_runner/command_server/__init__.py",
164
166
  "uncountable/integration/queue_runner/command_server/command_client.py",
@@ -175,6 +175,27 @@ def emit_constant_ts(ctx: EmitTypescriptContext, sconst: builder.SpecConstant) -
175
175
  ctx.out.write(f"export const {const_name} = {value}\n")
176
176
 
177
177
 
178
+ def _collect_object_properties(
179
+ stype: builder.SpecTypeDefnObject,
180
+ ) -> dict[str, builder.SpecProperty]:
181
+ base_properties = (
182
+ _collect_object_properties(stype.base) if stype.base is not None else {}
183
+ )
184
+ return {**base_properties, **(stype.properties or {})}
185
+
186
+
187
+ def _type_info_ref_type_ts(*, stype: builder.SpecTypeDefnObject, base_ref: str) -> str:
188
+ defaulted_names = sorted(
189
+ ts_property_name(prop)
190
+ for prop in _collect_object_properties(stype).values()
191
+ if prop.has_default and not prop.parse_require and prop.default is not None
192
+ )
193
+ if len(defaulted_names) == 0:
194
+ return f"{base_ref}.TypeInfoRef<{stype.name}>"
195
+ defaulted_keys = " | ".join(f'"{name}"' for name in defaulted_names)
196
+ return f"{base_ref}.TypeInfoRef<{stype.name}, {base_ref}.WithDefaultsApplied<{stype.name}, {defaulted_keys}>>"
197
+
198
+
178
199
  def emit_type_info_ref_ts(
179
200
  ctx: EmitTypescriptContext,
180
201
  stype: builder.SpecTypeDefn,
@@ -184,9 +205,9 @@ def emit_type_info_ref_ts(
184
205
  assert ctx.namespace.emit_type_info, (
185
206
  f"{ctx.namespace.name}: $emit_type_info_refs requires $emit_type_info"
186
207
  )
187
- assert (
188
- isinstance(stype, builder.SpecTypeDefnObject) and len(stype.parameters) == 0
189
- ), (
208
+ if not isinstance(stype, builder.SpecTypeDefnObject):
209
+ return
210
+ assert len(stype.parameters) == 0, (
190
211
  f"{ctx.namespace.name}.{stype.name}: type info refs support non-generic objects only"
191
212
  )
192
213
 
@@ -195,7 +216,7 @@ def emit_type_info_ref_ts(
195
216
  ctx.out.write("\n")
196
217
  ctx.out.write(MODIFY_NOTICE)
197
218
  ctx.out.write(
198
- f"export const {stype.name}TypeInfoRef: {base_ref}.TypeInfoRef<{stype.name}> = {{\n"
219
+ f"export const {stype.name}TypeInfoRef: {_type_info_ref_type_ts(stype=stype, base_ref=base_ref)} = {{\n"
199
220
  )
200
221
  ctx.out.write(f'{INDENT}typeName: "{ctx.namespace.name}.{stype.name}",\n')
201
222
  ctx.out.write("}\n")
@@ -40,11 +40,16 @@ declare const brand: unique symbol
40
40
  export type Branded<TBase, TBrand extends string> = TBase & { readonly [brand]: TBrand }
41
41
 
42
42
  declare const typeInfoValue: unique symbol
43
- export interface TypeInfoRef<T> {
43
+ declare const typeInfoParsedValue: unique symbol
44
+ export interface TypeInfoRef<T, TParsed extends T = T> {
44
45
  readonly typeName: string
45
46
  readonly [typeInfoValue]?: T
47
+ readonly [typeInfoParsedValue]?: TParsed
46
48
  }
47
49
 
50
+ export type WithDefaultsApplied<T, TDefaultedKey extends keyof T> = Omit<T, TDefaultedKey> &
51
+ Required<Pick<T, TDefaultedKey>>
52
+
48
53
  // Ids matching a strict integer number are converted to integers
49
54
  export const ID_REGEX = /^-?[1-9][0-9]{0,20}$/
50
55
  // Matches ISO 8601 dates. Stricter than `Date.parse`.
@@ -89,6 +89,8 @@ def register_cancel_queued_job_parser(
89
89
  print(
90
90
  "Job successfully cancelled. The integration server will restart."
91
91
  )
92
+ case CommandCancelJobStatus.CANCELLED_QUEUED:
93
+ print("Queued job successfully cancelled.")
92
94
  case CommandCancelJobStatus.NO_JOB_FOUND:
93
95
  print("Job not found.")
94
96
  case CommandCancelJobStatus.JOB_ALREADY_COMPLETED:
@@ -10,7 +10,6 @@ from pkgs.argument_parser import CachedParser
10
10
  from pkgs.serialization_util import serialize_for_api
11
11
  from uncountable.core.async_batch import AsyncBatchProcessor
12
12
  from uncountable.core.client import Client
13
- from uncountable.core.environment import get_local_admin_server_port
14
13
  from uncountable.core.file_upload import FileUpload
15
14
  from uncountable.core.types import AuthDetailsOAuth
16
15
  from uncountable.integration.http_server import (
@@ -18,12 +17,7 @@ from uncountable.integration.http_server import (
18
17
  GenericHttpResponse,
19
18
  HttpException,
20
19
  )
21
- from uncountable.integration.queue_runner.command_server.command_client import (
22
- send_job_queue_message,
23
- )
24
- from uncountable.integration.queue_runner.command_server.types import (
25
- CommandServerException,
26
- )
20
+ from uncountable.integration.job_queue import JobQueue
27
21
  from uncountable.integration.request_context import extract_request_context
28
22
  from uncountable.integration.secret_retrieval import retrieve_secret
29
23
  from uncountable.integration.telemetry import JobLogger
@@ -111,11 +105,7 @@ class _RequestValidatorClient(Client):
111
105
  return self._oauth_bearer_token
112
106
 
113
107
 
114
- class CustomHttpJob(Job[GenericHttpRequest]):
115
- @property
116
- def payload_type(self) -> type[GenericHttpRequest]:
117
- return GenericHttpRequest
118
-
108
+ class HttpJob(ABC):
119
109
  @staticmethod
120
110
  @abstractmethod
121
111
  def validate_request(
@@ -130,6 +120,22 @@ class CustomHttpJob(Job[GenericHttpRequest]):
130
120
  """
131
121
  ...
132
122
 
123
+ @abstractmethod
124
+ def respond_to_request(
125
+ self,
126
+ *,
127
+ request: GenericHttpRequest,
128
+ job_definition: HttpJobDefinitionBase,
129
+ profile_meta: ProfileMetadata,
130
+ job_queue: JobQueue,
131
+ ) -> GenericHttpResponse: ...
132
+
133
+
134
+ class CustomHttpJob(Job[GenericHttpRequest], HttpJob):
135
+ @property
136
+ def payload_type(self) -> type[GenericHttpRequest]:
137
+ return GenericHttpRequest
138
+
133
139
  @staticmethod
134
140
  def get_validated_oauth_request_user_id(
135
141
  *, profile_metadata: ProfileMetadata, request: GenericHttpRequest
@@ -162,6 +168,19 @@ class CustomHttpJob(Job[GenericHttpRequest]):
162
168
  """
163
169
  ...
164
170
 
171
+ @typing.override
172
+ def respond_to_request(
173
+ self,
174
+ *,
175
+ request: GenericHttpRequest,
176
+ job_definition: HttpJobDefinitionBase,
177
+ profile_meta: ProfileMetadata,
178
+ job_queue: JobQueue,
179
+ ) -> GenericHttpResponse:
180
+ return self.handle_request(
181
+ request=request, job_definition=job_definition, profile_meta=profile_meta
182
+ )
183
+
165
184
  def run_outer(self, args: JobArguments) -> JobResult:
166
185
  args.logger.log_warning(
167
186
  message=f"Unexpected call to run_outer for CustomHttpJob: {args.job_definition.id}"
@@ -189,7 +208,7 @@ def _resolve_webhook_signature_key(
189
208
  )
190
209
 
191
210
 
192
- class WebhookJob[WPT](Job[webhook_job_t.WebhookEventPayload]):
211
+ class WebhookJob[WPT](Job[webhook_job_t.WebhookEventPayload], HttpJob):
193
212
  @property
194
213
  def payload_type(self) -> type[webhook_job_t.WebhookEventPayload]:
195
214
  return webhook_job_t.WebhookEventPayload
@@ -219,12 +238,14 @@ class WebhookJob[WPT](Job[webhook_job_t.WebhookEventPayload]):
219
238
  if not hmac.compare_digest(request_body_signature, passed_signature):
220
239
  raise HttpException.payload_failed_signature()
221
240
 
222
- @staticmethod
223
- def handle_request(
241
+ @typing.override
242
+ def respond_to_request(
243
+ self,
224
244
  *,
225
245
  request: GenericHttpRequest,
226
- job_definition: job_definition_t.HttpJobDefinitionBase,
227
- profile_meta: ProfileMetadata, # noqa: ARG004
246
+ job_definition: HttpJobDefinitionBase,
247
+ profile_meta: ProfileMetadata,
248
+ job_queue: JobQueue,
228
249
  ) -> GenericHttpResponse:
229
250
  try:
230
251
  request_body = simplejson.loads(request.body_text)
@@ -234,19 +255,15 @@ class WebhookJob[WPT](Job[webhook_job_t.WebhookEventPayload]):
234
255
 
235
256
  request_context = extract_request_context(headers=request.headers)
236
257
 
237
- try:
238
- send_job_queue_message(
239
- job_ref_name=job_definition.id,
240
- payload=queued_job_t.QueuedJobPayload(
241
- invocation_context=queued_job_t.InvocationContextWebhook(
242
- webhook_payload=webhook_payload,
243
- request_context=request_context,
244
- )
245
- ),
246
- port=get_local_admin_server_port(),
247
- )
248
- except CommandServerException as e:
249
- raise HttpException.unknown_error() from e
258
+ job_queue.enqueue(
259
+ job_ref_name=job_definition.id,
260
+ payload=queued_job_t.QueuedJobPayload(
261
+ invocation_context=queued_job_t.InvocationContextWebhook(
262
+ webhook_payload=webhook_payload,
263
+ request_context=request_context,
264
+ )
265
+ ),
266
+ )
250
267
 
251
268
  return GenericHttpResponse(
252
269
  response=simplejson.dumps(serialize_for_api(WebhookResponse())),
@@ -0,0 +1,14 @@
1
+ from uncountable.integration.queue_runner.datastore import Datastore
2
+ from uncountable.types import queued_job_t
3
+
4
+
5
+ class JobQueue:
6
+ def __init__(self, datastore: Datastore) -> None:
7
+ self._datastore = datastore
8
+
9
+ def enqueue(
10
+ self, *, job_ref_name: str, payload: queued_job_t.QueuedJobPayload
11
+ ) -> str:
12
+ return self._datastore.add_job_to_queue(
13
+ job_payload=payload, job_ref_name=job_ref_name
14
+ ).queued_job_uuid
@@ -0,0 +1,77 @@
1
+ import time
2
+ from collections.abc import Callable, Generator
3
+ from contextlib import contextmanager
4
+ from enum import StrEnum
5
+
6
+ from opentelemetry.trace import get_current_span
7
+ from sqlalchemy import Connection, text
8
+ from sqlalchemy.engine import Engine
9
+
10
+ from uncountable.integration.telemetry import Logger
11
+
12
+ OWNERSHIP_ADVISORY_LOCK_KEY = 0x756E635F696E7467
13
+ OWNERSHIP_LOCK_POLL_INTERVAL_SECS = 1
14
+
15
+
16
+ class OwnershipOutcome(StrEnum):
17
+ ACQUIRED = "acquired"
18
+ SHUTDOWN_REQUESTED = "shutdown_requested"
19
+
20
+
21
+ def _try_acquire_advisory_lock(connection: Connection) -> bool:
22
+ return bool(
23
+ connection.execute(
24
+ text("select pg_try_advisory_lock(:key)"),
25
+ {"key": OWNERSHIP_ADVISORY_LOCK_KEY},
26
+ ).scalar_one()
27
+ )
28
+
29
+
30
+ def _release_advisory_lock(connection: Connection) -> None:
31
+ connection.execute(
32
+ text("select pg_advisory_unlock(:key)"),
33
+ {"key": OWNERSHIP_ADVISORY_LOCK_KEY},
34
+ )
35
+
36
+
37
+ def _wait_for_advisory_lock(
38
+ *, connection: Connection, is_shutdown_requested: Callable[[], bool]
39
+ ) -> OwnershipOutcome:
40
+ logger = Logger(get_current_span())
41
+ if is_shutdown_requested():
42
+ return OwnershipOutcome.SHUTDOWN_REQUESTED
43
+ if _try_acquire_advisory_lock(connection):
44
+ return OwnershipOutcome.ACQUIRED
45
+ logger.log_info("waiting for the integration ownership lock held by another pod")
46
+ while not is_shutdown_requested():
47
+ time.sleep(OWNERSHIP_LOCK_POLL_INTERVAL_SECS)
48
+ if is_shutdown_requested():
49
+ return OwnershipOutcome.SHUTDOWN_REQUESTED
50
+ if _try_acquire_advisory_lock(connection):
51
+ logger.log_info("acquired the integration ownership lock")
52
+ return OwnershipOutcome.ACQUIRED
53
+ return OwnershipOutcome.SHUTDOWN_REQUESTED
54
+
55
+
56
+ @contextmanager
57
+ def hold_integration_ownership(
58
+ *, engine: Engine, is_shutdown_requested: Callable[[], bool]
59
+ ) -> Generator[OwnershipOutcome]:
60
+ dialect_name = engine.dialect.name
61
+ match dialect_name:
62
+ case "sqlite":
63
+ yield OwnershipOutcome.ACQUIRED
64
+ case "postgresql":
65
+ with engine.connect().execution_options(
66
+ isolation_level="AUTOCOMMIT"
67
+ ) as connection:
68
+ outcome = _wait_for_advisory_lock(
69
+ connection=connection, is_shutdown_requested=is_shutdown_requested
70
+ )
71
+ try:
72
+ yield outcome
73
+ finally:
74
+ if outcome == OwnershipOutcome.ACQUIRED:
75
+ _release_advisory_lock(connection)
76
+ case _:
77
+ raise ValueError(f"unsupported database dialect: {dialect_name!r}")
@@ -85,6 +85,8 @@ def send_job_cancellation_message(
85
85
  return CommandCancelJobStatus.NO_JOB_FOUND
86
86
  case CancelJobStatus.CANCELLED_WITH_RESTART:
87
87
  return CommandCancelJobStatus.CANCELLED_WITH_RESTART
88
+ case CancelJobStatus.CANCELLED_QUEUED:
89
+ return CommandCancelJobStatus.CANCELLED_QUEUED
88
90
  case CancelJobStatus.JOB_ALREADY_COMPLETED:
89
91
  return CommandCancelJobStatus.JOB_ALREADY_COMPLETED
90
92
  case _:
@@ -94,6 +94,8 @@ async def serve(command_queue: CommandQueue, datastore: Datastore) -> None:
94
94
  proto_status = CancelJobStatus.NO_JOB_FOUND
95
95
  case CommandCancelJobStatus.CANCELLED_WITH_RESTART:
96
96
  proto_status = CancelJobStatus.CANCELLED_WITH_RESTART
97
+ case CommandCancelJobStatus.CANCELLED_QUEUED:
98
+ proto_status = CancelJobStatus.CANCELLED_QUEUED
97
99
  case CommandCancelJobStatus.JOB_ALREADY_COMPLETED:
98
100
  proto_status = CancelJobStatus.JOB_ALREADY_COMPLETED
99
101
  case _:
@@ -71,6 +71,7 @@ enum CancelJobStatus {
71
71
  CANCELLED_WITH_RESTART = 0;
72
72
  NO_JOB_FOUND = 1;
73
73
  JOB_ALREADY_COMPLETED = 2;
74
+ CANCELLED_QUEUED = 3;
74
75
  }
75
76
 
76
77
  message CancelJobResult {
@@ -29,7 +29,7 @@ from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__
29
29
 
30
30
 
31
31
  DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
32
- b'\nQuncountable/integration/queue_runner/command_server/protocol/command_server.proto\x1a\x1fgoogle/protobuf/timestamp.proto"E\n\x11\x45nqueueJobRequest\x12\x14\n\x0cjob_ref_name\x18\x01 \x01(\t\x12\x1a\n\x12serialized_payload\x18\x02 \x01(\t"H\n\x10\x45nqueueJobResult\x12\x1b\n\x13successfully_queued\x18\x01 \x01(\x08\x12\x17\n\x0fqueued_job_uuid\x18\x02 \x01(\t"\x1f\n\x0fRetryJobRequest\x12\x0c\n\x04uuid\x18\x01 \x01(\t"F\n\x0eRetryJobResult\x12\x1b\n\x13successfully_queued\x18\x01 \x01(\x08\x12\x17\n\x0fqueued_job_uuid\x18\x02 \x01(\t"\x1a\n\x18VaccuumQueuedJobsRequest"\x19\n\x17VaccuumQueuedJobsResult"\x14\n\x12\x43heckHealthRequest"$\n\x11\x43heckHealthResult\x12\x0f\n\x07success\x18\x01 \x01(\x08"\xc2\x01\n\x15ListQueuedJobsRequest\x12\x0e\n\x06offset\x18\x01 \x01(\r\x12\r\n\x05limit\x18\x02 \x01(\r\x12\x0e\n\x06status\x18\x03 \x01(\t\x12\x14\n\x0cjob_ref_name\x18\x04 \x01(\t\x12\x32\n\x0esubmitted_from\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x0csubmitted_to\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xf4\x01\n\x14ListQueuedJobsResult\x12\x43\n\x0bqueued_jobs\x18\x01 \x03(\x0b\x32..ListQueuedJobsResult.ListQueuedJobsResultItem\x1a\x96\x01\n\x18ListQueuedJobsResultItem\x12\x0c\n\x04uuid\x18\x01 \x01(\t\x12\x14\n\x0cjob_ref_name\x18\x02 \x01(\t\x12\x14\n\x0cnum_attempts\x18\x03 \x01(\x03\x12\x30\n\x0csubmitted_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06status\x18\x05 \x01(\t"$\n\x10\x43\x61ncelJobRequest\x12\x10\n\x08job_uuid\x18\x01 \x01(\t"3\n\x0f\x43\x61ncelJobResult\x12 \n\x06status\x18\x01 \x01(\x0e\x32\x10.CancelJobStatus")\n\x17ProvisionWebhookRequest\x12\x0e\n\x06job_id\x18\x01 \x01(\t".\n\x16ProvisionWebhookResult\x12\x14\n\x0cprofile_name\x18\x01 \x01(\t*Z\n\x0f\x43\x61ncelJobStatus\x12\x1a\n\x16\x43\x41NCELLED_WITH_RESTART\x10\x00\x12\x10\n\x0cNO_JOB_FOUND\x10\x01\x12\x19\n\x15JOB_ALREADY_COMPLETED\x10\x02\x32\xbd\x03\n\rCommandServer\x12\x35\n\nEnqueueJob\x12\x12.EnqueueJobRequest\x1a\x11.EnqueueJobResult"\x00\x12/\n\x08RetryJob\x12\x10.RetryJobRequest\x1a\x0f.RetryJobResult"\x00\x12\x38\n\x0b\x43heckHealth\x12\x13.CheckHealthRequest\x1a\x12.CheckHealthResult"\x00\x12\x41\n\x0eListQueuedJobs\x12\x16.ListQueuedJobsRequest\x1a\x15.ListQueuedJobsResult"\x00\x12J\n\x11VaccuumQueuedJobs\x12\x19.VaccuumQueuedJobsRequest\x1a\x18.VaccuumQueuedJobsResult"\x00\x12\x32\n\tCancelJob\x12\x11.CancelJobRequest\x1a\x10.CancelJobResult"\x00\x12G\n\x10ProvisionWebhook\x12\x18.ProvisionWebhookRequest\x1a\x17.ProvisionWebhookResult"\x00\x62\x06proto3'
32
+ b'\nQuncountable/integration/queue_runner/command_server/protocol/command_server.proto\x1a\x1fgoogle/protobuf/timestamp.proto"E\n\x11\x45nqueueJobRequest\x12\x14\n\x0cjob_ref_name\x18\x01 \x01(\t\x12\x1a\n\x12serialized_payload\x18\x02 \x01(\t"H\n\x10\x45nqueueJobResult\x12\x1b\n\x13successfully_queued\x18\x01 \x01(\x08\x12\x17\n\x0fqueued_job_uuid\x18\x02 \x01(\t"\x1f\n\x0fRetryJobRequest\x12\x0c\n\x04uuid\x18\x01 \x01(\t"F\n\x0eRetryJobResult\x12\x1b\n\x13successfully_queued\x18\x01 \x01(\x08\x12\x17\n\x0fqueued_job_uuid\x18\x02 \x01(\t"\x1a\n\x18VaccuumQueuedJobsRequest"\x19\n\x17VaccuumQueuedJobsResult"\x14\n\x12\x43heckHealthRequest"$\n\x11\x43heckHealthResult\x12\x0f\n\x07success\x18\x01 \x01(\x08"\xc2\x01\n\x15ListQueuedJobsRequest\x12\x0e\n\x06offset\x18\x01 \x01(\r\x12\r\n\x05limit\x18\x02 \x01(\r\x12\x0e\n\x06status\x18\x03 \x01(\t\x12\x14\n\x0cjob_ref_name\x18\x04 \x01(\t\x12\x32\n\x0esubmitted_from\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x30\n\x0csubmitted_to\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"\xf4\x01\n\x14ListQueuedJobsResult\x12\x43\n\x0bqueued_jobs\x18\x01 \x03(\x0b\x32..ListQueuedJobsResult.ListQueuedJobsResultItem\x1a\x96\x01\n\x18ListQueuedJobsResultItem\x12\x0c\n\x04uuid\x18\x01 \x01(\t\x12\x14\n\x0cjob_ref_name\x18\x02 \x01(\t\x12\x14\n\x0cnum_attempts\x18\x03 \x01(\x03\x12\x30\n\x0csubmitted_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06status\x18\x05 \x01(\t"$\n\x10\x43\x61ncelJobRequest\x12\x10\n\x08job_uuid\x18\x01 \x01(\t"3\n\x0f\x43\x61ncelJobResult\x12 \n\x06status\x18\x01 \x01(\x0e\x32\x10.CancelJobStatus")\n\x17ProvisionWebhookRequest\x12\x0e\n\x06job_id\x18\x01 \x01(\t".\n\x16ProvisionWebhookResult\x12\x14\n\x0cprofile_name\x18\x01 \x01(\t*p\n\x0f\x43\x61ncelJobStatus\x12\x1a\n\x16\x43\x41NCELLED_WITH_RESTART\x10\x00\x12\x10\n\x0cNO_JOB_FOUND\x10\x01\x12\x19\n\x15JOB_ALREADY_COMPLETED\x10\x02\x12\x14\n\x10\x43\x41NCELLED_QUEUED\x10\x03\x32\xbd\x03\n\rCommandServer\x12\x35\n\nEnqueueJob\x12\x12.EnqueueJobRequest\x1a\x11.EnqueueJobResult"\x00\x12/\n\x08RetryJob\x12\x10.RetryJobRequest\x1a\x0f.RetryJobResult"\x00\x12\x38\n\x0b\x43heckHealth\x12\x13.CheckHealthRequest\x1a\x12.CheckHealthResult"\x00\x12\x41\n\x0eListQueuedJobs\x12\x16.ListQueuedJobsRequest\x1a\x15.ListQueuedJobsResult"\x00\x12J\n\x11VaccuumQueuedJobs\x12\x19.VaccuumQueuedJobsRequest\x1a\x18.VaccuumQueuedJobsResult"\x00\x12\x32\n\tCancelJob\x12\x11.CancelJobRequest\x1a\x10.CancelJobResult"\x00\x12G\n\x10ProvisionWebhook\x12\x18.ProvisionWebhookRequest\x1a\x17.ProvisionWebhookResult"\x00\x62\x06proto3'
33
33
  )
34
34
 
35
35
  _globals = globals()
@@ -42,7 +42,7 @@ _builder.BuildTopDescriptorsAndMessages(
42
42
  if not _descriptor._USE_C_DESCRIPTORS:
43
43
  DESCRIPTOR._loaded_options = None
44
44
  _globals["_CANCELJOBSTATUS"]._serialized_start = 1109
45
- _globals["_CANCELJOBSTATUS"]._serialized_end = 1199
45
+ _globals["_CANCELJOBSTATUS"]._serialized_end = 1221
46
46
  _globals["_ENQUEUEJOBREQUEST"]._serialized_start = 118
47
47
  _globals["_ENQUEUEJOBREQUEST"]._serialized_end = 187
48
48
  _globals["_ENQUEUEJOBRESULT"]._serialized_start = 189
@@ -73,6 +73,6 @@ if not _descriptor._USE_C_DESCRIPTORS:
73
73
  _globals["_PROVISIONWEBHOOKREQUEST"]._serialized_end = 1059
74
74
  _globals["_PROVISIONWEBHOOKRESULT"]._serialized_start = 1061
75
75
  _globals["_PROVISIONWEBHOOKRESULT"]._serialized_end = 1107
76
- _globals["_COMMANDSERVER"]._serialized_start = 1202
77
- _globals["_COMMANDSERVER"]._serialized_end = 1647
76
+ _globals["_COMMANDSERVER"]._serialized_start = 1224
77
+ _globals["_COMMANDSERVER"]._serialized_end = 1669
78
78
  # @@protoc_insertion_point(module_scope)
@@ -16,10 +16,12 @@ class CancelJobStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
16
16
  CANCELLED_WITH_RESTART: _ClassVar[CancelJobStatus]
17
17
  NO_JOB_FOUND: _ClassVar[CancelJobStatus]
18
18
  JOB_ALREADY_COMPLETED: _ClassVar[CancelJobStatus]
19
+ CANCELLED_QUEUED: _ClassVar[CancelJobStatus]
19
20
 
20
21
  CANCELLED_WITH_RESTART: CancelJobStatus
21
22
  NO_JOB_FOUND: CancelJobStatus
22
23
  JOB_ALREADY_COMPLETED: CancelJobStatus
24
+ CANCELLED_QUEUED: CancelJobStatus
23
25
 
24
26
  class EnqueueJobRequest(_message.Message):
25
27
  __slots__ = ("job_ref_name", "serialized_payload")
@@ -8,6 +8,7 @@ from uncountable.types import queued_job_t
8
8
 
9
9
  class CommandCancelJobStatus(StrEnum):
10
10
  CANCELLED_WITH_RESTART = "cancelled_with_restart"
11
+ CANCELLED_QUEUED = "cancelled_queued"
11
12
  NO_JOB_FOUND = "no_job_found"
12
13
  JOB_ALREADY_COMPLETED = "job_already_completed"
13
14
 
@@ -204,7 +204,19 @@ class DatastoreSqlBase(Datastore):
204
204
 
205
205
  return None
206
206
 
207
- def load_job_queue(self) -> list[queued_job_t.QueuedJob]:
207
+ def list_queued_job_uuids(self) -> list[str]:
208
+ with self.session_maker() as session:
209
+ select_stmt = select(QueuedJob.id).filter(
210
+ or_(
211
+ QueuedJob.status == queued_job_t.JobStatus.QUEUED,
212
+ QueuedJob.status.is_(None),
213
+ )
214
+ )
215
+ return [row.id for row in session.execute(select_stmt)]
216
+
217
+ def load_queued_jobs(
218
+ self, *, queued_job_uuids: list[str]
219
+ ) -> list[queued_job_t.QueuedJob]:
208
220
  with self.session_maker() as session:
209
221
  select_stmt = (
210
222
  select(
@@ -215,12 +227,7 @@ class DatastoreSqlBase(Datastore):
215
227
  QueuedJob.status,
216
228
  QueuedJob.submitted_at,
217
229
  )
218
- .filter(
219
- or_(
220
- QueuedJob.status == queued_job_t.JobStatus.QUEUED,
221
- QueuedJob.status.is_(None),
222
- )
223
- )
230
+ .filter(QueuedJob.id.in_(queued_job_uuids))
224
231
  .order_by(QueuedJob.submitted_at)
225
232
  )
226
233
 
@@ -31,7 +31,12 @@ class Datastore(ABC):
31
31
  def increment_num_attempts(self, queued_job_uuid: str) -> int: ...
32
32
 
33
33
  @abstractmethod
34
- def load_job_queue(self) -> list[queued_job_t.QueuedJob]: ...
34
+ def list_queued_job_uuids(self) -> list[str]: ...
35
+
36
+ @abstractmethod
37
+ def load_queued_jobs(
38
+ self, *, queued_job_uuids: list[str]
39
+ ) -> list[queued_job_t.QueuedJob]: ...
35
40
 
36
41
  @abstractmethod
37
42
  def get_next_queued_job_for_ref_name(
@@ -31,6 +31,9 @@ from uncountable.types import job_definition_t, queued_job_t
31
31
 
32
32
  from .types import RESTART_EXIT_CODE, ResultQueue, ResultTask
33
33
 
34
+ QUEUED_JOB_POLL_INTERVAL_SECS = 1
35
+ RESTART_DELAY_SECS = 5
36
+
34
37
 
35
38
  @dataclass(kw_only=True, frozen=True)
36
39
  class JobListenerKey:
@@ -65,10 +68,12 @@ def on_worker_crash(
65
68
 
66
69
 
67
70
  def _start_workers(
71
+ *,
68
72
  process_pool: ProcessPoolExecutor,
69
73
  result_queue: ResultQueue,
70
74
  datastore: Datastore,
71
75
  worker_tasks: list[asyncio.Task],
76
+ running_job_uuids: set[str],
72
77
  ) -> dict[str, Worker]:
73
78
  profiles = load_profiles()
74
79
  job_queue_worker_lookup: dict[JobListenerKey, Worker] = {}
@@ -84,6 +89,7 @@ def _start_workers(
84
89
  listen_queue=asyncio.Queue(),
85
90
  result_queue=result_queue,
86
91
  datastore=datastore,
92
+ running_job_uuids=running_job_uuids,
87
93
  )
88
94
  task = asyncio.create_task(worker.run_worker_loop())
89
95
  task.add_done_callback(on_worker_crash(job_worker_key))
@@ -108,11 +114,16 @@ async def start_scheduler(
108
114
  with ProcessPoolExecutor(
109
115
  max_workers=get_max_job_workers(), max_tasks_per_child=1
110
116
  ) as process_pool:
117
+ running_job_uuids: set[str] = set()
111
118
  job_worker_lookup = _start_workers(
112
- process_pool, result_queue, datastore=datastore, worker_tasks=worker_tasks
119
+ process_pool=process_pool,
120
+ result_queue=result_queue,
121
+ datastore=datastore,
122
+ worker_tasks=worker_tasks,
123
+ running_job_uuids=running_job_uuids,
113
124
  )
114
125
 
115
- queued_jobs = datastore.load_job_queue()
126
+ dispatched_job_uuids: set[str] = set()
116
127
 
117
128
  async def enqueue_queued_job(queued_job: queued_job_t.QueuedJob) -> None:
118
129
  try:
@@ -123,8 +134,22 @@ async def start_scheduler(
123
134
  queued_job.queued_job_uuid, queued_job_t.JobStatus.FAILED
124
135
  )
125
136
  return
137
+ dispatched_job_uuids.add(queued_job.queued_job_uuid)
126
138
  await worker.listen_queue.put(queued_job)
127
139
 
140
+ async def _dispatch_undispatched_queued_jobs() -> None:
141
+ undispatched_job_uuids = [
142
+ queued_job_uuid
143
+ for queued_job_uuid in datastore.list_queued_job_uuids()
144
+ if queued_job_uuid not in dispatched_job_uuids
145
+ ]
146
+ if len(undispatched_job_uuids) == 0:
147
+ return
148
+ for queued_job in datastore.load_queued_jobs(
149
+ queued_job_uuids=undispatched_job_uuids
150
+ ):
151
+ await enqueue_queued_job(queued_job)
152
+
128
153
  async def _enqueue_or_deduplicate_job(
129
154
  job_ref_name: str,
130
155
  payload: queued_job_t.QueuedJobPayload,
@@ -172,30 +197,36 @@ async def start_scheduler(
172
197
  )
173
198
  return
174
199
 
175
- if queued_job.status == queued_job_t.JobStatus.QUEUED:
176
- datastore.remove_job_from_queue(command.queued_job_uuid)
200
+ if queued_job.status != queued_job_t.JobStatus.QUEUED:
177
201
  await command.response_queue.put(
178
202
  CommandCancelJobResponse(
179
- status=CommandCancelJobStatus.CANCELLED_WITH_RESTART
203
+ status=CommandCancelJobStatus.JOB_ALREADY_COMPLETED
180
204
  )
181
205
  )
206
+ return
182
207
 
183
- logger.log_debug(
184
- "Job cancelled successfully.",
185
- attributes={"job.queued_job_uuid": command.queued_job_uuid},
186
- )
187
-
188
- def delayed_exit() -> None:
189
- os._exit(RESTART_EXIT_CODE)
190
-
191
- threading.Timer(interval=5, function=delayed_exit).start()
192
-
208
+ datastore.remove_job_from_queue(command.queued_job_uuid)
209
+ if command.queued_job_uuid in running_job_uuids:
210
+ cancel_status = CommandCancelJobStatus.CANCELLED_WITH_RESTART
211
+ threading.Timer(
212
+ interval=RESTART_DELAY_SECS,
213
+ function=os._exit,
214
+ args=[RESTART_EXIT_CODE],
215
+ ).start()
193
216
  else:
194
- await command.response_queue.put(
195
- CommandCancelJobResponse(
196
- status=CommandCancelJobStatus.JOB_ALREADY_COMPLETED
197
- )
198
- )
217
+ dispatched_job_uuids.discard(command.queued_job_uuid)
218
+ cancel_status = CommandCancelJobStatus.CANCELLED_QUEUED
219
+
220
+ await command.response_queue.put(
221
+ CommandCancelJobResponse(status=cancel_status)
222
+ )
223
+ logger.log_debug(
224
+ "Job cancelled successfully.",
225
+ attributes={
226
+ "job.queued_job_uuid": command.queued_job_uuid,
227
+ "job.cancel_status": cancel_status,
228
+ },
229
+ )
199
230
 
200
231
  async def _handle_retry_job_command(command: CommandRetryJob) -> None:
201
232
  queued_job = datastore.retry_job(command.queued_job_uuid)
@@ -221,17 +252,21 @@ async def start_scheduler(
221
252
  attributes={"job.queued_job_uuids": deleted_uuids},
222
253
  )
223
254
 
224
- for queued_job in queued_jobs:
225
- await enqueue_queued_job(queued_job)
255
+ await _dispatch_undispatched_queued_jobs()
226
256
 
227
257
  result_task: ResultTask = asyncio.create_task(result_queue.get())
228
258
  command_task: CommandTask = asyncio.create_task(command_queue.get())
259
+ poll_task = asyncio.create_task(asyncio.sleep(QUEUED_JOB_POLL_INTERVAL_SECS))
229
260
  shutdown_task: asyncio.Task[object] | None = None
230
261
  if shutdown_event is not None:
231
262
  shutdown_task = asyncio.create_task(shutdown_event.wait())
232
263
 
233
264
  while True:
234
- waitables: list[asyncio.Task[object]] = [result_task, command_task]
265
+ waitables: list[asyncio.Task[object]] = [
266
+ result_task,
267
+ command_task,
268
+ poll_task,
269
+ ]
235
270
  if shutdown_task is not None:
236
271
  waitables.append(shutdown_task)
237
272
 
@@ -246,6 +281,7 @@ async def start_scheduler(
246
281
  worker_task.cancel()
247
282
  command_task.cancel()
248
283
  result_task.cancel()
284
+ poll_task.cancel()
249
285
  process_pool.shutdown(wait=False, cancel_futures=True)
250
286
  return
251
287
  if task == command_task:
@@ -275,4 +311,11 @@ async def start_scheduler(
275
311
  queued_job_result.queued_job_uuid,
276
312
  queued_job_t.JobStatus.FAILED,
277
313
  )
314
+ dispatched_job_uuids.discard(queued_job_result.queued_job_uuid)
315
+ running_job_uuids.discard(queued_job_result.queued_job_uuid)
278
316
  result_task = asyncio.create_task(result_queue.get())
317
+ elif task == poll_task:
318
+ await _dispatch_undispatched_queued_jobs()
319
+ poll_task = asyncio.create_task(
320
+ asyncio.sleep(QUEUED_JOB_POLL_INTERVAL_SECS)
321
+ )