python-dropbox-api 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl

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,4 +1,4 @@
1
- from collections.abc import AsyncIterator
1
+ from collections.abc import AsyncIterator, Callable
2
2
  import json
3
3
 
4
4
  from aiohttp import ClientResponse
@@ -38,15 +38,24 @@ def parse_property_groups(property_groups: list[dict]) -> list[PropertyGroup]:
38
38
 
39
39
  async def handle_common_errors(response: ClientResponse) -> None:
40
40
  """Handle common errors."""
41
- response_json = await response.json()
41
+ if response.ok:
42
+ return
42
43
 
43
- error_summary = response_json.get("error_summary")
44
+ try:
45
+ response_json = await response.json(content_type=None)
44
46
 
45
- if error_summary is not None:
46
- if error_summary.startswith("invalid_access_token/"):
47
- raise DropboxAuthException("Unauthorized")
48
- if error_summary.startswith("path/not_found/"):
49
- raise DropboxFileOrFolderNotFoundException("File or folder not found")
47
+ error_summary = response_json.get("error_summary") if isinstance(response_json, dict) else None
48
+
49
+ if error_summary is not None:
50
+ if error_summary.startswith("invalid_access_token/"):
51
+ raise DropboxAuthException("Unauthorized")
52
+ if error_summary.startswith("path/not_found/"):
53
+ raise DropboxFileOrFolderNotFoundException("File or folder not found")
54
+ except (DropboxAuthException, DropboxFileOrFolderNotFoundException):
55
+ raise
56
+ except json.JSONDecodeError:
57
+ text = await response.text()
58
+ raise DropboxUnknownException(f"{response.status}: {text}")
50
59
 
51
60
  response.raise_for_status()
52
61
 
@@ -229,6 +238,7 @@ class DropboxAPIClient:
229
238
  path: str,
230
239
  file_stream: AsyncIterator[bytes],
231
240
  property_groups: list[PropertyGroup] | None = None,
241
+ progress_callback: Callable[[int], None] | None = None,
232
242
  ) -> None:
233
243
  """Upload a file to Dropbox using chunked upload sessions.
234
244
 
@@ -236,13 +246,19 @@ class DropboxAPIClient:
236
246
  path: The Dropbox path where the file should be uploaded.
237
247
  file_stream: An async iterator of bytes to upload.
238
248
  property_groups: Optional list of property groups to attach to the uploaded file.
249
+ progress_callback: Optional callback called with the total number of bytes uploaded after each chunk is sent.
239
250
  """
240
251
  try:
241
252
  CHUNK_SIZE = 16 * 1024 * 1024 # 16MB chunks
242
253
 
243
254
  # Always use chunked upload approach
244
255
  await self._upload_file_chunked(
245
- CONTENT_API_BASE_URL, path, file_stream, CHUNK_SIZE, property_groups
256
+ CONTENT_API_BASE_URL,
257
+ path,
258
+ file_stream,
259
+ CHUNK_SIZE,
260
+ property_groups,
261
+ progress_callback,
246
262
  )
247
263
  except (DropboxAuthException, DropboxFileOrFolderNotFoundException):
248
264
  raise
@@ -256,6 +272,7 @@ class DropboxAPIClient:
256
272
  file_stream: AsyncIterator[bytes],
257
273
  chunk_size: int,
258
274
  property_groups: list[PropertyGroup] | None = None,
275
+ progress_callback: Callable[[int], None] | None = None,
259
276
  ) -> None:
260
277
  """Upload a file using chunked upload sessions, streaming chunks without loading entire file into memory."""
261
278
  session_id = None
@@ -286,6 +303,8 @@ class DropboxAPIClient:
286
303
  )
287
304
 
288
305
  offset += len(chunk_data)
306
+ if progress_callback:
307
+ progress_callback(offset)
289
308
 
290
309
  # Handle remaining data in buffer (final partial chunk)
291
310
  if buffer:
@@ -298,6 +317,13 @@ class DropboxAPIClient:
298
317
  content_api_url, session_id, offset, buffer
299
318
  )
300
319
  offset += len(buffer)
320
+ if progress_callback:
321
+ progress_callback(offset)
322
+
323
+ # Empty file (or a stream that yielded no bytes): no session was ever
324
+ # started, so open one with no data before finishing.
325
+ if session_id is None:
326
+ session_id = await self._start_upload_session(content_api_url, b"")
301
327
 
302
328
  # Finish upload session
303
329
  await self._finish_upload_session(
@@ -429,7 +455,7 @@ class DropboxAPIClient:
429
455
  chunk_size = 1024 * 1024 # 1MB
430
456
 
431
457
  async with self._websession.post(url, headers=headers) as response:
432
- response.raise_for_status()
458
+ await handle_common_errors(response)
433
459
  async for chunk in response.content.iter_chunked(chunk_size):
434
460
  if chunk:
435
461
  yield chunk
@@ -465,6 +491,18 @@ class DropboxAPIClient:
465
491
  url, headers=headers, json=payload
466
492
  ) as response:
467
493
  await handle_common_errors(response)
494
+
495
+ response_json = await response.json()
496
+
497
+ # create_folder_v2 wraps the folder metadata under "metadata".
498
+ metadata = response_json["metadata"]
499
+
500
+ return FileOrFolderInfo(
501
+ is_folder=True,
502
+ name=metadata["name"],
503
+ size=None,
504
+ property_groups=None,
505
+ )
468
506
  except (DropboxAuthException, DropboxFileOrFolderNotFoundException):
469
507
  raise
470
508
  except Exception as err:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-dropbox-api
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Summary: This is a lightweight wrapper for the Dropbox API intended for use in Home Assistant.
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -10,6 +10,7 @@ Requires-Python: >=3.13
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.13
12
12
  Classifier: Programming Language :: Python :: 3.14
13
+ Classifier: Programming Language :: Python :: 3.15
13
14
  Requires-Dist: aiohttp (>=3.13.0,<4.0.0)
14
15
  Description-Content-Type: text/markdown
15
16
 
@@ -1,10 +1,10 @@
1
1
  python_dropbox_api/__init__.py,sha256=8NVlyqQZvA0Mgds5Pb80irBac5mGPJFwW4MEeMFPOvU,575
2
2
  python_dropbox_api/auth.py,sha256=qrPop5F2_UGDmEtNOwhuYwgoHyaQ_wCjeP0B7tMNx38,448
3
- python_dropbox_api/client.py,sha256=J3_ZBamgEesGIy7fPn6qUXbPBMix-0KbqkEcrDWS914,22463
3
+ python_dropbox_api/client.py,sha256=_Y0Fh7KAE-Zv2rEAuKO1ueT-zpllIA2EOzBGXT8tYKU,24004
4
4
  python_dropbox_api/exceptions.py,sha256=HZ8Cf1ET6uqMmMIOXmtRqgLaz59GdYm8fBKLPBnXt-M,706
5
5
  python_dropbox_api/models.py,sha256=yhkfVMKPsMJxMzLA0KhSUPt_uoJkrvVjeskKffTKt-M,1604
6
6
  python_dropbox_api/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- python_dropbox_api-0.1.3.dist-info/METADATA,sha256=FVo2n4lb7u5zcZfcNs0u0w7kJA_oH5FTRB40WhtttiA,647
8
- python_dropbox_api-0.1.3.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
9
- python_dropbox_api-0.1.3.dist-info/licenses/LICENSE,sha256=KEwQxPRqHS1CBUYJkUYSb-dkreLlur_kqtOdilr7SC4,1075
10
- python_dropbox_api-0.1.3.dist-info/RECORD,,
7
+ python_dropbox_api-0.1.5.dist-info/METADATA,sha256=i6YwiiWXBYjGofCKrm-5HxdbS2WNSEgi6oMYZzytK9o,698
8
+ python_dropbox_api-0.1.5.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
9
+ python_dropbox_api-0.1.5.dist-info/licenses/LICENSE,sha256=KEwQxPRqHS1CBUYJkUYSb-dkreLlur_kqtOdilr7SC4,1075
10
+ python_dropbox_api-0.1.5.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.3.1
2
+ Generator: poetry-core 2.5.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any