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.
Files changed (33) hide show
  1. {pyzotero-1.6.16 → pyzotero-1.6.17}/PKG-INFO +1 -1
  2. {pyzotero-1.6.16 → pyzotero-1.6.17}/pyproject.toml +1 -1
  3. {pyzotero-1.6.16 → pyzotero-1.6.17}/src/pyzotero/zotero.py +9 -14
  4. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/test_zotero.py +42 -0
  5. {pyzotero-1.6.16 → pyzotero-1.6.17}/LICENSE.md +0 -0
  6. {pyzotero-1.6.16 → pyzotero-1.6.17}/README.md +0 -0
  7. {pyzotero-1.6.16 → pyzotero-1.6.17}/doc/Makefile +0 -0
  8. {pyzotero-1.6.16 → pyzotero-1.6.17}/doc/_templates/layout.html +0 -0
  9. {pyzotero-1.6.16 → pyzotero-1.6.17}/doc/cat.png +0 -0
  10. {pyzotero-1.6.16 → pyzotero-1.6.17}/doc/conf.py +0 -0
  11. {pyzotero-1.6.16 → pyzotero-1.6.17}/doc/index.rst +0 -0
  12. {pyzotero-1.6.16 → pyzotero-1.6.17}/src/pyzotero/__init__.py +0 -0
  13. {pyzotero-1.6.16 → pyzotero-1.6.17}/src/pyzotero/filetransport.py +0 -0
  14. {pyzotero-1.6.16 → pyzotero-1.6.17}/src/pyzotero/zotero_errors.py +0 -0
  15. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/__init__.py +0 -0
  16. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/attachments_doc.json +0 -0
  17. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/citation_doc.xml +0 -0
  18. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/collection_doc.json +0 -0
  19. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/collection_tags.json +0 -0
  20. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/collection_versions.json +0 -0
  21. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/collections_doc.json +0 -0
  22. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/creation_doc.json +0 -0
  23. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/groups_doc.json +0 -0
  24. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/item_doc.json +0 -0
  25. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/item_fields.json +0 -0
  26. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/item_file.pdf +0 -0
  27. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/item_template.json +0 -0
  28. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/item_types.json +0 -0
  29. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/item_versions.json +0 -0
  30. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/items_doc.json +0 -0
  31. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/keys_doc.txt +0 -0
  32. {pyzotero-1.6.16 → pyzotero-1.6.17}/tests/api_responses/tags_doc.json +0 -0
  33. {pyzotero-1.6.16 → 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.16
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyzotero"
3
- version = "1.6.16"
3
+ version = "1.6.17"
4
4
  description = "Python wrapper for the Zotero API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -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 = list(urlparse(self.self_link))
522
- # strip 'format' query parameter
523
- stripped = "&".join(
524
- ["=".join(p) for p in parse_qsl(parsed[4])[:2] if p[0] != "format"],
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
- [parsed[0], parsed[1], parsed[2], parsed[3], stripped, parsed[5]],
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