figshare_client 0.0.1__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Charles Tapley Hoyt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,382 @@
1
+ Metadata-Version: 2.4
2
+ Name: figshare_client
3
+ Version: 0.0.1
4
+ Summary: A client to Figshare
5
+ Keywords: snekpack,cookiecutter,figshare
6
+ Author: Charles Tapley Hoyt
7
+ Author-email: Charles Tapley Hoyt <cthoyt@gmail.com>
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 1 - Planning
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Framework :: Pytest
15
+ Classifier: Framework :: tox
16
+ Classifier: Framework :: Sphinx
17
+ Classifier: Natural Language :: English
18
+ Classifier: Programming Language :: Python
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Programming Language :: Python :: 3 :: Only
25
+ Classifier: Typing :: Typed
26
+ Requires-Dist: pydantic
27
+ Requires-Dist: requests
28
+ Requires-Dist: pystow
29
+ Maintainer: Charles Tapley Hoyt
30
+ Maintainer-email: Charles Tapley Hoyt <cthoyt@gmail.com>
31
+ Requires-Python: >=3.10
32
+ Project-URL: Bug Tracker, https://github.com/cthoyt/figshare-client/issues
33
+ Project-URL: Homepage, https://github.com/cthoyt/figshare-client
34
+ Project-URL: Repository, https://github.com/cthoyt/figshare-client.git
35
+ Project-URL: Documentation, https://figshare_client.readthedocs.io
36
+ Project-URL: Funding, https://github.com/sponsors/cthoyt
37
+ Description-Content-Type: text/markdown
38
+
39
+ <!--
40
+ <p align="center">
41
+ <img src="https://github.com/cthoyt/figshare-client/raw/main/docs/source/logo.png" height="150">
42
+ </p>
43
+ -->
44
+
45
+ <h1 align="center">
46
+ Figshare Client
47
+ </h1>
48
+
49
+ <p align="center">
50
+ <a href="https://github.com/cthoyt/figshare-client/actions/workflows/tests.yml">
51
+ <img alt="Tests" src="https://github.com/cthoyt/figshare-client/actions/workflows/tests.yml/badge.svg" /></a>
52
+ <a href="https://pypi.org/project/figshare_client">
53
+ <img alt="PyPI" src="https://img.shields.io/pypi/v/figshare_client" /></a>
54
+ <a href="https://pypi.org/project/figshare_client">
55
+ <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/figshare_client" /></a>
56
+ <a href="https://github.com/cthoyt/figshare-client/blob/main/LICENSE">
57
+ <img alt="PyPI - License" src="https://img.shields.io/pypi/l/figshare_client" /></a>
58
+ <a href='https://figshare_client.readthedocs.io/en/latest/?badge=latest'>
59
+ <img src='https://readthedocs.org/projects/figshare_client/badge/?version=latest' alt='Documentation Status' /></a>
60
+ <a href="https://codecov.io/gh/cthoyt/figshare-client/branch/main">
61
+ <img src="https://codecov.io/gh/cthoyt/figshare-client/branch/main/graph/badge.svg" alt="Codecov status" /></a>
62
+ <a href="https://github.com/cthoyt/cookiecutter-python-package">
63
+ <img alt="Cookiecutter template from @cthoyt" src="https://img.shields.io/badge/Cookiecutter-snekpack-blue" /></a>
64
+ <a href="https://github.com/astral-sh/ruff">
65
+ <img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff" style="max-width:100%;"></a>
66
+ <a href="https://github.com/cthoyt/figshare-client/blob/main/.github/CODE_OF_CONDUCT.md">
67
+ <img src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg" alt="Contributor Covenant"/></a>
68
+ <!-- uncomment if you archive on zenodo
69
+ <a href="https://zenodo.org/badge/latestdoi/XXXXXX">
70
+ <img src="https://zenodo.org/badge/XXXXXX.svg" alt="DOI"></a>
71
+ -->
72
+ </p>
73
+
74
+ A client to Figshare.
75
+
76
+ ## 💪 Getting Started
77
+
78
+ ```python
79
+ from figshare_client import get_files, ensure_files
80
+
81
+ opencitations_record_id = 24356626
82
+
83
+ # get objects describing files
84
+ infos = get_files(opencitations_record_id)
85
+
86
+ # download files locally
87
+ path_to_info = ensure_files(opencitations_record_id)
88
+ ```
89
+
90
+ ### Command Line Interface
91
+
92
+ The `figshare_client` command line tool is automatically installed. It can be
93
+ used from the console with the `--help` flag to show all subcommands:
94
+
95
+ ```console
96
+ $ figshare_client --help
97
+ ```
98
+
99
+ > TODO show the most useful thing the CLI does! The CLI will have documentation
100
+ > auto-generated by `sphinx`.
101
+
102
+ ## 🚀 Installation
103
+
104
+ <!-- Uncomment this section after your first ``tox -e finish``
105
+ The most recent release can be installed from
106
+ [PyPI](https://pypi.org/project/figshare_client/) with uv:
107
+
108
+ ```console
109
+ $ uv pip install figshare_client
110
+ ```
111
+
112
+ or with pip:
113
+
114
+ ```console
115
+ $ python3 -m pip install figshare_client
116
+ ```
117
+ -->
118
+
119
+ The most recent code and data can be installed directly from GitHub with uv:
120
+
121
+ ```console
122
+ $ uv pip install git+https://github.com/cthoyt/figshare-client.git
123
+ ```
124
+
125
+ or with pip:
126
+
127
+ ```console
128
+ $ python3 -m pip install git+https://github.com/cthoyt/figshare-client.git
129
+ ```
130
+
131
+ ## 👐 Contributing
132
+
133
+ Contributions, whether filing an issue, making a pull request, or forking, are
134
+ appreciated. See
135
+ [CONTRIBUTING.md](https://github.com/cthoyt/figshare-client/blob/master/.github/CONTRIBUTING.md)
136
+ for more information on getting involved.
137
+
138
+ ## 👋 Attribution
139
+
140
+ ### ⚖️ License
141
+
142
+ The code in this package is licensed under the MIT License.
143
+
144
+ <!--
145
+ ### 📖 Citation
146
+
147
+ Citation goes here!
148
+ -->
149
+
150
+ <!--
151
+ ### 🎁 Support
152
+
153
+ This project has been supported by the following organizations (in alphabetical order):
154
+
155
+ - [Biopragmatics Lab](https://biopragmatics.github.io)
156
+
157
+ -->
158
+
159
+ <!--
160
+ ### 💰 Funding
161
+
162
+ This project has been supported by the following grants:
163
+
164
+ | Funding Body | Program | Grant Number |
165
+ |---------------|--------------------------------------------------------------|--------------|
166
+ | Funder | [Grant Name (GRANT-ACRONYM)](https://example.com/grant-link) | ABCXYZ |
167
+ -->
168
+
169
+ ### 🍪 Cookiecutter
170
+
171
+ This package was created with
172
+ [@audreyfeldroy](https://github.com/audreyfeldroy)'s
173
+ [cookiecutter](https://github.com/cookiecutter/cookiecutter) package using
174
+ [@cthoyt](https://github.com/cthoyt)'s
175
+ [cookiecutter-snekpack](https://github.com/cthoyt/cookiecutter-snekpack)
176
+ template.
177
+
178
+ ## 🛠️ For Developers
179
+
180
+ <details>
181
+ <summary>See developer instructions</summary>
182
+
183
+ The final section of the README is for if you want to get involved by making a
184
+ code contribution.
185
+
186
+ ### Development Installation
187
+
188
+ To install in development mode, use the following:
189
+
190
+ ```console
191
+ $ git clone git+https://github.com/cthoyt/figshare-client.git
192
+ $ cd figshare-client
193
+ $ uv pip install -e .
194
+ ```
195
+
196
+ Alternatively, install using pip:
197
+
198
+ ```console
199
+ $ python3 -m pip install -e .
200
+ ```
201
+
202
+ ### Pre-commit
203
+
204
+ You can optionally use [pre-commit](https://pre-commit.com) to automate running
205
+ key code quality checks on each commit. Enable it with:
206
+
207
+ ```console
208
+ $ uvx pre-commit install
209
+ ```
210
+
211
+ Or using `pip`:
212
+
213
+ ```console
214
+ $ pip install pre-commit
215
+ $ pre-commit install
216
+ ```
217
+
218
+ ### 🥼 Testing
219
+
220
+ After cloning the repository and installing `tox` with
221
+ `uv tool install tox --with tox-uv` or `python3 -m pip install tox tox-uv`, the
222
+ unit tests in the `tests/` folder can be run reproducibly with:
223
+
224
+ ```console
225
+ $ tox -e py
226
+ ```
227
+
228
+ Additionally, these tests are automatically re-run with each commit in a
229
+ [GitHub Action](https://github.com/cthoyt/figshare-client/actions?query=workflow%3ATests).
230
+
231
+ ### 📖 Building the Documentation
232
+
233
+ The documentation can be built locally using the following:
234
+
235
+ ```console
236
+ $ git clone git+https://github.com/cthoyt/figshare-client.git
237
+ $ cd figshare-client
238
+ $ tox -e docs
239
+ $ open docs/build/html/index.html
240
+ ```
241
+
242
+ The documentation automatically installs the package as well as the `docs` extra
243
+ specified in the [`pyproject.toml`](pyproject.toml). `sphinx` plugins like
244
+ `texext` can be added there. Additionally, they need to be added to the
245
+ `extensions` list in [`docs/source/conf.py`](docs/source/conf.py).
246
+
247
+ The documentation can be deployed to [ReadTheDocs](https://readthedocs.io) using
248
+ [this guide](https://docs.readthedocs.io/en/stable/intro/import-guide.html). The
249
+ [`.readthedocs.yml`](.readthedocs.yml) YAML file contains all the configuration
250
+ you'll need. You can also set up continuous integration on GitHub to check not
251
+ only that Sphinx can build the documentation in an isolated environment (i.e.,
252
+ with `tox -e docs-test`) but also that
253
+ [ReadTheDocs can build it too](https://docs.readthedocs.io/en/stable/pull-requests.html).
254
+
255
+ </details>
256
+
257
+ ## 🧑‍💻 For Maintainers
258
+
259
+ <details>
260
+ <summary>See maintainer instructions</summary>
261
+
262
+ ### Initial Configuration
263
+
264
+ #### Configuring ReadTheDocs
265
+
266
+ [ReadTheDocs](https://readthedocs.org) is an external documentation hosting
267
+ service that integrates with GitHub's CI/CD. Do the following for each
268
+ repository:
269
+
270
+ 1. Log in to ReadTheDocs with your GitHub account to install the integration at
271
+ https://readthedocs.org/accounts/login/?next=/dashboard/
272
+ 2. Import your project by navigating to https://readthedocs.org/dashboard/import
273
+ then clicking the plus icon next to your repository
274
+ 3. You can rename the repository on the next screen using a more stylized name
275
+ (i.e., with spaces and capital letters)
276
+ 4. Click next, and you're good to go!
277
+
278
+ #### Configuring Archival on Zenodo
279
+
280
+ [Zenodo](https://zenodo.org) is a long-term archival system that assigns a DOI
281
+ to each release of your package. Do the following for each repository:
282
+
283
+ 1. Log in to Zenodo via GitHub with this link:
284
+ https://zenodo.org/oauth/login/github/?next=%2F. This brings you to a page
285
+ that lists all of your organizations and asks you to approve installing the
286
+ Zenodo app on GitHub. Click "grant" next to any organizations you want to
287
+ enable the integration for, then click the big green "approve" button. This
288
+ step only needs to be done once.
289
+ 2. Navigate to https://zenodo.org/account/settings/github/, which lists all of
290
+ your GitHub repositories (both in your username and any organizations you
291
+ enabled). Click the on/off toggle for any relevant repositories. When you
292
+ make a new repository, you'll have to come back to this
293
+
294
+ After these steps, you're ready to go! After you make "release" on GitHub (steps
295
+ for this are below), you can navigate to
296
+ https://zenodo.org/account/settings/github/repository/cthoyt/figshare-client to
297
+ see the DOI for the release and link to the Zenodo record for it.
298
+
299
+ #### Registering with the Python Package Index (PyPI)
300
+
301
+ The [Python Package Index (PyPI)](https://pypi.org) hosts packages so they can
302
+ be easily installed with `pip`, `uv`, and equivalent tools.
303
+
304
+ 1. Register for an account [here](https://pypi.org/account/register)
305
+ 2. Navigate to https://pypi.org/manage/account and make sure you have verified
306
+ your email address. A verification email might not have been sent by default,
307
+ so you might have to click the "options" dropdown next to your address to get
308
+ to the "re-send verification email" button
309
+ 3. 2-Factor authentication is required for PyPI since the end of 2023 (see this
310
+ [blog post from PyPI](https://blog.pypi.org/posts/2023-05-25-securing-pypi-with-2fa/)).
311
+ This means you have to first issue account recovery codes, then set up
312
+ 2-factor authentication
313
+ 4. Issue an API token from https://pypi.org/manage/account/token
314
+
315
+ This only needs to be done once per developer.
316
+
317
+ #### Configuring your machine's connection to PyPI
318
+
319
+ This needs to be done once per machine.
320
+
321
+ ```console
322
+ $ uv tool install keyring
323
+ $ keyring set https://upload.pypi.org/legacy/ __token__
324
+ $ keyring set https://test.pypi.org/legacy/ __token__
325
+ ```
326
+
327
+ Note that this deprecates previous workflows using `.pypirc`.
328
+
329
+ ### 📦 Making a Release
330
+
331
+ #### Uploading to PyPI
332
+
333
+ After installing the package in development mode and installing `tox` with
334
+ `uv tool install tox --with tox-uv` or `python3 -m pip install tox tox-uv`, run
335
+ the following from the console:
336
+
337
+ ```console
338
+ $ tox -e finish
339
+ ```
340
+
341
+ This script does the following:
342
+
343
+ 1. Uses [bump-my-version](https://github.com/callowayproject/bump-my-version) to
344
+ switch the version number in the `pyproject.toml`, `CITATION.cff`,
345
+ `src/figshare_client/version.py`, and
346
+ [`docs/source/conf.py`](docs/source/conf.py) to not have the `-dev` suffix
347
+ 2. Packages the code in both a tar archive and a wheel using
348
+ [`uv build`](https://docs.astral.sh/uv/guides/publish/#building-your-package)
349
+ 3. Uploads to PyPI using
350
+ [`uv publish`](https://docs.astral.sh/uv/guides/publish/#publishing-your-package).
351
+ 4. Push to GitHub. You'll need to make a release going with the commit where the
352
+ version was bumped.
353
+ 5. Bump the version to the next patch. If you made big changes and want to bump
354
+ the version by minor, you can use `tox -e bumpversion -- minor` after.
355
+
356
+ #### Releasing on GitHub
357
+
358
+ 1. Navigate to https://github.com/cthoyt/figshare-client/releases/new to draft a
359
+ new release
360
+ 2. Click the "Choose a Tag" dropdown and select the tag corresponding to the
361
+ release you just made
362
+ 3. Click the "Generate Release Notes" button to get a quick outline of recent
363
+ changes. Modify the title and description as you see fit
364
+ 4. Click the big green "Publish Release" button
365
+
366
+ This will trigger Zenodo to assign a DOI to your release as well.
367
+
368
+ ### Updating Package Boilerplate
369
+
370
+ This project uses `cruft` to keep boilerplate (i.e., configuration, contribution
371
+ guidelines, documentation configuration) up-to-date with the upstream
372
+ cookiecutter package. Install cruft with either `uv tool install cruft` or
373
+ `python3 -m pip install cruft` then run:
374
+
375
+ ```console
376
+ $ cruft update
377
+ ```
378
+
379
+ More info on Cruft's update command is available
380
+ [here](https://github.com/cruft/cruft?tab=readme-ov-file#updating-a-project).
381
+
382
+ </details>
@@ -0,0 +1,344 @@
1
+ <!--
2
+ <p align="center">
3
+ <img src="https://github.com/cthoyt/figshare-client/raw/main/docs/source/logo.png" height="150">
4
+ </p>
5
+ -->
6
+
7
+ <h1 align="center">
8
+ Figshare Client
9
+ </h1>
10
+
11
+ <p align="center">
12
+ <a href="https://github.com/cthoyt/figshare-client/actions/workflows/tests.yml">
13
+ <img alt="Tests" src="https://github.com/cthoyt/figshare-client/actions/workflows/tests.yml/badge.svg" /></a>
14
+ <a href="https://pypi.org/project/figshare_client">
15
+ <img alt="PyPI" src="https://img.shields.io/pypi/v/figshare_client" /></a>
16
+ <a href="https://pypi.org/project/figshare_client">
17
+ <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/figshare_client" /></a>
18
+ <a href="https://github.com/cthoyt/figshare-client/blob/main/LICENSE">
19
+ <img alt="PyPI - License" src="https://img.shields.io/pypi/l/figshare_client" /></a>
20
+ <a href='https://figshare_client.readthedocs.io/en/latest/?badge=latest'>
21
+ <img src='https://readthedocs.org/projects/figshare_client/badge/?version=latest' alt='Documentation Status' /></a>
22
+ <a href="https://codecov.io/gh/cthoyt/figshare-client/branch/main">
23
+ <img src="https://codecov.io/gh/cthoyt/figshare-client/branch/main/graph/badge.svg" alt="Codecov status" /></a>
24
+ <a href="https://github.com/cthoyt/cookiecutter-python-package">
25
+ <img alt="Cookiecutter template from @cthoyt" src="https://img.shields.io/badge/Cookiecutter-snekpack-blue" /></a>
26
+ <a href="https://github.com/astral-sh/ruff">
27
+ <img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff" style="max-width:100%;"></a>
28
+ <a href="https://github.com/cthoyt/figshare-client/blob/main/.github/CODE_OF_CONDUCT.md">
29
+ <img src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg" alt="Contributor Covenant"/></a>
30
+ <!-- uncomment if you archive on zenodo
31
+ <a href="https://zenodo.org/badge/latestdoi/XXXXXX">
32
+ <img src="https://zenodo.org/badge/XXXXXX.svg" alt="DOI"></a>
33
+ -->
34
+ </p>
35
+
36
+ A client to Figshare.
37
+
38
+ ## 💪 Getting Started
39
+
40
+ ```python
41
+ from figshare_client import get_files, ensure_files
42
+
43
+ opencitations_record_id = 24356626
44
+
45
+ # get objects describing files
46
+ infos = get_files(opencitations_record_id)
47
+
48
+ # download files locally
49
+ path_to_info = ensure_files(opencitations_record_id)
50
+ ```
51
+
52
+ ### Command Line Interface
53
+
54
+ The `figshare_client` command line tool is automatically installed. It can be
55
+ used from the console with the `--help` flag to show all subcommands:
56
+
57
+ ```console
58
+ $ figshare_client --help
59
+ ```
60
+
61
+ > TODO show the most useful thing the CLI does! The CLI will have documentation
62
+ > auto-generated by `sphinx`.
63
+
64
+ ## 🚀 Installation
65
+
66
+ <!-- Uncomment this section after your first ``tox -e finish``
67
+ The most recent release can be installed from
68
+ [PyPI](https://pypi.org/project/figshare_client/) with uv:
69
+
70
+ ```console
71
+ $ uv pip install figshare_client
72
+ ```
73
+
74
+ or with pip:
75
+
76
+ ```console
77
+ $ python3 -m pip install figshare_client
78
+ ```
79
+ -->
80
+
81
+ The most recent code and data can be installed directly from GitHub with uv:
82
+
83
+ ```console
84
+ $ uv pip install git+https://github.com/cthoyt/figshare-client.git
85
+ ```
86
+
87
+ or with pip:
88
+
89
+ ```console
90
+ $ python3 -m pip install git+https://github.com/cthoyt/figshare-client.git
91
+ ```
92
+
93
+ ## 👐 Contributing
94
+
95
+ Contributions, whether filing an issue, making a pull request, or forking, are
96
+ appreciated. See
97
+ [CONTRIBUTING.md](https://github.com/cthoyt/figshare-client/blob/master/.github/CONTRIBUTING.md)
98
+ for more information on getting involved.
99
+
100
+ ## 👋 Attribution
101
+
102
+ ### ⚖️ License
103
+
104
+ The code in this package is licensed under the MIT License.
105
+
106
+ <!--
107
+ ### 📖 Citation
108
+
109
+ Citation goes here!
110
+ -->
111
+
112
+ <!--
113
+ ### 🎁 Support
114
+
115
+ This project has been supported by the following organizations (in alphabetical order):
116
+
117
+ - [Biopragmatics Lab](https://biopragmatics.github.io)
118
+
119
+ -->
120
+
121
+ <!--
122
+ ### 💰 Funding
123
+
124
+ This project has been supported by the following grants:
125
+
126
+ | Funding Body | Program | Grant Number |
127
+ |---------------|--------------------------------------------------------------|--------------|
128
+ | Funder | [Grant Name (GRANT-ACRONYM)](https://example.com/grant-link) | ABCXYZ |
129
+ -->
130
+
131
+ ### 🍪 Cookiecutter
132
+
133
+ This package was created with
134
+ [@audreyfeldroy](https://github.com/audreyfeldroy)'s
135
+ [cookiecutter](https://github.com/cookiecutter/cookiecutter) package using
136
+ [@cthoyt](https://github.com/cthoyt)'s
137
+ [cookiecutter-snekpack](https://github.com/cthoyt/cookiecutter-snekpack)
138
+ template.
139
+
140
+ ## 🛠️ For Developers
141
+
142
+ <details>
143
+ <summary>See developer instructions</summary>
144
+
145
+ The final section of the README is for if you want to get involved by making a
146
+ code contribution.
147
+
148
+ ### Development Installation
149
+
150
+ To install in development mode, use the following:
151
+
152
+ ```console
153
+ $ git clone git+https://github.com/cthoyt/figshare-client.git
154
+ $ cd figshare-client
155
+ $ uv pip install -e .
156
+ ```
157
+
158
+ Alternatively, install using pip:
159
+
160
+ ```console
161
+ $ python3 -m pip install -e .
162
+ ```
163
+
164
+ ### Pre-commit
165
+
166
+ You can optionally use [pre-commit](https://pre-commit.com) to automate running
167
+ key code quality checks on each commit. Enable it with:
168
+
169
+ ```console
170
+ $ uvx pre-commit install
171
+ ```
172
+
173
+ Or using `pip`:
174
+
175
+ ```console
176
+ $ pip install pre-commit
177
+ $ pre-commit install
178
+ ```
179
+
180
+ ### 🥼 Testing
181
+
182
+ After cloning the repository and installing `tox` with
183
+ `uv tool install tox --with tox-uv` or `python3 -m pip install tox tox-uv`, the
184
+ unit tests in the `tests/` folder can be run reproducibly with:
185
+
186
+ ```console
187
+ $ tox -e py
188
+ ```
189
+
190
+ Additionally, these tests are automatically re-run with each commit in a
191
+ [GitHub Action](https://github.com/cthoyt/figshare-client/actions?query=workflow%3ATests).
192
+
193
+ ### 📖 Building the Documentation
194
+
195
+ The documentation can be built locally using the following:
196
+
197
+ ```console
198
+ $ git clone git+https://github.com/cthoyt/figshare-client.git
199
+ $ cd figshare-client
200
+ $ tox -e docs
201
+ $ open docs/build/html/index.html
202
+ ```
203
+
204
+ The documentation automatically installs the package as well as the `docs` extra
205
+ specified in the [`pyproject.toml`](pyproject.toml). `sphinx` plugins like
206
+ `texext` can be added there. Additionally, they need to be added to the
207
+ `extensions` list in [`docs/source/conf.py`](docs/source/conf.py).
208
+
209
+ The documentation can be deployed to [ReadTheDocs](https://readthedocs.io) using
210
+ [this guide](https://docs.readthedocs.io/en/stable/intro/import-guide.html). The
211
+ [`.readthedocs.yml`](.readthedocs.yml) YAML file contains all the configuration
212
+ you'll need. You can also set up continuous integration on GitHub to check not
213
+ only that Sphinx can build the documentation in an isolated environment (i.e.,
214
+ with `tox -e docs-test`) but also that
215
+ [ReadTheDocs can build it too](https://docs.readthedocs.io/en/stable/pull-requests.html).
216
+
217
+ </details>
218
+
219
+ ## 🧑‍💻 For Maintainers
220
+
221
+ <details>
222
+ <summary>See maintainer instructions</summary>
223
+
224
+ ### Initial Configuration
225
+
226
+ #### Configuring ReadTheDocs
227
+
228
+ [ReadTheDocs](https://readthedocs.org) is an external documentation hosting
229
+ service that integrates with GitHub's CI/CD. Do the following for each
230
+ repository:
231
+
232
+ 1. Log in to ReadTheDocs with your GitHub account to install the integration at
233
+ https://readthedocs.org/accounts/login/?next=/dashboard/
234
+ 2. Import your project by navigating to https://readthedocs.org/dashboard/import
235
+ then clicking the plus icon next to your repository
236
+ 3. You can rename the repository on the next screen using a more stylized name
237
+ (i.e., with spaces and capital letters)
238
+ 4. Click next, and you're good to go!
239
+
240
+ #### Configuring Archival on Zenodo
241
+
242
+ [Zenodo](https://zenodo.org) is a long-term archival system that assigns a DOI
243
+ to each release of your package. Do the following for each repository:
244
+
245
+ 1. Log in to Zenodo via GitHub with this link:
246
+ https://zenodo.org/oauth/login/github/?next=%2F. This brings you to a page
247
+ that lists all of your organizations and asks you to approve installing the
248
+ Zenodo app on GitHub. Click "grant" next to any organizations you want to
249
+ enable the integration for, then click the big green "approve" button. This
250
+ step only needs to be done once.
251
+ 2. Navigate to https://zenodo.org/account/settings/github/, which lists all of
252
+ your GitHub repositories (both in your username and any organizations you
253
+ enabled). Click the on/off toggle for any relevant repositories. When you
254
+ make a new repository, you'll have to come back to this
255
+
256
+ After these steps, you're ready to go! After you make "release" on GitHub (steps
257
+ for this are below), you can navigate to
258
+ https://zenodo.org/account/settings/github/repository/cthoyt/figshare-client to
259
+ see the DOI for the release and link to the Zenodo record for it.
260
+
261
+ #### Registering with the Python Package Index (PyPI)
262
+
263
+ The [Python Package Index (PyPI)](https://pypi.org) hosts packages so they can
264
+ be easily installed with `pip`, `uv`, and equivalent tools.
265
+
266
+ 1. Register for an account [here](https://pypi.org/account/register)
267
+ 2. Navigate to https://pypi.org/manage/account and make sure you have verified
268
+ your email address. A verification email might not have been sent by default,
269
+ so you might have to click the "options" dropdown next to your address to get
270
+ to the "re-send verification email" button
271
+ 3. 2-Factor authentication is required for PyPI since the end of 2023 (see this
272
+ [blog post from PyPI](https://blog.pypi.org/posts/2023-05-25-securing-pypi-with-2fa/)).
273
+ This means you have to first issue account recovery codes, then set up
274
+ 2-factor authentication
275
+ 4. Issue an API token from https://pypi.org/manage/account/token
276
+
277
+ This only needs to be done once per developer.
278
+
279
+ #### Configuring your machine's connection to PyPI
280
+
281
+ This needs to be done once per machine.
282
+
283
+ ```console
284
+ $ uv tool install keyring
285
+ $ keyring set https://upload.pypi.org/legacy/ __token__
286
+ $ keyring set https://test.pypi.org/legacy/ __token__
287
+ ```
288
+
289
+ Note that this deprecates previous workflows using `.pypirc`.
290
+
291
+ ### 📦 Making a Release
292
+
293
+ #### Uploading to PyPI
294
+
295
+ After installing the package in development mode and installing `tox` with
296
+ `uv tool install tox --with tox-uv` or `python3 -m pip install tox tox-uv`, run
297
+ the following from the console:
298
+
299
+ ```console
300
+ $ tox -e finish
301
+ ```
302
+
303
+ This script does the following:
304
+
305
+ 1. Uses [bump-my-version](https://github.com/callowayproject/bump-my-version) to
306
+ switch the version number in the `pyproject.toml`, `CITATION.cff`,
307
+ `src/figshare_client/version.py`, and
308
+ [`docs/source/conf.py`](docs/source/conf.py) to not have the `-dev` suffix
309
+ 2. Packages the code in both a tar archive and a wheel using
310
+ [`uv build`](https://docs.astral.sh/uv/guides/publish/#building-your-package)
311
+ 3. Uploads to PyPI using
312
+ [`uv publish`](https://docs.astral.sh/uv/guides/publish/#publishing-your-package).
313
+ 4. Push to GitHub. You'll need to make a release going with the commit where the
314
+ version was bumped.
315
+ 5. Bump the version to the next patch. If you made big changes and want to bump
316
+ the version by minor, you can use `tox -e bumpversion -- minor` after.
317
+
318
+ #### Releasing on GitHub
319
+
320
+ 1. Navigate to https://github.com/cthoyt/figshare-client/releases/new to draft a
321
+ new release
322
+ 2. Click the "Choose a Tag" dropdown and select the tag corresponding to the
323
+ release you just made
324
+ 3. Click the "Generate Release Notes" button to get a quick outline of recent
325
+ changes. Modify the title and description as you see fit
326
+ 4. Click the big green "Publish Release" button
327
+
328
+ This will trigger Zenodo to assign a DOI to your release as well.
329
+
330
+ ### Updating Package Boilerplate
331
+
332
+ This project uses `cruft` to keep boilerplate (i.e., configuration, contribution
333
+ guidelines, documentation configuration) up-to-date with the upstream
334
+ cookiecutter package. Install cruft with either `uv tool install cruft` or
335
+ `python3 -m pip install cruft` then run:
336
+
337
+ ```console
338
+ $ cruft update
339
+ ```
340
+
341
+ More info on Cruft's update command is available
342
+ [here](https://github.com/cruft/cruft?tab=readme-ov-file#updating-a-project).
343
+
344
+ </details>
@@ -0,0 +1,260 @@
1
+ [build-system]
2
+ requires = ["uv_build>=0.9.6,<1.0"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "figshare_client"
7
+ version = "0.0.1"
8
+ description = "A client to Figshare"
9
+ readme = "README.md"
10
+ authors = [
11
+ { name = "Charles Tapley Hoyt", email = "cthoyt@gmail.com" }
12
+ ]
13
+ maintainers = [
14
+ { name = "Charles Tapley Hoyt", email = "cthoyt@gmail.com" }
15
+ ]
16
+
17
+ # See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#classifiers
18
+ # Search tags using the controlled vocabulary at https://pypi.org/classifiers
19
+ classifiers = [
20
+ "Development Status :: 1 - Planning",
21
+ "Environment :: Console",
22
+ "Intended Audience :: Developers",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Operating System :: OS Independent",
25
+ "Framework :: Pytest",
26
+ "Framework :: tox",
27
+ "Framework :: Sphinx",
28
+ "Natural Language :: English",
29
+ "Programming Language :: Python",
30
+ "Programming Language :: Python :: 3.10",
31
+ "Programming Language :: Python :: 3.11",
32
+ "Programming Language :: Python :: 3.12",
33
+ "Programming Language :: Python :: 3.13",
34
+ "Programming Language :: Python :: 3.14",
35
+ "Programming Language :: Python :: 3 :: Only",
36
+ "Typing :: Typed",
37
+ # TODO add your topics from the Trove controlled vocabulary (see https://pypi.org/classifiers)
38
+ ]
39
+ keywords = [
40
+ "snekpack", # please keep this keyword to credit the cookiecutter-snekpack template
41
+ "cookiecutter",
42
+ "figshare",
43
+ ]
44
+
45
+ # License Information.
46
+ # See PEP-639 at https://peps.python.org/pep-0639/#add-license-files-key
47
+ license-files = [
48
+ "LICENSE",
49
+ ]
50
+
51
+ requires-python = ">=3.10"
52
+ dependencies = [
53
+ "pydantic",
54
+ "requests",
55
+ "pystow",
56
+ ]
57
+
58
+ # see https://peps.python.org/pep-0735/ and https://docs.astral.sh/uv/concepts/dependencies/#dependency-groups
59
+ [dependency-groups]
60
+ tests = [
61
+ "pytest",
62
+ "coverage[toml]",
63
+ ]
64
+ docs = [
65
+ "sphinx>=8,<9",
66
+ "sphinx-rtd-theme>=3.0",
67
+ "sphinx-click",
68
+ "sphinx_automodapi",
69
+ "autodoc_pydantic",
70
+ ]
71
+ lint = [
72
+ "ruff",
73
+ ]
74
+ typing = [
75
+ { include-group = "tests" },
76
+ "mypy",
77
+ "pydantic",
78
+ "types-click",
79
+ "types-requests",
80
+ ]
81
+ docs-lint = [
82
+ { include-group = "docs" },
83
+ "doc8",
84
+ ]
85
+ format-docs = [
86
+ { include-group = "docs" },
87
+ "docstrfmt",
88
+ ]
89
+ doctests = [
90
+ "xdoctest",
91
+ "pygments",
92
+ ]
93
+ pyroma = [
94
+ "pyroma",
95
+ "pygments",
96
+ ]
97
+
98
+ # see https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#dependencies-optional-dependencies
99
+ [project.optional-dependencies]
100
+
101
+
102
+ # See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#urls
103
+ # and also https://packaging.python.org/en/latest/specifications/well-known-project-urls/
104
+ [project.urls]
105
+ "Bug Tracker" = "https://github.com/cthoyt/figshare-client/issues"
106
+ Homepage = "https://github.com/cthoyt/figshare-client"
107
+ Repository = "https://github.com/cthoyt/figshare-client.git"
108
+ Documentation = "https://figshare_client.readthedocs.io"
109
+ Funding = "https://github.com/sponsors/cthoyt"
110
+
111
+ [project.scripts]
112
+ figshare_client = "figshare_client.cli:main"
113
+
114
+ [tool.cruft]
115
+ skip = [
116
+ "**/__init__.py",
117
+ "tests/*"
118
+ ]
119
+
120
+ # MyPy, see https://mypy.readthedocs.io/en/stable/config_file.html
121
+ [tool.mypy]
122
+ plugins = [
123
+ "pydantic.mypy",
124
+ ]
125
+
126
+ # Doc8, see https://doc8.readthedocs.io/en/stable/readme.html#ini-file-usage
127
+ [tool.doc8]
128
+ max-line-length = 120
129
+
130
+ # Pytest, see https://docs.pytest.org/en/stable/reference/customize.html#pyproject-toml
131
+ [tool.pytest.ini_options]
132
+ markers = [
133
+ "slow: marks tests as slow (deselect with '-m \"not slow\"')",
134
+ ]
135
+
136
+ # Coverage, see https://coverage.readthedocs.io/en/latest/config.html
137
+ [tool.coverage.run]
138
+ branch = true
139
+ source = [
140
+ "figshare_client",
141
+ ]
142
+ omit = [
143
+ "tests/*",
144
+ "docs/*",
145
+ "src/figshare_client/version.py",
146
+ "src/figshare_client/__main__.py",
147
+ "src/figshare_client/cli.py",
148
+ ]
149
+
150
+ [tool.coverage.paths]
151
+ source = [
152
+ "src/figshare_client",
153
+ ".tox/*/lib/python*/site-packages/figshare_client",
154
+ ]
155
+
156
+ [tool.coverage.report]
157
+ show_missing = true
158
+ exclude_lines = [
159
+ "pragma: no cover",
160
+ "raise NotImplementedError",
161
+ "if __name__ == \"__main__\":",
162
+ "if TYPE_CHECKING:",
163
+ "def __str__",
164
+ "def __repr__",
165
+ ]
166
+
167
+ [tool.ruff]
168
+ line-length = 100
169
+ extend-include = ["*.ipynb"]
170
+
171
+ [tool.ruff.lint]
172
+ # See https://docs.astral.sh/ruff/rules
173
+ extend-select = [
174
+ "F", # pyflakes
175
+ "E", # pycodestyle errors
176
+ "W", # pycodestyle warnings
177
+ "C90", # mccabe
178
+ "I", # isort
179
+ "UP", # pyupgrade
180
+ "D", # pydocstyle
181
+ "DOC", # pydoclint
182
+ "B", # bugbear
183
+ "S", # bandit
184
+ "T20", # print
185
+ "N", # pep8 naming
186
+ "ERA", # eradicate commented out code
187
+ "NPY", # numpy checks
188
+ "RUF", # ruff rules
189
+ "C4", # comprehensions
190
+ ]
191
+ ignore = [
192
+ "D105", # Missing docstring in magic method
193
+ "E203", # Black conflicts with the following
194
+ ]
195
+
196
+ # See https://docs.astral.sh/ruff/settings/#per-file-ignores
197
+ [tool.ruff.lint.per-file-ignores]
198
+ # Ignore security issues in the version.py, which are inconsistent
199
+ "src/figshare_client/version.py" = ["S603", "S607"]
200
+ # Ignore commented out code in Sphinx configuration file
201
+ "docs/source/conf.py" = ["ERA001"]
202
+ # Prints are okay in notebooks
203
+ "notebooks/**/*.ipynb" = ["T201"]
204
+
205
+ [tool.ruff.lint.pydocstyle]
206
+ convention = "pep257"
207
+
208
+ [tool.ruff.lint.isort]
209
+ relative-imports-order = "closest-to-furthest"
210
+ known-third-party = [
211
+ "tqdm",
212
+ ]
213
+ known-first-party = [
214
+ "figshare_client",
215
+ "tests",
216
+ ]
217
+
218
+ [tool.ruff.format]
219
+ # see https://docs.astral.sh/ruff/settings/#format_docstring-code-format
220
+ docstring-code-format = true
221
+
222
+ [tool.bumpversion]
223
+ current_version = "0.0.1"
224
+ parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(?:-(?P<release>[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+(?P<build>[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?"
225
+ serialize = [
226
+ "{major}.{minor}.{patch}-{release}+{build}",
227
+ "{major}.{minor}.{patch}+{build}",
228
+ "{major}.{minor}.{patch}-{release}",
229
+ "{major}.{minor}.{patch}",
230
+ ]
231
+ commit = true
232
+ tag = false
233
+
234
+ [tool.bumpversion.parts.release]
235
+ optional_value = "production"
236
+ first_value = "dev"
237
+ values = [
238
+ "dev",
239
+ "production",
240
+ ]
241
+
242
+ [[tool.bumpversion.files]]
243
+ filename = "pyproject.toml"
244
+ search = "version = \"{current_version}\""
245
+ replace = "version = \"{new_version}\""
246
+
247
+ [[tool.bumpversion.files]]
248
+ filename = "docs/source/conf.py"
249
+ search = "release = \"{current_version}\""
250
+ replace = "release = \"{new_version}\""
251
+
252
+ [[tool.bumpversion.files]]
253
+ filename = "src/figshare_client/version.py"
254
+ search = "VERSION = \"{current_version}\""
255
+ replace = "VERSION = \"{new_version}\""
256
+
257
+ [[tool.bumpversion.files]]
258
+ filename = "CITATION.cff"
259
+ search = "version: {current_version}"
260
+ replace = "version: {new_version}"
@@ -0,0 +1,9 @@
1
+ """A client to Figshare."""
2
+
3
+ from .api import File, ensure_files, get_files
4
+
5
+ __all__ = [
6
+ "File",
7
+ "ensure_files",
8
+ "get_files",
9
+ ]
@@ -0,0 +1,6 @@
1
+ """Entrypoint module, in case you use `python -m figshare_client`."""
2
+
3
+ from .cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,53 @@
1
+ """Interact with the Figshare API."""
2
+
3
+ from pathlib import Path
4
+
5
+ import pystow
6
+ import requests
7
+ from pydantic import BaseModel, ByteSize, HttpUrl
8
+ from tqdm.contrib import tmap
9
+ from tqdm.contrib.concurrent import thread_map
10
+
11
+ __all__ = [
12
+ "File",
13
+ "ensure_files",
14
+ "get_files",
15
+ ]
16
+
17
+ BASE_URL = "https://api.figshare.com/v2/articles"
18
+ MODULE = pystow.module("figshare")
19
+
20
+
21
+ class File(BaseModel):
22
+ """An object representing a file in Figshare."""
23
+
24
+ id: int
25
+ name: str
26
+ size: ByteSize
27
+ is_link_only: bool
28
+ download_url: HttpUrl
29
+ supplied_md5: str | None = None
30
+ computed_md5: str | None = None
31
+ mimetype: str | None = None
32
+
33
+
34
+ def get_files(record_id: int) -> list[File]:
35
+ """Get files for a record."""
36
+ url = f"{BASE_URL}/{record_id}/files"
37
+ res = requests.get(url, timeout=5)
38
+ res.raise_for_status()
39
+ return [File.model_validate(f) for f in res.json()]
40
+
41
+
42
+ def ensure_files(record_id: int, *, concurrent: bool = True) -> dict[Path, File]:
43
+ """Ensure all files for a record."""
44
+ files = get_files(record_id)
45
+ submodule = MODULE.module(str(record_id))
46
+
47
+ def _func(file: File) -> tuple[Path, File]:
48
+ return submodule.ensure(url=str(file.download_url)), file
49
+
50
+ if concurrent:
51
+ return dict(thread_map(_func, files))
52
+ else:
53
+ return dict(tmap(_func, files))
@@ -0,0 +1,16 @@
1
+ """Command line interface for :mod:`figshare_client`."""
2
+
3
+ import click
4
+
5
+ __all__ = [
6
+ "main",
7
+ ]
8
+
9
+
10
+ @click.command()
11
+ def main() -> None:
12
+ """CLI for figshare_client."""
13
+
14
+
15
+ if __name__ == "__main__":
16
+ main()
@@ -0,0 +1,39 @@
1
+ """Version information for :mod:`figshare_client`.
2
+
3
+ Run with ``python -m figshare_client.version``
4
+ """
5
+
6
+ import os
7
+ from subprocess import CalledProcessError, check_output
8
+
9
+ __all__ = [
10
+ "VERSION",
11
+ "get_git_hash",
12
+ "get_version",
13
+ ]
14
+
15
+ VERSION = "0.0.1"
16
+
17
+
18
+ def get_git_hash() -> str:
19
+ """Get the :mod:`figshare_client` git hash."""
20
+ with open(os.devnull, "w") as devnull:
21
+ try:
22
+ ret = check_output(
23
+ ["git", "rev-parse", "HEAD"],
24
+ cwd=os.path.dirname(__file__),
25
+ stderr=devnull,
26
+ )
27
+ except CalledProcessError:
28
+ return "UNHASHED"
29
+ else:
30
+ return ret.strip().decode("utf-8")[:8]
31
+
32
+
33
+ def get_version(with_git_hash: bool = False) -> str:
34
+ """Get the :mod:`figshare_client` version string, including a git hash."""
35
+ return f"{VERSION}-{get_git_hash()}" if with_git_hash else VERSION
36
+
37
+
38
+ if __name__ == "__main__":
39
+ print(get_version(with_git_hash=True)) # noqa:T201