pyzotero 1.6.15__tar.gz → 1.6.17__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.
- {pyzotero-1.6.15 → pyzotero-1.6.17}/PKG-INFO +10 -8
- {pyzotero-1.6.15 → pyzotero-1.6.17}/README.md +9 -7
- {pyzotero-1.6.15 → pyzotero-1.6.17}/doc/index.rst +1 -1
- {pyzotero-1.6.15 → pyzotero-1.6.17}/pyproject.toml +1 -1
- {pyzotero-1.6.15 → pyzotero-1.6.17}/src/pyzotero/zotero.py +23 -19
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/test_zotero.py +75 -10
- {pyzotero-1.6.15 → pyzotero-1.6.17}/LICENSE.md +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/doc/Makefile +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/doc/_templates/layout.html +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/doc/cat.png +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/doc/conf.py +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/src/pyzotero/__init__.py +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/src/pyzotero/filetransport.py +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/src/pyzotero/zotero_errors.py +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/__init__.py +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/attachments_doc.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/citation_doc.xml +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/collection_doc.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/collection_tags.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/collection_versions.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/collections_doc.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/creation_doc.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/groups_doc.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/item_doc.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/item_fields.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/item_file.pdf +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/item_template.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/item_types.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/item_versions.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/items_doc.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/keys_doc.txt +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/api_responses/tags_doc.json +0 -0
- {pyzotero-1.6.15 → pyzotero-1.6.17}/tests/test_async.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pyzotero
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.17
|
|
4
4
|
Summary: Python wrapper for the Zotero API
|
|
5
5
|
Keywords: Zotero,DH
|
|
6
6
|
Author: Stephan Hügel
|
|
@@ -86,7 +86,7 @@ items = zot.top(limit=5)
|
|
|
86
86
|
# we've retrieved the latest five top-level items in our library
|
|
87
87
|
# we can print each item's item type and ID
|
|
88
88
|
for item in items:
|
|
89
|
-
print(
|
|
89
|
+
print(f"Item: {item['data']['itemType']} | Key: {item['data']['key']}")
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
# Documentation
|
|
@@ -95,8 +95,9 @@ Full documentation of available Pyzotero methods, code examples, and sample outp
|
|
|
95
95
|
|
|
96
96
|
# Installation
|
|
97
97
|
|
|
98
|
-
* Using [
|
|
99
|
-
* Using
|
|
98
|
+
* Using [uv][11]: `uv add pyzotero`
|
|
99
|
+
* Using [pip][10]: `pip install pyzotero`
|
|
100
|
+
* Using Anaconda:`conda install conda-forge::pyzotero`
|
|
100
101
|
* From a local clone, if you wish to install Pyzotero from a specific branch:
|
|
101
102
|
|
|
102
103
|
Example:
|
|
@@ -105,12 +106,13 @@ Example:
|
|
|
105
106
|
git clone git://github.com/urschrei/pyzotero.git
|
|
106
107
|
cd pyzotero
|
|
107
108
|
git checkout main
|
|
108
|
-
|
|
109
|
+
# specify --dev if you're planning on running tests
|
|
110
|
+
uv sync
|
|
109
111
|
```
|
|
110
112
|
|
|
111
113
|
## Testing
|
|
112
114
|
|
|
113
|
-
Run `pytest .` from the top-level directory.
|
|
115
|
+
Run `pytest .` from the top-level directory. This requires the `dev` dependency group to be installed: `uv sync --dev` / `pip install --group dev`
|
|
114
116
|
|
|
115
117
|
## Issues
|
|
116
118
|
|
|
@@ -118,14 +120,13 @@ The latest commits can be found on the [main branch][9], although new features a
|
|
|
118
120
|
|
|
119
121
|
## Pull Requests
|
|
120
122
|
|
|
121
|
-
Pull requests are welcomed. Please read the [contribution guidelines](CONTRIBUTING.md). In particular, please **base your PR on the `
|
|
123
|
+
Pull requests are welcomed. Please read the [contribution guidelines](CONTRIBUTING.md). In particular, please **base your PR on the `main` branch**.
|
|
122
124
|
|
|
123
125
|
## Versioning
|
|
124
126
|
|
|
125
127
|
As of v1.0.0, Pyzotero is versioned according to [Semver](http://semver.org); version increments are performed as follows:
|
|
126
128
|
|
|
127
129
|
|
|
128
|
-
|
|
129
130
|
1. MAJOR version will increment with incompatible API changes,
|
|
130
131
|
2. MINOR version will increment when functionality is added in a backwards-compatible manner, and
|
|
131
132
|
3. PATCH version will increment with backwards-compatible bug fixes.
|
|
@@ -149,5 +150,6 @@ Pyzotero is licensed under the [Blue Oak Model Licence 1.0.0][8]. See [LICENSE.m
|
|
|
149
150
|
[8]: https://opensource.org/license/blue-oak-model-license
|
|
150
151
|
[9]: https://github.com/urschrei/pyzotero/tree/main
|
|
151
152
|
[10]: http://www.pip-installer.org/en/latest/index.html
|
|
153
|
+
[11]: https://docs.astral.sh/uv
|
|
152
154
|
† This isn't strictly true: you only need an API key for personal libraries and non-public group libraries.
|
|
153
155
|
|
|
@@ -21,7 +21,7 @@ items = zot.top(limit=5)
|
|
|
21
21
|
# we've retrieved the latest five top-level items in our library
|
|
22
22
|
# we can print each item's item type and ID
|
|
23
23
|
for item in items:
|
|
24
|
-
print(
|
|
24
|
+
print(f"Item: {item['data']['itemType']} | Key: {item['data']['key']}")
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
# Documentation
|
|
@@ -30,8 +30,9 @@ Full documentation of available Pyzotero methods, code examples, and sample outp
|
|
|
30
30
|
|
|
31
31
|
# Installation
|
|
32
32
|
|
|
33
|
-
* Using [
|
|
34
|
-
* Using
|
|
33
|
+
* Using [uv][11]: `uv add pyzotero`
|
|
34
|
+
* Using [pip][10]: `pip install pyzotero`
|
|
35
|
+
* Using Anaconda:`conda install conda-forge::pyzotero`
|
|
35
36
|
* From a local clone, if you wish to install Pyzotero from a specific branch:
|
|
36
37
|
|
|
37
38
|
Example:
|
|
@@ -40,12 +41,13 @@ Example:
|
|
|
40
41
|
git clone git://github.com/urschrei/pyzotero.git
|
|
41
42
|
cd pyzotero
|
|
42
43
|
git checkout main
|
|
43
|
-
|
|
44
|
+
# specify --dev if you're planning on running tests
|
|
45
|
+
uv sync
|
|
44
46
|
```
|
|
45
47
|
|
|
46
48
|
## Testing
|
|
47
49
|
|
|
48
|
-
Run `pytest .` from the top-level directory.
|
|
50
|
+
Run `pytest .` from the top-level directory. This requires the `dev` dependency group to be installed: `uv sync --dev` / `pip install --group dev`
|
|
49
51
|
|
|
50
52
|
## Issues
|
|
51
53
|
|
|
@@ -53,14 +55,13 @@ The latest commits can be found on the [main branch][9], although new features a
|
|
|
53
55
|
|
|
54
56
|
## Pull Requests
|
|
55
57
|
|
|
56
|
-
Pull requests are welcomed. Please read the [contribution guidelines](CONTRIBUTING.md). In particular, please **base your PR on the `
|
|
58
|
+
Pull requests are welcomed. Please read the [contribution guidelines](CONTRIBUTING.md). In particular, please **base your PR on the `main` branch**.
|
|
57
59
|
|
|
58
60
|
## Versioning
|
|
59
61
|
|
|
60
62
|
As of v1.0.0, Pyzotero is versioned according to [Semver](http://semver.org); version increments are performed as follows:
|
|
61
63
|
|
|
62
64
|
|
|
63
|
-
|
|
64
65
|
1. MAJOR version will increment with incompatible API changes,
|
|
65
66
|
2. MINOR version will increment when functionality is added in a backwards-compatible manner, and
|
|
66
67
|
3. PATCH version will increment with backwards-compatible bug fixes.
|
|
@@ -84,5 +85,6 @@ Pyzotero is licensed under the [Blue Oak Model Licence 1.0.0][8]. See [LICENSE.m
|
|
|
84
85
|
[8]: https://opensource.org/license/blue-oak-model-license
|
|
85
86
|
[9]: https://github.com/urschrei/pyzotero/tree/main
|
|
86
87
|
[10]: http://www.pip-installer.org/en/latest/index.html
|
|
88
|
+
[11]: https://docs.astral.sh/uv
|
|
87
89
|
† This isn't strictly true: you only need an API key for personal libraries and non-public group libraries.
|
|
88
90
|
|
|
@@ -37,7 +37,7 @@ Getting started (short version)
|
|
|
37
37
|
# we've retrieved the latest five top-level items in our library
|
|
38
38
|
# we can print each item's item type and ID
|
|
39
39
|
for item in items:
|
|
40
|
-
print(
|
|
40
|
+
print(f"Item: {item['data']['itemType']} | Key: {item['data']['key']}")
|
|
41
41
|
|
|
42
42
|
Refer to the :ref:`read` and :ref:`write`.
|
|
43
43
|
|
|
@@ -43,10 +43,9 @@ from .filetransport import Client as File_Client
|
|
|
43
43
|
# Avoid hanging the application if there's no server response
|
|
44
44
|
timeout = 30
|
|
45
45
|
|
|
46
|
-
NOT_MODIFIED = 304
|
|
47
46
|
ONE_HOUR = 3600
|
|
48
47
|
DEFAULT_NUM_ITEMS = 50
|
|
49
|
-
|
|
48
|
+
DEFAULT_ITEM_LIMIT = 100
|
|
50
49
|
|
|
51
50
|
|
|
52
51
|
def build_url(base_url, path, args_dict=None):
|
|
@@ -450,8 +449,6 @@ class Zotero:
|
|
|
450
449
|
Returns a JSON document
|
|
451
450
|
"""
|
|
452
451
|
full_url = build_url(self.endpoint, request)
|
|
453
|
-
# The API doesn't return this any more, so we have to cheat
|
|
454
|
-
self.self_link = request
|
|
455
452
|
# ensure that we wait if there's an active backoff
|
|
456
453
|
self._check_backoff()
|
|
457
454
|
# don't set locale if the url already contains it
|
|
@@ -470,7 +467,7 @@ class Zotero:
|
|
|
470
467
|
params = {}
|
|
471
468
|
if not self.url_params:
|
|
472
469
|
self.url_params = {}
|
|
473
|
-
merged_params = {**
|
|
470
|
+
merged_params = {**self.url_params, **params}
|
|
474
471
|
# our incoming url might be from the "links" dict, in which case it will contain url parameters.
|
|
475
472
|
# Unfortunately, httpx doesn't like to merge query paramaters in the url string and passed params
|
|
476
473
|
# so we strip the url params, combining them with our existing url_params
|
|
@@ -484,6 +481,8 @@ class Zotero:
|
|
|
484
481
|
timeout=timeout,
|
|
485
482
|
)
|
|
486
483
|
self.request.encoding = "utf-8"
|
|
484
|
+
# The API doesn't return this any more, so we have to cheat
|
|
485
|
+
self.self_link = self.request.url
|
|
487
486
|
except httpx.UnsupportedProtocol:
|
|
488
487
|
# File URI handler logic
|
|
489
488
|
fc = File_Client()
|
|
@@ -517,15 +516,12 @@ class Zotero:
|
|
|
517
516
|
fragment = f"{parsed[2]}?{parsed[4]}"
|
|
518
517
|
extracted[key] = fragment
|
|
519
518
|
# add a 'self' link
|
|
520
|
-
parsed =
|
|
521
|
-
# strip 'format' query parameter
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
)
|
|
525
|
-
# rebuild url fragment
|
|
526
|
-
# this is a death march
|
|
519
|
+
parsed = urlparse(str(self.self_link))
|
|
520
|
+
# strip 'format' query parameter and rebuild query string
|
|
521
|
+
query_params = [(k, v) for k, v in parse_qsl(parsed.query) if k != "format"]
|
|
522
|
+
# rebuild url fragment with just path and query (consistent with other links)
|
|
527
523
|
extracted["self"] = urlunparse(
|
|
528
|
-
|
|
524
|
+
("", "", parsed.path, "", urlencode(query_params), "")
|
|
529
525
|
)
|
|
530
526
|
except KeyError:
|
|
531
527
|
# No links present, because it's a single item
|
|
@@ -571,7 +567,7 @@ class Zotero:
|
|
|
571
567
|
)
|
|
572
568
|
if backoff:
|
|
573
569
|
self._set_backoff(backoff)
|
|
574
|
-
return req.status_code == NOT_MODIFIED
|
|
570
|
+
return req.status_code == httpx.codes.NOT_MODIFIED
|
|
575
571
|
# Still plenty of life left in't
|
|
576
572
|
return False
|
|
577
573
|
|
|
@@ -580,7 +576,13 @@ class Zotero:
|
|
|
580
576
|
|
|
581
577
|
Also ensure that only valid format/content combinations are requested
|
|
582
578
|
"""
|
|
583
|
-
|
|
579
|
+
# Preserve constructor-level parameters (like locale) while allowing method-level overrides
|
|
580
|
+
if self.url_params is None:
|
|
581
|
+
self.url_params = {}
|
|
582
|
+
|
|
583
|
+
# Store existing params to preserve things like locale
|
|
584
|
+
preserved_params = self.url_params.copy()
|
|
585
|
+
|
|
584
586
|
# we want JSON by default
|
|
585
587
|
if not params.get("format"):
|
|
586
588
|
params["format"] = "json"
|
|
@@ -589,7 +591,7 @@ class Zotero:
|
|
|
589
591
|
params["format"] = "atom"
|
|
590
592
|
# TODO: rewrite format=atom, content=json request
|
|
591
593
|
if "limit" not in params or params.get("limit") == 0:
|
|
592
|
-
params["limit"] =
|
|
594
|
+
params["limit"] = DEFAULT_ITEM_LIMIT
|
|
593
595
|
# Need ability to request arbitrary number of results for version
|
|
594
596
|
# response
|
|
595
597
|
# -1 value is hack that works with current version
|
|
@@ -597,8 +599,10 @@ class Zotero:
|
|
|
597
599
|
del params["limit"]
|
|
598
600
|
# bib format can't have a limit
|
|
599
601
|
if params.get("format") == "bib":
|
|
600
|
-
|
|
601
|
-
|
|
602
|
+
params.pop("limit", None)
|
|
603
|
+
|
|
604
|
+
# Merge preserved params with new params (new params override existing ones)
|
|
605
|
+
self.url_params = {**preserved_params, **params}
|
|
602
606
|
|
|
603
607
|
def _build_query(self, query_string, no_params=False):
|
|
604
608
|
"""Set request parameters. Will always add the user ID if it hasn't
|
|
@@ -1638,7 +1642,7 @@ def error_handler(zot, req, exc=None):
|
|
|
1638
1642
|
|
|
1639
1643
|
if error_codes.get(req.status_code):
|
|
1640
1644
|
# check to see whether its 429
|
|
1641
|
-
if req.status_code == TOO_MANY_REQUESTS:
|
|
1645
|
+
if req.status_code == httpx.codes.TOO_MANY_REQUESTS:
|
|
1642
1646
|
# try to get backoff or delay duration
|
|
1643
1647
|
delay = req.headers.get("backoff") or req.headers.get("retry-after")
|
|
1644
1648
|
if not delay:
|
|
@@ -21,9 +21,10 @@ from httpretty import HTTPretty
|
|
|
21
21
|
|
|
22
22
|
try:
|
|
23
23
|
from pyzotero.pyzotero import zotero as z
|
|
24
|
+
from pyzotero.pyzotero.zotero import DEFAULT_ITEM_LIMIT
|
|
24
25
|
except ModuleNotFoundError:
|
|
25
26
|
from pyzotero import zotero as z
|
|
26
|
-
|
|
27
|
+
from pyzotero.zotero import DEFAULT_ITEM_LIMIT
|
|
27
28
|
from urllib.parse import urlencode
|
|
28
29
|
|
|
29
30
|
|
|
@@ -88,7 +89,7 @@ class ZoteroTests(unittest.TestCase):
|
|
|
88
89
|
zot = z.Zotero("myuserID", "user", "myuserkey")
|
|
89
90
|
zot.add_parameters(limit=0, start=7)
|
|
90
91
|
self.assertEqual(
|
|
91
|
-
parse_qs("start=7&limit=
|
|
92
|
+
parse_qs(f"start=7&limit={DEFAULT_ITEM_LIMIT}&format=json"),
|
|
92
93
|
parse_qs(urlencode(zot.url_params, doseq=True)),
|
|
93
94
|
)
|
|
94
95
|
|
|
@@ -104,7 +105,26 @@ class ZoteroTests(unittest.TestCase):
|
|
|
104
105
|
zot = z.Zotero("myuserID", "user", "myuserkey")
|
|
105
106
|
_ = zot.items()
|
|
106
107
|
req = zot.request
|
|
107
|
-
self.
|
|
108
|
+
self.assertIn("locale=en-US", str(req.url))
|
|
109
|
+
|
|
110
|
+
@httpretty.activate
|
|
111
|
+
def testLocalePreservedWithMethodParams(self):
|
|
112
|
+
"""Should preserve locale when methods provide their own parameters"""
|
|
113
|
+
HTTPretty.register_uri(
|
|
114
|
+
HTTPretty.GET,
|
|
115
|
+
"https://api.zotero.org/users/myuserID/items/top",
|
|
116
|
+
content_type="application/json",
|
|
117
|
+
body=self.items_doc,
|
|
118
|
+
)
|
|
119
|
+
# Test with non-default locale
|
|
120
|
+
zot = z.Zotero("myuserID", "user", "myuserkey", locale="de-DE")
|
|
121
|
+
# Call top() with limit which internally adds parameters
|
|
122
|
+
_ = zot.top(limit=1)
|
|
123
|
+
req = zot.request
|
|
124
|
+
# Check that locale is preserved in the URL
|
|
125
|
+
self.assertIn("locale=de-DE", str(req.url))
|
|
126
|
+
# Also verify the method parameter is present
|
|
127
|
+
self.assertIn("limit=1", str(req.url))
|
|
108
128
|
|
|
109
129
|
@httpretty.activate
|
|
110
130
|
def testRequestBuilderLimitNone(self):
|
|
@@ -368,9 +388,13 @@ class ZoteroTests(unittest.TestCase):
|
|
|
368
388
|
body=self.tags_doc,
|
|
369
389
|
)
|
|
370
390
|
_ = zot.tags(limit=1)
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
391
|
+
# Check that all expected parameters are present
|
|
392
|
+
url_str = str(zot.request.url)
|
|
393
|
+
self.assertIn("locale=en-US", url_str)
|
|
394
|
+
self.assertIn("limit=1", url_str)
|
|
395
|
+
self.assertIn("format=json", url_str)
|
|
396
|
+
self.assertTrue(
|
|
397
|
+
url_str.startswith("https://api.zotero.org/users/myuserID/tags?")
|
|
374
398
|
)
|
|
375
399
|
|
|
376
400
|
@httpretty.activate
|
|
@@ -391,6 +415,27 @@ class ZoteroTests(unittest.TestCase):
|
|
|
391
415
|
self.assertEqual(zot.links["last"], "/users/436/items/top?limit=1&start=2319")
|
|
392
416
|
self.assertEqual(zot.links["alternate"], "/users/436/items/top?")
|
|
393
417
|
|
|
418
|
+
@httpretty.activate
|
|
419
|
+
def testParseLinkHeadersPreservesAllParameters(self):
|
|
420
|
+
"""Test that the self link preserves all parameters, not just the first 2"""
|
|
421
|
+
zot = z.Zotero("myuserID", "user", "myuserkey")
|
|
422
|
+
HTTPretty.register_uri(
|
|
423
|
+
HTTPretty.GET,
|
|
424
|
+
"https://api.zotero.org/users/myuserID/items/top",
|
|
425
|
+
content_type="application/json",
|
|
426
|
+
body=self.items_doc,
|
|
427
|
+
adding_headers={
|
|
428
|
+
"Link": '<https://api.zotero.org/users/myuserID/items/top?start=1>; rel="next"'
|
|
429
|
+
},
|
|
430
|
+
)
|
|
431
|
+
# Call with multiple parameters including limit
|
|
432
|
+
zot.top(limit=1)
|
|
433
|
+
# The self link should preserve all parameters except format
|
|
434
|
+
self.assertIn("limit=1", zot.links["self"])
|
|
435
|
+
self.assertIn("locale=", zot.links["self"])
|
|
436
|
+
# format should be stripped
|
|
437
|
+
self.assertNotIn("format=", zot.links["self"])
|
|
438
|
+
|
|
394
439
|
@httpretty.activate
|
|
395
440
|
def testParseGroupsJSONDoc(self):
|
|
396
441
|
"""Should successfully return a list of group dicts, ID should match
|
|
@@ -408,15 +453,14 @@ class ZoteroTests(unittest.TestCase):
|
|
|
408
453
|
self.assertEqual("smart_cities", groups_data[0]["data"]["name"])
|
|
409
454
|
|
|
410
455
|
def testParamsReset(self):
|
|
411
|
-
"""Should
|
|
412
|
-
is built
|
|
413
|
-
"""
|
|
456
|
+
"""Should preserve existing URL parameters when add_parameters is called multiple times"""
|
|
414
457
|
zot = z.Zotero("myuserID", "user", "myuserkey")
|
|
415
458
|
zot.add_parameters(start=5, limit=10)
|
|
416
459
|
zot._build_query("/whatever")
|
|
417
460
|
zot.add_parameters(start=2)
|
|
461
|
+
# Should get default limit=100 since no limit specified in second call
|
|
418
462
|
self.assertEqual(
|
|
419
|
-
parse_qs("start=2&format=json&limit=
|
|
463
|
+
parse_qs(f"start=2&format=json&limit={DEFAULT_ITEM_LIMIT}"),
|
|
420
464
|
parse_qs(urlencode(zot.url_params, doseq=True)),
|
|
421
465
|
)
|
|
422
466
|
|
|
@@ -1591,6 +1635,27 @@ class ZoteroTests(unittest.TestCase):
|
|
|
1591
1635
|
# Verify the mock was called
|
|
1592
1636
|
mock_iterfollow.assert_called_once()
|
|
1593
1637
|
|
|
1638
|
+
def test_makeiter_preserves_limit_parameter(self):
|
|
1639
|
+
"""Test that makeiter preserves the limit parameter in the self link"""
|
|
1640
|
+
zot = z.Zotero("myuserID", "user", "myuserkey")
|
|
1641
|
+
|
|
1642
|
+
# Simulate a self link with multiple parameters including limit
|
|
1643
|
+
# This mimics what _extract_links() creates
|
|
1644
|
+
test_self_link = "/users/myuserID/items/top?limit=1&locale=en-US"
|
|
1645
|
+
zot.links = {
|
|
1646
|
+
"self": test_self_link,
|
|
1647
|
+
"next": "/users/myuserID/items/top?start=1",
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
# Call makeiter (with a dummy function since we're testing link manipulation)
|
|
1651
|
+
with patch.object(zot, "iterfollow"):
|
|
1652
|
+
zot.makeiter(lambda: None)
|
|
1653
|
+
|
|
1654
|
+
# Verify that the 'next' link was set to 'self' and still contains limit parameter
|
|
1655
|
+
self.assertEqual(zot.links["next"], test_self_link)
|
|
1656
|
+
self.assertIn("limit=1", zot.links["next"])
|
|
1657
|
+
self.assertIn("locale=en-US", zot.links["next"])
|
|
1658
|
+
|
|
1594
1659
|
@httpretty.activate
|
|
1595
1660
|
def test_publications_user(self):
|
|
1596
1661
|
"""Test the publications method for user libraries"""
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|