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.
Files changed (45) hide show
  1. {papers_cli-2.15.4 → papers_cli-2.15.5}/PKG-INFO +1 -1
  2. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/_version.py +3 -3
  3. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/extract.py +89 -15
  4. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers_cli.egg-info/PKG-INFO +1 -1
  5. {papers_cli-2.15.4 → papers_cli-2.15.5}/.gitattributes +0 -0
  6. {papers_cli-2.15.4 → papers_cli-2.15.5}/.github/FUNDING.yml +0 -0
  7. {papers_cli-2.15.4 → papers_cli-2.15.5}/.github/workflows/pypi.yml +0 -0
  8. {papers_cli-2.15.4 → papers_cli-2.15.5}/.github/workflows/tests.yml +0 -0
  9. {papers_cli-2.15.4 → papers_cli-2.15.5}/.gitignore +0 -0
  10. {papers_cli-2.15.4 → papers_cli-2.15.5}/LICENSE +0 -0
  11. {papers_cli-2.15.4 → papers_cli-2.15.5}/README.md +0 -0
  12. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/__init__.py +0 -0
  13. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/__main__.py +0 -0
  14. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/bib.py +0 -0
  15. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/config.py +0 -0
  16. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/duplicate.py +0 -0
  17. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/encoding.py +0 -0
  18. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/filename.py +0 -0
  19. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/latexenc.py +0 -0
  20. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers/utils.py +0 -0
  21. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers_cli.egg-info/SOURCES.txt +0 -0
  22. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers_cli.egg-info/dependency_links.txt +0 -0
  23. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers_cli.egg-info/entry_points.txt +0 -0
  24. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers_cli.egg-info/requires.txt +0 -0
  25. {papers_cli-2.15.4 → papers_cli-2.15.5}/papers_cli.egg-info/top_level.txt +0 -0
  26. {papers_cli-2.15.4 → papers_cli-2.15.5}/pyproject.toml +0 -0
  27. {papers_cli-2.15.4 → papers_cli-2.15.5}/requirements.txt +0 -0
  28. {papers_cli-2.15.4 → papers_cli-2.15.5}/setup.cfg +0 -0
  29. {papers_cli-2.15.4 → papers_cli-2.15.5}/setup.py +0 -0
  30. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/__init__.py +0 -0
  31. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/common.py +0 -0
  32. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/config.py +0 -0
  33. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/download.py +0 -0
  34. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/downloadedpapers/.gitignore +0 -0
  35. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/downloadedpapers/bg-8-515-2011.txt +0 -0
  36. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_add.py +0 -0
  37. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_biblio.py +0 -0
  38. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_duplicates.py +0 -0
  39. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_encoding.py +0 -0
  40. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_extract.py +0 -0
  41. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_filecheck.py +0 -0
  42. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_install.py +0 -0
  43. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_list.py +0 -0
  44. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_misc.py +0 -0
  45. {papers_cli-2.15.4 → papers_cli-2.15.5}/tests/test_undo.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: papers-cli
3
- Version: 2.15.4
3
+ Version: 2.15.5
4
4
  Summary: utilities to keep your PDF library organized
5
5
  Author-email: Mahé Perrette <mahe.perrette@gmail.com>
6
6
  License: MIT
@@ -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.4'
32
- __version_tuple__ = version_tuple = (2, 15, 4)
31
+ __version__ = version = '2.15.5'
32
+ __version_tuple__ = version_tuple = (2, 15, 5)
33
33
 
34
- __commit_id__ = commit_id = 'g552f2d284'
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
- # PDF extraction sometimes splits DOIs across lines - join them back
179
- # e.g., "10.1073/pnas.\n2511370123" -> "10.1073/pnas.2511370123"
180
- # Also remove invisible Unicode characters that can appear in PDFs
181
- txt_clean = txt.replace('\u200b', '').replace('\xad', '') # zero-width space, soft hyphen
182
- # Handle both direct newlines and space+newline patterns
183
- txt_normalized = re.sub(r'([a-z0-9\.\-_/])\s*\n\s*([a-z0-9])', r'\1\2', txt_clean.lower())
184
- matches = REGEXP.findall(txt_normalized)
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(txt_normalized)
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
- # Use the first match (typically the article's own DOI, not a citation)
208
+ # Start with first match as default
195
209
  doi = matches[0]
196
210
 
197
- # Remove common non-DOI suffixes (may be chained)
198
- # These can include path segments, document sections, or text that got concatenated
199
- doi = re.sub(r'(\.?((/-/dcsupplemental)|(/-/dc)|received|published|\.pdf|\.full|\.abstract|reference:|www\.[a-z]+\.[a-z]+|[0-9]+\.introduction))+$', '', doi, flags=re.IGNORECASE)
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
- # Clean up trailing periods, colons
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: papers-cli
3
- Version: 2.15.4
3
+ Version: 2.15.5
4
4
  Summary: utilities to keep your PDF library organized
5
5
  Author-email: Mahé Perrette <mahe.perrette@gmail.com>
6
6
  License: MIT
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