grobid-client-python 0.2.1__py3-none-any.whl → 0.2.2__py3-none-any.whl

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.
@@ -34,7 +34,7 @@ import shutil
34
34
  import tarfile
35
35
  import tempfile
36
36
  import zipfile
37
- from typing import Any, BinaryIO, Callable, Optional, Tuple, Union
37
+ from typing import Any, BinaryIO, Callable, List, Optional, Tuple, Union
38
38
  import copy
39
39
 
40
40
  from .format.TEI2LossyJSON import TEI2LossyJSONConverter
@@ -449,10 +449,15 @@ class GrobidClient(ApiClient):
449
449
  input_file_path = pathlib.Path(input_file)
450
450
 
451
451
  if output is not None:
452
- # Calculate relative path from input_path, then join with output directory
452
+ # Mirror the input tree under the output directory: the path of the
453
+ # file relative to input_path is kept (only the extension changes),
454
+ # so sub/paper.pdf -> <output>/sub/paper.grobid.tei.xml. Keeping the
455
+ # subdirectories also prevents two inputs that share a basename
456
+ # from mapping to the same output file.
453
457
  input_path_abs = pathlib.Path(input_path).resolve()
454
458
  input_file_rel = input_file_path.resolve().relative_to(input_path_abs)
455
- filename = pathlib.Path(output) / f"{input_file_rel.stem}.grobid.tei.xml"
459
+ filename = pathlib.Path(output) / input_file_rel.with_name(
460
+ f"{input_file_rel.stem}.grobid.tei.xml")
456
461
  else:
457
462
  # Use the same directory as the input file
458
463
  filename = input_file_path.parent / f"{input_file_path.stem}.grobid.tei.xml"
@@ -629,9 +634,14 @@ class GrobidClient(ApiClient):
629
634
 
630
635
  # Partition into archives (streamed), remote loose files (s3) and local
631
636
  # filesystem files (directories are expanded to their eligible files).
637
+ # Local files are grouped with the base directory their output paths
638
+ # are computed from: a directory given as input is the base of the
639
+ # files found inside it (so the output mirrors its tree), while loose
640
+ # files (single paths, glob matches) share a common ancestor.
632
641
  archive_paths = []
633
642
  remote_files = []
634
- fs_files = []
643
+ fs_groups: List[Tuple[str, list]] = []
644
+ loose_files = []
635
645
  for path in matched_paths:
636
646
  if self._is_s3(path):
637
647
  if self._looks_like_archive(path):
@@ -643,14 +653,20 @@ class GrobidClient(ApiClient):
643
653
  elif self._is_archive(path):
644
654
  archive_paths.append(path)
645
655
  elif os.path.isdir(path):
646
- fs_files.extend(self._collect_directory_files(path, service))
656
+ dir_files = self._collect_directory_files(path, service)
657
+ if dir_files:
658
+ fs_groups.append((path, dir_files))
647
659
  elif os.path.isfile(path) and self._is_eligible_input(os.path.basename(path), service):
648
- fs_files.append(path)
660
+ loose_files.append(path)
649
661
  elif not os.path.exists(path):
650
662
  self.logger.warning(f"Input path does not exist: {path}")
651
663
  else:
652
664
  self.logger.debug(f"Skipping input (not an eligible file/dir/archive): {path}")
653
665
 
666
+ if loose_files:
667
+ fs_groups.append((self._common_base(loose_files), loose_files))
668
+ fs_files = [f for _, files in fs_groups for f in files]
669
+
654
670
  if not fs_files and not archive_paths and not remote_files:
655
671
  self.logger.warning(f"No eligible files found in input(s): {inputs}")
656
672
  return
@@ -669,16 +685,17 @@ class GrobidClient(ApiClient):
669
685
  # Local files gathered from directories and/or loose glob matches
670
686
  if fs_files:
671
687
  print(f"Found {len(fs_files)} local file(s) to process")
672
- bp, be, bs = self._run_file_batches(
673
- service, fs_files, self._common_base(fs_files), output, n,
674
- generate_ids, consolidate_header, consolidate_citations,
675
- include_raw_citations, include_raw_affiliations, tei_coordinates,
676
- segment_sentences, force, verbose, flavor, json_output, markdown_output,
677
- skip_errors=skip_errors
678
- )
679
- processed_files_count += bp
680
- errors_files_count += be
681
- skipped_files_count += bs
688
+ for base_dir, group_files in fs_groups:
689
+ bp, be, bs = self._run_file_batches(
690
+ service, group_files, base_dir, output, n,
691
+ generate_ids, consolidate_header, consolidate_citations,
692
+ include_raw_citations, include_raw_affiliations, tei_coordinates,
693
+ segment_sentences, force, verbose, flavor, json_output, markdown_output,
694
+ skip_errors=skip_errors
695
+ )
696
+ processed_files_count += bp
697
+ errors_files_count += be
698
+ skipped_files_count += bs
682
699
  total_files += len(fs_files)
683
700
 
684
701
  # Loose remote (s3) files: streamed into memory one chunk at a time
@@ -1096,8 +1113,9 @@ class GrobidClient(ApiClient):
1096
1113
  the chunk size and nothing but the results ever touches the disk. (The
1097
1114
  exception is ``processCitationList``, whose ``.txt`` inputs are read
1098
1115
  from a path and therefore still go through a temporary directory.)
1099
- Output files follow the same flat naming convention as directory
1100
- processing (one ``<stem>`` per result, in ``output``).
1116
+ Output files follow the same naming convention as directory
1117
+ processing: the entry's path inside the archive is mirrored under
1118
+ ``output`` (``nested/y.pdf`` -> ``<output>/nested/y.grobid.tei.xml``).
1101
1119
  """
1102
1120
  start_time = time.time()
1103
1121
  self._warn_on_consolidation_timeout(consolidate_citations)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: grobid-client-python
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Simple python client for GROBID REST services
5
5
  Author-email: Patrice Lopez <patrice.lopez@science-miner.com>
6
6
  Maintainer-email: Patrice Lopez <patrice.lopez@science-miner.com>, Luca Foppiano <lucanoro@duck.com>
@@ -411,6 +411,9 @@ grobid_client --input "~/data/**/*.pdf" --output ~/results processFulltextDocu
411
411
 
412
412
  > [!NOTE]
413
413
  > `--input` accepts a directory, a single file, an **archive**, or a **glob pattern**:
414
+ > - **Directories** are processed recursively and the output mirrors the input tree: `~/pdfs/2023/a.pdf` →
415
+ > `~/results/2023/a.grobid.tei.xml` (subdirectories of `--output` are created as needed, and the JSON/Markdown
416
+ > outputs and error files sit next to each TEI file). Archives keep their internal paths the same way.
414
417
  > - **Archives** (`.zip`, `.tar`, `.tar.gz`/`.tgz`, `.tar.bz2`/`.tbz2`) are streamed: eligible entries are read into
415
418
  > memory in chunks of `queue_size` and sent to GROBID straight from there — the archive is never fully decompressed and
416
419
  > nothing but the results in `--output` ever touches the disk. If `--output` is omitted, results go to a directory named
@@ -1,6 +1,6 @@
1
1
  grobid_client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  grobid_client/client.py,sha256=kAmRDQECMt6ck7AytnDAvMz8-KnM4QCIEp6rfFHqdzI,6925
3
- grobid_client/grobid_client.py,sha256=fQF8_U7oh9WBcKAsVD0JEHvlR7UjjDlvB4jtHrugrZA,93879
3
+ grobid_client/grobid_client.py,sha256=slqvpBW-3m-p1JQMQtfDJiZTrEEeLQ4-TyC3Ns_CRQc,94970
4
4
  grobid_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  grobid_client/format/TEI2LossyJSON.py,sha256=5KtHaQbP2leMEiFLi3yV70SPSDqKvCq-Zf3jmHTSEj8,50337
6
6
  grobid_client/format/TEI2LossyJSON_cli.py,sha256=TPsrE913MJBlTsVrd4Oc16AF3tXqwi_IzTt8UYcpZnU,3848
@@ -9,8 +9,8 @@ grobid_client/format/TEI2Markdown_cli.py,sha256=SZ5zMT0Lo7QG0DXypPyuA5TuJrgieVJs
9
9
  grobid_client/format/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  grobid_client/format/__main__.py,sha256=jklj0sd9_2QbZm7MWwnZsdH1XtepKTJX2hbZBCMvK78,1848
11
11
  grobid_client/format/validate_json_refs.py,sha256=iJimV0d9iPYqpw9b54C-c39AmtAKuyfMEWLS2F-QwRQ,14893
12
- grobid_client_python-0.2.1.dist-info/METADATA,sha256=IyjSEPUi_Q7pJgjcmf0gqp4oQgjfQNIPUQFkMejPdAY,43665
13
- grobid_client_python-0.2.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
14
- grobid_client_python-0.2.1.dist-info/entry_points.txt,sha256=-WwPi8D8eXEbDB6QvitvHjxSGBeyLCyFvAoZcaDGswQ,67
15
- grobid_client_python-0.2.1.dist-info/top_level.txt,sha256=HnjgWxdJNKTLIN86XkgRAfVeEK4C71fc9X4fe7qdMvA,14
16
- grobid_client_python-0.2.1.dist-info/RECORD,,
12
+ grobid_client_python-0.2.2.dist-info/METADATA,sha256=X1FrPOqH42bSaFRAjPXQ1Qgg529YjpbR-YtQQes__Ss,43992
13
+ grobid_client_python-0.2.2.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
14
+ grobid_client_python-0.2.2.dist-info/entry_points.txt,sha256=-WwPi8D8eXEbDB6QvitvHjxSGBeyLCyFvAoZcaDGswQ,67
15
+ grobid_client_python-0.2.2.dist-info/top_level.txt,sha256=HnjgWxdJNKTLIN86XkgRAfVeEK4C71fc9X4fe7qdMvA,14
16
+ grobid_client_python-0.2.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (84.0.0)
2
+ Generator: setuptools (84.2.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5