dapi 0.4.5__tar.gz → 0.4.7__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.
- {dapi-0.4.5 → dapi-0.4.7}/PKG-INFO +2 -8
- {dapi-0.4.5 → dapi-0.4.7}/README.md +1 -8
- {dapi-0.4.5 → dapi-0.4.7}/dapi/files.py +51 -16
- {dapi-0.4.5 → dapi-0.4.7}/dapi/jobs.py +3 -7
- {dapi-0.4.5 → dapi-0.4.7}/pyproject.toml +2 -1
- {dapi-0.4.5 → dapi-0.4.7}/AUTHORS.md +0 -0
- {dapi-0.4.5 → dapi-0.4.7}/LICENSE.md +0 -0
- {dapi-0.4.5 → dapi-0.4.7}/dapi/__init__.py +0 -0
- {dapi-0.4.5 → dapi-0.4.7}/dapi/apps.py +0 -0
- {dapi-0.4.5 → dapi-0.4.7}/dapi/auth.py +0 -0
- {dapi-0.4.5 → dapi-0.4.7}/dapi/client.py +0 -0
- {dapi-0.4.5 → dapi-0.4.7}/dapi/db/__init__.py +0 -0
- {dapi-0.4.5 → dapi-0.4.7}/dapi/db/accessor.py +0 -0
- {dapi-0.4.5 → dapi-0.4.7}/dapi/db/config.py +0 -0
- {dapi-0.4.5 → dapi-0.4.7}/dapi/db/db.py +0 -0
- {dapi-0.4.5 → dapi-0.4.7}/dapi/exceptions.py +0 -0
- {dapi-0.4.5 → dapi-0.4.7}/dapi/systems.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: dapi
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.7
|
|
4
4
|
Summary: DesignSafe API
|
|
5
5
|
Author: Krishna Kumar
|
|
6
6
|
Author-email: krishnak@utexas.edu
|
|
@@ -18,6 +18,7 @@ Requires-Dist: mkdocs (>=1.6.1,<2.0.0)
|
|
|
18
18
|
Requires-Dist: mkdocs-autorefs (>=1.4.2,<2.0.0)
|
|
19
19
|
Requires-Dist: mkdocs-material (>=9.6.14,<10.0.0)
|
|
20
20
|
Requires-Dist: mkdocstrings (>=0.29.1,<0.30.0)
|
|
21
|
+
Requires-Dist: mkdocstrings-python (>=1.16.12,<2.0.0)
|
|
21
22
|
Requires-Dist: numpy (>=1.21.0) ; python_version >= "3.10"
|
|
22
23
|
Requires-Dist: numpy (>=1.21.0,<2.0) ; python_version == "3.9"
|
|
23
24
|
Requires-Dist: pandas (>=1.3.0,<3.0.0) ; python_version == "3.9"
|
|
@@ -180,10 +181,3 @@ poetry run mkdocs serve
|
|
|
180
181
|
```
|
|
181
182
|
|
|
182
183
|
This will start a local server at `http://127.0.0.1:8000/dapi/` where you can view the documentation.
|
|
183
|
-
|
|
184
|
-
### API docs
|
|
185
|
-
To generate API docs:
|
|
186
|
-
|
|
187
|
-
```
|
|
188
|
-
pdoc --html --output-dir api-docs dapi --force
|
|
189
|
-
```
|
|
@@ -148,11 +148,4 @@ poetry install
|
|
|
148
148
|
poetry run mkdocs serve
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
-
This will start a local server at `http://127.0.0.1:8000/dapi/` where you can view the documentation.
|
|
152
|
-
|
|
153
|
-
### API docs
|
|
154
|
-
To generate API docs:
|
|
155
|
-
|
|
156
|
-
```
|
|
157
|
-
pdoc --html --output-dir api-docs dapi --force
|
|
158
|
-
```
|
|
151
|
+
This will start a local server at `http://127.0.0.1:8000/dapi/` where you can view the documentation.
|
|
@@ -11,6 +11,36 @@ from .exceptions import FileOperationError, AuthenticationError
|
|
|
11
11
|
from typing import List
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def _safe_quote(path: str) -> str:
|
|
15
|
+
"""Safely URL-encode a path, avoiding double encoding.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
path (str): The path to encode
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
str: URL-encoded path
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
>>> _safe_quote("folder with spaces")
|
|
25
|
+
'folder%20with%20spaces'
|
|
26
|
+
>>> _safe_quote("folder%20with%20spaces") # Already encoded
|
|
27
|
+
'folder%20with%20spaces'
|
|
28
|
+
"""
|
|
29
|
+
# Check if the path appears to be already URL-encoded
|
|
30
|
+
# by trying to decode it and seeing if it changes
|
|
31
|
+
try:
|
|
32
|
+
decoded = urllib.parse.unquote(path)
|
|
33
|
+
if decoded != path:
|
|
34
|
+
# Path was URL-encoded, return as-is to avoid double encoding
|
|
35
|
+
return path
|
|
36
|
+
else:
|
|
37
|
+
# Path was not URL-encoded, encode it
|
|
38
|
+
return urllib.parse.quote(path)
|
|
39
|
+
except Exception:
|
|
40
|
+
# If there's any error in decoding, just encode the original path
|
|
41
|
+
return urllib.parse.quote(path)
|
|
42
|
+
|
|
43
|
+
|
|
14
44
|
# _parse_tapis_uri helper remains the same
|
|
15
45
|
def _parse_tapis_uri(tapis_uri: str) -> (str, str):
|
|
16
46
|
"""Parse a Tapis URI into system ID and path components.
|
|
@@ -19,7 +49,7 @@ def _parse_tapis_uri(tapis_uri: str) -> (str, str):
|
|
|
19
49
|
tapis_uri (str): URI in the format 'tapis://system_id/path'.
|
|
20
50
|
|
|
21
51
|
Returns:
|
|
22
|
-
tuple: A tuple containing (system_id, path)
|
|
52
|
+
tuple: A tuple containing (system_id, path).
|
|
23
53
|
|
|
24
54
|
Raises:
|
|
25
55
|
ValueError: If the URI format is invalid or missing required components.
|
|
@@ -190,8 +220,7 @@ def get_ds_path_uri(t: Tapis, path: str, verify_exists: bool = False) -> str:
|
|
|
190
220
|
)
|
|
191
221
|
else:
|
|
192
222
|
tapis_path = path_remainder
|
|
193
|
-
|
|
194
|
-
input_uri = f"tapis://{storage_system_id}/{encoded_path}"
|
|
223
|
+
input_uri = f"tapis://{storage_system_id}/{tapis_path}"
|
|
195
224
|
print(f"Translated '{path}' to '{input_uri}' using t.username")
|
|
196
225
|
break # Found match, exit loop
|
|
197
226
|
|
|
@@ -206,8 +235,7 @@ def get_ds_path_uri(t: Tapis, path: str, verify_exists: bool = False) -> str:
|
|
|
206
235
|
if pattern in path:
|
|
207
236
|
path_remainder = path.split(pattern, 1)[1].lstrip("/")
|
|
208
237
|
tapis_path = path_remainder
|
|
209
|
-
|
|
210
|
-
input_uri = f"tapis://{storage_system_id}/{encoded_path}"
|
|
238
|
+
input_uri = f"tapis://{storage_system_id}/{tapis_path}"
|
|
211
239
|
print(f"Translated '{path}' to '{input_uri}'")
|
|
212
240
|
break # Found match, exit loop
|
|
213
241
|
|
|
@@ -295,8 +323,7 @@ def get_ds_path_uri(t: Tapis, path: str, verify_exists: bool = False) -> str:
|
|
|
295
323
|
f"Could not resolve project ID '{project_id_part}' to a Tapis system ID."
|
|
296
324
|
)
|
|
297
325
|
|
|
298
|
-
|
|
299
|
-
input_uri = f"tapis://{found_system_id}/{encoded_path_within_project}"
|
|
326
|
+
input_uri = f"tapis://{found_system_id}/{path_within_project}"
|
|
300
327
|
print(f"Translated '{path}' to '{input_uri}' using Tapis v3 lookup")
|
|
301
328
|
break # Found match, exit loop
|
|
302
329
|
|
|
@@ -316,26 +343,26 @@ def get_ds_path_uri(t: Tapis, path: str, verify_exists: bool = False) -> str:
|
|
|
316
343
|
print(f"Verifying existence of translated path: {input_uri}")
|
|
317
344
|
try:
|
|
318
345
|
system_id, remote_path = _parse_tapis_uri(input_uri)
|
|
319
|
-
#
|
|
320
|
-
|
|
321
|
-
print(f"Checking system '{system_id}' for path '{
|
|
346
|
+
# The Tapis API expects URL-encoded paths when they contain spaces or special characters
|
|
347
|
+
encoded_remote_path = _safe_quote(remote_path)
|
|
348
|
+
print(f"Checking system '{system_id}' for path '{remote_path}'...")
|
|
322
349
|
# Use limit=1 for efficiency, we only care if it *exists*
|
|
323
350
|
# Note: listFiles might return successfully for the *parent* directory
|
|
324
351
|
# if the final component doesn't exist. A more robust check might
|
|
325
352
|
# involve checking the result count or specific item name, but this
|
|
326
353
|
# basic check catches non-existent parent directories.
|
|
327
|
-
t.files.listFiles(systemId=system_id, path=
|
|
354
|
+
t.files.listFiles(systemId=system_id, path=encoded_remote_path, limit=1)
|
|
328
355
|
print(f"Verification successful: Path exists.")
|
|
329
356
|
except BaseTapyException as e:
|
|
330
357
|
# Specifically check for 404 on the listFiles call
|
|
331
358
|
if hasattr(e, "response") and e.response and e.response.status_code == 404:
|
|
332
359
|
raise FileOperationError(
|
|
333
|
-
f"Verification failed: Path '{
|
|
360
|
+
f"Verification failed: Path '{remote_path}' does not exist on system '{system_id}'. Translated URI: {input_uri}"
|
|
334
361
|
) from e
|
|
335
362
|
else:
|
|
336
363
|
# Re-raise other Tapis errors encountered during verification
|
|
337
364
|
raise FileOperationError(
|
|
338
|
-
f"Verification error for path '{
|
|
365
|
+
f"Verification error for path '{remote_path}' on system '{system_id}': {e}"
|
|
339
366
|
) from e
|
|
340
367
|
except (
|
|
341
368
|
ValueError
|
|
@@ -379,8 +406,12 @@ def upload_file(t: Tapis, local_path: str, remote_uri: str):
|
|
|
379
406
|
print(
|
|
380
407
|
f"Uploading '{local_path}' to system '{system_id}' at path '{dest_path}'..."
|
|
381
408
|
)
|
|
409
|
+
# URL-encode the destination path for API call
|
|
410
|
+
encoded_dest_path = _safe_quote(dest_path)
|
|
382
411
|
t.upload(
|
|
383
|
-
system_id=system_id,
|
|
412
|
+
system_id=system_id,
|
|
413
|
+
source_file_path=local_path,
|
|
414
|
+
dest_file_path=encoded_dest_path,
|
|
384
415
|
)
|
|
385
416
|
print("Upload complete.")
|
|
386
417
|
except BaseTapyException as e:
|
|
@@ -424,8 +455,10 @@ def download_file(t: Tapis, remote_uri: str, local_path: str):
|
|
|
424
455
|
os.makedirs(local_dir, exist_ok=True)
|
|
425
456
|
# Use getContents which returns the raw bytes
|
|
426
457
|
# Set stream=True for potentially large files
|
|
458
|
+
# URL-encode the source path for API call
|
|
459
|
+
encoded_source_path = _safe_quote(source_path)
|
|
427
460
|
response = t.files.getContents(
|
|
428
|
-
systemId=system_id, path=
|
|
461
|
+
systemId=system_id, path=encoded_source_path, stream=True
|
|
429
462
|
)
|
|
430
463
|
|
|
431
464
|
# Write the streamed content to the local file
|
|
@@ -477,8 +510,10 @@ def list_files(
|
|
|
477
510
|
try:
|
|
478
511
|
system_id, path = _parse_tapis_uri(remote_uri)
|
|
479
512
|
print(f"Listing files in system '{system_id}' at path '{path}'...")
|
|
513
|
+
# URL-encode the path for API call
|
|
514
|
+
encoded_path = _safe_quote(path)
|
|
480
515
|
results = t.files.listFiles(
|
|
481
|
-
systemId=system_id, path=
|
|
516
|
+
systemId=system_id, path=encoded_path, limit=limit, offset=offset
|
|
482
517
|
)
|
|
483
518
|
print(f"Found {len(results)} items.")
|
|
484
519
|
return results
|
|
@@ -1007,9 +1007,7 @@ class SubmittedJob:
|
|
|
1007
1007
|
details = self._get_details()
|
|
1008
1008
|
if details.archiveSystemId and details.archiveSystemDir:
|
|
1009
1009
|
archive_path = details.archiveSystemDir.lstrip("/")
|
|
1010
|
-
return
|
|
1011
|
-
f"tapis://{details.archiveSystemId}/{urllib.parse.quote(archive_path)}"
|
|
1012
|
-
)
|
|
1010
|
+
return f"tapis://{details.archiveSystemId}/{archive_path}"
|
|
1013
1011
|
return None
|
|
1014
1012
|
|
|
1015
1013
|
def list_outputs(
|
|
@@ -1048,7 +1046,7 @@ class SubmittedJob:
|
|
|
1048
1046
|
full_archive_path = os.path.join(details.archiveSystemDir, path.lstrip("/"))
|
|
1049
1047
|
full_archive_path = os.path.normpath(full_archive_path).lstrip("/")
|
|
1050
1048
|
try:
|
|
1051
|
-
archive_base_uri = f"tapis://{details.archiveSystemId}/{
|
|
1049
|
+
archive_base_uri = f"tapis://{details.archiveSystemId}/{full_archive_path}"
|
|
1052
1050
|
from .files import list_files
|
|
1053
1051
|
|
|
1054
1052
|
return list_files(self._tapis, archive_base_uri, limit=limit, offset=offset)
|
|
@@ -1084,9 +1082,7 @@ class SubmittedJob:
|
|
|
1084
1082
|
details.archiveSystemDir, remote_path.lstrip("/")
|
|
1085
1083
|
)
|
|
1086
1084
|
full_archive_path = os.path.normpath(full_archive_path).lstrip("/")
|
|
1087
|
-
remote_uri =
|
|
1088
|
-
f"tapis://{details.archiveSystemId}/{urllib.parse.quote(full_archive_path)}"
|
|
1089
|
-
)
|
|
1085
|
+
remote_uri = f"tapis://{details.archiveSystemId}/{full_archive_path}"
|
|
1090
1086
|
try:
|
|
1091
1087
|
from .files import download_file
|
|
1092
1088
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "dapi"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.7"
|
|
4
4
|
description = "DesignSafe API"
|
|
5
5
|
authors = [
|
|
6
6
|
"Krishna Kumar <krishnak@utexas.edu>",
|
|
@@ -38,6 +38,7 @@ mkdocs-material = "^9.6.14"
|
|
|
38
38
|
mkdocstrings = "^0.29.1"
|
|
39
39
|
mkdocs-autorefs = "^1.4.2"
|
|
40
40
|
griffe = "^1.7.3"
|
|
41
|
+
mkdocstrings-python = "^1.16.12"
|
|
41
42
|
|
|
42
43
|
[tool.poetry.group.dev.dependencies]
|
|
43
44
|
pytest = "^7.4.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|