FastWARC 1.0.0__tar.gz → 1.0.2__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.
- fastwarc-1.0.2/FastWARC.egg-info/PKG-INFO +110 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/FastWARC.egg-info/SOURCES.txt +1 -1
- {fastwarc-1.0.0 → fastwarc-1.0.2}/FastWARC.egg-info/requires.txt +1 -1
- fastwarc-1.0.2/PKG-INFO +110 -0
- fastwarc-1.0.2/README.md +86 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/__init__.py +10 -13
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/__init__.pyi +10 -2
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/cli.py +115 -29
- fastwarc-1.0.2/fastwarc/legacy/__init__.pyi +19 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/legacy/_shims.py +3 -3
- fastwarc-1.0.2/fastwarc/stream_io.pyi +188 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/warc.pyi +114 -70
- {fastwarc-1.0.0 → fastwarc-1.0.2}/pyproject.toml +23 -18
- fastwarc-1.0.0/FastWARC.egg-info/PKG-INFO +0 -97
- fastwarc-1.0.0/PKG-INFO +0 -97
- fastwarc-1.0.0/README.md +0 -73
- fastwarc-1.0.0/fastwarc/_fastwarc.pyi +0 -35
- fastwarc-1.0.0/fastwarc/stream_io.pyi +0 -136
- {fastwarc-1.0.0 → fastwarc-1.0.2}/FastWARC.egg-info/dependency_links.txt +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/FastWARC.egg-info/entry_points.txt +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/FastWARC.egg-info/top_level.txt +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/MANIFEST.in +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/legacy/__init__.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/legacy/__init__.py +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/legacy/stream_io.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/legacy/stream_io.pyi +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/legacy/stream_io.pyx +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/legacy/tools.pyi +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/legacy/tools.pyx +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/legacy/warc.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/legacy/warc.pyi +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/legacy/warc.pyx +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/fastwarc/py.typed +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_common/__init__.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_common/string_util.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/__init__.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/algorithm.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/atomic.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/boost_regex.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/cctype.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/cstdlib.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/cstring.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/dlfcn.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/errno.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/lexbor.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/lz4frame.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/lz4hc.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/pthread.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/re2.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/stdio.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/string_view.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/time.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/uchardet.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/unistd.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/utility.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/resiliparse_inc/zlib.pxd +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/setup.cfg +0 -0
- {fastwarc-1.0.0 → fastwarc-1.0.2}/setup.py +0 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: FastWARC
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: A high-performance WARC parsing library written in Rust with bindings for Python.
|
|
5
|
+
Author: Janek Bevendorff
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: repository, https://github.com/chatnoir-eu/chatnoir-resiliparse
|
|
8
|
+
Project-URL: homepage, https://resiliparse.chatnoir.eu
|
|
9
|
+
Project-URL: documentation, https://resiliparse.chatnoir.eu
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: brotli
|
|
13
|
+
Requires-Dist: click
|
|
14
|
+
Requires-Dist: tqdm
|
|
15
|
+
Provides-Extra: all
|
|
16
|
+
Requires-Dist: fastwarc[fsspec]; extra == "all"
|
|
17
|
+
Provides-Extra: fsspec
|
|
18
|
+
Requires-Dist: fsspec; extra == "fsspec"
|
|
19
|
+
Requires-Dist: s3fs; extra == "fsspec"
|
|
20
|
+
Provides-Extra: test
|
|
21
|
+
Requires-Dist: fastwarc[all]; extra == "test"
|
|
22
|
+
Requires-Dist: pytest>=7.2.2; extra == "test"
|
|
23
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
|
24
|
+
|
|
25
|
+
# FastWARC (Python)
|
|
26
|
+
|
|
27
|
+
FastWARC is a high-performance WARC parsing library written in Rust with bindings for Python.
|
|
28
|
+
FastWARC supports compressed and uncompressed WARC/1.0 and WARC/1.1 streams. Supported compression algorithms are Gzip,
|
|
29
|
+
Zstd, and LZ4.
|
|
30
|
+
|
|
31
|
+
This package contains the Python bindings for FastWARC.
|
|
32
|
+
|
|
33
|
+
FastWARC belongs to the [ChatNoir Resiliparse toolkit](https://github.com/chatnoir-eu/chatnoir-resiliparse/) for fast
|
|
34
|
+
and robust web data processing.
|
|
35
|
+
|
|
36
|
+
## Installing FastWARC
|
|
37
|
+
|
|
38
|
+
Pre-built FastWARC binaries for Python can be installed from [PyPi](https://pypi.org/project/FastWARC/):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install fastwarc
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Building Python Bindings From Source
|
|
45
|
+
|
|
46
|
+
To be able to build FastWARC, you need the Rust toolchain installed (see above). Then to build directly from the GitHub
|
|
47
|
+
repository, follow these steps:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Clone repository
|
|
51
|
+
|
|
52
|
+
git clone https://github.com/chatnoir-eu/chatnoir-resiliparse.git
|
|
53
|
+
cd chatnoir-resiliparse
|
|
54
|
+
|
|
55
|
+
# Install Rust toolchain (if not already installed)
|
|
56
|
+
|
|
57
|
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
58
|
+
|
|
59
|
+
# Create a fresh venv first (recommended)
|
|
60
|
+
|
|
61
|
+
python3 -m venv venv && source venv/bin/activate
|
|
62
|
+
|
|
63
|
+
# Option 1: Build and install in editable mode (best for development)
|
|
64
|
+
|
|
65
|
+
python3 -m pip install -e ./fastwarc-py
|
|
66
|
+
|
|
67
|
+
# Option 2 (alternative): Build and install wheels in separate steps (best for redistribution)
|
|
68
|
+
|
|
69
|
+
python3 -m pip wheel -w build ./fastwarc-py
|
|
70
|
+
ls ./build/*.whl | xargs python3 -m pip install
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Usage Instructions
|
|
74
|
+
|
|
75
|
+
For detailed usage instructions, please consult
|
|
76
|
+
the [FastWARC User Manual](https://resiliparse.chatnoir.eu/en/latest/man/fastwarc.html).
|
|
77
|
+
|
|
78
|
+
## Cite Us
|
|
79
|
+
|
|
80
|
+
If you use FastWARC, please consider citing our [OSSYM 2021 abstract paper](https://arxiv.org/abs/2112.03103):
|
|
81
|
+
|
|
82
|
+
```bibtex
|
|
83
|
+
@InProceedings{bevendorff:2021,
|
|
84
|
+
author = {Janek Bevendorff and Martin Potthast and Benno Stein},
|
|
85
|
+
booktitle = {3rd International Symposium on Open Search Technology (OSSYM 2021)},
|
|
86
|
+
editor = {Andreas Wagner and Christian Guetl and Michael Granitzer and Stefan Voigt},
|
|
87
|
+
month = oct,
|
|
88
|
+
publisher = {International Open Search Symposium},
|
|
89
|
+
site = {CERN, Geneva, Switzerland},
|
|
90
|
+
title = {{FastWARC: Optimizing Large-Scale Web Archive Analytics}},
|
|
91
|
+
year = 2021
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
You can also cite our [ECIR 2018 demo paper](https://webis.de/downloads/publications/papers/bevendorff_2018.pdf):
|
|
96
|
+
|
|
97
|
+
```bibtex
|
|
98
|
+
@InProceedings{bevendorff:2018,
|
|
99
|
+
address = {Berlin Heidelberg New York},
|
|
100
|
+
author = {Janek Bevendorff and Benno Stein and Matthias Hagen and Martin Potthast},
|
|
101
|
+
booktitle = {Advances in Information Retrieval. 40th European Conference on IR Research (ECIR 2018)},
|
|
102
|
+
editor = {Leif Azzopardi and Allan Hanbury and Gabriella Pasi and Benjamin Piwowarski},
|
|
103
|
+
month = mar,
|
|
104
|
+
publisher = {Springer},
|
|
105
|
+
series = {Lecture Notes in Computer Science},
|
|
106
|
+
site = {Grenoble, France},
|
|
107
|
+
title = {{Elastic ChatNoir: Search Engine for the ClueWeb and the Common Crawl}},
|
|
108
|
+
year = 2018
|
|
109
|
+
}
|
|
110
|
+
```
|
|
@@ -10,13 +10,13 @@ FastWARC.egg-info/requires.txt
|
|
|
10
10
|
FastWARC.egg-info/top_level.txt
|
|
11
11
|
fastwarc/__init__.py
|
|
12
12
|
fastwarc/__init__.pyi
|
|
13
|
-
fastwarc/_fastwarc.pyi
|
|
14
13
|
fastwarc/cli.py
|
|
15
14
|
fastwarc/py.typed
|
|
16
15
|
fastwarc/stream_io.pyi
|
|
17
16
|
fastwarc/warc.pyi
|
|
18
17
|
fastwarc/legacy/__init__.pxd
|
|
19
18
|
fastwarc/legacy/__init__.py
|
|
19
|
+
fastwarc/legacy/__init__.pyi
|
|
20
20
|
fastwarc/legacy/_shims.py
|
|
21
21
|
fastwarc/legacy/stream_io.pxd
|
|
22
22
|
fastwarc/legacy/stream_io.pyi
|
fastwarc-1.0.2/PKG-INFO
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: FastWARC
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: A high-performance WARC parsing library written in Rust with bindings for Python.
|
|
5
|
+
Author: Janek Bevendorff
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: repository, https://github.com/chatnoir-eu/chatnoir-resiliparse
|
|
8
|
+
Project-URL: homepage, https://resiliparse.chatnoir.eu
|
|
9
|
+
Project-URL: documentation, https://resiliparse.chatnoir.eu
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: brotli
|
|
13
|
+
Requires-Dist: click
|
|
14
|
+
Requires-Dist: tqdm
|
|
15
|
+
Provides-Extra: all
|
|
16
|
+
Requires-Dist: fastwarc[fsspec]; extra == "all"
|
|
17
|
+
Provides-Extra: fsspec
|
|
18
|
+
Requires-Dist: fsspec; extra == "fsspec"
|
|
19
|
+
Requires-Dist: s3fs; extra == "fsspec"
|
|
20
|
+
Provides-Extra: test
|
|
21
|
+
Requires-Dist: fastwarc[all]; extra == "test"
|
|
22
|
+
Requires-Dist: pytest>=7.2.2; extra == "test"
|
|
23
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
|
24
|
+
|
|
25
|
+
# FastWARC (Python)
|
|
26
|
+
|
|
27
|
+
FastWARC is a high-performance WARC parsing library written in Rust with bindings for Python.
|
|
28
|
+
FastWARC supports compressed and uncompressed WARC/1.0 and WARC/1.1 streams. Supported compression algorithms are Gzip,
|
|
29
|
+
Zstd, and LZ4.
|
|
30
|
+
|
|
31
|
+
This package contains the Python bindings for FastWARC.
|
|
32
|
+
|
|
33
|
+
FastWARC belongs to the [ChatNoir Resiliparse toolkit](https://github.com/chatnoir-eu/chatnoir-resiliparse/) for fast
|
|
34
|
+
and robust web data processing.
|
|
35
|
+
|
|
36
|
+
## Installing FastWARC
|
|
37
|
+
|
|
38
|
+
Pre-built FastWARC binaries for Python can be installed from [PyPi](https://pypi.org/project/FastWARC/):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install fastwarc
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Building Python Bindings From Source
|
|
45
|
+
|
|
46
|
+
To be able to build FastWARC, you need the Rust toolchain installed (see above). Then to build directly from the GitHub
|
|
47
|
+
repository, follow these steps:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Clone repository
|
|
51
|
+
|
|
52
|
+
git clone https://github.com/chatnoir-eu/chatnoir-resiliparse.git
|
|
53
|
+
cd chatnoir-resiliparse
|
|
54
|
+
|
|
55
|
+
# Install Rust toolchain (if not already installed)
|
|
56
|
+
|
|
57
|
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
58
|
+
|
|
59
|
+
# Create a fresh venv first (recommended)
|
|
60
|
+
|
|
61
|
+
python3 -m venv venv && source venv/bin/activate
|
|
62
|
+
|
|
63
|
+
# Option 1: Build and install in editable mode (best for development)
|
|
64
|
+
|
|
65
|
+
python3 -m pip install -e ./fastwarc-py
|
|
66
|
+
|
|
67
|
+
# Option 2 (alternative): Build and install wheels in separate steps (best for redistribution)
|
|
68
|
+
|
|
69
|
+
python3 -m pip wheel -w build ./fastwarc-py
|
|
70
|
+
ls ./build/*.whl | xargs python3 -m pip install
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Usage Instructions
|
|
74
|
+
|
|
75
|
+
For detailed usage instructions, please consult
|
|
76
|
+
the [FastWARC User Manual](https://resiliparse.chatnoir.eu/en/latest/man/fastwarc.html).
|
|
77
|
+
|
|
78
|
+
## Cite Us
|
|
79
|
+
|
|
80
|
+
If you use FastWARC, please consider citing our [OSSYM 2021 abstract paper](https://arxiv.org/abs/2112.03103):
|
|
81
|
+
|
|
82
|
+
```bibtex
|
|
83
|
+
@InProceedings{bevendorff:2021,
|
|
84
|
+
author = {Janek Bevendorff and Martin Potthast and Benno Stein},
|
|
85
|
+
booktitle = {3rd International Symposium on Open Search Technology (OSSYM 2021)},
|
|
86
|
+
editor = {Andreas Wagner and Christian Guetl and Michael Granitzer and Stefan Voigt},
|
|
87
|
+
month = oct,
|
|
88
|
+
publisher = {International Open Search Symposium},
|
|
89
|
+
site = {CERN, Geneva, Switzerland},
|
|
90
|
+
title = {{FastWARC: Optimizing Large-Scale Web Archive Analytics}},
|
|
91
|
+
year = 2021
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
You can also cite our [ECIR 2018 demo paper](https://webis.de/downloads/publications/papers/bevendorff_2018.pdf):
|
|
96
|
+
|
|
97
|
+
```bibtex
|
|
98
|
+
@InProceedings{bevendorff:2018,
|
|
99
|
+
address = {Berlin Heidelberg New York},
|
|
100
|
+
author = {Janek Bevendorff and Benno Stein and Matthias Hagen and Martin Potthast},
|
|
101
|
+
booktitle = {Advances in Information Retrieval. 40th European Conference on IR Research (ECIR 2018)},
|
|
102
|
+
editor = {Leif Azzopardi and Allan Hanbury and Gabriella Pasi and Benjamin Piwowarski},
|
|
103
|
+
month = mar,
|
|
104
|
+
publisher = {Springer},
|
|
105
|
+
series = {Lecture Notes in Computer Science},
|
|
106
|
+
site = {Grenoble, France},
|
|
107
|
+
title = {{Elastic ChatNoir: Search Engine for the ClueWeb and the Common Crawl}},
|
|
108
|
+
year = 2018
|
|
109
|
+
}
|
|
110
|
+
```
|
fastwarc-1.0.2/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# FastWARC (Python)
|
|
2
|
+
|
|
3
|
+
FastWARC is a high-performance WARC parsing library written in Rust with bindings for Python.
|
|
4
|
+
FastWARC supports compressed and uncompressed WARC/1.0 and WARC/1.1 streams. Supported compression algorithms are Gzip,
|
|
5
|
+
Zstd, and LZ4.
|
|
6
|
+
|
|
7
|
+
This package contains the Python bindings for FastWARC.
|
|
8
|
+
|
|
9
|
+
FastWARC belongs to the [ChatNoir Resiliparse toolkit](https://github.com/chatnoir-eu/chatnoir-resiliparse/) for fast
|
|
10
|
+
and robust web data processing.
|
|
11
|
+
|
|
12
|
+
## Installing FastWARC
|
|
13
|
+
|
|
14
|
+
Pre-built FastWARC binaries for Python can be installed from [PyPi](https://pypi.org/project/FastWARC/):
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install fastwarc
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Building Python Bindings From Source
|
|
21
|
+
|
|
22
|
+
To be able to build FastWARC, you need the Rust toolchain installed (see above). Then to build directly from the GitHub
|
|
23
|
+
repository, follow these steps:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Clone repository
|
|
27
|
+
|
|
28
|
+
git clone https://github.com/chatnoir-eu/chatnoir-resiliparse.git
|
|
29
|
+
cd chatnoir-resiliparse
|
|
30
|
+
|
|
31
|
+
# Install Rust toolchain (if not already installed)
|
|
32
|
+
|
|
33
|
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
34
|
+
|
|
35
|
+
# Create a fresh venv first (recommended)
|
|
36
|
+
|
|
37
|
+
python3 -m venv venv && source venv/bin/activate
|
|
38
|
+
|
|
39
|
+
# Option 1: Build and install in editable mode (best for development)
|
|
40
|
+
|
|
41
|
+
python3 -m pip install -e ./fastwarc-py
|
|
42
|
+
|
|
43
|
+
# Option 2 (alternative): Build and install wheels in separate steps (best for redistribution)
|
|
44
|
+
|
|
45
|
+
python3 -m pip wheel -w build ./fastwarc-py
|
|
46
|
+
ls ./build/*.whl | xargs python3 -m pip install
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage Instructions
|
|
50
|
+
|
|
51
|
+
For detailed usage instructions, please consult
|
|
52
|
+
the [FastWARC User Manual](https://resiliparse.chatnoir.eu/en/latest/man/fastwarc.html).
|
|
53
|
+
|
|
54
|
+
## Cite Us
|
|
55
|
+
|
|
56
|
+
If you use FastWARC, please consider citing our [OSSYM 2021 abstract paper](https://arxiv.org/abs/2112.03103):
|
|
57
|
+
|
|
58
|
+
```bibtex
|
|
59
|
+
@InProceedings{bevendorff:2021,
|
|
60
|
+
author = {Janek Bevendorff and Martin Potthast and Benno Stein},
|
|
61
|
+
booktitle = {3rd International Symposium on Open Search Technology (OSSYM 2021)},
|
|
62
|
+
editor = {Andreas Wagner and Christian Guetl and Michael Granitzer and Stefan Voigt},
|
|
63
|
+
month = oct,
|
|
64
|
+
publisher = {International Open Search Symposium},
|
|
65
|
+
site = {CERN, Geneva, Switzerland},
|
|
66
|
+
title = {{FastWARC: Optimizing Large-Scale Web Archive Analytics}},
|
|
67
|
+
year = 2021
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
You can also cite our [ECIR 2018 demo paper](https://webis.de/downloads/publications/papers/bevendorff_2018.pdf):
|
|
72
|
+
|
|
73
|
+
```bibtex
|
|
74
|
+
@InProceedings{bevendorff:2018,
|
|
75
|
+
address = {Berlin Heidelberg New York},
|
|
76
|
+
author = {Janek Bevendorff and Benno Stein and Matthias Hagen and Martin Potthast},
|
|
77
|
+
booktitle = {Advances in Information Retrieval. 40th European Conference on IR Research (ECIR 2018)},
|
|
78
|
+
editor = {Leif Azzopardi and Allan Hanbury and Gabriella Pasi and Benjamin Piwowarski},
|
|
79
|
+
month = mar,
|
|
80
|
+
publisher = {Springer},
|
|
81
|
+
series = {Lecture Notes in Computer Science},
|
|
82
|
+
site = {Grenoble, France},
|
|
83
|
+
title = {{Elastic ChatNoir: Search Engine for the ClueWeb and the Common Crawl}},
|
|
84
|
+
year = 2018
|
|
85
|
+
}
|
|
86
|
+
```
|
|
@@ -13,23 +13,20 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
import importlib
|
|
16
|
-
import sys
|
|
17
16
|
|
|
18
17
|
_native = importlib.import_module("._fastwarc", __name__)
|
|
19
18
|
_legacy_shims = importlib.import_module(".legacy._shims", __name__)
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
import fastwarc.stream_io as stream_io # noqa: F401
|
|
21
|
+
import fastwarc.warc as warc # noqa: F401
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
sys.modules[__name__ + ".stream_io"] = stream_io
|
|
27
|
-
sys.modules[__name__ + ".warc"] = warc
|
|
23
|
+
# Re-export module attributes
|
|
24
|
+
globals().update({attr: getattr(_native, attr) for attr in _native.__all__})
|
|
28
25
|
|
|
29
26
|
# Patch legacy shims onto stream_io
|
|
30
27
|
for name in _legacy_shims.__all__:
|
|
31
28
|
setattr(stream_io, name, getattr(_legacy_shims, name))
|
|
32
|
-
stream_io.__all__ += getattr(_legacy_shims, "__all__", ())
|
|
29
|
+
stream_io.__all__ += getattr(_legacy_shims, "__all__", ()) # type: ignore[attr-defined]
|
|
33
30
|
|
|
34
31
|
FileStream = _legacy_shims.FileStream
|
|
35
32
|
GZipStream = _legacy_shims.GZipStream
|
|
@@ -39,9 +36,9 @@ StreamError = _legacy_shims.StreamError
|
|
|
39
36
|
|
|
40
37
|
__all__ = [
|
|
41
38
|
*_native.__all__,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
'FileStream',
|
|
40
|
+
'GZipStream',
|
|
41
|
+
'LZ4Stream',
|
|
42
|
+
'FastWARCError',
|
|
43
|
+
'StreamError',
|
|
47
44
|
]
|
|
@@ -12,8 +12,16 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
from .
|
|
16
|
-
from .
|
|
15
|
+
from .warc import ArchiveIterator, WarcRecord, WarcRecordType
|
|
16
|
+
from .stream_io import (
|
|
17
|
+
GzipReader,
|
|
18
|
+
GzipWriter,
|
|
19
|
+
ZstdReader,
|
|
20
|
+
ZstdWriter,
|
|
21
|
+
Lz4Reader,
|
|
22
|
+
Lz4Writer,
|
|
23
|
+
|
|
24
|
+
# Legacy names
|
|
17
25
|
FileStream,
|
|
18
26
|
GZipStream,
|
|
19
27
|
LZ4Stream,
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import getpass
|
|
16
16
|
import importlib
|
|
17
|
+
from enum import IntFlag
|
|
17
18
|
from itertools import chain
|
|
18
19
|
import json
|
|
19
20
|
import os
|
|
@@ -24,14 +25,13 @@ import urllib.request
|
|
|
24
25
|
import click
|
|
25
26
|
from tqdm import tqdm
|
|
26
27
|
|
|
27
|
-
from fastwarc.
|
|
28
|
-
from fastwarc.
|
|
29
|
-
from fastwarc.legacy.tools import CompressionAlg, detect_compression_algorithm, wrap_warc_stream,
|
|
30
|
-
recompress_warc_interactive, verify_digests
|
|
28
|
+
from fastwarc.stream_io import *
|
|
29
|
+
from fastwarc.warc import ArchiveIterator, WarcRecordType
|
|
30
|
+
from fastwarc.legacy.tools import CompressionAlg, detect_compression_algorithm, wrap_warc_stream, verify_digests
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
def exception_handler(exctype, value, _):
|
|
34
|
-
if exctype ==
|
|
34
|
+
if exctype == OSError:
|
|
35
35
|
click.echo(str(value), err=True)
|
|
36
36
|
sys.exit(1)
|
|
37
37
|
|
|
@@ -56,46 +56,132 @@ 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
|
+
|
|
59
146
|
@main.command()
|
|
60
147
|
@click.argument('infile', type=click.Path(dir_okay=False, exists=True))
|
|
61
148
|
@click.argument('outfile', type=click.Path(dir_okay=False, exists=False))
|
|
62
|
-
@click.option('-c', '--compress-alg', type=click.Choice(['gzip', '
|
|
149
|
+
@click.option('-c', '--compress-alg', type=click.Choice(['uncompressed', 'gzip', 'zstd', 'lz4', 'auto']),
|
|
63
150
|
default='auto', show_default=True, help='Compression algorithm to use for output file')
|
|
64
|
-
@click.option('-d', '--decompress-alg', type=click.Choice(['gzip', '
|
|
151
|
+
@click.option('-d', '--decompress-alg', type=click.Choice(['uncompressed', 'gzip', 'zstd', 'lz4', 'auto']),
|
|
65
152
|
default='auto', show_default=True,
|
|
66
153
|
help='Decompression algorithm for decoding input file (auto tries to detect based on file extension)')
|
|
67
|
-
@click.option('-l', '--compress-level', type=int, default=None, help='Compression level
|
|
154
|
+
@click.option('-l', '--compress-level', type=int, default=None, help='Compression level')
|
|
68
155
|
@click.option('-q', '--quiet', is_flag=True, help='Do not print progress information')
|
|
69
156
|
def recompress(infile, outfile, compress_alg, decompress_alg, compress_level, quiet):
|
|
70
157
|
"""
|
|
71
158
|
Recompress a WARC file.
|
|
72
159
|
|
|
73
|
-
This command allows you to recompress a WARC file if it is uncompressed
|
|
74
|
-
properly at the record-level if
|
|
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.
|
|
75
162
|
"""
|
|
76
163
|
|
|
77
|
-
compress_alg = getattr(CompressionAlg, compress_alg)
|
|
78
|
-
decompress_alg = getattr(CompressionAlg, decompress_alg)
|
|
164
|
+
compress_alg = getattr(CompressionAlg, compress_alg.upper())
|
|
165
|
+
decompress_alg = getattr(CompressionAlg, decompress_alg.upper())
|
|
79
166
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
comp_args['compression_level'] = 9
|
|
84
|
-
elif compress_alg == CompressionAlg.lz4:
|
|
85
|
-
comp_args['compression_level'] = 12
|
|
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)
|
|
86
170
|
|
|
87
|
-
bytes_written = 0
|
|
88
171
|
num = 0
|
|
89
|
-
start = time.
|
|
172
|
+
start = time.monotonic()
|
|
90
173
|
try:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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()
|
|
96
182
|
|
|
97
|
-
|
|
98
|
-
|
|
183
|
+
if not quiet:
|
|
184
|
+
click.echo('Recompression completed.')
|
|
99
185
|
except KeyboardInterrupt:
|
|
100
186
|
if not quiet:
|
|
101
187
|
click.echo('Recompression aborted.')
|
|
@@ -103,8 +189,8 @@ def recompress(infile, outfile, compress_alg, decompress_alg, compress_level, qu
|
|
|
103
189
|
finally:
|
|
104
190
|
if not quiet and num > 0:
|
|
105
191
|
click.echo(f' - Records recompressed: {num}')
|
|
106
|
-
click.echo(f' - Bytes written: {_human_readable_bytes(
|
|
107
|
-
click.echo(f' - Completed in: {time.
|
|
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')
|
|
108
194
|
|
|
109
195
|
|
|
110
196
|
@main.command()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .stream_io import (
|
|
2
|
+
FileStream,
|
|
3
|
+
GZipStream,
|
|
4
|
+
LZ4Stream,
|
|
5
|
+
FastWARCError,
|
|
6
|
+
StreamError,
|
|
7
|
+
)
|
|
8
|
+
from .warc import ArchiveIterator, WarcRecord, WarcRecordType
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"FileStream",
|
|
12
|
+
"GZipStream",
|
|
13
|
+
"LZ4Stream",
|
|
14
|
+
"FastWARCError",
|
|
15
|
+
"StreamError",
|
|
16
|
+
"ArchiveIterator",
|
|
17
|
+
"WarcRecord",
|
|
18
|
+
"WarcRecordType",
|
|
19
|
+
]
|
|
@@ -172,7 +172,7 @@ class BytesIOStream:
|
|
|
172
172
|
|
|
173
173
|
class GZipStream:
|
|
174
174
|
def __new__(cls, raw_stream, compression_level=9, zlib=False, fsspec_args=None):
|
|
175
|
-
from
|
|
175
|
+
from ..stream_io import GzipReader, GzipWriter
|
|
176
176
|
stream = CompressingStream(
|
|
177
177
|
_reader_factory=lambda: GzipReader(raw_stream, zlib=zlib, fsspec_args=fsspec_args),
|
|
178
178
|
_writer_factory=lambda: GzipWriter(raw_stream, compression_level=compression_level, zlib=zlib,
|
|
@@ -184,7 +184,7 @@ class GZipStream:
|
|
|
184
184
|
class LZ4Stream:
|
|
185
185
|
def __new__(cls, raw_stream, mode='r', compression_level=12,
|
|
186
186
|
favor_dec_speed=True, fsspec_args=None):
|
|
187
|
-
from
|
|
187
|
+
from ..stream_io import Lz4Reader, Lz4Writer
|
|
188
188
|
stream = CompressingStream(
|
|
189
189
|
_reader_factory=lambda: Lz4Reader(raw_stream, fsspec_args=fsspec_args),
|
|
190
190
|
_writer_factory=lambda: Lz4Writer(raw_stream, fsspec_args=fsspec_args))
|
|
@@ -194,7 +194,7 @@ class LZ4Stream:
|
|
|
194
194
|
|
|
195
195
|
class BrotliStream:
|
|
196
196
|
def __new__(cls, raw_stream, quality=11, lgwin=22, lgblock=0, fsspec_args=None):
|
|
197
|
-
from
|
|
197
|
+
from ..stream_io import BrotliReader, BrotliWriter
|
|
198
198
|
stream = CompressingStream(
|
|
199
199
|
_reader_factory=lambda: BrotliReader(raw_stream, fsspec_args=fsspec_args),
|
|
200
200
|
_writer_factory=lambda: BrotliWriter(raw_stream, fsspec_args=fsspec_args))
|