pyzotero 1.7.6__tar.gz → 1.9.0__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 (45) hide show
  1. {pyzotero-1.7.6 → pyzotero-1.9.0}/PKG-INFO +3 -3
  2. {pyzotero-1.7.6 → pyzotero-1.9.0}/README.md +2 -2
  3. {pyzotero-1.7.6 → pyzotero-1.9.0}/doc/conf.py +1 -2
  4. {pyzotero-1.7.6 → pyzotero-1.9.0}/doc/index.rst +73 -8
  5. {pyzotero-1.7.6 → pyzotero-1.9.0}/pyproject.toml +8 -3
  6. pyzotero-1.9.0/src/pyzotero/__init__.py +67 -0
  7. pyzotero-1.7.6/src/pyzotero/zotero.py → pyzotero-1.9.0/src/pyzotero/_client.py +253 -893
  8. pyzotero-1.9.0/src/pyzotero/_decorators.py +195 -0
  9. pyzotero-1.9.0/src/pyzotero/_search.py +190 -0
  10. pyzotero-1.9.0/src/pyzotero/_upload.py +241 -0
  11. pyzotero-1.9.0/src/pyzotero/_utils.py +86 -0
  12. pyzotero-1.9.0/src/pyzotero/cli.py +1302 -0
  13. pyzotero-1.9.0/src/pyzotero/errors.py +185 -0
  14. {pyzotero-1.7.6 → pyzotero-1.9.0}/src/pyzotero/filetransport.py +2 -2
  15. pyzotero-1.9.0/src/pyzotero/semantic_scholar.py +441 -0
  16. pyzotero-1.9.0/src/pyzotero/zotero.py +69 -0
  17. pyzotero-1.9.0/src/pyzotero/zotero_errors.py +58 -0
  18. pyzotero-1.9.0/tests/mock_client.py +185 -0
  19. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/test_async.py +0 -1
  20. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/test_zotero.py +321 -306
  21. pyzotero-1.7.6/src/pyzotero/__init__.py +0 -7
  22. pyzotero-1.7.6/src/pyzotero/cli.py +0 -517
  23. pyzotero-1.7.6/src/pyzotero/zotero_errors.py +0 -141
  24. {pyzotero-1.7.6 → pyzotero-1.9.0}/LICENSE.md +0 -0
  25. {pyzotero-1.7.6 → pyzotero-1.9.0}/doc/Makefile +0 -0
  26. {pyzotero-1.7.6 → pyzotero-1.9.0}/doc/_templates/layout.html +0 -0
  27. {pyzotero-1.7.6 → pyzotero-1.9.0}/doc/cat.png +0 -0
  28. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/__init__.py +0 -0
  29. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/attachments_doc.json +0 -0
  30. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/citation_doc.xml +0 -0
  31. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/collection_doc.json +0 -0
  32. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/collection_tags.json +0 -0
  33. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/collection_versions.json +0 -0
  34. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/collections_doc.json +0 -0
  35. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/creation_doc.json +0 -0
  36. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/groups_doc.json +0 -0
  37. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/item_doc.json +0 -0
  38. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/item_fields.json +0 -0
  39. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/item_file.pdf +0 -0
  40. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/item_template.json +0 -0
  41. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/item_types.json +0 -0
  42. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/item_versions.json +0 -0
  43. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/items_doc.json +0 -0
  44. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/keys_doc.txt +0 -0
  45. {pyzotero-1.7.6 → pyzotero-1.9.0}/tests/api_responses/tags_doc.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pyzotero
3
- Version: 1.7.6
3
+ Version: 1.9.0
4
4
  Summary: Python wrapper for the Zotero API
5
5
  Keywords: Zotero,DH
6
6
  Author: Stephan Hügel
@@ -82,8 +82,8 @@ Description-Content-Type: text/markdown
82
82
  Then:
83
83
 
84
84
  ``` python
85
- from pyzotero import zotero
86
- zot = zotero.Zotero(library_id, library_type, api_key) # local=True for read access to local Zotero
85
+ from pyzotero import Zotero
86
+ zot = Zotero(library_id, library_type, api_key) # local=True for read access to local Zotero
87
87
  items = zot.top(limit=5)
88
88
  # we've retrieved the latest five top-level items in our library
89
89
  # we can print each item's item type and ID
@@ -15,8 +15,8 @@
15
15
  Then:
16
16
 
17
17
  ``` python
18
- from pyzotero import zotero
19
- zot = zotero.Zotero(library_id, library_type, api_key) # local=True for read access to local Zotero
18
+ from pyzotero import Zotero
19
+ zot = Zotero(library_id, library_type, api_key) # local=True for read access to local Zotero
20
20
  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
@@ -16,7 +16,6 @@ import sys
16
16
 
17
17
  sys.path.insert(1, "..")
18
18
  import pyzotero
19
- from pyzotero import zotero as zot
20
19
 
21
20
  # Tell Jinja2 templates the build is running on Read the Docs
22
21
  if os.environ.get("READTHEDOCS", "") == "True":
@@ -24,7 +23,7 @@ if os.environ.get("READTHEDOCS", "") == "True":
24
23
  html_context = {}
25
24
  html_context["READTHEDOCS"] = True
26
25
 
27
- author = zot.__author__
26
+ author = "Stephan Hügel"
28
27
  current_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year
29
28
 
30
29
  html_context = {
@@ -30,8 +30,8 @@ Getting started (short version)
30
30
  .. code-block:: python
31
31
  :emphasize-lines: 1,2,3
32
32
 
33
- from pyzotero import zotero
34
- zot = zotero.Zotero(library_id, library_type, api_key)
33
+ from pyzotero import Zotero
34
+ zot = Zotero(library_id, library_type, api_key)
35
35
  items = zot.top(limit=5)
36
36
  # we've retrieved the latest five top-level items in our library
37
37
  # we can print each item's item type and ID
@@ -135,6 +135,18 @@ Output as JSON for machine processing:
135
135
 
136
136
  pyzotero search -q "climate" --json
137
137
 
138
+ Filter by tags (multiple tags use AND logic):
139
+
140
+ .. code-block:: bash
141
+
142
+ pyzotero search -q "topic" --tag "climate" --tag "adaptation"
143
+
144
+ Paginate results:
145
+
146
+ .. code-block:: bash
147
+
148
+ pyzotero search -q "topic" --limit 20 --offset 20 --json
149
+
138
150
  List all collections:
139
151
 
140
152
  .. code-block:: bash
@@ -147,6 +159,59 @@ List available item types:
147
159
 
148
160
  pyzotero itemtypes
149
161
 
162
+ Item and Attachment Commands
163
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
164
+
165
+ Get a single item by key:
166
+
167
+ .. code-block:: bash
168
+
169
+ pyzotero item ABC123 --json
170
+
171
+ Get child items (attachments, notes) of an item:
172
+
173
+ .. code-block:: bash
174
+
175
+ pyzotero children ABC123 --json
176
+
177
+ Get multiple items by key in a single call (up to 50):
178
+
179
+ .. code-block:: bash
180
+
181
+ pyzotero subset ABC123 DEF456 GHI789 --json
182
+
183
+ Get full-text content of an attachment:
184
+
185
+ .. code-block:: bash
186
+
187
+ pyzotero fulltext ABC123
188
+
189
+ Tag Commands
190
+ ~~~~~~~~~~~~
191
+
192
+ List all tags in the library:
193
+
194
+ .. code-block:: bash
195
+
196
+ pyzotero tags
197
+
198
+ List tags from a specific collection:
199
+
200
+ .. code-block:: bash
201
+
202
+ pyzotero tags --collection ABC123
203
+
204
+ DOI Index
205
+ ~~~~~~~~~
206
+
207
+ Output the complete DOI-to-key mapping for caching:
208
+
209
+ .. code-block:: bash
210
+
211
+ pyzotero doiindex > doi_cache.json
212
+
213
+ This returns a JSON mapping of normalised DOIs to item keys and original DOIs, allowing external tools to cache the index and avoid repeated full-library scans.
214
+
150
215
  Search Behaviour
151
216
  ~~~~~~~~~~~~~~~~
152
217
 
@@ -205,8 +270,8 @@ Example:
205
270
  .. code-block:: python
206
271
  :emphasize-lines: 4
207
272
 
208
- from pyzotero import zotero
209
- zot = zotero.Zotero('123', 'user', 'ABC1234XYZ')
273
+ from pyzotero import Zotero
274
+ zot = Zotero('123', 'user', 'ABC1234XYZ')
210
275
  # we now have a Zotero object, zot, and access to all its methods
211
276
  first_ten = zot.items(limit=10)
212
277
  # a list containing dicts of the ten most recently modified library items
@@ -731,8 +796,8 @@ Example:
731
796
 
732
797
  .. code-block:: python
733
798
 
734
- from pyzotero import zotero
735
- zot = zotero.Zotero(library_id, library_type, api_key)
799
+ from pyzotero import Zotero
800
+ zot = Zotero(library_id, library_type, api_key)
736
801
  # only retrieve a single item
737
802
  # this will retrieve the most recently added/modified top-level item
738
803
  first_item = zot.top(limit=1)
@@ -747,8 +812,8 @@ Example:
747
812
 
748
813
  .. code-block:: python
749
814
 
750
- from pyzotero import zotero
751
- zot = zotero.Zotero(library_id, library_type, api_key)
815
+ from pyzotero import Zotero
816
+ zot = Zotero(library_id, library_type, api_key)
752
817
  # retrieve all top-level items
753
818
  toplevel = zot.everything(zot.top())
754
819
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyzotero"
3
- version = "1.7.6"
3
+ version = "1.9.0"
4
4
  description = "Python wrapper for the Zotero API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -43,12 +43,11 @@ pyzotero = "pyzotero.cli:main"
43
43
  dev = [
44
44
  "pytest >= 8.4.2",
45
45
  "pytz>=2025.2",
46
- "httpretty >= 1.1.4",
47
46
  "python-dateutil",
48
47
  "ipython",
49
48
  "pytest-asyncio",
50
49
  "pytest-cov>=6.0.0",
51
- "tzdata>=2025.2"
50
+ "tzdata>=2025.2",
52
51
  ]
53
52
  doc = [
54
53
  "sphinx",
@@ -110,9 +109,15 @@ ignore = ["ANN001", "ANN003", "ANN202", "ANN201", "DOC201", "E501", "PLR0904", "
110
109
  fixable = ["ALL"]
111
110
  unfixable = []
112
111
 
112
+ [tool.ruff.lint.per-file-ignores]
113
+ "tests/*" = ["S101"] # Allow assert in tests
114
+
113
115
  [tool.ruff.format]
114
116
  # Like Black, use double quotes for strings.
115
117
  quote-style = "double"
116
118
 
117
119
  # Like Black, indent with spaces, rather than tabs.
118
120
  indent-style = "space"
121
+
122
+ [tool.ty.rules]
123
+ possibly-missing-attribute = "ignore"
@@ -0,0 +1,67 @@
1
+ """Pyzotero - Python wrapper for the Zotero API."""
2
+
3
+ import importlib.metadata
4
+
5
+ try:
6
+ # __package__ allows for the case where __name__ is "__main__"
7
+ __version__ = importlib.metadata.version(__package__ or __name__)
8
+ except importlib.metadata.PackageNotFoundError:
9
+ __version__ = "0.0.0"
10
+
11
+ # Public API exports
12
+ from pyzotero._client import Zotero
13
+ from pyzotero._search import SavedSearch
14
+ from pyzotero._upload import Zupload
15
+ from pyzotero._utils import chunks
16
+ from pyzotero.errors import (
17
+ CallDoesNotExistError,
18
+ ConflictError,
19
+ CouldNotReachURLError,
20
+ FileDoesNotExistError,
21
+ HTTPError,
22
+ InvalidItemFieldsError,
23
+ MissingCredentialsError,
24
+ ParamNotPassedError,
25
+ PreConditionFailedError,
26
+ PreConditionRequiredError,
27
+ PyZoteroError,
28
+ RequestEntityTooLargeError,
29
+ ResourceNotFoundError,
30
+ TooManyItemsError,
31
+ TooManyRequestsError,
32
+ TooManyRetriesError,
33
+ UnsupportedParamsError,
34
+ UploadError,
35
+ UserNotAuthorisedError,
36
+ )
37
+
38
+ __all__ = [
39
+ # Exceptions
40
+ "CallDoesNotExistError",
41
+ "ConflictError",
42
+ "CouldNotReachURLError",
43
+ "FileDoesNotExistError",
44
+ "HTTPError",
45
+ "InvalidItemFieldsError",
46
+ "MissingCredentialsError",
47
+ "ParamNotPassedError",
48
+ "PreConditionFailedError",
49
+ "PreConditionRequiredError",
50
+ "PyZoteroError",
51
+ "RequestEntityTooLargeError",
52
+ "ResourceNotFoundError",
53
+ "SavedSearch",
54
+ "TooManyItemsError",
55
+ "TooManyRequestsError",
56
+ "TooManyRetriesError",
57
+ "UnsupportedParamsError",
58
+ "UploadError",
59
+ "UserNotAuthorisedError",
60
+ # Main classes
61
+ "Zotero",
62
+ "Zupload",
63
+ # Version
64
+ "__version__",
65
+ # Utilities
66
+ "chunks",
67
+ ]