pmcfetch 0.1.1__tar.gz → 0.2.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.
- {pmcfetch-0.1.1/src/pmcfetch.egg-info → pmcfetch-0.2.1}/PKG-INFO +83 -11
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/README.md +81 -9
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/pyproject.toml +1 -1
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch/__init__.py +12 -3
- pmcfetch-0.2.1/src/pmcfetch/cli.py +420 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch/errors.py +5 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch/manifest.py +10 -0
- pmcfetch-0.2.1/src/pmcfetch/search.py +249 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1/src/pmcfetch.egg-info}/PKG-INFO +83 -11
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch.egg-info/SOURCES.txt +3 -1
- pmcfetch-0.2.1/tests/conftest.py +93 -0
- pmcfetch-0.2.1/tests/test_cli.py +278 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/tests/test_manifest.py +13 -1
- pmcfetch-0.2.1/tests/test_search.py +161 -0
- pmcfetch-0.1.1/src/pmcfetch/cli.py +0 -228
- pmcfetch-0.1.1/tests/conftest.py +0 -44
- pmcfetch-0.1.1/tests/test_cli.py +0 -112
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/LICENSE +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/MANIFEST.in +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/setup.cfg +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch/batch.py +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch/ids.py +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch/pdf.py +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch/py.typed +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch/ratelimit.py +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch/s3source.py +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch.egg-info/dependency_links.txt +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch.egg-info/entry_points.txt +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch.egg-info/requires.txt +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/src/pmcfetch.egg-info/top_level.txt +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/tests/test_batch.py +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/tests/test_ids.py +0 -0
- {pmcfetch-0.1.1 → pmcfetch-0.2.1}/tests/test_s3source.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pmcfetch
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Search the PMC Open Access Subset by keyword and download article PDFs from AWS. Zero dependencies.
|
|
5
5
|
Author: Kavach Shah
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/kms2229/pmcfetch
|
|
@@ -32,15 +32,40 @@ Dynamic: license-file
|
|
|
32
32
|
|
|
33
33
|
# pmcfetch
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
Download open-access research papers as PDFs from **PubMed Central**, straight
|
|
36
|
+
from the PMC Open Access Subset on AWS, with resume, retries and atomic writes.
|
|
37
|
+
Requires Python 3.10+, and nothing outside the standard library.
|
|
37
38
|
|
|
38
39
|
```bash
|
|
39
40
|
pip install pmcfetch
|
|
40
|
-
pmcfetch download --corpus corpus.json --out pdfs/ --workers 16 --rps 0
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
## What it does
|
|
44
|
+
|
|
45
|
+
**1. Download papers by id.** You already know which papers you want: list their
|
|
46
|
+
PMC ids in a text file, one per line (like `PMC10007932`).
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pmcfetch download --corpus ids.txt --out pdfs/
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**2. Search by keyword.** See how many open-access papers match before
|
|
53
|
+
downloading anything.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pmcfetch search "burn scar"
|
|
57
|
+
# [pmcfetch] 20,349 open-access article(s) match 'burn scar'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**3. Download the matches.** Take all of them, or only as many as you want.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pmcfetch search "burn scar" --download 50 # the 50 most relevant
|
|
64
|
+
pmcfetch search "burn scar" --download all # every match
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
In a terminal, `pmcfetch search` asks how many to download right after showing
|
|
68
|
+
the count, so steps 2 and 3 can happen in one go.
|
|
44
69
|
|
|
45
70
|
## Why
|
|
46
71
|
|
|
@@ -97,14 +122,39 @@ with S3OpenAccessClient() as c:
|
|
|
97
122
|
`license_code` and `is_retracted` come free with every article — filter on licence
|
|
98
123
|
terms and drop retracted papers before they reach your corpus.
|
|
99
124
|
|
|
125
|
+
Keyword search — check the count first, then take as many as you want:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from pmcfetch import PMCSearch, S3OpenAccessClient, download_many
|
|
129
|
+
|
|
130
|
+
search = PMCSearch() # PMCSearch(api_key=...) for NCBI's higher rate limit
|
|
131
|
+
summary = search.summarize("burn scar AND 2020:2025[pdat]")
|
|
132
|
+
print(summary.count, summary.warnings) # 12013 ()
|
|
133
|
+
|
|
134
|
+
records = search.records(summary.query, limit=100, namespace="burns") # limit=None for all
|
|
135
|
+
with S3OpenAccessClient() as client:
|
|
136
|
+
report = download_many(records, "pdfs/", client=client, workers=16)
|
|
137
|
+
```
|
|
138
|
+
|
|
100
139
|
## CLI
|
|
101
140
|
|
|
102
141
|
```bash
|
|
142
|
+
# how many open-access papers match? (in a terminal, it then asks how many to download)
|
|
143
|
+
pmcfetch search "wound healing AND diabetic foot"
|
|
144
|
+
|
|
145
|
+
# download some or all of them, without the question
|
|
146
|
+
pmcfetch search "burn scar" --download 100 --out pdfs/
|
|
147
|
+
pmcfetch search "burn scar" --download all --namespace burns
|
|
148
|
+
|
|
149
|
+
# or save every match now and download later, e.g. in an HPC array job
|
|
150
|
+
pmcfetch search "burn scar" --namespace burns --save burns.json
|
|
151
|
+
pmcfetch download --corpus burns.json --out pdfs/
|
|
152
|
+
|
|
103
153
|
# what's in a corpus file?
|
|
104
154
|
pmcfetch namespaces --corpus corpus.json
|
|
105
155
|
|
|
106
156
|
# pull one namespace
|
|
107
|
-
pmcfetch download --corpus corpus.json --out pdfs/ --namespace cardiology --workers 16
|
|
157
|
+
pmcfetch download --corpus corpus.json --out pdfs/ --namespace cardiology --workers 16
|
|
108
158
|
|
|
109
159
|
# dry run, no requests
|
|
110
160
|
pmcfetch download --corpus corpus.json --out pdfs/ --limit 20 --dry-run
|
|
@@ -115,10 +165,19 @@ pmcfetch download --corpus pdfs/failed.txt --out pdfs/ --flat
|
|
|
115
165
|
|
|
116
166
|
Exit code is `1` when any article failed, `0` otherwise — so a batch job fails
|
|
117
167
|
loudly instead of silently producing a short corpus. Bad arguments or an
|
|
118
|
-
unreadable corpus file exit `2`, and Ctrl-C exits `130`.
|
|
168
|
+
unreadable corpus file exit `2`, and Ctrl-C exits `130`. With `--json`, stdout
|
|
169
|
+
carries only the JSON summary and progress goes to stderr, so it pipes cleanly.
|
|
170
|
+
|
|
171
|
+
S3 has no rate limit, so downloads are not paced by default and `--workers` can
|
|
172
|
+
go wide; throughput is bound by your bandwidth. `--rps N` caps requests per
|
|
173
|
+
second across all workers if you want to go easier on a shared link.
|
|
119
174
|
|
|
120
|
-
|
|
121
|
-
|
|
175
|
+
`search` takes PMC's search syntax: field tags (`smith j[author]`), date ranges
|
|
176
|
+
(`2020:2025[pdat]`), `AND`/`OR`/`NOT`. It prints how NCBI read the query and warns
|
|
177
|
+
about words it dropped. `--sort relevance` (the default) or `--sort newest` picks
|
|
178
|
+
which papers come first when you take fewer than all. `--save` writes the chosen
|
|
179
|
+
ids to a corpus file that `pmcfetch download` reads; without `--download`, it
|
|
180
|
+
saves every match and downloads none.
|
|
122
181
|
|
|
123
182
|
## Deployment
|
|
124
183
|
|
|
@@ -166,6 +225,18 @@ bad key can't abort a 20k-article batch.
|
|
|
166
225
|
raise `ArticleNotInOASubsetError`. Both land in the failure log with a reason.
|
|
167
226
|
- **Any object implementing `fetch_pdf(pmcid) -> bytes`** satisfies the
|
|
168
227
|
`PdfSource` protocol and works with `download_many`.
|
|
228
|
+
- **Search counts only open-access articles** (`"open access"[filter]`), the
|
|
229
|
+
ones the bucket holds. In a sample of 800 matches, 98% had an article PDF; the
|
|
230
|
+
rest are licensed for XML and text only, and fail with `PDFNotAvailableError`
|
|
231
|
+
like any other run.
|
|
232
|
+
- **Search has no 10,000 cap.** NCBI returns at most 9,999 ids per query, so
|
|
233
|
+
larger result sets are collected by splitting the query into PMC id ranges.
|
|
234
|
+
NCBI ranks only the first 9,999 by relevance; the rest follow newest first.
|
|
235
|
+
- **Search goes through NCBI E-utilities**, which allow 3 requests per second,
|
|
236
|
+
or 10 with an NCBI API key from your
|
|
237
|
+
[NCBI account settings](https://account.ncbi.nlm.nih.gov/settings/)
|
|
238
|
+
(`--api-key`, or `$NCBI_API_KEY`). Collecting 20,000 ids takes about 15 seconds.
|
|
239
|
+
NCBI's count can drift by a few between calls.
|
|
169
240
|
|
|
170
241
|
## Tests
|
|
171
242
|
|
|
@@ -177,7 +248,8 @@ python -m pytest tests/ -q
|
|
|
177
248
|
No network required — the S3 layer is exercised against scripted listings and
|
|
178
249
|
responses: multi-version and paginated listings, supplementary-PDF
|
|
179
250
|
disambiguation, 429 storms and `Retry-After`, 404s, truncated resumes, and
|
|
180
|
-
concurrent and interrupted batches.
|
|
251
|
+
concurrent and interrupted batches. Search runs against a scripted ESearch that
|
|
252
|
+
enforces NCBI's per-query cap, so the id-range splitting is tested too.
|
|
181
253
|
|
|
182
254
|
## Attribution
|
|
183
255
|
|
|
@@ -1,14 +1,39 @@
|
|
|
1
1
|
# pmcfetch
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Download open-access research papers as PDFs from **PubMed Central**, straight
|
|
4
|
+
from the PMC Open Access Subset on AWS, with resume, retries and atomic writes.
|
|
5
|
+
Requires Python 3.10+, and nothing outside the standard library.
|
|
5
6
|
|
|
6
7
|
```bash
|
|
7
8
|
pip install pmcfetch
|
|
8
|
-
pmcfetch download --corpus corpus.json --out pdfs/ --workers 16 --rps 0
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## What it does
|
|
12
|
+
|
|
13
|
+
**1. Download papers by id.** You already know which papers you want: list their
|
|
14
|
+
PMC ids in a text file, one per line (like `PMC10007932`).
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pmcfetch download --corpus ids.txt --out pdfs/
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**2. Search by keyword.** See how many open-access papers match before
|
|
21
|
+
downloading anything.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pmcfetch search "burn scar"
|
|
25
|
+
# [pmcfetch] 20,349 open-access article(s) match 'burn scar'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**3. Download the matches.** Take all of them, or only as many as you want.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pmcfetch search "burn scar" --download 50 # the 50 most relevant
|
|
32
|
+
pmcfetch search "burn scar" --download all # every match
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
In a terminal, `pmcfetch search` asks how many to download right after showing
|
|
36
|
+
the count, so steps 2 and 3 can happen in one go.
|
|
12
37
|
|
|
13
38
|
## Why
|
|
14
39
|
|
|
@@ -65,14 +90,39 @@ with S3OpenAccessClient() as c:
|
|
|
65
90
|
`license_code` and `is_retracted` come free with every article — filter on licence
|
|
66
91
|
terms and drop retracted papers before they reach your corpus.
|
|
67
92
|
|
|
93
|
+
Keyword search — check the count first, then take as many as you want:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from pmcfetch import PMCSearch, S3OpenAccessClient, download_many
|
|
97
|
+
|
|
98
|
+
search = PMCSearch() # PMCSearch(api_key=...) for NCBI's higher rate limit
|
|
99
|
+
summary = search.summarize("burn scar AND 2020:2025[pdat]")
|
|
100
|
+
print(summary.count, summary.warnings) # 12013 ()
|
|
101
|
+
|
|
102
|
+
records = search.records(summary.query, limit=100, namespace="burns") # limit=None for all
|
|
103
|
+
with S3OpenAccessClient() as client:
|
|
104
|
+
report = download_many(records, "pdfs/", client=client, workers=16)
|
|
105
|
+
```
|
|
106
|
+
|
|
68
107
|
## CLI
|
|
69
108
|
|
|
70
109
|
```bash
|
|
110
|
+
# how many open-access papers match? (in a terminal, it then asks how many to download)
|
|
111
|
+
pmcfetch search "wound healing AND diabetic foot"
|
|
112
|
+
|
|
113
|
+
# download some or all of them, without the question
|
|
114
|
+
pmcfetch search "burn scar" --download 100 --out pdfs/
|
|
115
|
+
pmcfetch search "burn scar" --download all --namespace burns
|
|
116
|
+
|
|
117
|
+
# or save every match now and download later, e.g. in an HPC array job
|
|
118
|
+
pmcfetch search "burn scar" --namespace burns --save burns.json
|
|
119
|
+
pmcfetch download --corpus burns.json --out pdfs/
|
|
120
|
+
|
|
71
121
|
# what's in a corpus file?
|
|
72
122
|
pmcfetch namespaces --corpus corpus.json
|
|
73
123
|
|
|
74
124
|
# pull one namespace
|
|
75
|
-
pmcfetch download --corpus corpus.json --out pdfs/ --namespace cardiology --workers 16
|
|
125
|
+
pmcfetch download --corpus corpus.json --out pdfs/ --namespace cardiology --workers 16
|
|
76
126
|
|
|
77
127
|
# dry run, no requests
|
|
78
128
|
pmcfetch download --corpus corpus.json --out pdfs/ --limit 20 --dry-run
|
|
@@ -83,10 +133,19 @@ pmcfetch download --corpus pdfs/failed.txt --out pdfs/ --flat
|
|
|
83
133
|
|
|
84
134
|
Exit code is `1` when any article failed, `0` otherwise — so a batch job fails
|
|
85
135
|
loudly instead of silently producing a short corpus. Bad arguments or an
|
|
86
|
-
unreadable corpus file exit `2`, and Ctrl-C exits `130`.
|
|
136
|
+
unreadable corpus file exit `2`, and Ctrl-C exits `130`. With `--json`, stdout
|
|
137
|
+
carries only the JSON summary and progress goes to stderr, so it pipes cleanly.
|
|
138
|
+
|
|
139
|
+
S3 has no rate limit, so downloads are not paced by default and `--workers` can
|
|
140
|
+
go wide; throughput is bound by your bandwidth. `--rps N` caps requests per
|
|
141
|
+
second across all workers if you want to go easier on a shared link.
|
|
87
142
|
|
|
88
|
-
|
|
89
|
-
|
|
143
|
+
`search` takes PMC's search syntax: field tags (`smith j[author]`), date ranges
|
|
144
|
+
(`2020:2025[pdat]`), `AND`/`OR`/`NOT`. It prints how NCBI read the query and warns
|
|
145
|
+
about words it dropped. `--sort relevance` (the default) or `--sort newest` picks
|
|
146
|
+
which papers come first when you take fewer than all. `--save` writes the chosen
|
|
147
|
+
ids to a corpus file that `pmcfetch download` reads; without `--download`, it
|
|
148
|
+
saves every match and downloads none.
|
|
90
149
|
|
|
91
150
|
## Deployment
|
|
92
151
|
|
|
@@ -134,6 +193,18 @@ bad key can't abort a 20k-article batch.
|
|
|
134
193
|
raise `ArticleNotInOASubsetError`. Both land in the failure log with a reason.
|
|
135
194
|
- **Any object implementing `fetch_pdf(pmcid) -> bytes`** satisfies the
|
|
136
195
|
`PdfSource` protocol and works with `download_many`.
|
|
196
|
+
- **Search counts only open-access articles** (`"open access"[filter]`), the
|
|
197
|
+
ones the bucket holds. In a sample of 800 matches, 98% had an article PDF; the
|
|
198
|
+
rest are licensed for XML and text only, and fail with `PDFNotAvailableError`
|
|
199
|
+
like any other run.
|
|
200
|
+
- **Search has no 10,000 cap.** NCBI returns at most 9,999 ids per query, so
|
|
201
|
+
larger result sets are collected by splitting the query into PMC id ranges.
|
|
202
|
+
NCBI ranks only the first 9,999 by relevance; the rest follow newest first.
|
|
203
|
+
- **Search goes through NCBI E-utilities**, which allow 3 requests per second,
|
|
204
|
+
or 10 with an NCBI API key from your
|
|
205
|
+
[NCBI account settings](https://account.ncbi.nlm.nih.gov/settings/)
|
|
206
|
+
(`--api-key`, or `$NCBI_API_KEY`). Collecting 20,000 ids takes about 15 seconds.
|
|
207
|
+
NCBI's count can drift by a few between calls.
|
|
137
208
|
|
|
138
209
|
## Tests
|
|
139
210
|
|
|
@@ -145,7 +216,8 @@ python -m pytest tests/ -q
|
|
|
145
216
|
No network required — the S3 layer is exercised against scripted listings and
|
|
146
217
|
responses: multi-version and paginated listings, supplementary-PDF
|
|
147
218
|
disambiguation, 429 storms and `Retry-After`, 404s, truncated resumes, and
|
|
148
|
-
concurrent and interrupted batches.
|
|
219
|
+
concurrent and interrupted batches. Search runs against a scripted ESearch that
|
|
220
|
+
enforces NCBI's per-query cap, so the id-range splitting is tested too.
|
|
149
221
|
|
|
150
222
|
## Attribution
|
|
151
223
|
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pmcfetch"
|
|
7
7
|
dynamic = ["version"]
|
|
8
|
-
description = "
|
|
8
|
+
description = "Search the PMC Open Access Subset by keyword and download article PDFs from AWS. Zero dependencies."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
license-files = ["LICENSE"]
|
|
@@ -8,7 +8,9 @@ and it is the access route PMC sanctions for systematic retrieval.
|
|
|
8
8
|
care about. This package fills that gap: give it a list of PMC ids and it
|
|
9
9
|
resolves each article's version, picks the article PDF (not the publisher's
|
|
10
10
|
supplementary PDFs sitting in the same prefix), and writes the results with
|
|
11
|
-
resume, retries, atomic writes, and per-namespace layout.
|
|
11
|
+
resume, retries, atomic writes, and per-namespace layout. Or start from a
|
|
12
|
+
keyword search: :class:`PMCSearch` counts the matching open-access articles
|
|
13
|
+
before anything is downloaded.
|
|
12
14
|
|
|
13
15
|
Zero third-party dependencies.
|
|
14
16
|
|
|
@@ -22,7 +24,7 @@ Zero third-party dependencies.
|
|
|
22
24
|
|
|
23
25
|
from __future__ import annotations
|
|
24
26
|
|
|
25
|
-
__version__ = "0.
|
|
27
|
+
__version__ = "0.2.1"
|
|
26
28
|
|
|
27
29
|
from pmcfetch.batch import (
|
|
28
30
|
BatchReport,
|
|
@@ -40,10 +42,11 @@ from pmcfetch.errors import (
|
|
|
40
42
|
InvalidPMCIDError,
|
|
41
43
|
NotAPDFError,
|
|
42
44
|
PMCFetchError,
|
|
45
|
+
SearchError,
|
|
43
46
|
TransportError,
|
|
44
47
|
)
|
|
45
48
|
from pmcfetch.ids import normalize_pmcid, try_normalize_pmcid
|
|
46
|
-
from pmcfetch.manifest import CorpusRecord, LoadResult, iter_records, load_records
|
|
49
|
+
from pmcfetch.manifest import CorpusRecord, LoadResult, iter_records, load_records, save_records
|
|
47
50
|
from pmcfetch.pdf import DEFAULT_MIN_PDF_BYTES, DEFAULT_TIMEOUT, PdfSource, is_pdf
|
|
48
51
|
from pmcfetch.ratelimit import MinIntervalLimiter, NullLimiter
|
|
49
52
|
from pmcfetch.s3source import (
|
|
@@ -56,9 +59,13 @@ from pmcfetch.s3source import (
|
|
|
56
59
|
list_article_versions,
|
|
57
60
|
resolve_latest_version,
|
|
58
61
|
)
|
|
62
|
+
from pmcfetch.search import PMCSearch, SearchSummary
|
|
59
63
|
|
|
60
64
|
__all__ = [
|
|
61
65
|
"__version__",
|
|
66
|
+
# search
|
|
67
|
+
"PMCSearch",
|
|
68
|
+
"SearchSummary",
|
|
62
69
|
# source
|
|
63
70
|
"S3OpenAccessClient",
|
|
64
71
|
"OA_BUCKET",
|
|
@@ -77,6 +84,7 @@ __all__ = [
|
|
|
77
84
|
"write_pdf_atomic",
|
|
78
85
|
# manifest
|
|
79
86
|
"load_records",
|
|
87
|
+
"save_records",
|
|
80
88
|
"iter_records",
|
|
81
89
|
"CorpusRecord",
|
|
82
90
|
"LoadResult",
|
|
@@ -99,4 +107,5 @@ __all__ = [
|
|
|
99
107
|
"ArticleNotInOASubsetError",
|
|
100
108
|
"InvalidPMCIDError",
|
|
101
109
|
"TransportError",
|
|
110
|
+
"SearchError",
|
|
102
111
|
]
|