UncountablePythonSDK 0.0.41__tar.gz → 0.0.42__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.

Files changed (250) hide show
  1. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/.github/workflows/documentation.yml +2 -3
  2. {uncountablepythonsdk-0.0.41/UncountablePythonSDK.egg-info → uncountablepythonsdk-0.0.42}/PKG-INFO +5 -1
  3. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42/UncountablePythonSDK.egg-info}/PKG-INFO +5 -1
  4. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/UncountablePythonSDK.egg-info/SOURCES.txt +20 -2
  5. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/UncountablePythonSDK.egg-info/requires.txt +4 -0
  6. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/requirements.txt +3 -3
  7. uncountablepythonsdk-0.0.42/examples/invoke_uploader.py +23 -0
  8. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/argument_parser/argument_parser.py +1 -1
  9. uncountablepythonsdk-0.0.42/pkgs/filesystem_utils/__init__.py +17 -0
  10. uncountablepythonsdk-0.0.42/pkgs/filesystem_utils/_gdrive_session.py +306 -0
  11. uncountablepythonsdk-0.0.42/pkgs/filesystem_utils/_local_session.py +69 -0
  12. uncountablepythonsdk-0.0.42/pkgs/filesystem_utils/_sftp_session.py +147 -0
  13. uncountablepythonsdk-0.0.42/pkgs/filesystem_utils/file_type_utils.py +61 -0
  14. uncountablepythonsdk-0.0.42/pkgs/filesystem_utils/filesystem_session.py +39 -0
  15. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/emit_open_api.py +4 -2
  16. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/emit_open_api_util.py +4 -2
  17. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/emit_python.py +13 -14
  18. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pyproject.toml +5 -1
  19. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/core/file_upload.py +13 -3
  20. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/integration/construct_client.py +1 -1
  21. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/integration/cron.py +9 -6
  22. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/integration/entrypoint.py +1 -1
  23. uncountablepythonsdk-0.0.42/uncountable/integration/executors/executors.py +24 -0
  24. uncountablepythonsdk-0.0.42/uncountable/integration/executors/generic_upload_executor.py +245 -0
  25. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/integration/executors/script_executor.py +1 -1
  26. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/integration/job.py +18 -1
  27. uncountablepythonsdk-0.0.42/uncountable/integration/secret_retrieval/__init__.py +3 -0
  28. uncountablepythonsdk-0.0.42/uncountable/integration/secret_retrieval/retrieve_secret.py +40 -0
  29. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/integration/server.py +1 -1
  30. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/__init__.py +8 -0
  31. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/batch/execute_batch.py +5 -5
  32. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/batch/execute_batch_load_async.py +3 -3
  33. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/chemical/convert_chemical_formats.py +4 -4
  34. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/entity/create_entities.py +4 -4
  35. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/entity/create_entity.py +4 -4
  36. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/entity/get_entities_data.py +4 -4
  37. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/entity/list_entities.py +5 -5
  38. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/entity/lock_entity.py +3 -3
  39. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/entity/resolve_entity_ids.py +4 -4
  40. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/entity/set_values.py +3 -3
  41. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/entity/transition_entity_phase.py +5 -5
  42. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/entity/unlock_entity.py +3 -3
  43. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/equipment/associate_equipment_input.py +3 -3
  44. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/field_options/upsert_field_options.py +4 -4
  45. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/id_source/list_id_source.py +4 -4
  46. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/id_source/match_id_source.py +4 -4
  47. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/input_groups/get_input_group_names.py +4 -4
  48. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/inputs/create_inputs.py +5 -5
  49. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/inputs/get_input_data.py +7 -7
  50. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/inputs/get_input_names.py +4 -4
  51. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/inputs/get_inputs_data.py +7 -7
  52. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/inputs/set_input_attribute_values.py +4 -4
  53. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/inputs/set_input_category.py +3 -3
  54. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/inputs/set_input_subcategories.py +3 -3
  55. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/inputs/set_intermediate_type.py +3 -3
  56. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/material_families/update_entity_material_families.py +3 -3
  57. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/outputs/get_output_data.py +7 -7
  58. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/outputs/get_output_names.py +4 -4
  59. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/outputs/resolve_output_conditions.py +6 -6
  60. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/permissions/set_core_permissions.py +7 -7
  61. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/project/get_projects.py +4 -4
  62. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/project/get_projects_data.py +4 -4
  63. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipe_links/create_recipe_link.py +3 -3
  64. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipe_links/remove_recipe_link.py +3 -3
  65. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipe_metadata/get_recipe_metadata_data.py +4 -4
  66. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/add_recipe_to_project.py +3 -3
  67. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/archive_recipes.py +3 -3
  68. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/associate_recipe_as_input.py +3 -3
  69. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/associate_recipe_as_lot.py +3 -3
  70. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/create_recipe.py +3 -3
  71. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/create_recipes.py +5 -5
  72. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/disassociate_recipe_as_input.py +3 -3
  73. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/edit_recipe_inputs.py +12 -12
  74. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/get_curve.py +3 -3
  75. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/get_recipe_calculations.py +4 -4
  76. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/get_recipe_links.py +3 -3
  77. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/get_recipe_names.py +4 -4
  78. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/get_recipe_output_metadata.py +4 -4
  79. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/get_recipes_data.py +12 -12
  80. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/lock_recipes.py +4 -4
  81. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/remove_recipe_from_project.py +3 -3
  82. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/set_recipe_inputs.py +4 -4
  83. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/set_recipe_metadata.py +3 -3
  84. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/set_recipe_output_annotations.py +7 -7
  85. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/set_recipe_outputs.py +5 -5
  86. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/set_recipe_tags.py +7 -7
  87. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/unarchive_recipes.py +3 -3
  88. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/unlock_recipes.py +3 -3
  89. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/triggers/run_trigger.py +3 -3
  90. uncountablepythonsdk-0.0.42/uncountable/types/api/uploader/__init__.py +1 -0
  91. uncountablepythonsdk-0.0.42/uncountable/types/api/uploader/invoke_uploader.py +38 -0
  92. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/async_batch_processor.py +36 -0
  93. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/async_batch_t.py +6 -4
  94. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/calculations_t.py +2 -2
  95. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/chemical_structure_t.py +2 -2
  96. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/client_base.py +25 -2
  97. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/curves_t.py +3 -3
  98. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/entity_t.py +2 -2
  99. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/experiment_groups_t.py +2 -2
  100. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/field_values_t.py +5 -5
  101. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/fields_t.py +2 -2
  102. uncountablepythonsdk-0.0.42/uncountable/types/generic_upload.py +9 -0
  103. uncountablepythonsdk-0.0.42/uncountable/types/generic_upload_t.py +41 -0
  104. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/id_source_t.py +5 -5
  105. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/identifier_t.py +4 -4
  106. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/input_attributes_t.py +2 -2
  107. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/inputs_t.py +2 -2
  108. uncountablepythonsdk-0.0.42/uncountable/types/job_definition.py +26 -0
  109. uncountablepythonsdk-0.0.42/uncountable/types/job_definition_t.py +203 -0
  110. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/outputs_t.py +2 -2
  111. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/phases_t.py +2 -2
  112. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_identifiers_t.py +4 -4
  113. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_links_t.py +2 -2
  114. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_metadata_t.py +4 -4
  115. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_output_metadata_t.py +2 -2
  116. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_tags_t.py +2 -2
  117. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_workflow_steps_t.py +5 -5
  118. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipes_t.py +2 -2
  119. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/response_t.py +2 -2
  120. uncountablepythonsdk-0.0.42/uncountable/types/secret_retrieval.py +12 -0
  121. uncountablepythonsdk-0.0.42/uncountable/types/secret_retrieval_t.py +69 -0
  122. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/units_t.py +2 -2
  123. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/users_t.py +2 -2
  124. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/workflows_t.py +3 -3
  125. uncountablepythonsdk-0.0.41/uncountable/integration/types.py +0 -89
  126. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/.github/workflows/publish.yml +0 -0
  127. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/.gitignore +0 -0
  128. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/README.md +0 -0
  129. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/UncountablePythonSDK.egg-info/dependency_links.txt +0 -0
  130. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/UncountablePythonSDK.egg-info/top_level.txt +0 -0
  131. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/.gitignore +0 -0
  132. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/conf.py +0 -0
  133. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/index.md +0 -0
  134. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/justfile +0 -0
  135. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/quickstart.md +0 -0
  136. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/static/favicons/android-chrome-192x192.png +0 -0
  137. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/static/favicons/android-chrome-512x512.png +0 -0
  138. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/static/favicons/apple-touch-icon.png +0 -0
  139. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/static/favicons/browserconfig.xml +0 -0
  140. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/static/favicons/favicon-16x16.png +0 -0
  141. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/static/favicons/favicon-32x32.png +0 -0
  142. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/static/favicons/manifest.json +0 -0
  143. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/static/favicons/mstile-150x150.png +0 -0
  144. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/static/favicons/safari-pinned-tab.svg +0 -0
  145. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/docs/static/logo_blue.png +0 -0
  146. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/examples/async_batch.py +0 -0
  147. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/examples/create_entity.py +0 -0
  148. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/examples/edit_recipe_inputs.py +0 -0
  149. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/examples/upload_files.py +0 -0
  150. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/__init__.py +0 -0
  151. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/argument_parser/__init__.py +0 -0
  152. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/argument_parser/_is_enum.py +0 -0
  153. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/argument_parser/_is_namedtuple.py +0 -0
  154. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/argument_parser/case_convert.py +0 -0
  155. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/serialization/__init__.py +0 -0
  156. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/serialization/missing_sentry.py +0 -0
  157. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/serialization/opaque_key.py +0 -0
  158. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/serialization/serial_class.py +0 -0
  159. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/serialization/serial_union.py +0 -0
  160. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/serialization/yaml.py +0 -0
  161. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/serialization_util/__init__.py +0 -0
  162. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/serialization_util/_get_type_for_serialization.py +0 -0
  163. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/serialization_util/convert_to_snakecase.py +0 -0
  164. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/serialization_util/serialization_helpers.py +0 -0
  165. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/strenum_compat/__init__.py +0 -0
  166. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/strenum_compat/strenum_compat.py +0 -0
  167. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/__init__.py +0 -0
  168. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/__main__.py +0 -0
  169. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/actions_registry/__init__.py +0 -0
  170. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/actions_registry/__main__.py +0 -0
  171. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/actions_registry/emit_typescript.py +0 -0
  172. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/builder.py +0 -0
  173. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/config.py +0 -0
  174. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/emit_io_ts.py +0 -0
  175. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/emit_typescript.py +0 -0
  176. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/emit_typescript_util.py +0 -0
  177. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/load_types.py +0 -0
  178. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/open_api_util.py +0 -0
  179. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/parts/base.py.prepart +0 -0
  180. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/parts/base.ts.prepart +0 -0
  181. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/test.py +0 -0
  182. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/type_info/__main__.py +0 -0
  183. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/type_info/emit_type_info.py +0 -0
  184. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/util.py +0 -0
  185. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/value_spec/__init__.py +0 -0
  186. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/value_spec/__main__.py +0 -0
  187. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/value_spec/convert_type.py +0 -0
  188. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/value_spec/emit_python.py +0 -0
  189. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/pkgs/type_spec/value_spec/types.py +0 -0
  190. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/setup.cfg +0 -0
  191. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/__init__.py +0 -0
  192. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/core/__init__.py +0 -0
  193. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/core/async_batch.py +0 -0
  194. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/core/client.py +0 -0
  195. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/core/types.py +0 -0
  196. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/integration/__init__.py +0 -0
  197. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/integration/db/__init__.py +0 -0
  198. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/integration/db/connect.py +0 -0
  199. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/integration/executors/__init__.py +0 -0
  200. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/py.typed +0 -0
  201. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/__init__.py +0 -0
  202. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/batch/__init__.py +0 -0
  203. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/chemical/__init__.py +0 -0
  204. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/entity/__init__.py +0 -0
  205. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/equipment/__init__.py +0 -0
  206. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/field_options/__init__.py +0 -0
  207. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/id_source/__init__.py +0 -0
  208. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/input_groups/__init__.py +0 -0
  209. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/inputs/__init__.py +0 -0
  210. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/material_families/__init__.py +0 -0
  211. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/outputs/__init__.py +0 -0
  212. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/permissions/__init__.py +0 -0
  213. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/project/__init__.py +0 -0
  214. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipe_links/__init__.py +0 -0
  215. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipe_metadata/__init__.py +0 -0
  216. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/recipes/__init__.py +0 -0
  217. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/api/triggers/__init__.py +0 -0
  218. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/async_batch.py +0 -0
  219. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/base.py +0 -0
  220. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/base_t.py +0 -0
  221. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/calculations.py +0 -0
  222. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/chemical_structure.py +0 -0
  223. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/curves.py +0 -0
  224. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/entity.py +0 -0
  225. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/experiment_groups.py +0 -0
  226. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/field_values.py +0 -0
  227. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/fields.py +0 -0
  228. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/id_source.py +0 -0
  229. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/identifier.py +0 -0
  230. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/input_attributes.py +0 -0
  231. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/inputs.py +0 -0
  232. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/outputs.py +0 -0
  233. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/permissions.py +0 -0
  234. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/permissions_t.py +0 -0
  235. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/phases.py +0 -0
  236. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/post_base.py +0 -0
  237. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/post_base_t.py +0 -0
  238. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_identifiers.py +0 -0
  239. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_inputs.py +0 -0
  240. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_inputs_t.py +0 -0
  241. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_links.py +0 -0
  242. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_metadata.py +0 -0
  243. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_output_metadata.py +0 -0
  244. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_tags.py +0 -0
  245. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipe_workflow_steps.py +0 -0
  246. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/recipes.py +0 -0
  247. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/response.py +0 -0
  248. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/units.py +0 -0
  249. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/users.py +0 -0
  250. {uncountablepythonsdk-0.0.41 → uncountablepythonsdk-0.0.42}/uncountable/types/workflows.py +0 -0
@@ -11,10 +11,9 @@ jobs:
11
11
  steps:
12
12
  - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
13
13
  - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5
14
- - uses: extractions/setup-just@dd310ad5a97d8e7b41793f8ef055398d51ad4de6 # v2
14
+ - uses: extractions/setup-crate@4993624604c307fbca528d28a3c8b60fa5ecc859 # v1
15
15
  with:
16
- just-version: '1.8.0'
17
- github-token: ${{ secrets.GITHUB_TOKEN }}
16
+ repo: casey/just@0.10
18
17
  - name: Install dependencies
19
18
  run: |
20
19
  # install the dependencies for the package
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: UncountablePythonSDK
3
- Version: 0.0.41
3
+ Version: 0.0.42
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
@@ -24,6 +24,10 @@ Requires-Dist: APScheduler==3.*
24
24
  Requires-Dist: python-dateutil==2.*
25
25
  Requires-Dist: shelljob==0.*
26
26
  Requires-Dist: PyYAML==6.*
27
+ Requires-Dist: google-api-python-client==2.*
28
+ Requires-Dist: tqdm==4.*
29
+ Requires-Dist: pysftp==0.*
30
+ Requires-Dist: paramiko==3.*
27
31
  Provides-Extra: test
28
32
  Requires-Dist: mypy==1.*; extra == "test"
29
33
  Requires-Dist: ruff==0.*; extra == "test"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: UncountablePythonSDK
3
- Version: 0.0.41
3
+ Version: 0.0.42
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
@@ -24,6 +24,10 @@ Requires-Dist: APScheduler==3.*
24
24
  Requires-Dist: python-dateutil==2.*
25
25
  Requires-Dist: shelljob==0.*
26
26
  Requires-Dist: PyYAML==6.*
27
+ Requires-Dist: google-api-python-client==2.*
28
+ Requires-Dist: tqdm==4.*
29
+ Requires-Dist: pysftp==0.*
30
+ Requires-Dist: paramiko==3.*
27
31
  Provides-Extra: test
28
32
  Requires-Dist: mypy==1.*; extra == "test"
29
33
  Requires-Dist: ruff==0.*; extra == "test"
@@ -27,6 +27,7 @@ docs/static/favicons/safari-pinned-tab.svg
27
27
  examples/async_batch.py
28
28
  examples/create_entity.py
29
29
  examples/edit_recipe_inputs.py
30
+ examples/invoke_uploader.py
30
31
  examples/upload_files.py
31
32
  pkgs/__init__.py
32
33
  pkgs/argument_parser/__init__.py
@@ -34,6 +35,12 @@ pkgs/argument_parser/_is_enum.py
34
35
  pkgs/argument_parser/_is_namedtuple.py
35
36
  pkgs/argument_parser/argument_parser.py
36
37
  pkgs/argument_parser/case_convert.py
38
+ pkgs/filesystem_utils/__init__.py
39
+ pkgs/filesystem_utils/_gdrive_session.py
40
+ pkgs/filesystem_utils/_local_session.py
41
+ pkgs/filesystem_utils/_sftp_session.py
42
+ pkgs/filesystem_utils/file_type_utils.py
43
+ pkgs/filesystem_utils/filesystem_session.py
37
44
  pkgs/serialization/__init__.py
38
45
  pkgs/serialization/missing_sentry.py
39
46
  pkgs/serialization/opaque_key.py
@@ -85,11 +92,14 @@ uncountable/integration/cron.py
85
92
  uncountable/integration/entrypoint.py
86
93
  uncountable/integration/job.py
87
94
  uncountable/integration/server.py
88
- uncountable/integration/types.py
89
95
  uncountable/integration/db/__init__.py
90
96
  uncountable/integration/db/connect.py
91
97
  uncountable/integration/executors/__init__.py
98
+ uncountable/integration/executors/executors.py
99
+ uncountable/integration/executors/generic_upload_executor.py
92
100
  uncountable/integration/executors/script_executor.py
101
+ uncountable/integration/secret_retrieval/__init__.py
102
+ uncountable/integration/secret_retrieval/retrieve_secret.py
93
103
  uncountable/types/__init__.py
94
104
  uncountable/types/async_batch.py
95
105
  uncountable/types/async_batch_processor.py
@@ -111,6 +121,8 @@ uncountable/types/field_values.py
111
121
  uncountable/types/field_values_t.py
112
122
  uncountable/types/fields.py
113
123
  uncountable/types/fields_t.py
124
+ uncountable/types/generic_upload.py
125
+ uncountable/types/generic_upload_t.py
114
126
  uncountable/types/id_source.py
115
127
  uncountable/types/id_source_t.py
116
128
  uncountable/types/identifier.py
@@ -119,6 +131,8 @@ uncountable/types/input_attributes.py
119
131
  uncountable/types/input_attributes_t.py
120
132
  uncountable/types/inputs.py
121
133
  uncountable/types/inputs_t.py
134
+ uncountable/types/job_definition.py
135
+ uncountable/types/job_definition_t.py
122
136
  uncountable/types/outputs.py
123
137
  uncountable/types/outputs_t.py
124
138
  uncountable/types/permissions.py
@@ -145,6 +159,8 @@ uncountable/types/recipes.py
145
159
  uncountable/types/recipes_t.py
146
160
  uncountable/types/response.py
147
161
  uncountable/types/response_t.py
162
+ uncountable/types/secret_retrieval.py
163
+ uncountable/types/secret_retrieval_t.py
148
164
  uncountable/types/units.py
149
165
  uncountable/types/units_t.py
150
166
  uncountable/types/users.py
@@ -226,4 +242,6 @@ uncountable/types/api/recipes/set_recipe_tags.py
226
242
  uncountable/types/api/recipes/unarchive_recipes.py
227
243
  uncountable/types/api/recipes/unlock_recipes.py
228
244
  uncountable/types/api/triggers/__init__.py
229
- uncountable/types/api/triggers/run_trigger.py
245
+ uncountable/types/api/triggers/run_trigger.py
246
+ uncountable/types/api/uploader/__init__.py
247
+ uncountable/types/api/uploader/invoke_uploader.py
@@ -6,6 +6,10 @@ APScheduler==3.*
6
6
  python-dateutil==2.*
7
7
  shelljob==0.*
8
8
  PyYAML==6.*
9
+ google-api-python-client==2.*
10
+ tqdm==4.*
11
+ pysftp==0.*
12
+ paramiko==3.*
9
13
 
10
14
  [test]
11
15
  mypy==1.*
@@ -1,7 +1,7 @@
1
- furo==2024.5.6
1
+ furo==2024.7.18
2
2
  myst-parser==3.0.1
3
- sphinx-autoapi==3.1.0
3
+ sphinx-autoapi==3.2.0
4
4
  sphinx-copybutton==0.5.2
5
- Sphinx==7.3.7
5
+ Sphinx==7.4.4
6
6
  sphinx_design==0.6.0
7
7
  sphinx-favicon==1.0.1
@@ -0,0 +1,23 @@
1
+ import os
2
+
3
+ from uncountable.core import AuthDetailsApiKey, Client, MediaFileUpload
4
+ from uncountable.types.identifier_t import IdentifierKeyId
5
+
6
+ client = Client(
7
+ base_url="http://localhost:5000",
8
+ auth_details=AuthDetailsApiKey(
9
+ api_id=os.environ["UNC_API_ID"],
10
+ api_secret_key=os.environ["UNC_API_SECRET_KEY"],
11
+ ),
12
+ )
13
+ uploaded = client.upload_files(
14
+ file_uploads=[
15
+ MediaFileUpload(path="~/Downloads/my_file_to_upload.csv"),
16
+ ]
17
+ )
18
+
19
+ client.invoke_uploader(
20
+ file_ids=[file.file_id for file in uploaded],
21
+ uploader_key=IdentifierKeyId(id=48),
22
+ material_family_keys=[IdentifierKeyId(id=1)],
23
+ )
@@ -218,7 +218,7 @@ def _build_parser_inner(
218
218
  arg_parsers = [_build_parser_inner(arg, context) for arg in sorted_args]
219
219
  return lambda value: _invoke_fallback_parsers(parsed_type, arg_parsers, value)
220
220
 
221
- if parsed_type is typing.Any:
221
+ if parsed_type is typing.Any: # type: ignore[comparison-overlap]
222
222
  return lambda value: value
223
223
 
224
224
  if origin in (list, set):
@@ -0,0 +1,17 @@
1
+ from ._gdrive_session import GDriveSession as GDriveSession
2
+ from ._gdrive_session import delete_gdrive_file as delete_gdrive_file
3
+ from ._gdrive_session import download_gdrive_file as download_gdrive_file
4
+ from ._gdrive_session import list_gdrive_files as list_gdrive_files
5
+ from ._gdrive_session import move_gdrive_file as move_gdrive_file
6
+ from ._gdrive_session import upload_file_gdrive as upload_file_gdrive
7
+ from ._local_session import LocalSession as LocalSession
8
+ from ._sftp_session import SFTPSession as SFTPSession
9
+ from ._sftp_session import list_sftp_files as list_sftp_files
10
+ from ._sftp_session import move_sftp_files as move_sftp_files
11
+ from .file_type_utils import FileObjectData as FileObjectData
12
+ from .file_type_utils import FileSystemFileReference as FileSystemFileReference
13
+ from .file_type_utils import FileSystemObject as FileSystemObject
14
+ from .file_type_utils import FileSystemSFTPConfig as FileSystemSFTPConfig
15
+ from .file_type_utils import FileTransfer as FileTransfer
16
+ from .file_type_utils import IncompatibleFileReference as IncompatibleFileReference
17
+ from .file_type_utils import RemoteObjectReference as RemoteObjectReference
@@ -0,0 +1,306 @@
1
+ import os
2
+ from io import BytesIO
3
+ from typing import Any, Optional
4
+
5
+ from google.oauth2 import service_account
6
+ from googleapiclient.discovery import build as build_gdrive_connection
7
+ from googleapiclient.errors import HttpError
8
+ from googleapiclient.http import MediaIoBaseDownload, MediaIoBaseUpload
9
+ from tqdm import tqdm
10
+
11
+ from pkgs.filesystem_utils.file_type_utils import (
12
+ FileObjectData,
13
+ FileSystemFileReference,
14
+ FileSystemObject,
15
+ FileTransfer,
16
+ IncompatibleFileReference,
17
+ RemoteObjectReference,
18
+ )
19
+
20
+ from .filesystem_session import FileSystemSession
21
+
22
+ # NOTE: google apis do not have static types
23
+ GDriveResource = Any
24
+
25
+
26
+ def download_gdrive_file(
27
+ gdrive_connection: GDriveResource,
28
+ file_id: str,
29
+ filename: str,
30
+ mime_type: str,
31
+ *,
32
+ verbose: bool = False,
33
+ ) -> Optional[FileObjectData]:
34
+ if "folder" in mime_type:
35
+ if verbose:
36
+ print(f"{filename} is a folder and will not be downloaded.")
37
+ return None
38
+ elif "google-apps" in mime_type:
39
+ # Handle google workspace doc
40
+ if "spreadsheet" in mime_type:
41
+ if verbose:
42
+ print(f"{filename} is a Google Sheet, exporting.")
43
+ file_request = gdrive_connection.files().export_media(
44
+ fileId=file_id, mimeType="text/csv"
45
+ )
46
+ filename += ".csv"
47
+ elif "document" in mime_type:
48
+ if verbose:
49
+ print(f"{filename} is a Google Doc, exporting.")
50
+ file_request = gdrive_connection.files().export_media(
51
+ fileId=file_id, mimeType="application/msword"
52
+ )
53
+ filename += ".doc"
54
+ else:
55
+ if verbose:
56
+ print(f"{filename} is an unsupported google workspace filetype.")
57
+ print(f"Skipping. mimeType: {mime_type}.")
58
+ return None
59
+ else:
60
+ file_request = gdrive_connection.files().get_media(fileId=file_id)
61
+
62
+ file_handler = BytesIO()
63
+ downloader = MediaIoBaseDownload(file_handler, file_request)
64
+ download_complete = False
65
+ while not download_complete:
66
+ status, download_complete = downloader.next_chunk()
67
+
68
+ file_handler.seek(0)
69
+ file_data = file_handler.read()
70
+ return FileObjectData(
71
+ file_data=file_data,
72
+ file_IO=BytesIO(file_data),
73
+ filename=filename,
74
+ filepath=file_id,
75
+ metadata={"id": file_id},
76
+ mime_type=mime_type,
77
+ )
78
+
79
+
80
+ def list_gdrive_files(
81
+ gdrive_connection: GDriveResource, gdrive_folder_id: str, *, recurse: bool = False
82
+ ) -> list[dict[str, str]]:
83
+ query = f"parents = '{gdrive_folder_id}'"
84
+ print("Listing files", end="", flush=True)
85
+ paginated_files_in_folder = [
86
+ (
87
+ gdrive_connection.files()
88
+ .list(
89
+ q=query,
90
+ corpora="allDrives",
91
+ includeItemsFromAllDrives=True,
92
+ supportsAllDrives=True,
93
+ )
94
+ .execute()
95
+ )
96
+ ]
97
+ while paginated_files_in_folder[-1].get("nextPageToken") is not None:
98
+ print(".", end="", flush=True)
99
+ paginated_files_in_folder.append(
100
+ gdrive_connection.files()
101
+ .list(
102
+ q=query,
103
+ corpora="allDrives",
104
+ includeItemsFromAllDrives=True,
105
+ supportsAllDrives=True,
106
+ pageToken=paginated_files_in_folder[-1]["nextPageToken"],
107
+ )
108
+ .execute()
109
+ )
110
+ print()
111
+ # Get available files: https://developers.google.com/drive/api/v3/manage-downloads#python
112
+ files: list[dict[str, str]] = []
113
+ for files_in_folder in paginated_files_in_folder:
114
+ files.extend(files_in_folder.get("files", []))
115
+ subfiles: list[dict[str, str]] = []
116
+ if recurse:
117
+ for file in files:
118
+ if file["mimeType"] == "application/vnd.google-apps.folder":
119
+ subfiles.extend(
120
+ list_gdrive_files(
121
+ gdrive_connection=gdrive_connection,
122
+ gdrive_folder_id=file["id"],
123
+ recurse=True,
124
+ )
125
+ )
126
+ return [*files, *subfiles]
127
+
128
+
129
+ def upload_file_gdrive(
130
+ gdrive_connection: GDriveResource,
131
+ src_file: BytesIO,
132
+ mime_type: str,
133
+ dest_folder_id: str,
134
+ dest_filename: str,
135
+ ) -> None:
136
+ file_metadata = {"name": dest_filename, "parents": [dest_folder_id]}
137
+ media = MediaIoBaseUpload(src_file, mimetype=mime_type)
138
+ try:
139
+ gdrive_connection.files().create(
140
+ body=file_metadata, media_body=media, fields="id", supportsAllDrives=True
141
+ ).execute()
142
+ except HttpError:
143
+ print("FileSystemObject Upload to GDrive Unsuccessful")
144
+
145
+
146
+ def move_gdrive_file(
147
+ gdrive_connection: GDriveResource,
148
+ src_file_id: str,
149
+ dest_folder_id: str,
150
+ *,
151
+ dest_filename: Optional[str] = None,
152
+ ) -> None:
153
+ # Retrieve the existing parents to remove
154
+ file = (
155
+ gdrive_connection.files()
156
+ .get(fileId=src_file_id, fields="parents, name", supportsTeamDrives=True)
157
+ .execute()
158
+ )
159
+
160
+ new_filename = file["name"]
161
+ if dest_filename is not None:
162
+ new_filename = dest_filename
163
+ previous_parents = ",".join(file.get("parents"))
164
+ metadata = {"name": new_filename}
165
+ gdrive_connection.files().update(
166
+ fileId=src_file_id, body=metadata, fields="name", supportsTeamDrives=True
167
+ ).execute()
168
+ gdrive_connection.files().update(
169
+ fileId=src_file_id,
170
+ addParents=dest_folder_id,
171
+ removeParents=previous_parents,
172
+ fields="id, parents",
173
+ supportsTeamDrives=True,
174
+ ).execute()
175
+
176
+
177
+ def delete_gdrive_file(gdrive_connection: GDriveResource, file_id: str) -> None:
178
+ gdrive_connection.files().delete(fileId=file_id, supportsAllDrives=True).execute()
179
+
180
+
181
+ class GDriveSession(FileSystemSession):
182
+ def __init__(self, service_account_json_path: str) -> None:
183
+ super().__init__()
184
+ self.service_account_json_path = service_account_json_path
185
+
186
+ def start(self) -> None:
187
+ credentials = service_account.Credentials.from_service_account_file( # type: ignore[no-untyped-call]
188
+ self.service_account_json_path
189
+ )
190
+ gdrive_connection = build_gdrive_connection(
191
+ "drive", "v3", credentials=credentials
192
+ )
193
+ self.connection = gdrive_connection
194
+
195
+ def list_files(
196
+ self,
197
+ dir_path: FileSystemObject,
198
+ *,
199
+ recursive: bool = False,
200
+ valid_file_extensions: Optional[tuple[str, ...]] = None,
201
+ ) -> list[FileSystemObject]:
202
+ if not isinstance(dir_path, RemoteObjectReference):
203
+ raise IncompatibleFileReference(
204
+ "Incompatible FileSystemObject to GDriveSession.list_files"
205
+ )
206
+ if not dir_path.is_dir:
207
+ raise IncompatibleFileReference(
208
+ "FileSystemObject does not reference a directory"
209
+ )
210
+ files = list_gdrive_files(self.connection, dir_path.file_id, recurse=recursive)
211
+ gdrive_files: list[FileSystemObject] = []
212
+ for file_context in files:
213
+ if (
214
+ valid_file_extensions is not None
215
+ and os.path.splitext(file_context["name"])[1] not in valid_file_extensions
216
+ ):
217
+ continue
218
+ gdrive_files.append(
219
+ RemoteObjectReference(
220
+ file_id=file_context["id"],
221
+ mime_type=file_context["mimeType"],
222
+ filename=file_context["name"],
223
+ )
224
+ )
225
+ return gdrive_files
226
+
227
+ def delete_files(self, filepaths: list[FileSystemObject]) -> None:
228
+ """Warning:
229
+ Security account must have sufficient permissions to perform delete!
230
+ https://developers.google.com/drive/api/v3/reference/files/delete?hl=en
231
+ https://developers.google.com/drive/api/v3/ref-roles
232
+ """
233
+ for file_object in filepaths:
234
+ if not isinstance(file_object, RemoteObjectReference):
235
+ raise IncompatibleFileReference(
236
+ "Incompatible FileSystemObject provided to GDriveSession.delete_files"
237
+ )
238
+ delete_gdrive_file(self.connection, file_object.file_id)
239
+
240
+ def move_files(self, file_mappings: list[FileTransfer]) -> None:
241
+ for src_file, dest_file in file_mappings:
242
+ if (
243
+ isinstance(src_file, FileSystemFileReference)
244
+ or not isinstance(dest_file, RemoteObjectReference)
245
+ or not dest_file.is_dir
246
+ or (isinstance(src_file, RemoteObjectReference) and src_file.is_dir)
247
+ ):
248
+ continue
249
+ new_filename = dest_file.filename
250
+ if isinstance(src_file, RemoteObjectReference):
251
+ if new_filename is not None:
252
+ move_gdrive_file(
253
+ self.connection,
254
+ src_file.file_id,
255
+ dest_file.file_id,
256
+ dest_filename=new_filename,
257
+ )
258
+ else:
259
+ move_gdrive_file(self.connection, src_file.file_id, dest_file.file_id)
260
+ elif isinstance(src_file, FileObjectData):
261
+ if src_file.mime_type is None:
262
+ raise IncompatibleFileReference(
263
+ "No mime_type present on source file data."
264
+ )
265
+ new_filename = src_file.filename
266
+ if dest_file.filename is not None:
267
+ new_filename = dest_file.filename
268
+ upload_file_gdrive(
269
+ self.connection,
270
+ src_file.file_IO,
271
+ src_file.mime_type,
272
+ dest_file.file_id,
273
+ new_filename,
274
+ )
275
+ else:
276
+ raise IncompatibleFileReference(
277
+ "Unrecognized file reference in FileTransfer object"
278
+ )
279
+
280
+ def download_files(self, filepaths: list[FileSystemObject]) -> list[FileObjectData]:
281
+ downloaded_files: list[FileObjectData] = []
282
+ print(f"Downloading {len(filepaths)} files")
283
+ for file_object in tqdm(filepaths):
284
+ if (
285
+ not isinstance(file_object, RemoteObjectReference)
286
+ or file_object.filename is None
287
+ ):
288
+ raise IncompatibleFileReference(
289
+ "Incompatible FileSystemObject included in filepaths"
290
+ )
291
+ downloaded_file = download_gdrive_file(
292
+ self.connection,
293
+ file_object.file_id,
294
+ file_object.filename,
295
+ file_object.mime_type,
296
+ )
297
+ if downloaded_file is not None:
298
+ downloaded_files.append(downloaded_file)
299
+ return downloaded_files
300
+
301
+ def __enter__(self) -> "GDriveSession":
302
+ self.start()
303
+ return self
304
+
305
+ def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None:
306
+ self.connection.close()
@@ -0,0 +1,69 @@
1
+ import os
2
+ from io import BytesIO
3
+
4
+ from pkgs.filesystem_utils.file_type_utils import (
5
+ FileObjectData,
6
+ FileSystemFileReference,
7
+ FileSystemObject,
8
+ FileTransfer,
9
+ IncompatibleFileReference,
10
+ )
11
+
12
+ from .filesystem_session import FileSystemSession
13
+
14
+
15
+ class LocalSession(FileSystemSession):
16
+ def __init__(self) -> None:
17
+ super().__init__()
18
+
19
+ def start(self) -> None:
20
+ return None
21
+
22
+ def __enter__(self) -> "LocalSession":
23
+ return self
24
+
25
+ def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None:
26
+ return None
27
+
28
+ def move_files(self, file_mappings: list[FileTransfer]) -> None:
29
+ for src_file, dest_file in file_mappings:
30
+ if not (
31
+ isinstance(src_file, FileSystemFileReference)
32
+ and isinstance(dest_file, FileSystemFileReference)
33
+ ):
34
+ raise IncompatibleFileReference()
35
+ os.rename(src_file.filepath, dest_file.filepath)
36
+
37
+ def download_files(self, filepaths: list[FileSystemObject]) -> list[FileObjectData]:
38
+ downloaded_files: list[FileObjectData] = []
39
+ for file_object in filepaths:
40
+ if (
41
+ not isinstance(file_object, FileSystemFileReference)
42
+ or file_object.filename is None
43
+ ):
44
+ raise IncompatibleFileReference()
45
+ with open(file_object.filepath, "rb") as file_data:
46
+ file_bytes = file_data.read()
47
+ downloaded_files.append(
48
+ FileObjectData(
49
+ file_bytes,
50
+ BytesIO(file_bytes),
51
+ file_object.filename,
52
+ filepath=file_object.filepath,
53
+ )
54
+ )
55
+ return downloaded_files
56
+
57
+ def list_files(
58
+ self, dir_path: FileSystemObject, *, recursive: bool = False
59
+ ) -> list[FileSystemObject]:
60
+ if not isinstance(dir_path, FileSystemFileReference) or not os.path.isdir(
61
+ dir_path.filepath
62
+ ):
63
+ raise IncompatibleFileReference()
64
+ if recursive:
65
+ raise NotImplementedError("recursive not implemented for local session")
66
+ return [
67
+ FileSystemFileReference(os.path.join(dir_path.filepath, filename))
68
+ for filename in os.listdir(dir_path.filepath)
69
+ ]