latch 2.53.7.dev2__tar.gz → 2.53.8__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 (193) hide show
  1. {latch-2.53.7.dev2/latch.egg-info → latch-2.53.8}/PKG-INFO +1 -3
  2. {latch-2.53.7.dev2 → latch-2.53.8}/latch/ldata/_transfer/upload.py +1 -1
  3. {latch-2.53.7.dev2 → latch-2.53.8}/latch/ldata/type.py +1 -1
  4. {latch-2.53.7.dev2 → latch-2.53.8/latch.egg-info}/PKG-INFO +1 -3
  5. {latch-2.53.7.dev2 → latch-2.53.8}/latch.egg-info/SOURCES.txt +5 -10
  6. {latch-2.53.7.dev2 → latch-2.53.8}/latch.egg-info/requires.txt +0 -2
  7. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/main.py +19 -14
  8. latch-2.53.8/latch_cli/services/cp/main.py +106 -0
  9. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/cp/utils.py +4 -22
  10. latch-2.53.8/latch_cli/services/k8s/attach.py +77 -0
  11. latch-2.53.8/latch_cli/services/k8s/execute.py +78 -0
  12. {latch-2.53.7.dev2/latch_cli/services/execute → latch-2.53.8/latch_cli/services/k8s}/utils.py +73 -12
  13. latch-2.53.7.dev2/latch_cli/services/execute/main.py → latch-2.53.8/latch_cli/services/k8s/ws_utils.py +19 -71
  14. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/utils/__init__.py +1 -2
  15. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/utils/path.py +21 -27
  16. {latch-2.53.7.dev2 → latch-2.53.8}/setup.py +1 -4
  17. latch-2.53.7.dev2/latch_cli/services/cp/download/main.py +0 -208
  18. latch-2.53.7.dev2/latch_cli/services/cp/download/worker.py +0 -121
  19. latch-2.53.7.dev2/latch_cli/services/cp/http_utils.py +0 -91
  20. latch-2.53.7.dev2/latch_cli/services/cp/main.py +0 -101
  21. latch-2.53.7.dev2/latch_cli/services/cp/upload/main.py +0 -124
  22. latch-2.53.7.dev2/latch_cli/services/cp/upload/worker.py +0 -223
  23. latch-2.53.7.dev2/latch_cli/tui/__init__.py +0 -0
  24. latch-2.53.7.dev2/tests/__init__.py +0 -0
  25. {latch-2.53.7.dev2 → latch-2.53.8}/LICENSE +0 -0
  26. {latch-2.53.7.dev2 → latch-2.53.8}/MANIFEST.in +0 -0
  27. {latch-2.53.7.dev2 → latch-2.53.8}/README.md +0 -0
  28. {latch-2.53.7.dev2 → latch-2.53.8}/latch/__init__.py +0 -0
  29. {latch-2.53.7.dev2 → latch-2.53.8}/latch/account.py +0 -0
  30. {latch-2.53.7.dev2 → latch-2.53.8}/latch/executions.py +0 -0
  31. {latch-2.53.7.dev2 → latch-2.53.8}/latch/functions/__init__.py +0 -0
  32. {latch-2.53.7.dev2 → latch-2.53.8}/latch/functions/messages.py +0 -0
  33. {latch-2.53.7.dev2 → latch-2.53.8}/latch/functions/operators.py +0 -0
  34. {latch-2.53.7.dev2 → latch-2.53.8}/latch/functions/secrets.py +0 -0
  35. {latch-2.53.7.dev2 → latch-2.53.8}/latch/ldata/__init__.py +0 -0
  36. {latch-2.53.7.dev2 → latch-2.53.8}/latch/ldata/_transfer/__init__.py +0 -0
  37. {latch-2.53.7.dev2 → latch-2.53.8}/latch/ldata/_transfer/download.py +0 -0
  38. {latch-2.53.7.dev2 → latch-2.53.8}/latch/ldata/_transfer/manager.py +0 -0
  39. {latch-2.53.7.dev2 → latch-2.53.8}/latch/ldata/_transfer/node.py +0 -0
  40. {latch-2.53.7.dev2 → latch-2.53.8}/latch/ldata/_transfer/progress.py +0 -0
  41. {latch-2.53.7.dev2 → latch-2.53.8}/latch/ldata/_transfer/remote_copy.py +0 -0
  42. {latch-2.53.7.dev2 → latch-2.53.8}/latch/ldata/_transfer/throttle.py +0 -0
  43. {latch-2.53.7.dev2 → latch-2.53.8}/latch/ldata/_transfer/utils.py +0 -0
  44. {latch-2.53.7.dev2 → latch-2.53.8}/latch/ldata/path.py +0 -0
  45. {latch-2.53.7.dev2 → latch-2.53.8}/latch/registry/__init__.py +0 -0
  46. {latch-2.53.7.dev2 → latch-2.53.8}/latch/registry/project.py +0 -0
  47. {latch-2.53.7.dev2 → latch-2.53.8}/latch/registry/record.py +0 -0
  48. {latch-2.53.7.dev2 → latch-2.53.8}/latch/registry/table.py +0 -0
  49. {latch-2.53.7.dev2 → latch-2.53.8}/latch/registry/types.py +0 -0
  50. {latch-2.53.7.dev2 → latch-2.53.8}/latch/registry/upstream_types/__init__.py +0 -0
  51. {latch-2.53.7.dev2 → latch-2.53.8}/latch/registry/upstream_types/types.py +0 -0
  52. {latch-2.53.7.dev2 → latch-2.53.8}/latch/registry/upstream_types/values.py +0 -0
  53. {latch-2.53.7.dev2 → latch-2.53.8}/latch/registry/utils.py +0 -0
  54. {latch-2.53.7.dev2 → latch-2.53.8}/latch/resources/__init__.py +0 -0
  55. {latch-2.53.7.dev2 → latch-2.53.8}/latch/resources/conditional.py +0 -0
  56. {latch-2.53.7.dev2 → latch-2.53.8}/latch/resources/dynamic.py +0 -0
  57. {latch-2.53.7.dev2 → latch-2.53.8}/latch/resources/launch_plan.py +0 -0
  58. {latch-2.53.7.dev2 → latch-2.53.8}/latch/resources/map_tasks.py +0 -0
  59. {latch-2.53.7.dev2 → latch-2.53.8}/latch/resources/reference_workflow.py +0 -0
  60. {latch-2.53.7.dev2 → latch-2.53.8}/latch/resources/tasks.py +0 -0
  61. {latch-2.53.7.dev2 → latch-2.53.8}/latch/resources/workflow.py +0 -0
  62. {latch-2.53.7.dev2 → latch-2.53.8}/latch/types/__init__.py +0 -0
  63. {latch-2.53.7.dev2 → latch-2.53.8}/latch/types/directory.py +0 -0
  64. {latch-2.53.7.dev2 → latch-2.53.8}/latch/types/file.py +0 -0
  65. {latch-2.53.7.dev2 → latch-2.53.8}/latch/types/glob.py +0 -0
  66. {latch-2.53.7.dev2 → latch-2.53.8}/latch/types/json.py +0 -0
  67. {latch-2.53.7.dev2 → latch-2.53.8}/latch/types/metadata.py +0 -0
  68. {latch-2.53.7.dev2 → latch-2.53.8}/latch/types/utils.py +0 -0
  69. {latch-2.53.7.dev2 → latch-2.53.8}/latch/utils.py +0 -0
  70. {latch-2.53.7.dev2 → latch-2.53.8}/latch/verified/__init__.py +0 -0
  71. {latch-2.53.7.dev2 → latch-2.53.8}/latch/verified/deseq2.py +0 -0
  72. {latch-2.53.7.dev2 → latch-2.53.8}/latch/verified/mafft.py +0 -0
  73. {latch-2.53.7.dev2 → latch-2.53.8}/latch/verified/pathway.py +0 -0
  74. {latch-2.53.7.dev2 → latch-2.53.8}/latch/verified/rnaseq.py +0 -0
  75. {latch-2.53.7.dev2 → latch-2.53.8}/latch/verified/trim_galore.py +0 -0
  76. {latch-2.53.7.dev2 → latch-2.53.8}/latch.egg-info/dependency_links.txt +0 -0
  77. {latch-2.53.7.dev2 → latch-2.53.8}/latch.egg-info/entry_points.txt +0 -0
  78. {latch-2.53.7.dev2 → latch-2.53.8}/latch.egg-info/top_level.txt +0 -0
  79. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/__init__.py +0 -0
  80. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/auth/__init__.py +0 -0
  81. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/auth/csrf.py +0 -0
  82. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/auth/oauth2.py +0 -0
  83. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/auth/pkce.py +0 -0
  84. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/auth/utils.py +0 -0
  85. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/centromere/__init__.py +0 -0
  86. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/centromere/ctx.py +0 -0
  87. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/centromere/utils.py +0 -0
  88. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/click_utils.py +0 -0
  89. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/constants.py +0 -0
  90. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/docker_utils/__init__.py +0 -0
  91. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/exceptions/__init__.py +0 -0
  92. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/exceptions/cache.py +0 -0
  93. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/exceptions/errors.py +0 -0
  94. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/exceptions/handler.py +0 -0
  95. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/exceptions/traceback.py +0 -0
  96. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/menus.py +0 -0
  97. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/nextflow/__init__.py +0 -0
  98. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/nextflow/config.py +0 -0
  99. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/nextflow/utils.py +0 -0
  100. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/nextflow/workflow.py +0 -0
  101. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/__init__.py +0 -0
  102. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/cp/__init__.py +0 -0
  103. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/cp/autocomplete.py +0 -0
  104. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/cp/glob.py +0 -0
  105. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/get.py +0 -0
  106. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/get_executions.py +0 -0
  107. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/get_params.py +0 -0
  108. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/__init__.py +0 -0
  109. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/__pycache__/__init__.cpython-310.pyc +0 -0
  110. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/__pycache__/__init__.cpython-311.pyc +0 -0
  111. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/__pycache__/__init__.cpython-38.pyc +0 -0
  112. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/__pycache__/__init__.cpython-39.pyc +0 -0
  113. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/__pycache__/init.cpython-310.pyc +0 -0
  114. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/__pycache__/init.cpython-311.pyc +0 -0
  115. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/__pycache__/init.cpython-38.pyc +0 -0
  116. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/__pycache__/init.cpython-39.pyc +0 -0
  117. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/assemble_and_sort/.env +0 -0
  118. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/assemble_and_sort/LICENSE +0 -0
  119. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/assemble_and_sort/README.md +0 -0
  120. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/assemble_and_sort/__init__.py +0 -0
  121. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/assemble_and_sort/__pycache__/__init__.cpython-310.pyc +0 -0
  122. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/assemble_and_sort/assemble.py +0 -0
  123. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/assemble_and_sort/sort.py +0 -0
  124. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/assemble_and_sort/system-requirements.txt +0 -0
  125. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/common/.dockerignore +0 -0
  126. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_conda/__init__.py +0 -0
  127. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_conda/__pycache__/__init__.cpython-310.pyc +0 -0
  128. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_conda/conda_task.py +0 -0
  129. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_conda/environment.yaml +0 -0
  130. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_docker/__init__.py +0 -0
  131. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_docker/task.py +0 -0
  132. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_nf_integration/latch_metadata/__pycache__/__init__.cpython-311.pyc +0 -0
  133. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_nfcore/Dockerfile +0 -0
  134. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_nfcore/__init__.py +0 -0
  135. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_nfcore/task.py +0 -0
  136. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_r/__init__.py +0 -0
  137. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_r/__pycache__/__init__.cpython-310.pyc +0 -0
  138. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_r/environment.R +0 -0
  139. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_r/r_task.py +0 -0
  140. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_snakemake/.latch/latch_entrypoint +0 -0
  141. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_snakemake/Dockerfile +0 -0
  142. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_snakemake/Snakefile +0 -0
  143. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_snakemake/config.yaml +0 -0
  144. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_snakemake/environment.yaml +0 -0
  145. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_snakemake/latch_metadata.py +0 -0
  146. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_snakemake/scripts/plot-quals.py +0 -0
  147. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/example_snakemake/version +0 -0
  148. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/init.py +0 -0
  149. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/template/LICENSE +0 -0
  150. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/template/README.md +0 -0
  151. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/template/__init__.py +0 -0
  152. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/template/__pycache__/__init__.cpython-310.pyc +0 -0
  153. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/init/template/task.py +0 -0
  154. {latch-2.53.7.dev2/latch_cli/services/cp/download → latch-2.53.8/latch_cli/services/k8s}/__init__.py +0 -0
  155. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/launch.py +0 -0
  156. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/local_dev.py +0 -0
  157. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/local_dev_old.py +0 -0
  158. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/login.py +0 -0
  159. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/ls.py +0 -0
  160. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/mkdir.py +0 -0
  161. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/move.py +0 -0
  162. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/preview.py +0 -0
  163. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/register/__init__.py +0 -0
  164. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/register/constants.py +0 -0
  165. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/register/register.py +0 -0
  166. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/register/utils.py +0 -0
  167. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/rm.py +0 -0
  168. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/stop_pod.py +0 -0
  169. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/sync.py +0 -0
  170. {latch-2.53.7.dev2/latch_cli/services/cp/upload → latch-2.53.8/latch_cli/services/test_data}/__init__.py +0 -0
  171. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/test_data/ls.py +0 -0
  172. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/test_data/remove.py +0 -0
  173. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/test_data/upload.py +0 -0
  174. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/test_data/utils.py +0 -0
  175. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/services/workspace.py +0 -0
  176. {latch-2.53.7.dev2/latch_cli/services/execute → latch-2.53.8/latch_cli/snakemake}/__init__.py +0 -0
  177. {latch-2.53.7.dev2/latch_cli/services/test_data → latch-2.53.8/latch_cli/snakemake/config}/__init__.py +0 -0
  178. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/snakemake/config/parser.py +0 -0
  179. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/snakemake/config/utils.py +0 -0
  180. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/snakemake/serialize.py +0 -0
  181. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/snakemake/serialize_utils.py +0 -0
  182. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/snakemake/single_task_snakemake.py +0 -0
  183. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/snakemake/utils.py +0 -0
  184. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/snakemake/workflow.py +0 -0
  185. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/tinyrequests.py +0 -0
  186. {latch-2.53.7.dev2/latch_cli/snakemake → latch-2.53.8/latch_cli/tui}/__init__.py +0 -0
  187. {latch-2.53.7.dev2 → latch-2.53.8}/latch_cli/workflow_config.py +0 -0
  188. {latch-2.53.7.dev2 → latch-2.53.8}/pyproject.toml +0 -0
  189. {latch-2.53.7.dev2 → latch-2.53.8}/setup.cfg +0 -0
  190. {latch-2.53.7.dev2/latch_cli/snakemake/config → latch-2.53.8/tests}/__init__.py +0 -0
  191. {latch-2.53.7.dev2 → latch-2.53.8}/tests/cp/__init__.py +0 -0
  192. {latch-2.53.7.dev2 → latch-2.53.8}/tests/fixtures.py +0 -0
  193. {latch-2.53.7.dev2 → latch-2.53.8}/tests/test_ls.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: latch
3
- Version: 2.53.7.dev2
3
+ Version: 2.53.8
4
4
  Summary: The Latch SDK
5
5
  Author-email: kenny@latch.bio
6
6
  Classifier: Programming Language :: Python :: 3.8
@@ -34,8 +34,6 @@ Requires-Dist: aioconsole==0.6.1
34
34
  Requires-Dist: asyncssh==2.13.2
35
35
  Requires-Dist: websockets==11.0.3
36
36
  Requires-Dist: watchfiles==0.19.0
37
- Requires-Dist: uvloop==0.19.0
38
- Requires-Dist: aiohttp==3.9.5
39
37
  Provides-Extra: snakemake
40
38
  Requires-Dist: snakemake<7.30.2,>=7.18.0; extra == "snakemake"
41
39
  Requires-Dist: pulp<2.8,>=2.0; extra == "snakemake"
@@ -71,7 +71,7 @@ def upload(
71
71
  dest_data = node_data.data[dest]
72
72
 
73
73
  if not (dest_data.exists() or dest_data.is_direct_parent()) and not create_parents:
74
- raise LatchPathError("no such Latch file or directory", dest, node_data.acc_id)
74
+ raise LatchPathError("no such Latch file or directory", dest)
75
75
 
76
76
  dest_is_dir = dest_data.type in {
77
77
  LDataNodeType.account_root,
@@ -6,7 +6,7 @@ class LatchPathError(RuntimeError):
6
6
  def __init__(
7
7
  self,
8
8
  message: str,
9
- remote_path: str,
9
+ remote_path: Optional[str] = None,
10
10
  acc_id: Optional[str] = None,
11
11
  ):
12
12
  super().__init__(message)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: latch
3
- Version: 2.53.7.dev2
3
+ Version: 2.53.8
4
4
  Summary: The Latch SDK
5
5
  Author-email: kenny@latch.bio
6
6
  Classifier: Programming Language :: Python :: 3.8
@@ -34,8 +34,6 @@ Requires-Dist: aioconsole==0.6.1
34
34
  Requires-Dist: asyncssh==2.13.2
35
35
  Requires-Dist: websockets==11.0.3
36
36
  Requires-Dist: watchfiles==0.19.0
37
- Requires-Dist: uvloop==0.19.0
38
- Requires-Dist: aiohttp==3.9.5
39
37
  Provides-Extra: snakemake
40
38
  Requires-Dist: snakemake<7.30.2,>=7.18.0; extra == "snakemake"
41
39
  Requires-Dist: pulp<2.8,>=2.0; extra == "snakemake"
@@ -103,18 +103,8 @@ latch_cli/services/workspace.py
103
103
  latch_cli/services/cp/__init__.py
104
104
  latch_cli/services/cp/autocomplete.py
105
105
  latch_cli/services/cp/glob.py
106
- latch_cli/services/cp/http_utils.py
107
106
  latch_cli/services/cp/main.py
108
107
  latch_cli/services/cp/utils.py
109
- latch_cli/services/cp/download/__init__.py
110
- latch_cli/services/cp/download/main.py
111
- latch_cli/services/cp/download/worker.py
112
- latch_cli/services/cp/upload/__init__.py
113
- latch_cli/services/cp/upload/main.py
114
- latch_cli/services/cp/upload/worker.py
115
- latch_cli/services/execute/__init__.py
116
- latch_cli/services/execute/main.py
117
- latch_cli/services/execute/utils.py
118
108
  latch_cli/services/init/__init__.py
119
109
  latch_cli/services/init/init.py
120
110
  latch_cli/services/init/__pycache__/__init__.cpython-310.pyc
@@ -161,6 +151,11 @@ latch_cli/services/init/template/README.md
161
151
  latch_cli/services/init/template/__init__.py
162
152
  latch_cli/services/init/template/task.py
163
153
  latch_cli/services/init/template/__pycache__/__init__.cpython-310.pyc
154
+ latch_cli/services/k8s/__init__.py
155
+ latch_cli/services/k8s/attach.py
156
+ latch_cli/services/k8s/execute.py
157
+ latch_cli/services/k8s/utils.py
158
+ latch_cli/services/k8s/ws_utils.py
164
159
  latch_cli/services/register/__init__.py
165
160
  latch_cli/services/register/constants.py
166
161
  latch_cli/services/register/register.py
@@ -23,8 +23,6 @@ aioconsole==0.6.1
23
23
  asyncssh==2.13.2
24
24
  websockets==11.0.3
25
25
  watchfiles==0.19.0
26
- uvloop==0.19.0
27
- aiohttp==3.9.5
28
26
 
29
27
  [pandas]
30
28
  pandas>=2.0.0
@@ -4,13 +4,15 @@ import os
4
4
  import sys
5
5
  from pathlib import Path
6
6
  from textwrap import dedent
7
- from typing import Callable, List, Literal, Optional, Tuple, TypeVar, Union
7
+ from typing import Callable, List, Optional, Tuple, TypeVar, Union
8
8
 
9
9
  import click
10
10
  from packaging.version import parse as parse_version
11
11
  from typing_extensions import ParamSpec
12
12
 
13
13
  import latch_cli.click_utils
14
+ from latch.ldata._transfer.progress import Progress as _Progress
15
+ from latch_cli.click_utils import EnumChoice
14
16
  from latch_cli.exceptions.handler import CrashHandler
15
17
  from latch_cli.services.cp.autocomplete import complete as cp_complete
16
18
  from latch_cli.services.cp.autocomplete import remote_complete
@@ -435,7 +437,7 @@ def execute(
435
437
  ):
436
438
  """Drops the user into an interactive shell from within a task."""
437
439
 
438
- from latch_cli.services.execute.main import exec
440
+ from latch_cli.services.k8s.execute import exec
439
441
 
440
442
  exec(execution_id=execution_id, egn_id=egn_id, container_index=container_index)
441
443
 
@@ -691,7 +693,7 @@ LDATA COMMANDS
691
693
  @click.option(
692
694
  "--progress",
693
695
  help="Type of progress information to show while copying",
694
- type=click.Choice(["none", "total", "tasks"]),
696
+ type=EnumChoice(_Progress, case_sensitive=False),
695
697
  default="tasks",
696
698
  show_default=True,
697
699
  )
@@ -703,14 +705,6 @@ LDATA COMMANDS
703
705
  default=False,
704
706
  show_default=True,
705
707
  )
706
- @click.option(
707
- "--force",
708
- "-f",
709
- help="Don't ask to confirm when overwriting files",
710
- is_flag=True,
711
- default=False,
712
- show_default=True,
713
- )
714
708
  @click.option(
715
709
  "--no-glob",
716
710
  "-G",
@@ -733,9 +727,8 @@ LDATA COMMANDS
733
727
  def cp(
734
728
  src: List[str],
735
729
  dest: str,
736
- progress: Literal["none", "total", "tasks"],
730
+ progress: _Progress,
737
731
  verbose: bool,
738
- force: bool,
739
732
  no_glob: bool,
740
733
  cores: Optional[int] = None,
741
734
  chunk_size_mib: Optional[int] = None,
@@ -754,7 +747,6 @@ def cp(
754
747
  src,
755
748
  dest,
756
749
  progress=progress,
757
- force=force,
758
750
  verbose=verbose,
759
751
  expand_globs=not no_glob,
760
752
  cores=cores,
@@ -1007,6 +999,19 @@ def generate_entrypoint(
1007
999
  )
1008
1000
 
1009
1001
 
1002
+ @nextflow.command("attach")
1003
+ @click.option(
1004
+ "--execution-id", "-e", type=str, help="Optional execution ID to inspect."
1005
+ )
1006
+ @requires_login
1007
+ def attach(execution_id: Optional[str]):
1008
+ """Drops the user into an interactive shell to inspect the workdir of a nextflow execution."""
1009
+
1010
+ from latch_cli.services.k8s.attach import attach
1011
+
1012
+ attach(execution_id)
1013
+
1014
+
1010
1015
  """
1011
1016
  POD COMMANDS
1012
1017
  """
@@ -0,0 +1,106 @@
1
+ from pathlib import Path
2
+ from textwrap import dedent
3
+ from typing import List, Optional
4
+
5
+ import click
6
+
7
+ from latch.ldata._transfer.download import download as _download
8
+ from latch.ldata._transfer.progress import Progress
9
+ from latch.ldata._transfer.remote_copy import remote_copy as _remote_copy
10
+ from latch.ldata._transfer.upload import upload as _upload
11
+ from latch.ldata.type import LatchPathError
12
+ from latch_cli.services.cp.glob import expand_pattern
13
+ from latch_cli.utils import human_readable_time, with_si_suffix
14
+ from latch_cli.utils.path import get_path_error, is_remote_path
15
+
16
+
17
+ def _copy_and_print(src: str, dst: str, progress: Progress) -> None:
18
+ _remote_copy(src, dst)
19
+ if progress != Progress.none:
20
+ click.echo(dedent(f"""
21
+ {click.style("Copy Requested.", fg="green")}
22
+ {click.style("Source: ", fg="blue")}{(src)}
23
+ {click.style("Destination: ", fg="blue")}{(dst)}"""))
24
+
25
+
26
+ def _download_and_print(src: str, dst: Path, progress: Progress, verbose: bool) -> None:
27
+ if progress != Progress.none:
28
+ click.secho(f"Downloading {dst.name}", fg="blue")
29
+ res = _download(src, dst, progress, verbose)
30
+ if progress != Progress.none:
31
+ click.echo(dedent(f"""
32
+ {click.style("Download Complete", fg="green")}
33
+ {click.style("Time Elapsed: ", fg="blue")}{human_readable_time(res.total_time)}
34
+ {click.style("Files Downloaded: ", fg="blue")}{res.num_files} ({with_si_suffix(res.total_bytes)})
35
+ """))
36
+
37
+
38
+ # todo(ayush): come up with a better behavior scheme than unix cp
39
+ def cp(
40
+ srcs: List[str],
41
+ dest: str,
42
+ *,
43
+ progress: Progress,
44
+ verbose: bool,
45
+ expand_globs: bool,
46
+ cores: Optional[int] = None,
47
+ chunk_size_mib: Optional[int] = None,
48
+ ):
49
+ if chunk_size_mib is not None and chunk_size_mib < 5:
50
+ click.secho(
51
+ "The chunk size specified by --chunk-size-mib must be at least 5. You"
52
+ f" provided `{chunk_size_mib}`",
53
+ fg="red",
54
+ )
55
+ raise click.exceptions.Exit(1)
56
+
57
+ dest_remote = is_remote_path(dest)
58
+
59
+ for src in srcs:
60
+ src_remote = is_remote_path(src)
61
+
62
+ try:
63
+ if src_remote and not dest_remote:
64
+ if expand_globs:
65
+ [
66
+ _download_and_print(p, Path(dest), progress, verbose)
67
+ for p in expand_pattern(src)
68
+ ]
69
+ else:
70
+ _download_and_print(src, Path(dest), progress, verbose)
71
+ elif not src_remote and dest_remote:
72
+ if progress != Progress.none:
73
+ click.secho(f"Uploading {src}", fg="blue")
74
+ res = _upload(
75
+ src,
76
+ dest,
77
+ progress=progress,
78
+ verbose=verbose,
79
+ cores=cores,
80
+ chunk_size_mib=chunk_size_mib,
81
+ )
82
+ if progress != Progress.none:
83
+ click.echo(dedent(f"""
84
+ {click.style("Upload Complete", fg="green")}
85
+ {click.style("Time Elapsed: ", fg="blue")}{human_readable_time(res.total_time)}
86
+ {click.style("Files Uploaded: ", fg="blue")}{res.num_files} ({with_si_suffix(res.total_bytes)})
87
+ """))
88
+ elif src_remote and dest_remote:
89
+ if expand_globs:
90
+ [_copy_and_print(p, dest, progress) for p in expand_pattern(src)]
91
+ else:
92
+ _copy_and_print(src, dest, progress)
93
+ else:
94
+ raise ValueError(
95
+ dedent(f"""
96
+ `latch cp` cannot be used for purely local file copying.
97
+
98
+ Please ensure at least one of your arguments is a remote path (beginning with `latch://`)
99
+ """).strip("\n"),
100
+ )
101
+ except LatchPathError as e:
102
+ click.secho(get_path_error(e.remote_path, e.message, e.acc_id), fg="red")
103
+ raise click.exceptions.Exit(1) from e
104
+ except Exception as e:
105
+ click.secho(str(e), fg="red")
106
+ raise click.exceptions.Exit(1) from e
@@ -1,9 +1,8 @@
1
- import sys
2
- from typing import Iterable, List, TypedDict, TypeVar
1
+ from typing import List, TypedDict
3
2
 
4
- if sys.version_info >= (3, 9):
3
+ try:
5
4
  from functools import cache
6
- else:
5
+ except ImportError:
7
6
  from functools import lru_cache as cache
8
7
 
9
8
  import gql
@@ -102,7 +101,7 @@ class AccountInfoCurrent(TypedDict):
102
101
 
103
102
  # todo(taras): support for gcp and azure mounts
104
103
  # skipping now due to time. This decision does not
105
- # influence correctness of the CLI and only
104
+ # influence correcetness of the CLI and only
106
105
  # reduces the set of returned autocomplete
107
106
  # suggestions
108
107
  @cache
@@ -163,20 +162,3 @@ def _get_known_domains_for_account() -> List[str]:
163
162
  res.extend(f"{x}.mount" for x in buckets)
164
163
 
165
164
  return res
166
-
167
-
168
- chunk_batch_size = 3
169
-
170
- T = TypeVar("T")
171
-
172
-
173
- def chunked(iter: Iterable[T]) -> Iterable[List[T]]:
174
- chunk = []
175
- for x in iter:
176
- if len(chunk) == chunk_batch_size:
177
- yield chunk
178
- chunk = []
179
-
180
- chunk.append(x)
181
-
182
- yield chunk
@@ -0,0 +1,77 @@
1
+ import asyncio
2
+ import json
3
+ import secrets
4
+ import sys
5
+ from typing import Optional
6
+ from urllib.parse import urljoin, urlparse
7
+
8
+ import click
9
+ import websockets.client as websockets
10
+ import websockets.exceptions as ws_exceptions
11
+ from latch_sdk_config.latch import NUCLEUS_URL
12
+
13
+ from latch_cli.utils import get_auth_header
14
+
15
+ from .utils import get_pvc_info
16
+ from .ws_utils import forward_stdio
17
+
18
+
19
+ async def connect(execution_id: str, session_id: str):
20
+ async with websockets.connect(
21
+ urlparse(urljoin(NUCLEUS_URL, "/workflows/cli/attach-nf-workdir"))
22
+ ._replace(scheme="wss")
23
+ .geturl(),
24
+ close_timeout=0,
25
+ extra_headers={"Authorization": get_auth_header()},
26
+ ) as ws:
27
+ request = {"execution_id": int(execution_id), "session_id": session_id}
28
+
29
+ await ws.send(json.dumps(request))
30
+ data = await ws.recv()
31
+
32
+ msg = ""
33
+ try:
34
+ res = json.loads(data)
35
+ if "error" in res:
36
+ raise RuntimeError(res["error"])
37
+ except json.JSONDecodeError:
38
+ msg = "Unable to connect to pod - internal error."
39
+ except RuntimeError as e:
40
+ msg = str(e)
41
+
42
+ if msg != "":
43
+ raise RuntimeError(msg)
44
+
45
+ await forward_stdio(ws)
46
+
47
+
48
+ def get_session_id():
49
+ return secrets.token_bytes(8).hex()
50
+
51
+
52
+ def attach(execution_id: Optional[str] = None):
53
+ execution_id = get_pvc_info(execution_id)
54
+ session_id = get_session_id()
55
+
56
+ click.secho(
57
+ "Attaching to workdir - this may take a few seconds...", dim=True, italic=True
58
+ )
59
+
60
+ import termios
61
+ import tty
62
+
63
+ old_settings_stdin = termios.tcgetattr(sys.stdin.fileno())
64
+ tty.setraw(sys.stdin)
65
+
66
+ msg = ""
67
+ try:
68
+ asyncio.run(connect(execution_id, session_id))
69
+ except ws_exceptions.ConnectionClosedError as e:
70
+ msg = json.loads(e.reason)["error"]
71
+ except RuntimeError as e:
72
+ msg = str(e)
73
+ finally:
74
+ termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, old_settings_stdin)
75
+
76
+ if msg != "":
77
+ click.secho(msg, fg="red")
@@ -0,0 +1,78 @@
1
+ import asyncio
2
+ import json
3
+ import sys
4
+ from typing import Optional
5
+ from urllib.parse import urljoin, urlparse
6
+
7
+ import websockets.client as websockets
8
+ from latch_sdk_config.latch import NUCLEUS_URL
9
+
10
+ from latch_cli.services.k8s.utils import (
11
+ ContainerNode,
12
+ EGNNode,
13
+ ExecutionInfoNode,
14
+ get_container_info,
15
+ get_egn_info,
16
+ get_execution_info,
17
+ )
18
+ from latch_cli.utils import get_auth_header
19
+
20
+ from .ws_utils import forward_stdio
21
+
22
+
23
+ async def connect(egn_info: EGNNode, container_info: Optional[ContainerNode]):
24
+ async with websockets.connect(
25
+ urlparse(urljoin(NUCLEUS_URL, "/workflows/cli/shell"))
26
+ ._replace(scheme="wss")
27
+ .geturl(),
28
+ close_timeout=0,
29
+ extra_headers={"Authorization": get_auth_header()},
30
+ ) as ws:
31
+ request = {
32
+ "egn_id": egn_info["id"],
33
+ "container_index": (
34
+ container_info["index"] if container_info is not None else None
35
+ ),
36
+ }
37
+
38
+ await ws.send(json.dumps(request))
39
+ data = await ws.recv()
40
+
41
+ msg = ""
42
+ try:
43
+ res = json.loads(data)
44
+ if "error" in res:
45
+ raise RuntimeError(res["error"])
46
+ except json.JSONDecodeError:
47
+ msg = "Unable to connect to pod - internal error."
48
+ except RuntimeError as e:
49
+ msg = str(e)
50
+
51
+ if msg != "":
52
+ raise RuntimeError(msg)
53
+
54
+ await forward_stdio(ws)
55
+
56
+
57
+ def exec(
58
+ execution_id: Optional[str] = None,
59
+ egn_id: Optional[str] = None,
60
+ container_index: Optional[int] = None,
61
+ ):
62
+ execution_info: Optional[ExecutionInfoNode] = None
63
+ if egn_id is None:
64
+ execution_info = get_execution_info(execution_id)
65
+
66
+ egn_info = get_egn_info(execution_info, egn_id)
67
+ container_info = get_container_info(egn_info, container_index)
68
+
69
+ import termios
70
+ import tty
71
+
72
+ old_settings_stdin = termios.tcgetattr(sys.stdin.fileno())
73
+ tty.setraw(sys.stdin)
74
+
75
+ try:
76
+ asyncio.run(connect(egn_info, container_info))
77
+ finally:
78
+ termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, old_settings_stdin)
@@ -9,7 +9,6 @@ from latch_sdk_gql.execute import execute
9
9
 
10
10
  from latch_cli.click_utils import bold, color
11
11
  from latch_cli.menus import select_tui
12
- from latch_cli.utils import current_workspace
13
12
 
14
13
 
15
14
  # todo(ayush): put this into latch_sdk_gql
@@ -179,35 +178,39 @@ def get_execution_info(execution_id: Optional[str]) -> ExecutionInfoNode:
179
178
  """),
180
179
  *fragments,
181
180
  ),
182
- {"createdBy": current_workspace()},
181
+ {"createdBy": user_config.workspace_id},
183
182
  )["runningExecutions"]
184
183
 
185
- if len(res["nodes"]) == 0:
186
- click.secho("You have no executions currently running.", dim=True)
184
+ nodes = res["nodes"]
185
+
186
+ if len(nodes) == 0:
187
+ click.secho(
188
+ f"You have no executions currently running.",
189
+ dim=True,
190
+ )
187
191
  raise click.exceptions.Exit(0)
188
192
 
189
- if len(res["nodes"]) == 1:
190
- execution = res["nodes"][0]
193
+ if len(nodes) == 1:
194
+ execution = nodes[0]
191
195
  click.secho(
192
196
  "Selecting execution"
193
197
  f" {color(execution['displayName'])} as it is"
194
- " the only"
195
- " one currently running in Workspace"
198
+ " the only one currently running in Workspace"
196
199
  f" {color(workspace_str)}.",
197
200
  )
198
201
 
199
202
  return execution
200
203
 
201
204
  selected_execution = select_tui(
202
- "You have multiple executions running in this workspace"
203
- f" ({color(workspace_str)}). Which"
204
- " execution would you like to inspect?",
205
+ "You have multiple executions running in"
206
+ f" this workspace ({color(workspace_str)}). Which execution would you like to"
207
+ " inspect?",
205
208
  [
206
209
  {
207
210
  "display_name": f'{x["displayName"]} ({x["workflow"]["displayName"]})',
208
211
  "value": x,
209
212
  }
210
- for x in res["nodes"]
213
+ for x in nodes
211
214
  ],
212
215
  clear_terminal=False,
213
216
  )
@@ -368,3 +371,61 @@ def get_container_info(
368
371
  raise click.exceptions.Exit(0)
369
372
 
370
373
  return selected_container_info
374
+
375
+
376
+ class Node(TypedDict):
377
+ id: str
378
+ displayName: str
379
+
380
+
381
+ class nfAvailablePvcs(TypedDict):
382
+ nodes: List[Node]
383
+
384
+
385
+ def get_pvc_info(execution_id: Optional[str]) -> str:
386
+ if execution_id is not None:
387
+ return execution_id
388
+
389
+ workspace_str: str = user_config.workspace_name or user_config.workspace_id
390
+
391
+ res: nfAvailablePvcs = execute(
392
+ gql.gql("""
393
+ query NFWorkdirs($wsId: BigInt!) {
394
+ nfAvailablePvcs(argWsId: $wsId) {
395
+ nodes {
396
+ id
397
+ displayName
398
+ }
399
+ }
400
+ }
401
+ """),
402
+ {"wsId": user_config.workspace_id},
403
+ )["nfAvailablePvcs"]
404
+
405
+ nodes = res["nodes"]
406
+
407
+ if len(nodes) == 0:
408
+ click.secho(
409
+ f"You have no available workdirs (all have expired).",
410
+ dim=True,
411
+ )
412
+ raise click.exceptions.Exit(0)
413
+
414
+ if len(nodes) == 1:
415
+ execution = nodes[0]
416
+ click.secho(
417
+ f"Selecting execution {color(execution['displayName'])} as it is the only"
418
+ f" one without an expired workDir in Workspace {color(workspace_str)}.",
419
+ )
420
+ return execution["id"]
421
+
422
+ selected_execution = select_tui(
423
+ "You have multiple available workDirs in this workspace"
424
+ f" ({color(workspace_str)}). Which execution would you like to attach to?",
425
+ options=[{"display_name": x["displayName"], "value": x["id"]} for x in nodes],
426
+ )
427
+ if selected_execution is None:
428
+ click.secho("No execution selected. Exiting.", dim=True)
429
+ raise click.exceptions.Exit(0)
430
+
431
+ return selected_execution