datalab-api 0.5.1__tar.gz → 0.5.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datalab-api
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: A Python API for instances of the datalab data management platform (https://github.com/datalab-org/datalab).
5
5
  Author-email: Matthew Evans <matthew@ml-evs.science>
6
6
  License-Expression: MIT
@@ -322,7 +322,12 @@ class DatalabClient(BaseDatalabClient):
322
322
  If provided, the new file will take the place of the old file in any blocks it was attached to.
323
323
 
324
324
  Returns:
325
- A dictionary of the uploaded file data.
325
+ A dictionary of the uploaded file data, with an additional
326
+ `not_modified` key. If `replace_file_id` was provided and the
327
+ server already holds identical content (compared by hash), the
328
+ update is a no-op: the server replies `304 Not Modified` and
329
+ `not_modified` is `True`. Servers older than datalab#1744 never
330
+ report this, so `not_modified` is always `False` against them.
326
331
 
327
332
  """
328
333
  if isinstance(file_path, str):
@@ -341,10 +346,23 @@ class DatalabClient(BaseDatalabClient):
341
346
  upload_url,
342
347
  files=files,
343
348
  data={"item_id": item_id, "replace_file": replace_file_id},
344
- expected_status=201,
349
+ expected_status=[201, 304],
345
350
  timeout=upload_timeout,
346
351
  )
347
- return upload
352
+
353
+ # A 201 always carries a body describing the stored file, so an
354
+ # empty dict here can only be the 304: the server compared hashes,
355
+ # found it already holds this content, and made no update. That is
356
+ # a success, so report it rather than raising.
357
+ if not upload:
358
+ return {
359
+ "status": "success",
360
+ "file_id": replace_file_id,
361
+ "is_update": True,
362
+ "not_modified": True,
363
+ }
364
+
365
+ return {**upload, "not_modified": False}
348
366
 
349
367
  def create_data_block(
350
368
  self,
@@ -317,6 +317,12 @@ This is likely a server-side bug. Please report this issue to the datalab develo
317
317
  f"HTTP {response.status_code} error at {url}: {error_message}"
318
318
  )
319
319
 
320
+ # 204 and 304 carry no entity body by definition
321
+ # Where such a status is expected, an empty body
322
+ # is the success case rather than a parse failure.
323
+ if response.status_code in (204, 304):
324
+ return {}
325
+
320
326
  # Parse JSON response
321
327
  try:
322
328
  data = response.json()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datalab-api
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: A Python API for instances of the datalab data management platform (https://github.com/datalab-org/datalab).
5
5
  Author-email: Matthew Evans <matthew@ml-evs.science>
6
6
  License-Expression: MIT
@@ -1,5 +1,6 @@
1
1
  import pytest
2
2
  import respx
3
+ from httpx import Response
3
4
 
4
5
  from datalab_api import DatalabClient
5
6
  from datalab_api._base import DatalabAPIError
@@ -71,3 +72,54 @@ def test_group_get(fake_api_url, mocked_api):
71
72
  assert mocked_api["search-groups"].called
72
73
  assert group["immutable_id"] == "67d0b01e03000cdc75134dbd"
73
74
  assert group["group_id"] == "test_group"
75
+
76
+
77
+ @respx.mock
78
+ def test_upload_file(fake_api_url, mocked_api, tmp_path):
79
+ """A new upload is stored by the server and reported as modified."""
80
+ fake_upload = mocked_api.post("/upload-file/", name="upload")
81
+ fake_upload.return_value = Response(
82
+ 201,
83
+ json={
84
+ "status": "success",
85
+ "file_id": "6810ab6bb2b13a2be6c1e37f",
86
+ "file_information": {"name": "data.csv"},
87
+ "is_update": False,
88
+ },
89
+ )
90
+
91
+ test_file = tmp_path / "data.csv"
92
+ test_file.write_text("a,b\n1,2\n")
93
+
94
+ with DatalabClient(fake_api_url) as client:
95
+ result = client.upload_file(item_id="KUVEKJ", file_path=test_file)
96
+
97
+ assert mocked_api["upload"].called
98
+ assert result["file_id"] == "6810ab6bb2b13a2be6c1e37f"
99
+ assert result["not_modified"] is False
100
+
101
+
102
+ @respx.mock
103
+ def test_upload_file_not_modified(fake_api_url, mocked_api, tmp_path):
104
+ """When replacing a file whose content the server already holds, it
105
+ replies 304 with an empty body. That is a successful no-op, so the
106
+ existing file ID is returned with `not_modified` set, rather than
107
+ the empty body being treated as an error."""
108
+ fake_upload = mocked_api.post("/upload-file/", name="upload")
109
+ fake_upload.return_value = Response(304, content=b"")
110
+
111
+ test_file = tmp_path / "data.csv"
112
+ test_file.write_text("a,b\n1,2\n")
113
+
114
+ with DatalabClient(fake_api_url) as client:
115
+ result = client.upload_file(
116
+ item_id="KUVEKJ",
117
+ file_path=test_file,
118
+ replace_file_id="6810ab6bb2b13a2be6c1e37f",
119
+ )
120
+
121
+ assert mocked_api["upload"].called
122
+ assert result["status"] == "success"
123
+ assert result["file_id"] == "6810ab6bb2b13a2be6c1e37f"
124
+ assert result["is_update"] is True
125
+ assert result["not_modified"] is True
File without changes
File without changes
File without changes
File without changes