papers-cli 2.15.4__tar.gz → 2.15.5__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.
- {papers_cli-2.15.4 → papers_cli-2.15.5}/PKG-INFO +1 -1
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/_version.py +3 -3
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/extract.py +89 -15
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers_cli.egg-info/PKG-INFO +1 -1
- {papers_cli-2.15.4 → papers_cli-2.15.5}/.gitattributes +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/.github/FUNDING.yml +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/.github/workflows/pypi.yml +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/.github/workflows/tests.yml +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/.gitignore +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/LICENSE +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/README.md +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/__init__.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/__main__.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/bib.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/config.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/duplicate.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/encoding.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/filename.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/latexenc.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/utils.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers_cli.egg-info/SOURCES.txt +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers_cli.egg-info/dependency_links.txt +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers_cli.egg-info/entry_points.txt +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers_cli.egg-info/requires.txt +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/papers_cli.egg-info/top_level.txt +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/pyproject.toml +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/requirements.txt +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/setup.cfg +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/setup.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/__init__.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/common.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/config.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/download.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/downloadedpapers/.gitignore +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/downloadedpapers/bg-8-515-2011.txt +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_add.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_biblio.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_duplicates.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_encoding.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_extract.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_filecheck.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_install.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_list.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_misc.py +0 -0
- {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_undo.py +0 -0
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '2.15.
|
|
32
|
-
__version_tuple__ = version_tuple = (2, 15,
|
|
31
|
+
__version__ = version = '2.15.5'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 15, 5)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'gbad573f0d'
|
|
@@ -175,30 +175,106 @@ def parse_doi_from_pdf_metadata(pdf_path):
|
|
|
175
175
|
return parse_doi_from_pdf_metadata_poppler(pdf_path)
|
|
176
176
|
|
|
177
177
|
def parse_doi(txt):
|
|
178
|
-
#
|
|
179
|
-
#
|
|
180
|
-
#
|
|
181
|
-
txt_clean = txt.replace('\u200b', '').replace('\xad', '')
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
178
|
+
# Remove invisible Unicode characters and normalize dashes and slashes
|
|
179
|
+
# U+200B: zero-width space, U+00AD: soft hyphen, U+2013: en-dash, U+2014: em-dash
|
|
180
|
+
# \x02: STX (Start of Text) sometimes used instead of slash in PDFs
|
|
181
|
+
txt_clean = txt.replace('\u200b', '').replace('\xad', '').replace('\u2013', '-').replace('\u2014', '-').replace('\x02', '/')
|
|
182
|
+
txt_lower = txt_clean.lower()
|
|
183
|
+
|
|
184
|
+
# Handle DOI split at "10.\n<registrant>" (e.g., "10.\n1073/pnas.123")
|
|
185
|
+
# Requires 4-9 digits (valid registrant) followed by slash
|
|
186
|
+
txt_lower = re.sub(r'(10\.)\s*\n\s*(\d{4,9}/)', r'\1\2', txt_lower)
|
|
187
|
+
|
|
188
|
+
# Special case: PNAS DOIs may be split at "10.1073/pnas. \n<digits>"
|
|
189
|
+
# This is a very specific pattern that's safe to join
|
|
190
|
+
txt_lower = re.sub(r'(10\.1073/pnas\.)\s*\n\s*(\d)', r'\1\2', txt_lower)
|
|
191
|
+
|
|
192
|
+
# Handle DOIs split at dash-newline-alphanumeric (common in many publishers)
|
|
193
|
+
# e.g., "10.1175/jcli-d-21-\n0636.1", "10.1175/JCLI-\nD-17-0112.s1"
|
|
194
|
+
txt_lower = re.sub(r'(10\.\d{4,9}/[-._;()/:a-z0-9]+-)\s*\n\s*([a-z0-9])', r'\1\2', txt_lower)
|
|
195
|
+
|
|
196
|
+
# Don't join across newlines generally - it causes more problems than it solves
|
|
197
|
+
# The newline naturally stops incorrect matches from extending into following text
|
|
198
|
+
matches = REGEXP.findall(txt_lower)
|
|
185
199
|
|
|
186
200
|
if not matches:
|
|
187
201
|
# Try arxiv pattern
|
|
188
|
-
match = ARXIV.search(
|
|
202
|
+
match = ARXIV.search(txt_lower)
|
|
189
203
|
if match:
|
|
190
204
|
return f"10.48550/arXiv.{match.group(1)}"
|
|
191
205
|
|
|
192
206
|
raise DOIParsingError('parse_doi::no matches')
|
|
193
207
|
|
|
194
|
-
#
|
|
208
|
+
# Start with first match as default
|
|
195
209
|
doi = matches[0]
|
|
196
210
|
|
|
197
|
-
#
|
|
198
|
-
# These
|
|
199
|
-
|
|
211
|
+
# Prefer DOIs that appear in full URL context (e.g., www.pnas.org/cgi/doi/10.1073/...)
|
|
212
|
+
# These are more likely to be the article's own DOI rather than citations
|
|
213
|
+
if len(matches) > 1:
|
|
214
|
+
url_patterns = [
|
|
215
|
+
r'www\.[a-z]+\.org/[a-z/]+/(10\.\d{4,9}/[-._;()/:a-z0-9]+)',
|
|
216
|
+
r'doi\.org/(10\.\d{4,9}/[-._;()/:a-z0-9]+)',
|
|
217
|
+
r'dx\.doi\.org/(10\.\d{4,9}/[-._;()/:a-z0-9]+)',
|
|
218
|
+
]
|
|
219
|
+
for pattern in url_patterns:
|
|
220
|
+
url_matches = re.findall(pattern, txt_lower)
|
|
221
|
+
# If any of our DOI matches appear in a URL, prefer the first one
|
|
222
|
+
for doi_match in matches:
|
|
223
|
+
if doi_match in url_matches:
|
|
224
|
+
doi = doi_match
|
|
225
|
+
break
|
|
226
|
+
if doi in url_matches: # Found a URL match, stop searching patterns
|
|
227
|
+
break
|
|
228
|
+
|
|
229
|
+
# If multiple matches and first is supplemental, prefer non-supplemental with same base
|
|
230
|
+
# e.g., prefer "10.1175/jcli-d-16-0271.1" over "10.1175/jcli-d-16-0271.s1"
|
|
231
|
+
if len(matches) > 1 and doi == matches[0]:
|
|
232
|
+
# Check if first match is supplemental (.s<digit>)
|
|
233
|
+
if re.search(r'\.s\d+\.?$', matches[0]):
|
|
234
|
+
# Look for non-supplemental version with same base
|
|
235
|
+
base = re.sub(r'\.s\d+\.?$', '', matches[0])
|
|
236
|
+
for m in matches[1:]:
|
|
237
|
+
if m.startswith(base) and not re.search(r'\.s\d+\.?$', m):
|
|
238
|
+
doi = m
|
|
239
|
+
break
|
|
240
|
+
|
|
241
|
+
# Remove non-DOI suffixes: known publisher paths and common junk
|
|
242
|
+
# Strategy: if we find these patterns, remove them AND everything after
|
|
243
|
+
suffixes = [
|
|
244
|
+
# Publisher-specific paths
|
|
245
|
+
'/-/dcsupplemental',
|
|
246
|
+
'/-/dc',
|
|
247
|
+
# Common file extension
|
|
248
|
+
'.pdf',
|
|
249
|
+
# Document status markers that clearly shouldn't be in DOI
|
|
250
|
+
'preprint',
|
|
251
|
+
'received',
|
|
252
|
+
'published',
|
|
253
|
+
'edited',
|
|
254
|
+
'advance',
|
|
255
|
+
'full',
|
|
256
|
+
'abstract',
|
|
257
|
+
'-supplement',
|
|
258
|
+
'supplement',
|
|
259
|
+
]
|
|
200
260
|
|
|
201
|
-
|
|
261
|
+
doi_lower = doi.lower()
|
|
262
|
+
for suffix in suffixes:
|
|
263
|
+
pos = doi_lower.find(suffix)
|
|
264
|
+
if pos > 0: # Found suffix (not at start)
|
|
265
|
+
# For word-only suffixes, ensure they follow a valid DOI character
|
|
266
|
+
if suffix.isalpha():
|
|
267
|
+
prev_char = doi[pos-1]
|
|
268
|
+
# Accept if preceded by alphanumeric, dash, underscore, slash, or dot
|
|
269
|
+
if prev_char.isalnum() or prev_char in '.-_/':
|
|
270
|
+
doi = doi[:pos]
|
|
271
|
+
break
|
|
272
|
+
else:
|
|
273
|
+
# Non-word suffixes (paths, extensions) - just cut
|
|
274
|
+
doi = doi[:pos]
|
|
275
|
+
break
|
|
276
|
+
|
|
277
|
+
# Clean up trailing periods and colons
|
|
202
278
|
doi = doi.rstrip('.:')
|
|
203
279
|
|
|
204
280
|
# Quality check
|
|
@@ -206,8 +282,6 @@ def parse_doi(txt):
|
|
|
206
282
|
raise DOIParsingError('failed to extract doi: '+doi)
|
|
207
283
|
|
|
208
284
|
return doi
|
|
209
|
-
|
|
210
|
-
|
|
211
285
|
def isvaliddoi(doi):
|
|
212
286
|
try:
|
|
213
287
|
doi2 = parse_doi('doi:'+doi)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|