pyzotero 1.7.5__tar.gz → 1.7.6__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 (34) hide show
  1. {pyzotero-1.7.5 → pyzotero-1.7.6}/PKG-INFO +1 -1
  2. {pyzotero-1.7.5 → pyzotero-1.7.6}/doc/conf.py +3 -1
  3. {pyzotero-1.7.5 → pyzotero-1.7.6}/doc/index.rst +121 -142
  4. {pyzotero-1.7.5 → pyzotero-1.7.6}/pyproject.toml +1 -1
  5. {pyzotero-1.7.5 → pyzotero-1.7.6}/src/pyzotero/zotero.py +6 -0
  6. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/test_zotero.py +104 -0
  7. {pyzotero-1.7.5 → pyzotero-1.7.6}/LICENSE.md +0 -0
  8. {pyzotero-1.7.5 → pyzotero-1.7.6}/README.md +0 -0
  9. {pyzotero-1.7.5 → pyzotero-1.7.6}/doc/Makefile +0 -0
  10. {pyzotero-1.7.5 → pyzotero-1.7.6}/doc/_templates/layout.html +0 -0
  11. {pyzotero-1.7.5 → pyzotero-1.7.6}/doc/cat.png +0 -0
  12. {pyzotero-1.7.5 → pyzotero-1.7.6}/src/pyzotero/__init__.py +0 -0
  13. {pyzotero-1.7.5 → pyzotero-1.7.6}/src/pyzotero/cli.py +0 -0
  14. {pyzotero-1.7.5 → pyzotero-1.7.6}/src/pyzotero/filetransport.py +0 -0
  15. {pyzotero-1.7.5 → pyzotero-1.7.6}/src/pyzotero/zotero_errors.py +0 -0
  16. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/__init__.py +0 -0
  17. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/attachments_doc.json +0 -0
  18. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/citation_doc.xml +0 -0
  19. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/collection_doc.json +0 -0
  20. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/collection_tags.json +0 -0
  21. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/collection_versions.json +0 -0
  22. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/collections_doc.json +0 -0
  23. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/creation_doc.json +0 -0
  24. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/groups_doc.json +0 -0
  25. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/item_doc.json +0 -0
  26. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/item_fields.json +0 -0
  27. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/item_file.pdf +0 -0
  28. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/item_template.json +0 -0
  29. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/item_types.json +0 -0
  30. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/item_versions.json +0 -0
  31. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/items_doc.json +0 -0
  32. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/keys_doc.txt +0 -0
  33. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/api_responses/tags_doc.json +0 -0
  34. {pyzotero-1.7.5 → pyzotero-1.7.6}/tests/test_async.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pyzotero
3
- Version: 1.7.5
3
+ Version: 1.7.6
4
4
  Summary: Python wrapper for the Zotero API
5
5
  Keywords: Zotero,DH
6
6
  Author: Stephan Hügel
@@ -117,7 +117,9 @@ RTD_NEW_THEME = True
117
117
  # Theme options are theme-specific and customize the look and feel of a theme
118
118
  # further. For a list of options available for each theme, see the
119
119
  # documentation.
120
- # html_theme_options = {}
120
+ html_theme_options = {
121
+ "collapse_navigation": False,
122
+ }
121
123
 
122
124
  # Add any paths that contain custom themes here, relative to this directory.
123
125
  # html_theme_path = []
@@ -1,5 +1,3 @@
1
- Description
2
- ===========
3
1
  Pyzotero is a Python wrapper for the `Zotero API (v3) <https://www.zotero.org/support/dev/web_api/v3/start>`_.
4
2
 
5
3
 
@@ -12,6 +10,7 @@ Pyzotero is a Python wrapper for the `Zotero API (v3) <https://www.zotero.org/su
12
10
 
13
11
 
14
12
 
13
+ ===============================
15
14
  Getting started (short version)
16
15
  ===============================
17
16
  1. ``uv add pyzotero`` or ``pip install pyzotero`` or ``conda install conda-forge::pyzotero``
@@ -42,21 +41,20 @@ Getting started (short version)
42
41
  Refer to the :ref:`read` and :ref:`write`.
43
42
 
44
43
 
44
+ =============================================
45
45
  Installation, testing, usage (longer version)
46
46
  =============================================
47
47
 
48
- ============
49
48
  Installation
50
- ============
49
+ ------------
51
50
  Using `uv <https://docs.astral.sh/uv/concepts/projects/dependencies/#adding-dependencies>`_: ``uv add pyzotero``
52
51
 
53
52
  Using `pip <http://www.pip-installer.org/en/latest/index.html>`_: ``pip install pyzotero``
54
53
 
55
54
  Using `Anaconda <https://www.anaconda.com/distribution/>`_: ``conda install conda-forge::pyzotero``
56
55
 
57
- -------------------------------
58
56
  Optional: Command-Line Interface
59
- -------------------------------
57
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60
58
 
61
59
  Pyzotero includes an optional command-line interface for searching and querying your local Zotero library.
62
60
 
@@ -86,29 +84,26 @@ The Pyzotero source tarball is also available from `PyPI <http://pypi.python.org
86
84
 
87
85
 
88
86
 
89
- ===============================
90
87
  Installing development versions
91
- ===============================
88
+ -------------------------------
92
89
  Pyzotero remains in development as of 2025. Unstable development versions can be found on the `Github main branch <https://github.com/urschrei/pyzotero/tree/main>`_, and installed directly from a checked-out ``main`` branch on a local clone, as in the example above: specify ``--dev`` (uv) / ``--group dev`` (pip).
93
90
 
94
91
 
95
- =======
96
92
  Testing
97
- =======
93
+ -------
98
94
  Testing requires installation of the ``dev`` dependency group (see above).
99
95
 
100
96
  Run ``pytest .`` from the top-level directory.
101
97
 
102
98
  .. _cli-usage:
103
99
 
104
- ==========================
105
100
  Command-Line Interface Usage
106
- ==========================
101
+ ----------------------------
107
102
 
108
103
  The Pyzotero CLI connects to your local Zotero installation and allows you to search your library, list collections, and view item types.
109
104
 
110
105
  Basic Commands
111
- --------------
106
+ ~~~~~~~~~~~~~~
112
107
 
113
108
  Search for top-level items:
114
109
 
@@ -153,14 +148,14 @@ List available item types:
153
148
  pyzotero itemtypes
154
149
 
155
150
  Search Behaviour
156
- ----------------
151
+ ~~~~~~~~~~~~~~~~
157
152
 
158
153
  By default, ``pyzotero search`` searches only top-level item titles and metadata fields.
159
154
 
160
155
  When the ``--fulltext`` flag is used, the search expands to include all full-text indexed content, including PDFs and other attachments. Since most full-text content comes from PDF attachments rather than top-level items, the CLI automatically retrieves the parent bibliographic items for any matching attachments. This ensures you receive useful bibliographic records (journal articles, books, etc.) rather than raw attachment items.
161
156
 
162
157
  Output Format
163
- -------------
158
+ ~~~~~~~~~~~~~
164
159
 
165
160
  By default, the CLI outputs human-readable text with all relevant metadata including:
166
161
 
@@ -171,18 +166,16 @@ By default, the CLI outputs human-readable text with all relevant metadata inclu
171
166
  Use the ``--json`` flag to output structured JSON suitable for consumption by other tools and agents.
172
167
 
173
168
 
174
- ======================
175
169
  Building Documentation
176
- ======================
170
+ ----------------------
177
171
  If you wish to build Pyzotero's documentation for offline use, it can be built from the ``doc`` directory of a local git repo by running ``make`` followed by the desired output format(s) (``html``, ``epub``, ``latexpdf`` etc.)
178
172
 
179
173
  This functionality requires Sphinx.
180
174
  See the `Sphinx documentation <http://sphinx.pocoo.org/tutorial.html#running-the-build>`_ for full details.
181
175
 
182
176
 
183
- ================
184
177
  Reporting issues
185
- ================
178
+ ----------------
186
179
  If you encounter an error while using Pyzotero, please open an issue on its `Github issues page <https://github.com/urschrei/pyzotero/issues>`_.
187
180
 
188
181
 
@@ -222,10 +215,11 @@ Example:
222
215
  .. _read:
223
216
 
224
217
  Errors
225
- =======
218
+ ------
226
219
  Where possible, any ``ZoteroError`` which is raised will preserve the underlying error in its ``__cause__`` and ``__context__`` properties, should you wish to work with these directly.
227
220
 
228
221
 
222
+ ====================
229
223
  Read API Methods
230
224
  ====================
231
225
 
@@ -235,6 +229,90 @@ Read API Methods
235
229
  .. tip::
236
230
  The Read API returns 25 results by default (the API documentation claims 50). In the interests of usability, Pyzotero returns 100 items by default, by setting the API ``limit`` parameter to 100, unless it's set by the user. If you wish to retrieve e.g. all top-level items without specifying a ``limit`` parameter, you'll have to wrap your call with :py:meth:`Zotero.everything()`: ``results = zot.everything(zot.top())``.
237
231
 
232
+ Search / Request Parameters for Read API calls
233
+ ----------------------------------------------
234
+
235
+ Additional parameters may be set on Read API methods **following any required parameters**, or set using the :py:meth:`Zotero.add_parameters()` method detailed below.
236
+
237
+ The following two examples produce the same result:
238
+
239
+ .. code-block:: python
240
+
241
+ # set parameters on the call itself
242
+ z = zot.top(limit=7, start=3)
243
+
244
+ # set parameters using explicit method
245
+ zot.add_parameters(limit=7, start=3)
246
+ z = zot.top()
247
+
248
+ The following parameters are **optional**.
249
+
250
+ **You may also set a search term here, using the 'itemType', 'q', 'qmode', or 'tag' parameters**.
251
+
252
+ This area of the Zotero Read API is under development, and may change frequently. See `the API documentation <https://www.zotero.org/support/dev/web_api/v3/basics#read_requests>`_ for the most up-to-date details of search syntax usage and export format details.
253
+
254
+
255
+
256
+ .. py:method:: Zotero.add_parameters([format=None, itemKey=None, itemType=None, q=None, qmode=None, since=None, tag=None, sort=None, direction=None, limit=None, start=None, [content=None[ ,style=None]]])
257
+
258
+ :param str format: "atom", "bib", "bibtex", json", "keys", "versions". Pyzotero retrieves and decodes JSON responses by default
259
+
260
+ .. attention::
261
+
262
+ Setting ``format='bib'`` will remove the ``limit`` parameter if it's been set, as **the API does not allow a limit on bibliography output**; it instead enforces a limit of 150 items, and if the set of items you are trying to generate a bibliography for exceeds 150, an error will be raised.
263
+
264
+ :param str itemKey: A comma-separated list of item keys. Valid only for item requests. Up to 50 items can be specified in a single request
265
+
266
+ Search parameters:
267
+
268
+ :param str itemType: item type search. See the `Search Syntax <https://www.zotero.org/support/dev/web_api/v3/basics#search_syntax>`_ for details
269
+ :param str q: Quick search. Searches titles and individual creator fields by default. Use the ``qmode`` parameter to change the mode. Currently supports phrase searching only
270
+ :param str qmode: Quick search mode. To include full-text content in the search, use ``everything``. Defaults to ``titleCreatorYear``. Searching of other fields will be possible in the future
271
+ :param int since: default ``0``. Return only objects modified after the specified library version
272
+ :param str tag: tag search. See the `Search Syntax <https://www.zotero.org/support/dev/web_api/v3/basics#search_syntax>`_ for details. More than one tag may be passed by passing a list of strings – These are treated as ``AND`` search terms, meaning only items which include all of the specified tags are returned. You can search for items matching any tag in a list by using ``OR``: ``"tag1 OR tag2"``, and all items which exclude a tag: ``"-tag"``.
273
+
274
+ The following parameters can be used for search requests:
275
+
276
+ :param str sort: The name of the field by which entries are sorted: (``dateAdded``, ``dateModified``, ``title``, ``creator``, ``type``, ``date``, ``publisher``, ``publicationTitle``, ``journalAbbreviation``, ``language``, ``accessDate``, ``libraryCatalog``, ``callNumber``, ``rights``, ``addedBy``, ``numItems``, ``tags``)
277
+ :param str direction: ``asc`` or ``desc``
278
+ :param int limit: 1 – 100 or None
279
+ :param int start: 1 – total number of items in your library or None
280
+
281
+
282
+ If you wish to retrieve citation or bibliography entries, use the following parameters:
283
+
284
+ :param str content: 'bib', 'html', or one of the export formats (see below). If 'bib' is passed, you may **also** pass:
285
+ :param str style: Any valid CSL style in the Zotero style repository
286
+ :param str linkwrap: Set this to "1" to have URLs in bibliography entries (see below) wrapped in ``<a>`` tags.
287
+ :rtype: list of HTML strings or None.
288
+
289
+
290
+ .. note::
291
+
292
+ Any parameters you set will be valid **for the next call only**. Any parameters set using ``add_parameters()`` will be overridden by parameters you pass in the call itself.
293
+
294
+
295
+ A note on the ``content`` and ``style`` parameters:
296
+
297
+ Example:
298
+
299
+ .. code-block:: python
300
+
301
+ zot.add_parameters(content='bib', style='mla')
302
+
303
+
304
+ If these are set, the return value is a list of UTF-8 formatted HTML ``div`` elements, each containing an item:
305
+
306
+ ``['<div class="csl-entry">(content)</div>']``.
307
+
308
+ You may also set ``content='citation'`` if you wish to retrieve citations. Similar to ``bib``, the result will be a list of one or more HTML ``span`` elements.
309
+
310
+
311
+ If you select one of the available `export formats <https://www.zotero.org/support/dev/web_api/v3/basics#export_formats>`_ as the ``content`` parameter, pyzotero will in most cases return a list of unicode strings in the format you specified. The exception is the ``csljson`` format, which is parsed into a list of dicts. Please note that you must provide a ``limit`` parameter if you specify one of these export formats. Multiple simultaneous retrieval of particular formats, e.g. ``content="json,coins"`` is not currently supported.
312
+
313
+ If you set ``format='keys'``, a newline-delimited string containing item keys will be returned
314
+
315
+ If you set ``format='bibtex'``, a `bibtexparser <https://bibtexparser.readthedocs.io/en/v0.6.2/bibtexparser.html#bibdatabase.BibDatabase.entries>`_ object containing citations will be returned. You can access the citations as a list of dicts using the ``.entries`` property. The bibtexparser object also implements a `dump method <https://bibtexparser.readthedocs.io/en/v0.6.2/bibtexparser.html#bibtexparser.dump>`_, if you'd like to write your citations to a ``.bib`` file.
238
316
 
239
317
  .. py:method:: Zotero.key_info()
240
318
 
@@ -242,9 +320,8 @@ Read API Methods
242
320
 
243
321
  :rtype: dict
244
322
 
245
- ====================
246
323
  Retrieving Items
247
- ====================
324
+ ----------------
248
325
 
249
326
  .. tip::
250
327
  In contrast to the v1 API, a great deal of additional metadata is now returned. In most cases, simply accessing items by referring to their ``item['data']`` key will suffice.
@@ -400,9 +477,8 @@ Example of returned item data:
400
477
 
401
478
  See :ref:`'Hello World' <hello-world>` example, above
402
479
 
403
- ====================
404
480
  Retrieving Files
405
- ====================
481
+ ----------------
406
482
 
407
483
  .. py:method:: Zotero.file(itemID[, search/request parameters])
408
484
 
@@ -440,9 +516,8 @@ Retrieving Files
440
516
 
441
517
  File retrieval and dumping should work for most common document, audio and video file formats. If you encounter an error, `please open an issue <https://github.com/urschrei/pyzotero/issues>`_.
442
518
 
443
- =======================
444
519
  Retrieving Collections
445
- =======================
520
+ ----------------------
446
521
  .. py:method:: Zotero.collections([search/request parameters])
447
522
 
448
523
  Returns a library's collections. **This includes subcollections**.
@@ -499,9 +574,8 @@ Example of returned collection data:
499
574
  u'version': 778}]
500
575
 
501
576
 
502
- ==========================
503
577
  Retrieving groups
504
- ==========================
578
+ -----------------
505
579
  .. py:method:: Zotero.groups([search/request parameters])
506
580
 
507
581
  Retrieve the Zotero group data to which the current library_id and api_key has access
@@ -535,9 +609,8 @@ Example of returned group data:
535
609
  u'version': 0}]
536
610
 
537
611
 
538
- ===================
539
612
  Retrieving Tags
540
- ===================
613
+ ---------------
541
614
 
542
615
  .. py:method:: Zotero.tags([search/request parameters])
543
616
 
@@ -558,9 +631,8 @@ Example of returned tag data:
558
631
 
559
632
  ['Authority in literature', 'Errata']
560
633
 
561
- ==============================
562
634
  Retrieving Version Information
563
- ==============================
635
+ ------------------------------
564
636
 
565
637
  The `Zotero API <https://www.zotero.org/support/dev/web_api/v3/syncing>`_ recommends requesting version information for all (or all changed) items and collections when implementing syncing. The following convenience methods (which by default return an unlimited number of responses) simplify this process.
566
638
 
@@ -585,9 +657,8 @@ Example of returned version data:
585
657
  {'C9KW275P': 3915, 'IB489TKM': 4025 }
586
658
 
587
659
 
588
- =================
589
660
  Full–Text Content
590
- =================
661
+ -----------------
591
662
 
592
663
  These methods allow the retrieval of full–text content for given library items
593
664
 
@@ -649,9 +720,8 @@ Example payload:
649
720
  "totalPages": 50
650
721
  }
651
722
 
652
- ==============================================
653
723
  The ``follow()``, and ``everything()`` methods
654
- ==============================================
724
+ ----------------------------------------------
655
725
 
656
726
  These methods (currently experimental) aim to make Pyzotero a little more RESTful. Following any Read API call which can retrieve **multiple items**, calling ``follow()`` will repeat that call, but for the next *x* number of items, where *x* is either a number set by the user for the original call, or 50 by default. Each subsequent call to ``follow()`` will extend the offset.
657
727
 
@@ -685,7 +755,7 @@ Example:
685
755
  The ``everything()`` method should work with all Pyzotero Read API calls which can return multiple items, but has not yet been extensively tested. `Feedback is welcomed <https://github.com/urschrei/pyzotero/issues>`_.
686
756
 
687
757
  Related generator methods
688
- -------------------------
758
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
689
759
 
690
760
  The :py:meth:`Zotero.iterfollow()` and :py:meth:`Zotero.makeiter()` methods are available for users who prefer to work directly with generators:
691
761
 
@@ -721,9 +791,8 @@ Example:
721
791
 
722
792
  .. warning:: The ``follow()``, ``everything()`` and ``makeiter()`` methods are only valid for methods which can return multiple library items. For instance, you cannot use ``follow()`` after an ``item()`` call. The generator methods will raise a ``StopIteration`` error when all available items retrievable by your chosen API call have been exhausted.
723
793
 
724
- ======================
725
794
  Retrieving item counts
726
- ======================
795
+ ----------------------
727
796
 
728
797
  If you wish to retrieve item counts for subsets of a library, you can use the following methods:
729
798
 
@@ -739,9 +808,8 @@ If you wish to retrieve item counts for subsets of a library, you can use the fo
739
808
 
740
809
  :rtype: int
741
810
 
742
- ================================
743
811
  Retrieving last modified version
744
- ================================
812
+ --------------------------------
745
813
 
746
814
  If you wish to retrieve the last modified version of a user or group library, you can use the following method:
747
815
 
@@ -751,103 +819,14 @@ If you wish to retrieve the last modified version of a user or group library, yo
751
819
 
752
820
  :rtype: int
753
821
 
754
-
755
- ==============================================
756
- Search / Request Parameters for Read API calls
757
- ==============================================
758
-
759
- Additional parameters may be set on Read API methods **following any required parameters**, or set using the :py:meth:`Zotero.add_parameters()` method detailed below.
760
-
761
-
762
- The following two examples produce the same result:
763
-
764
- .. code-block:: python
765
-
766
- # set parameters on the call itself
767
- z = zot.top(limit=7, start=3)
768
-
769
- # set parameters using explicit method
770
- zot.add_parameters(limit=7, start=3)
771
- z = zot.top()
772
-
773
- The following parameters are **optional**.
774
-
775
- **You may also set a search term here, using the 'itemType', 'q', 'qmode', or 'tag' parameters**.
776
-
777
- This area of the Zotero Read API is under development, and may change frequently. See `the API documentation <https://www.zotero.org/support/dev/web_api/v3/basics#read_requests>`_ for the most up-to-date details of search syntax usage and export format details.
778
-
779
-
780
-
781
- .. py:method:: Zotero.add_parameters([format=None, itemKey=None, itemType=None, q=None, qmode=None, since=None, tag=None, sort=None, direction=None, limit=None, start=None, [content=None[ ,style=None]]])
782
-
783
- :param str format: "atom", "bib", "bibtex", json", "keys", "versions". Pyzotero retrieves and decodes JSON responses by default
784
-
785
- .. attention::
786
-
787
- Setting ``format='bib'`` will remove the ``limit`` parameter if it's been set, as **the API does not allow a limit on bibliography output**; it instead enforces a limit of 150 items, and if the set of items you are trying to generate a bibliography for exceeds 150, an error will be raised.
788
-
789
- :param str itemKey: A comma-separated list of item keys. Valid only for item requests. Up to 50 items can be specified in a single request
790
-
791
- Search parameters:
792
-
793
- :param str itemType: item type search. See the `Search Syntax <https://www.zotero.org/support/dev/web_api/v3/basics#search_syntax>`_ for details
794
- :param str q: Quick search. Searches titles and individual creator fields by default. Use the ``qmode`` parameter to change the mode. Currently supports phrase searching only
795
- :param str qmode: Quick search mode. To include full-text content in the search, use ``everything``. Defaults to ``titleCreatorYear``. Searching of other fields will be possible in the future
796
- :param int since: default ``0``. Return only objects modified after the specified library version
797
- :param str tag: tag search. See the `Search Syntax <https://www.zotero.org/support/dev/web_api/v3/basics#search_syntax>`_ for details. More than one tag may be passed by passing a list of strings – These are treated as ``AND`` search terms, meaning only items which include all of the specified tags are returned. You can search for items matching any tag in a list by using ``OR``: ``"tag1 OR tag2"``, and all items which exclude a tag: ``"-tag"``.
798
-
799
- The following parameters can be used for search requests:
800
-
801
- :param str sort: The name of the field by which entries are sorted: (``dateAdded``, ``dateModified``, ``title``, ``creator``, ``type``, ``date``, ``publisher``, ``publicationTitle``, ``journalAbbreviation``, ``language``, ``accessDate``, ``libraryCatalog``, ``callNumber``, ``rights``, ``addedBy``, ``numItems``, ``tags``)
802
- :param str direction: ``asc`` or ``desc``
803
- :param int limit: 1 – 100 or None
804
- :param int start: 1 – total number of items in your library or None
805
-
806
-
807
- If you wish to retrieve citation or bibliography entries, use the following parameters:
808
-
809
- :param str content: 'bib', 'html', or one of the export formats (see below). If 'bib' is passed, you may **also** pass:
810
- :param str style: Any valid CSL style in the Zotero style repository
811
- :param str linkwrap: Set this to "1" to have URLs in bibliography entries (see below) wrapped in ``<a>`` tags.
812
- :rtype: list of HTML strings or None.
813
-
814
-
815
- .. note::
816
-
817
- Any parameters you set will be valid **for the next call only**. Any parameters set using ``add_parameters()`` will be overridden by parameters you pass in the call itself.
818
-
819
-
820
- A note on the ``content`` and ``style`` parameters:
821
-
822
- Example:
823
-
824
- .. code-block:: python
825
-
826
- zot.add_parameters(content='bib', style='mla')
827
-
828
-
829
- If these are set, the return value is a list of UTF-8 formatted HTML ``div`` elements, each containing an item:
830
-
831
- ``['<div class="csl-entry">(content)</div>']``.
832
-
833
- You may also set ``content='citation'`` if you wish to retrieve citations. Similar to ``bib``, the result will be a list of one or more HTML ``span`` elements.
834
-
835
-
836
- If you select one of the available `export formats <https://www.zotero.org/support/dev/web_api/v3/basics#export_formats>`_ as the ``content`` parameter, pyzotero will in most cases return a list of unicode strings in the format you specified. The exception is the ``csljson`` format, which is parsed into a list of dicts. Please note that you must provide a ``limit`` parameter if you specify one of these export formats. Multiple simultaneous retrieval of particular formats, e.g. ``content="json,coins"`` is not currently supported.
837
-
838
- If you set ``format='keys'``, a newline-delimited string containing item keys will be returned
839
-
840
- If you set ``format='bibtex'``, a `bibtexparser <https://bibtexparser.readthedocs.io/en/v0.6.2/bibtexparser.html#bibdatabase.BibDatabase.entries>`_ object containing citations will be returned. You can access the citations as a list of dicts using the ``.entries`` property. The bibtexparser object also implements a `dump method <https://bibtexparser.readthedocs.io/en/v0.6.2/bibtexparser.html#bibtexparser.dump>`_, if you'd like to write your citations to a ``.bib`` file.
841
-
842
-
843
822
  .. _write:
844
823
 
824
+ =================
845
825
  Write API Methods
846
826
  =================
847
827
 
848
- ==============
849
828
  Saved Searches
850
- ==============
829
+ --------------
851
830
  Pyzotero allows you to retrieve, delete, or modify saved searches:
852
831
 
853
832
  .. py:method:: Zotero.searches()
@@ -891,9 +870,8 @@ Pyzotero allows you to retrieve, delete, or modify saved searches:
891
870
  :param str condition: a valid saved search condition
892
871
  :rtype: list
893
872
 
894
- =================
895
873
  Item Methods
896
- =================
874
+ ------------
897
875
 
898
876
  .. py:method:: Zotero.item_types()
899
877
 
@@ -936,7 +914,7 @@ Item Methods
936
914
  :rtype: dict
937
915
 
938
916
  Creating and Updating Items
939
- ---------------------------
917
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
940
918
 
941
919
  .. py:method:: Zotero.create_items(items[, parentid, last_modified])
942
920
 
@@ -1010,7 +988,7 @@ Example:
1010
988
 
1011
989
 
1012
990
  Uploading files
1013
- ---------------
991
+ ~~~~~~~~~~~~~~~
1014
992
 
1015
993
  .. warning:: Attachment methods are in beta.
1016
994
 
@@ -1054,7 +1032,7 @@ Uploading files
1054
1032
  unlike the space-saving responses from the server, the return value here eschews the complex index / key lookup and simply passes back the ``imported_file`` item template populated with keys (if created successfully or passed in) corresponding to each result. This is the return type for all of these methods.
1055
1033
 
1056
1034
  Deleting items
1057
- --------------
1035
+ ~~~~~~~~~~~~~~
1058
1036
 
1059
1037
  .. py:method:: Zotero.delete_item(item[, last_modified])
1060
1038
 
@@ -1064,7 +1042,7 @@ Deleting items
1064
1042
  :param str/int last_modified: If not ``None``, will set the value of the If-Unmodified-Since-Version header.
1065
1043
 
1066
1044
  Deleting tags
1067
- --------------
1045
+ ~~~~~~~~~~~~~
1068
1046
 
1069
1047
  .. py:method:: Zotero.delete_tags(tag_a[, tag …])
1070
1048
 
@@ -1074,9 +1052,8 @@ Deleting tags
1074
1052
 
1075
1053
  You may also pass a list using ``zot.delete_tags(*[tag_list])``
1076
1054
 
1077
- ===========
1078
1055
  Adding tags
1079
- ===========
1056
+ -----------
1080
1057
 
1081
1058
  .. py:method:: Zotero.add_tags(item, tag[, tag …])
1082
1059
 
@@ -1098,9 +1075,8 @@ Example:
1098
1075
  # updated now contains a representation of the updated server item
1099
1076
 
1100
1077
 
1101
- ====================
1102
1078
  Collection Methods
1103
- ====================
1079
+ ------------------
1104
1080
 
1105
1081
  .. py:method:: Zotero.create_collections(dicts[, last_modified])
1106
1082
 
@@ -1180,17 +1156,20 @@ Examples:
1180
1156
 
1181
1157
 
1182
1158
 
1159
+ =====
1183
1160
  Notes
1184
1161
  =====
1185
1162
  Most Read API methods return **lists** of **dicts** or, in the case of tag methods, **lists** of **strings**. Most Write API methods return either ``True`` if successful, or raise an error. See ``zotero_errors.py`` for a full listing of these.
1186
1163
 
1187
1164
  .. warning:: URL parameters will supersede API calls which should return e.g. a single item: ``https://api.zotero.org/users/436/items/ABC?start=50&limit=10`` will return 10 items beginning at position 50, even though ``ABC`` does not exist. Be aware of this, and don't pass URL parameters which do not apply to a given API method.
1188
1165
 
1166
+ =======
1189
1167
  License
1190
1168
  =======
1191
1169
  Pyzotero is licensed under the `Blue Oak Model Licence <https://opensource.org/license/blue-oak-model-license>`_ license.
1192
1170
 
1193
1171
 
1172
+ ===========
1194
1173
  Cat Picture
1195
1174
  ===========
1196
1175
  This is The Grinch.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyzotero"
3
- version = "1.7.5"
3
+ version = "1.7.6"
4
4
  description = "Python wrapper for the Zotero API"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -1879,6 +1879,12 @@ class Zupload:
1879
1879
  msg,
1880
1880
  )
1881
1881
  return None # Don't do anything if payload comes with keys
1882
+ # Set contentType for each attachment if not already provided
1883
+ for item in self.payload:
1884
+ if not item.get("contentType"):
1885
+ filepath = str(self.basedir.joinpath(item["filename"]))
1886
+ detected_type = mimetypes.guess_type(filepath)[0]
1887
+ item["contentType"] = detected_type or "application/octet-stream"
1882
1888
  liblevel = "/{t}/{u}/items"
1883
1889
  # Create one or more new attachments
1884
1890
  headers = {"Zotero-Write-Token": token(), "Content-Type": "application/json"}
@@ -1156,6 +1156,110 @@ class ZoteroTests(unittest.TestCase):
1156
1156
  # Clean up
1157
1157
  os.remove(temp_file_path)
1158
1158
 
1159
+ @httpretty.activate
1160
+ def testFileUploadSetsContentType(self):
1161
+ """Tests that contentType is automatically set during upload based on file extension"""
1162
+ zot = z.Zotero("myuserID", "user", "myuserkey")
1163
+
1164
+ # Create a temporary PDF file for testing
1165
+ temp_file_path = os.path.join(self.cwd, "api_responses", "test_upload.pdf")
1166
+ with open(temp_file_path, "w") as f:
1167
+ f.write("Fake PDF content")
1168
+
1169
+ # Variable to capture the request body
1170
+ captured_body = []
1171
+
1172
+ def request_callback(request, uri, response_headers):
1173
+ body = json.loads(request.body)
1174
+ captured_body.append(body)
1175
+ return [200, response_headers, json.dumps({"success": {"0": "ITEMKEY123"}})]
1176
+
1177
+ HTTPretty.register_uri(
1178
+ HTTPretty.POST,
1179
+ "https://api.zotero.org/users/myuserID/items",
1180
+ body=request_callback,
1181
+ content_type="application/json",
1182
+ )
1183
+
1184
+ # Create payload with empty contentType (mimics Zotero API template)
1185
+ payload = [
1186
+ {
1187
+ "filename": "test_upload.pdf",
1188
+ "title": "Test PDF",
1189
+ "linkMode": "imported_file",
1190
+ "contentType": "",
1191
+ }
1192
+ ]
1193
+
1194
+ mock_auth_data = {"exists": True}
1195
+
1196
+ with (
1197
+ patch.object(z.Zupload, "_verify", return_value=None),
1198
+ patch.object(z.Zupload, "_get_auth", return_value=mock_auth_data),
1199
+ ):
1200
+ upload = z.Zupload(
1201
+ zot, payload, basedir=os.path.join(self.cwd, "api_responses")
1202
+ )
1203
+ upload.upload()
1204
+
1205
+ # Verify contentType was automatically set to application/pdf
1206
+ self.assertEqual(len(captured_body), 1)
1207
+ self.assertEqual(captured_body[0][0].get("contentType"), "application/pdf")
1208
+
1209
+ os.remove(temp_file_path)
1210
+
1211
+ @httpretty.activate
1212
+ def testFileUploadPreservesUserContentType(self):
1213
+ """Tests that user-provided contentType is not overridden"""
1214
+ zot = z.Zotero("myuserID", "user", "myuserkey")
1215
+
1216
+ temp_file_path = os.path.join(self.cwd, "api_responses", "test_upload.txt")
1217
+ with open(temp_file_path, "w") as f:
1218
+ f.write("Test content")
1219
+
1220
+ captured_body = []
1221
+
1222
+ def request_callback(request, uri, response_headers):
1223
+ body = json.loads(request.body)
1224
+ captured_body.append(body)
1225
+ return [200, response_headers, json.dumps({"success": {"0": "ITEMKEY123"}})]
1226
+
1227
+ HTTPretty.register_uri(
1228
+ HTTPretty.POST,
1229
+ "https://api.zotero.org/users/myuserID/items",
1230
+ body=request_callback,
1231
+ content_type="application/json",
1232
+ )
1233
+
1234
+ # Create payload WITH explicit contentType
1235
+ payload = [
1236
+ {
1237
+ "filename": "test_upload.txt",
1238
+ "title": "Test File",
1239
+ "linkMode": "imported_file",
1240
+ "contentType": "application/custom-type",
1241
+ }
1242
+ ]
1243
+
1244
+ mock_auth_data = {"exists": True}
1245
+
1246
+ with (
1247
+ patch.object(z.Zupload, "_verify", return_value=None),
1248
+ patch.object(z.Zupload, "_get_auth", return_value=mock_auth_data),
1249
+ ):
1250
+ upload = z.Zupload(
1251
+ zot, payload, basedir=os.path.join(self.cwd, "api_responses")
1252
+ )
1253
+ upload.upload()
1254
+
1255
+ # Verify user-provided contentType was preserved
1256
+ self.assertEqual(len(captured_body), 1)
1257
+ self.assertEqual(
1258
+ captured_body[0][0].get("contentType"), "application/custom-type"
1259
+ )
1260
+
1261
+ os.remove(temp_file_path)
1262
+
1159
1263
  @httpretty.activate
1160
1264
  def testFileUploadWithPreexistingKeys(self):
1161
1265
  """Tests file upload process when the payload already contains keys"""
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes