pyzotero 1.6.16__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.16 → pyzotero-1.6.17}/PKG-INFO +1 -1
- {pyzotero-1.6.16 → pyzotero-1.6.17}/pyproject.toml +1 -1
- {pyzotero-1.6.16 → pyzotero-1.6.17}/src/pyzotero/zotero.py +9 -14
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/test_zotero.py +42 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/LICENSE.md +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/README.md +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/doc/Makefile +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/doc/_templates/layout.html +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/doc/cat.png +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/doc/conf.py +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/doc/index.rst +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/src/pyzotero/__init__.py +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/src/pyzotero/filetransport.py +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/src/pyzotero/zotero_errors.py +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/__init__.py +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/attachments_doc.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/citation_doc.xml +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/collection_doc.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/collection_tags.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/collection_versions.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/collections_doc.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/creation_doc.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/groups_doc.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/item_doc.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/item_fields.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/item_file.pdf +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/item_template.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/item_types.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/item_versions.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/items_doc.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/keys_doc.txt +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/tags_doc.json +0 -0
- {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/test_async.py +0 -0
|
@@ -43,11 +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
|
-
TOO_MANY_REQUESTS = 429
|
|
51
49
|
|
|
52
50
|
|
|
53
51
|
def build_url(base_url, path, args_dict=None):
|
|
@@ -451,8 +449,6 @@ class Zotero:
|
|
|
451
449
|
Returns a JSON document
|
|
452
450
|
"""
|
|
453
451
|
full_url = build_url(self.endpoint, request)
|
|
454
|
-
# The API doesn't return this any more, so we have to cheat
|
|
455
|
-
self.self_link = request
|
|
456
452
|
# ensure that we wait if there's an active backoff
|
|
457
453
|
self._check_backoff()
|
|
458
454
|
# don't set locale if the url already contains it
|
|
@@ -485,6 +481,8 @@ class Zotero:
|
|
|
485
481
|
timeout=timeout,
|
|
486
482
|
)
|
|
487
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
|
|
488
486
|
except httpx.UnsupportedProtocol:
|
|
489
487
|
# File URI handler logic
|
|
490
488
|
fc = File_Client()
|
|
@@ -518,15 +516,12 @@ class Zotero:
|
|
|
518
516
|
fragment = f"{parsed[2]}?{parsed[4]}"
|
|
519
517
|
extracted[key] = fragment
|
|
520
518
|
# add a 'self' link
|
|
521
|
-
parsed =
|
|
522
|
-
# strip 'format' query parameter
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
)
|
|
526
|
-
# rebuild url fragment
|
|
527
|
-
# 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)
|
|
528
523
|
extracted["self"] = urlunparse(
|
|
529
|
-
|
|
524
|
+
("", "", parsed.path, "", urlencode(query_params), "")
|
|
530
525
|
)
|
|
531
526
|
except KeyError:
|
|
532
527
|
# No links present, because it's a single item
|
|
@@ -572,7 +567,7 @@ class Zotero:
|
|
|
572
567
|
)
|
|
573
568
|
if backoff:
|
|
574
569
|
self._set_backoff(backoff)
|
|
575
|
-
return req.status_code == NOT_MODIFIED
|
|
570
|
+
return req.status_code == httpx.codes.NOT_MODIFIED
|
|
576
571
|
# Still plenty of life left in't
|
|
577
572
|
return False
|
|
578
573
|
|
|
@@ -1647,7 +1642,7 @@ def error_handler(zot, req, exc=None):
|
|
|
1647
1642
|
|
|
1648
1643
|
if error_codes.get(req.status_code):
|
|
1649
1644
|
# check to see whether its 429
|
|
1650
|
-
if req.status_code == TOO_MANY_REQUESTS:
|
|
1645
|
+
if req.status_code == httpx.codes.TOO_MANY_REQUESTS:
|
|
1651
1646
|
# try to get backoff or delay duration
|
|
1652
1647
|
delay = req.headers.get("backoff") or req.headers.get("retry-after")
|
|
1653
1648
|
if not delay:
|
|
@@ -415,6 +415,27 @@ class ZoteroTests(unittest.TestCase):
|
|
|
415
415
|
self.assertEqual(zot.links["last"], "/users/436/items/top?limit=1&start=2319")
|
|
416
416
|
self.assertEqual(zot.links["alternate"], "/users/436/items/top?")
|
|
417
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
|
+
|
|
418
439
|
@httpretty.activate
|
|
419
440
|
def testParseGroupsJSONDoc(self):
|
|
420
441
|
"""Should successfully return a list of group dicts, ID should match
|
|
@@ -1614,6 +1635,27 @@ class ZoteroTests(unittest.TestCase):
|
|
|
1614
1635
|
# Verify the mock was called
|
|
1615
1636
|
mock_iterfollow.assert_called_once()
|
|
1616
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
|
+
|
|
1617
1659
|
@httpretty.activate
|
|
1618
1660
|
def test_publications_user(self):
|
|
1619
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
|
|
File without changes
|
|
File without changes
|