FastWARC 1.0.2__tar.gz → 1.0.3__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 (53) hide show
  1. {fastwarc-1.0.2 → fastwarc-1.0.3}/FastWARC.egg-info/PKG-INFO +1 -1
  2. {fastwarc-1.0.2 → fastwarc-1.0.3}/PKG-INFO +1 -1
  3. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/cli.py +31 -116
  4. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/stream_io.pyi +8 -7
  5. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/warc.pyi +1 -0
  6. {fastwarc-1.0.2 → fastwarc-1.0.3}/pyproject.toml +1 -1
  7. {fastwarc-1.0.2 → fastwarc-1.0.3}/FastWARC.egg-info/SOURCES.txt +0 -0
  8. {fastwarc-1.0.2 → fastwarc-1.0.3}/FastWARC.egg-info/dependency_links.txt +0 -0
  9. {fastwarc-1.0.2 → fastwarc-1.0.3}/FastWARC.egg-info/entry_points.txt +0 -0
  10. {fastwarc-1.0.2 → fastwarc-1.0.3}/FastWARC.egg-info/requires.txt +0 -0
  11. {fastwarc-1.0.2 → fastwarc-1.0.3}/FastWARC.egg-info/top_level.txt +0 -0
  12. {fastwarc-1.0.2 → fastwarc-1.0.3}/MANIFEST.in +0 -0
  13. {fastwarc-1.0.2 → fastwarc-1.0.3}/README.md +0 -0
  14. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/__init__.py +0 -0
  15. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/__init__.pyi +0 -0
  16. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/legacy/__init__.pxd +0 -0
  17. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/legacy/__init__.py +0 -0
  18. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/legacy/__init__.pyi +0 -0
  19. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/legacy/_shims.py +0 -0
  20. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/legacy/stream_io.pxd +0 -0
  21. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/legacy/stream_io.pyi +0 -0
  22. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/legacy/stream_io.pyx +0 -0
  23. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/legacy/tools.pyi +0 -0
  24. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/legacy/tools.pyx +0 -0
  25. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/legacy/warc.pxd +0 -0
  26. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/legacy/warc.pyi +0 -0
  27. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/legacy/warc.pyx +0 -0
  28. {fastwarc-1.0.2 → fastwarc-1.0.3}/fastwarc/py.typed +0 -0
  29. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_common/__init__.pxd +0 -0
  30. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_common/string_util.pxd +0 -0
  31. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/__init__.pxd +0 -0
  32. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/algorithm.pxd +0 -0
  33. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/atomic.pxd +0 -0
  34. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/boost_regex.pxd +0 -0
  35. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/cctype.pxd +0 -0
  36. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/cstdlib.pxd +0 -0
  37. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/cstring.pxd +0 -0
  38. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/dlfcn.pxd +0 -0
  39. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/errno.pxd +0 -0
  40. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/lexbor.pxd +0 -0
  41. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/lz4frame.pxd +0 -0
  42. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/lz4hc.pxd +0 -0
  43. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/pthread.pxd +0 -0
  44. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/re2.pxd +0 -0
  45. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/stdio.pxd +0 -0
  46. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/string_view.pxd +0 -0
  47. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/time.pxd +0 -0
  48. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/uchardet.pxd +0 -0
  49. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/unistd.pxd +0 -0
  50. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/utility.pxd +0 -0
  51. {fastwarc-1.0.2 → fastwarc-1.0.3}/resiliparse_inc/zlib.pxd +0 -0
  52. {fastwarc-1.0.2 → fastwarc-1.0.3}/setup.cfg +0 -0
  53. {fastwarc-1.0.2 → fastwarc-1.0.3}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: FastWARC
3
- Version: 1.0.2
3
+ Version: 1.0.3
4
4
  Summary: A high-performance WARC parsing library written in Rust with bindings for Python.
5
5
  Author: Janek Bevendorff
6
6
  License-Expression: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: FastWARC
3
- Version: 1.0.2
3
+ Version: 1.0.3
4
4
  Summary: A high-performance WARC parsing library written in Rust with bindings for Python.
5
5
  Author: Janek Bevendorff
6
6
  License-Expression: Apache-2.0
@@ -14,7 +14,6 @@
14
14
 
15
15
  import getpass
16
16
  import importlib
17
- from enum import IntFlag
18
17
  from itertools import chain
19
18
  import json
20
19
  import os
@@ -25,13 +24,14 @@ import urllib.request
25
24
  import click
26
25
  from tqdm import tqdm
27
26
 
28
- from fastwarc.stream_io import *
27
+ from fastwarc.stream_io import FileStream, StreamError, FastWARCError, PythonIOStreamAdapter
29
28
  from fastwarc.warc import ArchiveIterator, WarcRecordType
30
- from fastwarc.legacy.tools import CompressionAlg, detect_compression_algorithm, wrap_warc_stream, verify_digests
29
+ from fastwarc.legacy.tools import CompressionAlg, detect_compression_algorithm, wrap_warc_stream, \
30
+ recompress_warc_interactive, verify_digests
31
31
 
32
32
 
33
33
  def exception_handler(exctype, value, _):
34
- if exctype == OSError:
34
+ if exctype == FastWARCError:
35
35
  click.echo(str(value), err=True)
36
36
  sys.exit(1)
37
37
 
@@ -56,132 +56,46 @@ def _human_readable_bytes(byte_num):
56
56
  byte_num /= 1024
57
57
 
58
58
 
59
- class CompressionAlg(IntFlag):
60
- UNCOMPRESSED = 0
61
- GZIP = 1
62
- ZSTD = 2
63
- LZ4 = 3
64
- AUTO = 65535
65
-
66
-
67
- def detect_compression_algorithm(infile):
68
- """
69
- Try to detect the used compression algorithm from the given filename.
70
-
71
- :param infile: filename or file object
72
- :return: compression algorithm
73
- """
74
-
75
- if os.fspath(infile):
76
- infile = str(infile)
77
- elif hasattr(infile, 'name'):
78
- infile = infile.name
79
- else:
80
- raise ValueError('Invalid infile type.')
81
-
82
- if type(infile) is str:
83
- if infile.endswith('.gz'):
84
- return CompressionAlg.GZIP
85
- if infile.endswith('.zst'):
86
- return CompressionAlg.ZSTD
87
- if infile.endswith('.lz4'):
88
- return CompressionAlg.LZ4
89
- if infile.endswith('.warc'):
90
- return CompressionAlg.UNCOMPRESSED
91
-
92
- if not os.path.isfile(infile):
93
- raise click.UsageError('Could not determine compression algorithm.')
94
-
95
- with open(infile, 'rb') as infile:
96
- magic_bytes = infile.read(5)
97
-
98
- if magic_bytes[:3] == b'\x1F\x8B\x08':
99
- return CompressionAlg.GZIP
100
- elif magic_bytes[:4] == b'\x28\xB5\x2F\xFD' or magic_bytes[:4] == b'\x5D\x2A\x4D\x18':
101
- return CompressionAlg.ZSTD
102
- elif magic_bytes[:4] == b'\x04\x22\x4D\x18':
103
- return CompressionAlg.LZ4
104
- elif magic_bytes == b'WARC/':
105
- return CompressionAlg.UNCOMPRESSED
106
-
107
- raise click.FileError('No compression algorithm detected.')
108
-
109
-
110
- def get_compressor(filename, alg, level):
111
- if alg == CompressionAlg.AUTO:
112
- alg = detect_compression_algorithm(filename)
113
- if alg == CompressionAlg.UNCOMPRESSED:
114
- return open(filename, 'wb')
115
- elif alg == CompressionAlg.GZIP:
116
- return GzipWriter(filename, compression_level=level or 9)
117
- elif alg == CompressionAlg.ZSTD:
118
- return ZstdWriter(filename, compression_level=level or 3)
119
- elif alg == CompressionAlg.LZ4:
120
- return Lz4Writer(filename) # compression_level currently unsupported
121
- else:
122
- raise ValueError(f'Unknown compression algorithm: {alg}')
123
-
124
-
125
- def get_decompressor(filename, decompress_alg):
126
- if os.fspath(filename) and not decompress_alg:
127
- # Use plain filename to avoid Python IO stack
128
- return str(filename)
129
-
130
- if decompress_alg:
131
- alg = decompress_alg
132
- else:
133
- alg = detect_compression_algorithm(filename)
134
- if alg == CompressionAlg.UNCOMPRESSED:
135
- return open(filename, 'rb')
136
- if alg == CompressionAlg.GZIP:
137
- return GzipReader(filename)
138
- if alg == CompressionAlg.ZSTD:
139
- return ZstdReader(filename)
140
- if alg == CompressionAlg.LZ4:
141
- return Lz4Reader(filename) # compression_level currently unsupported
142
-
143
- raise ValueError(f'Unknown compression algorithm: {alg}')
144
-
145
-
146
59
  @main.command()
147
60
  @click.argument('infile', type=click.Path(dir_okay=False, exists=True))
148
61
  @click.argument('outfile', type=click.Path(dir_okay=False, exists=False))
149
- @click.option('-c', '--compress-alg', type=click.Choice(['uncompressed', 'gzip', 'zstd', 'lz4', 'auto']),
62
+ @click.option('-c', '--compress-alg', type=click.Choice(['gzip', 'lz4', 'uncompressed', 'auto']),
150
63
  default='auto', show_default=True, help='Compression algorithm to use for output file')
151
- @click.option('-d', '--decompress-alg', type=click.Choice(['uncompressed', 'gzip', 'zstd', 'lz4', 'auto']),
64
+ @click.option('-d', '--decompress-alg', type=click.Choice(['gzip', 'lz4', 'uncompressed', 'auto']),
152
65
  default='auto', show_default=True,
153
66
  help='Decompression algorithm for decoding input file (auto tries to detect based on file extension)')
154
- @click.option('-l', '--compress-level', type=int, default=None, help='Compression level')
67
+ @click.option('-l', '--compress-level', type=int, default=None, help='Compression level (defaults to max)')
155
68
  @click.option('-q', '--quiet', is_flag=True, help='Do not print progress information')
156
69
  def recompress(infile, outfile, compress_alg, decompress_alg, compress_level, quiet):
157
70
  """
158
71
  Recompress a WARC file.
159
72
 
160
- This command allows you to recompress a WARC file if it is uncompressed, not compressed
161
- properly at the record-level, or if you want to recompress a Gzip WARC as Zstd or LZ4 or vice versa.
73
+ This command allows you to recompress a WARC file if it is uncompressed or not compressed
74
+ properly at the record-level if or you want to recompress a GZip WARC as LZ4 or vice versa.
162
75
  """
163
76
 
164
- compress_alg = getattr(CompressionAlg, compress_alg.upper())
165
- decompress_alg = getattr(CompressionAlg, decompress_alg.upper())
77
+ compress_alg = getattr(CompressionAlg, compress_alg)
78
+ decompress_alg = getattr(CompressionAlg, decompress_alg)
166
79
 
167
- infile = get_decompressor(infile, decompress_alg if decompress_alg != CompressionAlg.AUTO else None)
168
- outfile_name = outfile
169
- outfile = get_compressor(outfile, compress_alg, compress_level)
80
+ comp_args = {}
81
+ if compress_level is None:
82
+ if compress_alg == CompressionAlg.gzip:
83
+ comp_args['compression_level'] = 9
84
+ elif compress_alg == CompressionAlg.lz4:
85
+ comp_args['compression_level'] = 12
170
86
 
87
+ bytes_written = 0
171
88
  num = 0
172
- start = time.monotonic()
89
+ start = time.time()
173
90
  try:
174
- with outfile as out:
175
- for r in tqdm(ArchiveIterator(infile),
176
- desc='Recompressing WARC file', unit=' record(s)', leave=False,
177
- disable=quiet, mininterval=0.3):
178
- num += 1
179
- r.write(out)
180
- if compress_alg != CompressionAlg.UNCOMPRESSED:
181
- out.finish()
91
+ for _, b in tqdm(recompress_warc_interactive(infile, outfile, decompress_alg, compress_alg, **comp_args),
92
+ desc='Recompressing WARC file', unit=' record(s)', leave=False,
93
+ disable=quiet, mininterval=0.2):
94
+ num += 1
95
+ bytes_written += b
182
96
 
183
- if not quiet:
184
- click.echo('Recompression completed.')
97
+ if not quiet:
98
+ click.echo('Recompression completed.')
185
99
  except KeyboardInterrupt:
186
100
  if not quiet:
187
101
  click.echo('Recompression aborted.')
@@ -189,8 +103,8 @@ def recompress(infile, outfile, compress_alg, decompress_alg, compress_level, qu
189
103
  finally:
190
104
  if not quiet and num > 0:
191
105
  click.echo(f' - Records recompressed: {num}')
192
- click.echo(f' - Bytes written: {_human_readable_bytes(os.stat(outfile_name).st_size)}')
193
- click.echo(f' - Completed in: {time.monotonic() - start:.02f} seconds')
106
+ click.echo(f' - Bytes written: {_human_readable_bytes(bytes_written)}')
107
+ click.echo(f' - Completed in: {time.time() - start:.02f} seconds')
194
108
 
195
109
 
196
110
  @main.command()
@@ -525,8 +439,9 @@ def benchmark(input_url, decompress_alg, endpoint_url, aws_access_key, aws_secre
525
439
  return num, time.monotonic() - start, interrupted
526
440
 
527
441
  def _fastwarc_iterator(f):
528
- s = _get_raw_stream_from_url(f, use_python_stream)
529
- s = wrap_warc_stream(s, 'rb', decompress_alg)
442
+ # s = _get_raw_stream_from_url(f, use_python_stream)
443
+ # s = wrap_warc_stream(s, 'rb', decompress_alg)
444
+ s = '/home/roce3528/code-in-progress/code-research/web-search/chatnoir/chatnoir-resiliparse/tmpfs/CC-MAIN-20231005012006-20231005042006-00899.warc'
530
445
  return ArchiveIterator(s, rec_type_filter, parse_http=parse_http, verify_digests=verify_digests)
531
446
 
532
447
  n, t_fastwarc, interrupted = _bench(input_url, _fastwarc_iterator, 'FastWARC')
@@ -128,18 +128,19 @@ _WriterInput = WarcWriter | BinaryIO | _GenericWriter | PathLike[str] | str
128
128
 
129
129
  @disjoint_base
130
130
  class GzipReader(WarcReader):
131
- def __new__(cls, inner: _ReaderInput, buffer_size: int = 4096, zlib: bool = False, fsspec_args=None) -> Self: ...
131
+ def __new__(cls, inner: _ReaderInput, buffer_size: int = 64 << 10, zlib: bool = False,
132
+ fsspec_args=None) -> Self: ...
132
133
 
133
134
 
134
135
  @disjoint_base
135
136
  class GzipWriter(WarcWriter):
136
- def __new__(cls, inner: _WriterInput, compression_level: int = 9, buffer_size=8192, zlib=False,
137
+ def __new__(cls, inner: _WriterInput, compression_level: int = 9, buffer_size: int = 8192, zlib=False,
137
138
  fsspec_args=None) -> Self: ...
138
139
 
139
140
 
140
141
  @disjoint_base
141
142
  class ZstdReader(WarcReader):
142
- def __new__(cls, inner: _ReaderInput, buffer_size: int = 4096, fsspec_args=None,
143
+ def __new__(cls, inner: _ReaderInput, buffer_size: int = 64 << 10, fsspec_args=None,
143
144
  dictionary: bytes | None = None) -> Self: ...
144
145
 
145
146
 
@@ -151,7 +152,7 @@ class ZstdWriter(WarcWriter):
151
152
 
152
153
  @disjoint_base
153
154
  class Lz4Reader(WarcReader):
154
- def __new__(cls, inner: _ReaderInput, buffer_size: int = 4096, fsspec_args=None) -> Self: ...
155
+ def __new__(cls, inner: _ReaderInput, buffer_size: int = 64 << 10, fsspec_args=None) -> Self: ...
155
156
 
156
157
 
157
158
  @disjoint_base
@@ -161,7 +162,7 @@ class Lz4Writer(WarcWriter):
161
162
 
162
163
  @disjoint_base
163
164
  class BrotliReader(WarcReader):
164
- def __new__(cls, inner: _ReaderInput, buffer_size: int = 4096, fsspec_args=None) -> Self: ...
165
+ def __new__(cls, inner: _ReaderInput, buffer_size: int = 64 << 10, fsspec_args=None) -> Self: ...
165
166
 
166
167
 
167
168
  @disjoint_base
@@ -171,12 +172,12 @@ class BrotliWriter(WarcWriter):
171
172
 
172
173
  @disjoint_base
173
174
  class ChunkedReader(WarcReader):
174
- def __new__(cls, inner: _ReaderInput, buffer_size: int = 1024, fsspec_args=None) -> Self: ...
175
+ def __new__(cls, inner: _ReaderInput, buffer_size: int = 4096, fsspec_args=None) -> Self: ...
175
176
 
176
177
 
177
178
  @disjoint_base
178
179
  class ChunkedWriter(WarcWriter):
179
- def __new__(cls, inner: _WriterInput, min_chunk_size: int = 512, fsspec_args=None) -> Self: ...
180
+ def __new__(cls, inner: _WriterInput, min_chunk_size: int = 1024, fsspec_args=None) -> Self: ...
180
181
 
181
182
 
182
183
  def zstd_train_dictionary_from_continuous(sample_data: bytes, sample_sizes: list[int], max_size: int) -> bytes: ...
@@ -333,6 +333,7 @@ class ArchiveIterator(Iterable[WarcRecord]):
333
333
  quirks_mode: bool = False,
334
334
  auto_decode: Literal['none', 'content', 'transfer', 'all'] = 'none',
335
335
  stream_detect: bool = True,
336
+ buffer_size: int = 64 << 10,
336
337
  fsspec_args=None,
337
338
  *,
338
339
  strict_mode: bool = True
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "FastWARC"
7
- version = "1.0.2"
7
+ version = "1.0.3"
8
8
  description = "A high-performance WARC parsing library written in Rust with bindings for Python."
9
9
  authors = [
10
10
  { name = "Janek Bevendorff" }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes