latch 2.32.0__tar.gz → 2.32.2__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.
- {latch-2.32.0/latch.egg-info → latch-2.32.2}/PKG-INFO +1 -1
- {latch-2.32.0 → latch-2.32.2}/latch/types/directory.py +1 -1
- {latch-2.32.0 → latch-2.32.2}/latch/types/file.py +1 -1
- {latch-2.32.0 → latch-2.32.2/latch.egg-info}/PKG-INFO +1 -1
- {latch-2.32.0 → latch-2.32.2}/latch.egg-info/SOURCES.txt +3 -1
- {latch-2.32.0 → latch-2.32.2}/latch_cli/click_utils.py +4 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/main.py +21 -93
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/autocomplete.py +12 -8
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/download.py +1 -1
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/ldata_utils.py +1 -1
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/main.py +1 -1
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/remote_copy.py +1 -1
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/throttle.py +1 -4
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/upload.py +1 -11
- latch-2.32.2/latch_cli/services/init/__pycache__/__init__.cpython-311.pyc +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/__pycache__/init.cpython-310.pyc +0 -0
- latch-2.32.2/latch_cli/services/init/__pycache__/init.cpython-311.pyc +0 -0
- latch-2.32.2/latch_cli/services/ls.py +198 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/move.py +1 -5
- latch-2.32.0/latch_cli/services/cp/path_utils.py → latch-2.32.2/latch_cli/utils/path.py +9 -5
- {latch-2.32.0 → latch-2.32.2}/setup.py +1 -1
- latch-2.32.0/latch_cli/services/ls.py +0 -55
- {latch-2.32.0 → latch-2.32.2}/LICENSE +0 -0
- {latch-2.32.0 → latch-2.32.2}/MANIFEST.in +0 -0
- {latch-2.32.0 → latch-2.32.2}/README.md +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/account.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/functions/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/functions/messages.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/functions/operators.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/functions/secrets.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/registry/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/registry/project.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/registry/record.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/registry/table.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/registry/types.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/registry/upstream_types/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/registry/upstream_types/types.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/registry/upstream_types/values.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/registry/utils.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/resources/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/resources/conditional.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/resources/launch_plan.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/resources/map_tasks.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/resources/reference_workflow.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/resources/tasks.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/resources/workflow.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/types/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/types/glob.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/types/json.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/types/metadata.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/types/utils.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/verified/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/verified/deseq2.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/verified/mafft.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/verified/pathway.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/verified/rnaseq.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch/verified/trim_galore.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch.egg-info/dependency_links.txt +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch.egg-info/entry_points.txt +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch.egg-info/requires.txt +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch.egg-info/top_level.txt +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/auth/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/auth/csrf.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/auth/oauth2.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/auth/pkce.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/auth/utils.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/centromere/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/centromere/ctx.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/centromere/utils.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/constants.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/docker_utils/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/exceptions/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/exceptions/cache.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/exceptions/errors.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/exceptions/handler.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/exceptions/traceback.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/menus.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/config.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/exceptions.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/glob.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/manager.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/progress.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/cp/utils.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/deprecated/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/deprecated/mkdir.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/deprecated/rm.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/deprecated/touch.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/execute.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/get.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/get_executions.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/get_params.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/__pycache__/__init__.cpython-310.pyc +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/__pycache__/__init__.cpython-38.pyc +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/__pycache__/init.cpython-38.pyc +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/assemble_and_sort/.env +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/assemble_and_sort/LICENSE +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/assemble_and_sort/README.md +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/assemble_and_sort/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/assemble_and_sort/__pycache__/__init__.cpython-310.pyc +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/assemble_and_sort/assemble.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/assemble_and_sort/sort.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/assemble_and_sort/system-requirements.txt +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/common/.dockerignore +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/example_conda/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/example_conda/__pycache__/__init__.cpython-310.pyc +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/example_conda/conda_task.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/example_conda/environment.yaml +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/example_docker/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/example_docker/task.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/example_nfcore/Dockerfile +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/example_nfcore/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/example_nfcore/task.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/example_r/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/example_r/__pycache__/__init__.cpython-310.pyc +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/example_r/environment.R +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/example_r/r_task.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/init.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/template/LICENSE +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/template/README.md +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/template/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/template/__pycache__/__init__.cpython-310.pyc +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/init/template/task.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/launch.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/local_dev.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/local_dev_old.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/login.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/open_file.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/preview.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/register/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/register/constants.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/register/register.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/register/utils.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/stop_pod.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/test_data/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/test_data/ls.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/test_data/remove.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/test_data/upload.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/test_data/utils.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/services/workspace.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/snakemake/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/snakemake/serialize.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/snakemake/serialize_utils.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/snakemake/single_task_snakemake.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/snakemake/workflow.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/tinyrequests.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/tui/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/utils/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/latch_cli/workflow_config.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/pyproject.toml +0 -0
- {latch-2.32.0 → latch-2.32.2}/setup.cfg +0 -0
- {latch-2.32.0 → latch-2.32.2}/tests/__init__.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/tests/fixtures.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/tests/test_cli.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/tests/test_launch.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/tests/test_login.py +0 -0
- {latch-2.32.0 → latch-2.32.2}/tests/test_types.py +0 -0
|
@@ -17,8 +17,8 @@ from typing_extensions import Annotated
|
|
|
17
17
|
|
|
18
18
|
from latch.types.file import LatchFile
|
|
19
19
|
from latch.types.utils import _is_valid_url
|
|
20
|
-
from latch_cli.services.cp.path_utils import normalize_path
|
|
21
20
|
from latch_cli.utils import urljoins
|
|
21
|
+
from latch_cli.utils.path import normalize_path
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
class Child(TypedDict):
|
|
@@ -13,7 +13,7 @@ from latch_sdk_gql.execute import execute
|
|
|
13
13
|
from typing_extensions import Annotated
|
|
14
14
|
|
|
15
15
|
from latch.types.utils import _is_valid_url
|
|
16
|
-
from latch_cli.
|
|
16
|
+
from latch_cli.utils.path import normalize_path
|
|
17
17
|
|
|
18
18
|
is_absolute_node_path = re.compile(r"^(latch)?://\d+.node(/)?$")
|
|
19
19
|
|
|
@@ -89,7 +89,6 @@ latch_cli/services/cp/glob.py
|
|
|
89
89
|
latch_cli/services/cp/ldata_utils.py
|
|
90
90
|
latch_cli/services/cp/main.py
|
|
91
91
|
latch_cli/services/cp/manager.py
|
|
92
|
-
latch_cli/services/cp/path_utils.py
|
|
93
92
|
latch_cli/services/cp/progress.py
|
|
94
93
|
latch_cli/services/cp/remote_copy.py
|
|
95
94
|
latch_cli/services/cp/throttle.py
|
|
@@ -102,8 +101,10 @@ latch_cli/services/deprecated/touch.py
|
|
|
102
101
|
latch_cli/services/init/__init__.py
|
|
103
102
|
latch_cli/services/init/init.py
|
|
104
103
|
latch_cli/services/init/__pycache__/__init__.cpython-310.pyc
|
|
104
|
+
latch_cli/services/init/__pycache__/__init__.cpython-311.pyc
|
|
105
105
|
latch_cli/services/init/__pycache__/__init__.cpython-38.pyc
|
|
106
106
|
latch_cli/services/init/__pycache__/init.cpython-310.pyc
|
|
107
|
+
latch_cli/services/init/__pycache__/init.cpython-311.pyc
|
|
107
108
|
latch_cli/services/init/__pycache__/init.cpython-38.pyc
|
|
108
109
|
latch_cli/services/init/assemble_and_sort/.env
|
|
109
110
|
latch_cli/services/init/assemble_and_sort/LICENSE
|
|
@@ -148,6 +149,7 @@ latch_cli/snakemake/single_task_snakemake.py
|
|
|
148
149
|
latch_cli/snakemake/workflow.py
|
|
149
150
|
latch_cli/tui/__init__.py
|
|
150
151
|
latch_cli/utils/__init__.py
|
|
152
|
+
latch_cli/utils/path.py
|
|
151
153
|
tests/__init__.py
|
|
152
154
|
tests/fixtures.py
|
|
153
155
|
tests/test_cli.py
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
|
-
from collections import OrderedDict
|
|
6
5
|
from pathlib import Path
|
|
7
6
|
from textwrap import dedent
|
|
8
|
-
from typing import List, Optional, Union
|
|
7
|
+
from typing import List, Optional, Tuple, Union
|
|
9
8
|
|
|
10
9
|
import click
|
|
11
10
|
from packaging.version import parse as parse_version
|
|
@@ -45,17 +44,17 @@ def main():
|
|
|
45
44
|
"""
|
|
46
45
|
try:
|
|
47
46
|
get_auth_header()
|
|
48
|
-
except AuthenticationError:
|
|
47
|
+
except AuthenticationError as e:
|
|
49
48
|
click.secho(
|
|
50
49
|
dedent("""
|
|
51
50
|
Unable to authenticate with Latch.
|
|
52
51
|
|
|
53
52
|
If you are on a machine with a browser, run `latch login`.
|
|
54
53
|
If not, navigate to `https://console.latch.bio/settings/developer` on a different machine, select `Access Tokens`, and copy your `API Key` to `~/.latch/token` on this machine.
|
|
55
|
-
"""),
|
|
54
|
+
""").strip("\n"),
|
|
56
55
|
fg="red",
|
|
57
56
|
)
|
|
58
|
-
raise click.exceptions.Exit()
|
|
57
|
+
raise click.exceptions.Exit() from e
|
|
59
58
|
|
|
60
59
|
local_ver = parse_version(get_local_package_version())
|
|
61
60
|
latest_ver = parse_version(get_latest_package_version())
|
|
@@ -357,103 +356,32 @@ def mv(src: str, dest: str):
|
|
|
357
356
|
is_flag=True,
|
|
358
357
|
default=False,
|
|
359
358
|
)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
@click.argument("remote_directories", nargs=-1)
|
|
363
|
-
def ls(group_directories_first: bool, remote_directories: Union[None, List[str]]):
|
|
359
|
+
@click.argument("paths", nargs=-1, shell_complete=remote_complete)
|
|
360
|
+
def ls(paths: Tuple[str], group_directories_first: bool):
|
|
364
361
|
"""
|
|
365
362
|
List the contents of a Latch Data directory
|
|
366
363
|
"""
|
|
367
364
|
|
|
368
|
-
crash_handler.message = f"Unable to display contents of {
|
|
365
|
+
crash_handler.message = f"Unable to display contents of {paths}"
|
|
369
366
|
crash_handler.pkg_root = str(Path.cwd())
|
|
370
367
|
|
|
371
|
-
from datetime import datetime
|
|
372
|
-
|
|
373
368
|
from latch_cli.services.ls import ls
|
|
374
|
-
from latch_cli.utils import with_si_suffix
|
|
375
369
|
|
|
376
370
|
# If the user doesn't provide any arguments, default to root
|
|
377
|
-
if
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
for remote_directory in remote_directories:
|
|
381
|
-
if len(remote_directories) > 1:
|
|
382
|
-
click.echo(f"{remote_directory}:")
|
|
383
|
-
|
|
384
|
-
output = ls(remote_directory)
|
|
385
|
-
|
|
386
|
-
output.sort(key=lambda row: row["name"])
|
|
387
|
-
if group_directories_first:
|
|
388
|
-
output.sort(key=lambda row: row["type"])
|
|
389
|
-
|
|
390
|
-
formatted = []
|
|
391
|
-
for row in output:
|
|
392
|
-
vals = {
|
|
393
|
-
"contentSize": (
|
|
394
|
-
click.style(
|
|
395
|
-
with_si_suffix(int(row["contentSize"]), suffix="", styled=True),
|
|
396
|
-
fg="bright_green",
|
|
397
|
-
)
|
|
398
|
-
if row["contentSize"] != "-" and row["type"] != "dir"
|
|
399
|
-
else click.style("-", dim=True)
|
|
400
|
-
),
|
|
401
|
-
"modifyTime": (
|
|
402
|
-
click.style(
|
|
403
|
-
datetime.fromisoformat(row["modifyTime"]).strftime(
|
|
404
|
-
"%d %b %H:%M"
|
|
405
|
-
),
|
|
406
|
-
fg="blue",
|
|
407
|
-
)
|
|
408
|
-
if row["modifyTime"] != "-" and row["type"] != "dir"
|
|
409
|
-
else click.style("-", dim=True)
|
|
410
|
-
),
|
|
411
|
-
"name": (
|
|
412
|
-
row["name"] if len(row["name"]) <= 50 else f"{row['name'][:47]}..."
|
|
413
|
-
),
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
if row["type"] == "dir":
|
|
417
|
-
vals["name"] = (
|
|
418
|
-
click.style(row["name"], fg="bright_blue", bold=True) + "/"
|
|
419
|
-
)
|
|
420
|
-
|
|
421
|
-
formatted.append(vals)
|
|
422
|
-
|
|
423
|
-
columns = OrderedDict(
|
|
424
|
-
contentSize="Size", modifyTime="Date Modified", name="Name"
|
|
425
|
-
)
|
|
371
|
+
if len(paths) == 0:
|
|
372
|
+
paths = ("/",)
|
|
426
373
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
pad = " " * (l - cur)
|
|
436
|
-
if align_right:
|
|
437
|
-
return pad + x
|
|
438
|
-
return x + pad
|
|
439
|
-
|
|
440
|
-
click.echo(
|
|
441
|
-
" ".join(
|
|
442
|
-
pad_styled(
|
|
443
|
-
click.style(title, underline=True),
|
|
444
|
-
column_width[key],
|
|
445
|
-
key == "contentSize",
|
|
446
|
-
)
|
|
447
|
-
for key, title in columns.items()
|
|
448
|
-
)
|
|
374
|
+
for path in paths:
|
|
375
|
+
if len(paths) > 1:
|
|
376
|
+
click.echo(f"{path}:")
|
|
377
|
+
|
|
378
|
+
ls(
|
|
379
|
+
path,
|
|
380
|
+
group_directories_first=group_directories_first,
|
|
449
381
|
)
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
pad_styled(row[k], column_width[k], k == "contentSize")
|
|
454
|
-
for k in columns
|
|
455
|
-
)
|
|
456
|
-
)
|
|
382
|
+
|
|
383
|
+
if len(paths) > 1:
|
|
384
|
+
click.echo("")
|
|
457
385
|
|
|
458
386
|
|
|
459
387
|
@main.command("launch")
|
|
@@ -576,7 +504,7 @@ def mkdir(remote_directory: str):
|
|
|
576
504
|
from latch_cli.services.deprecated.mkdir import mkdir
|
|
577
505
|
|
|
578
506
|
click.secho(
|
|
579
|
-
|
|
507
|
+
"Warning: `latch mkdir` is deprecated and will be removed soon.",
|
|
580
508
|
fg="yellow",
|
|
581
509
|
)
|
|
582
510
|
mkdir(remote_directory)
|
|
@@ -593,7 +521,7 @@ def touch(remote_file: str):
|
|
|
593
521
|
from latch_cli.services.deprecated.touch import touch
|
|
594
522
|
|
|
595
523
|
click.secho(
|
|
596
|
-
|
|
524
|
+
"Warning: `latch touch` is deprecated and will be removed soon.",
|
|
597
525
|
fg="yellow",
|
|
598
526
|
)
|
|
599
527
|
touch(remote_file)
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
try:
|
|
2
|
-
from functools import cache
|
|
3
|
-
except ImportError:
|
|
4
|
-
from functools import lru_cache as cache
|
|
5
|
-
|
|
6
1
|
import os
|
|
7
2
|
import re
|
|
3
|
+
from functools import lru_cache
|
|
8
4
|
from pathlib import Path
|
|
9
5
|
from typing import List
|
|
10
6
|
|
|
@@ -16,13 +12,17 @@ from latch_cli.services.cp.ldata_utils import (
|
|
|
16
12
|
_get_known_domains_for_account,
|
|
17
13
|
)
|
|
18
14
|
|
|
15
|
+
cache = lru_cache(maxsize=None)
|
|
19
16
|
completion_type = re.compile(
|
|
20
17
|
r"""
|
|
21
18
|
^
|
|
22
19
|
(latch)?
|
|
23
20
|
:/?/?
|
|
24
21
|
(?P<domain>[^/]*)
|
|
25
|
-
(
|
|
22
|
+
(
|
|
23
|
+
(?P<parent>(/[^/]*)*)?
|
|
24
|
+
(?P<path>/[^/]*)
|
|
25
|
+
)?
|
|
26
26
|
$
|
|
27
27
|
""",
|
|
28
28
|
re.VERBOSE,
|
|
@@ -30,7 +30,10 @@ completion_type = re.compile(
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def complete(
|
|
33
|
-
ctx: click.Context,
|
|
33
|
+
ctx: click.Context,
|
|
34
|
+
param: click.Argument,
|
|
35
|
+
incomplete: str,
|
|
36
|
+
allow_local: bool = True,
|
|
34
37
|
) -> List[sc.CompletionItem]:
|
|
35
38
|
match = completion_type.match(incomplete)
|
|
36
39
|
|
|
@@ -81,9 +84,10 @@ def _complete_local_path(incomplete: str) -> List[sc.CompletionItem]:
|
|
|
81
84
|
@cache
|
|
82
85
|
def _complete_remote_path(match: re.Match[str]) -> List[sc.CompletionItem]:
|
|
83
86
|
domain = match["domain"]
|
|
87
|
+
parent = match["parent"]
|
|
84
88
|
path = match["path"][1:]
|
|
85
89
|
|
|
86
|
-
parent = f"://{domain}"
|
|
90
|
+
parent = f"://{domain}{parent}"
|
|
87
91
|
if match[0].startswith("latch"):
|
|
88
92
|
parent = f"latch{parent}"
|
|
89
93
|
|
|
@@ -14,10 +14,10 @@ from latch_cli.constants import Units
|
|
|
14
14
|
from latch_cli.services.cp.config import CPConfig, Progress
|
|
15
15
|
from latch_cli.services.cp.ldata_utils import LDataNodeType, get_node_data
|
|
16
16
|
from latch_cli.services.cp.manager import CPStateManager
|
|
17
|
-
from latch_cli.services.cp.path_utils import normalize_path
|
|
18
17
|
from latch_cli.services.cp.progress import ProgressBars, get_free_index
|
|
19
18
|
from latch_cli.services.cp.utils import get_max_workers, human_readable_time
|
|
20
19
|
from latch_cli.utils import get_auth_header, with_si_suffix
|
|
20
|
+
from latch_cli.utils.path import normalize_path
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class GetSignedUrlData(TypedDict):
|
|
@@ -12,7 +12,7 @@ import graphql.language as l
|
|
|
12
12
|
from latch_sdk_gql.execute import execute
|
|
13
13
|
from latch_sdk_gql.utils import _name_node, _parse_selection
|
|
14
14
|
|
|
15
|
-
from latch_cli.
|
|
15
|
+
from latch_cli.utils.path import get_path_error, normalize_path
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class LDataNodeType(str, Enum):
|
|
@@ -4,9 +4,9 @@ from typing import List
|
|
|
4
4
|
from latch_cli.services.cp.config import CPConfig, Progress
|
|
5
5
|
from latch_cli.services.cp.download import download
|
|
6
6
|
from latch_cli.services.cp.glob import expand_pattern
|
|
7
|
-
from latch_cli.services.cp.path_utils import is_remote_path
|
|
8
7
|
from latch_cli.services.cp.remote_copy import remote_copy
|
|
9
8
|
from latch_cli.services.cp.upload import upload
|
|
9
|
+
from latch_cli.utils.path import is_remote_path
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
# todo(ayush): come up with a better behavior scheme than unix cp
|
|
@@ -4,7 +4,7 @@ from gql.transport.exceptions import TransportQueryError
|
|
|
4
4
|
from latch_sdk_gql.execute import execute
|
|
5
5
|
|
|
6
6
|
from latch_cli.services.cp.ldata_utils import LDataNodeType, get_node_data
|
|
7
|
-
from latch_cli.
|
|
7
|
+
from latch_cli.utils.path import get_name_from_path, get_path_error
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
# todo(ayush): figure out how to do progress for this
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import datetime
|
|
2
|
-
import json
|
|
3
|
-
import logging
|
|
4
1
|
import math
|
|
5
2
|
import mimetypes
|
|
6
|
-
import multiprocessing
|
|
7
3
|
import os
|
|
8
4
|
import random
|
|
9
5
|
import time
|
|
@@ -25,11 +21,11 @@ from latch_cli.constants import latch_constants, units
|
|
|
25
21
|
from latch_cli.services.cp.config import CPConfig, Progress
|
|
26
22
|
from latch_cli.services.cp.ldata_utils import LDataNodeType, get_node_data
|
|
27
23
|
from latch_cli.services.cp.manager import CPStateManager
|
|
28
|
-
from latch_cli.services.cp.path_utils import normalize_path
|
|
29
24
|
from latch_cli.services.cp.progress import ProgressBars
|
|
30
25
|
from latch_cli.services.cp.throttle import Throttle
|
|
31
26
|
from latch_cli.services.cp.utils import get_max_workers, human_readable_time
|
|
32
27
|
from latch_cli.utils import get_auth_header, urljoins, with_si_suffix
|
|
28
|
+
from latch_cli.utils.path import normalize_path
|
|
33
29
|
|
|
34
30
|
if TYPE_CHECKING:
|
|
35
31
|
PathQueueType: TypeAlias = "Queue[Optional[Path]]"
|
|
@@ -37,12 +33,6 @@ if TYPE_CHECKING:
|
|
|
37
33
|
PartsBySrcType: TypeAlias = DictProxy[Path, ListProxy["CompletedPart"]]
|
|
38
34
|
UploadInfoBySrcType: TypeAlias = DictProxy[Path, "StartUploadReturnType"]
|
|
39
35
|
|
|
40
|
-
logging.basicConfig()
|
|
41
|
-
multiprocessing.get_logger().setLevel(logging.DEBUG)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
start_upload_batch_size = 100
|
|
45
|
-
|
|
46
36
|
|
|
47
37
|
class EmptyUploadData(TypedDict):
|
|
48
38
|
version_id: str
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""Service to list files in a remote directory."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from textwrap import dedent
|
|
6
|
+
from typing import List, Optional, TypedDict
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
import gql
|
|
10
|
+
from latch_sdk_gql.execute import execute
|
|
11
|
+
|
|
12
|
+
from latch_cli.click_utils import bold
|
|
13
|
+
from latch_cli.services.cp.ldata_utils import LDataNodeType
|
|
14
|
+
from latch_cli.utils import with_si_suffix
|
|
15
|
+
from latch_cli.utils.path import normalize_path
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class _LdataObjectMeta(TypedDict):
|
|
19
|
+
modifyTime: Optional[str]
|
|
20
|
+
contentSize: Optional[int]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class _Child(TypedDict):
|
|
24
|
+
name: str
|
|
25
|
+
ldataObjectMeta: Optional[_LdataObjectMeta]
|
|
26
|
+
type: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class _Node(TypedDict):
|
|
30
|
+
child: _Child
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class _ChildLdataTreeEdges(TypedDict):
|
|
34
|
+
nodes: List[_Node]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _FinalLinkTarget(TypedDict):
|
|
38
|
+
childLdataTreeEdges: _ChildLdataTreeEdges
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class _LdataResolvePathData(TypedDict):
|
|
42
|
+
name: str
|
|
43
|
+
type: str
|
|
44
|
+
ldataObjectMeta: Optional[_LdataObjectMeta]
|
|
45
|
+
finalLinkTarget: _FinalLinkTarget
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class _Row:
|
|
50
|
+
name: str
|
|
51
|
+
type: LDataNodeType
|
|
52
|
+
size: Optional[int]
|
|
53
|
+
modify_time: Optional[datetime]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def ls(path: str, *, group_directories_first: bool = False):
|
|
57
|
+
"""Lists the children of a remote directory in Latch.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
path: A valid remote path
|
|
61
|
+
group_directories_first: Option to display directories/links before
|
|
62
|
+
objects
|
|
63
|
+
|
|
64
|
+
This function will list all of the entites under the remote directory
|
|
65
|
+
specified in the path `path`. Will error if the path is invalid
|
|
66
|
+
or the directory doesn't exist.
|
|
67
|
+
|
|
68
|
+
Examples:
|
|
69
|
+
>>> ls("")
|
|
70
|
+
# Lists all entities in the user's root directory
|
|
71
|
+
>>> ls("latch:///dir1/dir2/dir_name")
|
|
72
|
+
# Lists all entities inside dir1/dir2/dir_name
|
|
73
|
+
"""
|
|
74
|
+
if path == "":
|
|
75
|
+
path = "/"
|
|
76
|
+
|
|
77
|
+
normalized_path = normalize_path(path, assume_remote=True)
|
|
78
|
+
|
|
79
|
+
query = execute(
|
|
80
|
+
gql.gql("""
|
|
81
|
+
query LdataInfo ($argPath: String!) {
|
|
82
|
+
accountInfoCurrent {
|
|
83
|
+
id
|
|
84
|
+
}
|
|
85
|
+
ldataResolvePathData(argPath: $argPath) {
|
|
86
|
+
name
|
|
87
|
+
ldataObjectMeta {
|
|
88
|
+
modifyTime
|
|
89
|
+
contentSize
|
|
90
|
+
}
|
|
91
|
+
type
|
|
92
|
+
finalLinkTarget {
|
|
93
|
+
childLdataTreeEdges(filter: {
|
|
94
|
+
child: {
|
|
95
|
+
removed: { equalTo: false },
|
|
96
|
+
pending: { equalTo: false }
|
|
97
|
+
}
|
|
98
|
+
}) {
|
|
99
|
+
nodes {
|
|
100
|
+
child {
|
|
101
|
+
name
|
|
102
|
+
ldataObjectMeta {
|
|
103
|
+
modifyTime
|
|
104
|
+
contentSize
|
|
105
|
+
}
|
|
106
|
+
type
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
"""),
|
|
114
|
+
{"argPath": normalized_path},
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
res: Optional[_LdataResolvePathData] = query["ldataResolvePathData"]
|
|
118
|
+
acc_id: str = query["accountInfoCurrent"]["id"]
|
|
119
|
+
|
|
120
|
+
if res is None:
|
|
121
|
+
click.secho(
|
|
122
|
+
dedent(f"""
|
|
123
|
+
{bold(path)}: no such file or directory.
|
|
124
|
+
|
|
125
|
+
{bold("Check that:")}
|
|
126
|
+
1. The target directory exists,
|
|
127
|
+
2. Account {bold(acc_id)} has permissions to view the target directory, and
|
|
128
|
+
3. The correct workspace is selected.
|
|
129
|
+
|
|
130
|
+
For privacy reasons, non-viewable objects and non-existent objects are indistinguishable.
|
|
131
|
+
""").strip("\n"),
|
|
132
|
+
fg="red",
|
|
133
|
+
)
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
nodes = res["finalLinkTarget"]["childLdataTreeEdges"]["nodes"]
|
|
137
|
+
if LDataNodeType(res["type"].lower()) == LDataNodeType.obj:
|
|
138
|
+
# ls object should just display the object's info
|
|
139
|
+
nodes.append({"child": res})
|
|
140
|
+
|
|
141
|
+
rows: List[_Row] = []
|
|
142
|
+
for node in nodes:
|
|
143
|
+
child = node["child"]
|
|
144
|
+
|
|
145
|
+
meta = child["ldataObjectMeta"]
|
|
146
|
+
size = modify_time = None
|
|
147
|
+
if meta is not None:
|
|
148
|
+
if meta["contentSize"] is not None:
|
|
149
|
+
size = int(meta["contentSize"])
|
|
150
|
+
if meta["modifyTime"] is not None:
|
|
151
|
+
modify_time = datetime.fromisoformat(meta["modifyTime"])
|
|
152
|
+
|
|
153
|
+
rows.append(
|
|
154
|
+
_Row(
|
|
155
|
+
name=child["name"],
|
|
156
|
+
type=LDataNodeType(child["type"].lower()),
|
|
157
|
+
size=size,
|
|
158
|
+
modify_time=modify_time,
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
rows.sort(key=lambda row: row.name)
|
|
163
|
+
if group_directories_first:
|
|
164
|
+
rows.sort(key=lambda row: 1 if row.type == LDataNodeType.obj else 0)
|
|
165
|
+
|
|
166
|
+
headers = [
|
|
167
|
+
" " + click.style("Size", underline=True),
|
|
168
|
+
click.style("Date Modified", underline=True),
|
|
169
|
+
click.style("Name", underline=True),
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
click.echo(" ".join(headers))
|
|
173
|
+
|
|
174
|
+
for row in rows:
|
|
175
|
+
mt_str = f'{"-": <13}'
|
|
176
|
+
size_str = f'{"-": >6}'
|
|
177
|
+
if row.type == LDataNodeType.obj:
|
|
178
|
+
if row.modify_time is not None:
|
|
179
|
+
mt_str = click.style(
|
|
180
|
+
f'{row.modify_time.strftime("%d %b %H:%M"): <13}', fg="blue"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if row.size is not None:
|
|
184
|
+
size_str = with_si_suffix(row.size, suffix="")
|
|
185
|
+
size_str = click.style(f"{size_str: >6}", fg="bright_green")
|
|
186
|
+
|
|
187
|
+
name_str = row.name
|
|
188
|
+
if len(name_str) > 50:
|
|
189
|
+
name_str = f"{name_str[:47]}..."
|
|
190
|
+
|
|
191
|
+
if row.type != LDataNodeType.obj:
|
|
192
|
+
color = "bright_blue"
|
|
193
|
+
if row.type == LDataNodeType.link:
|
|
194
|
+
color = "bright_magenta"
|
|
195
|
+
|
|
196
|
+
name_str = click.style(f"{name_str}/", bold=True, fg=color)
|
|
197
|
+
|
|
198
|
+
click.echo(f"{size_str} {mt_str} {name_str}")
|
|
@@ -4,11 +4,7 @@ from gql.transport.exceptions import TransportQueryError
|
|
|
4
4
|
from latch_sdk_gql.execute import execute
|
|
5
5
|
|
|
6
6
|
from latch_cli.services.cp.ldata_utils import LDataNodeType, get_node_data
|
|
7
|
-
from latch_cli.
|
|
8
|
-
get_name_from_path,
|
|
9
|
-
get_path_error,
|
|
10
|
-
is_remote_path,
|
|
11
|
-
)
|
|
7
|
+
from latch_cli.utils.path import get_name_from_path, get_path_error, is_remote_path
|
|
12
8
|
|
|
13
9
|
|
|
14
10
|
def move(
|
|
@@ -52,7 +52,11 @@ domain = re.compile(
|
|
|
52
52
|
# ://domain/a/b/c => latch://domain/a/b/c
|
|
53
53
|
# /a/b/c => file:///a/b/c
|
|
54
54
|
# a/b/c => file://${pwd}/a/b/c
|
|
55
|
-
|
|
55
|
+
#
|
|
56
|
+
# if assume_remote = True (i.e. in the ls case):
|
|
57
|
+
# /a/b/c => latch:///a/b/c
|
|
58
|
+
# a/b/c => latch:///a/b/c
|
|
59
|
+
def append_scheme(path: str, *, assume_remote: bool = False) -> str:
|
|
56
60
|
match = scheme.match(path)
|
|
57
61
|
if match is None:
|
|
58
62
|
raise PathResolutionError(f"{path} is not in a valid format")
|
|
@@ -60,9 +64,9 @@ def append_scheme(path: str) -> str:
|
|
|
60
64
|
if match["implicit_url"] is not None:
|
|
61
65
|
path = f"latch{path}"
|
|
62
66
|
elif match["absolute_path"] is not None:
|
|
63
|
-
path = f"file://{path}"
|
|
67
|
+
path = f"latch://{path}" if assume_remote else f"file://{path}"
|
|
64
68
|
elif match["relative_path"] is not None:
|
|
65
|
-
path = f"file://{Path.cwd()}/{path}"
|
|
69
|
+
path = f"latch:///{path}" if assume_remote else f"file://{Path.cwd()}/{path}"
|
|
66
70
|
|
|
67
71
|
return path
|
|
68
72
|
|
|
@@ -108,8 +112,8 @@ def is_account_relative(path: str) -> bool:
|
|
|
108
112
|
return match["account_relative"] is not None
|
|
109
113
|
|
|
110
114
|
|
|
111
|
-
def normalize_path(path: str) -> str:
|
|
112
|
-
path = append_scheme(path)
|
|
115
|
+
def normalize_path(path: str, *, assume_remote: bool = False) -> str:
|
|
116
|
+
path = append_scheme(path, assume_remote=assume_remote)
|
|
113
117
|
|
|
114
118
|
if path.startswith("file://"):
|
|
115
119
|
return path
|