pyzotero 1.7.6__py3-none-any.whl → 1.9.0__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.
pyzotero/_utils.py ADDED
@@ -0,0 +1,86 @@
1
+ """Utility functions for Pyzotero.
2
+
3
+ This module contains helper functions used throughout the library.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import uuid
9
+ from collections.abc import Iterator
10
+ from pathlib import PurePosixPath
11
+ from typing import TypeVar
12
+ from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
13
+
14
+ # Avoid hanging the application if there's no server response
15
+ DEFAULT_TIMEOUT = 30
16
+
17
+ ONE_HOUR = 3600
18
+ DEFAULT_NUM_ITEMS = 50
19
+ DEFAULT_ITEM_LIMIT = 100
20
+
21
+ T = TypeVar("T")
22
+
23
+
24
+ def build_url(base_url: str, path: str, args_dict: dict | None = None) -> str:
25
+ """Build a valid URL from base, path, and optional query parameters.
26
+
27
+ This avoids string concatenation errors and leading/trailing slash issues.
28
+ """
29
+ base_url = base_url.removesuffix("/")
30
+ parsed = urlparse(base_url)
31
+ new_path = str(PurePosixPath(parsed.path) / path.removeprefix("/"))
32
+ if args_dict:
33
+ return urlunparse(parsed._replace(path=new_path, query=urlencode(args_dict)))
34
+ return urlunparse(parsed._replace(path=new_path))
35
+
36
+
37
+ def merge_params(url: str, params: dict) -> tuple[str, dict]:
38
+ """Strip query parameters from URL and merge with provided params.
39
+
40
+ Returns a tuple of (base_url, merged_params).
41
+ """
42
+ parsed = urlparse(url)
43
+ # Extract query parameters from URL
44
+ incoming = parse_qs(parsed.query)
45
+ incoming = {k: v[0] for k, v in incoming.items()}
46
+
47
+ # Create new params dict by merging
48
+ merged = {**incoming, **params}
49
+
50
+ # Get base URL by zeroing out the query component
51
+ base_url = urlunparse(parsed._replace(query=""))
52
+
53
+ return base_url, merged
54
+
55
+
56
+ def token() -> str:
57
+ """Return a unique 32-char write-token."""
58
+ return str(uuid.uuid4().hex)
59
+
60
+
61
+ def chunks(iterable: list[T], n: int) -> Iterator[list[T]]:
62
+ """Yield successive n-sized chunks from an iterable."""
63
+ for i in range(0, len(iterable), n):
64
+ yield iterable[i : i + n]
65
+
66
+
67
+ def get_backoff_duration(headers) -> str | None:
68
+ """Extract backoff duration from response headers.
69
+
70
+ The Zotero API may return backoff instructions via either the
71
+ 'Backoff' or 'Retry-After' header.
72
+ """
73
+ return headers.get("backoff") or headers.get("retry-after")
74
+
75
+
76
+ __all__ = [
77
+ "DEFAULT_ITEM_LIMIT",
78
+ "DEFAULT_NUM_ITEMS",
79
+ "DEFAULT_TIMEOUT",
80
+ "ONE_HOUR",
81
+ "build_url",
82
+ "chunks",
83
+ "get_backoff_duration",
84
+ "merge_params",
85
+ "token",
86
+ ]