FastWARC 1.0.7__tar.gz → 1.0.9__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.7 → fastwarc-1.0.9}/FastWARC.egg-info/PKG-INFO +2 -2
- {fastwarc-1.0.7 → fastwarc-1.0.9}/FastWARC.egg-info/SOURCES.txt +10 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/FastWARC.egg-info/requires.txt +1 -1
- {fastwarc-1.0.7 → fastwarc-1.0.9}/MANIFEST.in +1 -1
- {fastwarc-1.0.7 → fastwarc-1.0.9}/PKG-INFO +2 -2
- fastwarc-1.0.9/fastwarc/Cargo.toml +28 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/__init__.pyi +2 -1
- fastwarc-1.0.9/fastwarc/build.rs +18 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/legacy/_shims.py +4 -2
- fastwarc-1.0.9/fastwarc/src/lib.rs +116 -0
- fastwarc-1.0.9/fastwarc/src/stream_io/brotli.rs +148 -0
- fastwarc-1.0.9/fastwarc/src/stream_io/chunked.rs +140 -0
- fastwarc-1.0.9/fastwarc/src/stream_io/gzip.rs +161 -0
- fastwarc-1.0.9/fastwarc/src/stream_io/lz4.rs +145 -0
- fastwarc-1.0.9/fastwarc/src/stream_io/mod.rs +712 -0
- fastwarc-1.0.9/fastwarc/src/stream_io/zstd.rs +206 -0
- fastwarc-1.0.9/fastwarc/src/warc.rs +1712 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/warc.pyi +62 -16
- {fastwarc-1.0.7 → fastwarc-1.0.9}/pyproject.toml +8 -3
- {fastwarc-1.0.7 → fastwarc-1.0.9}/setup.py +56 -13
- {fastwarc-1.0.7 → fastwarc-1.0.9}/FastWARC.egg-info/dependency_links.txt +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/FastWARC.egg-info/entry_points.txt +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/FastWARC.egg-info/top_level.txt +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/README.md +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/__init__.py +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/cli.py +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/legacy/__init__.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/legacy/__init__.py +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/legacy/__init__.pyi +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/legacy/stream_io.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/legacy/stream_io.pyi +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/legacy/stream_io.pyx +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/legacy/tools.pyi +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/legacy/tools.pyx +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/legacy/warc.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/legacy/warc.pyi +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/legacy/warc.pyx +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/py.typed +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/fastwarc/stream_io.pyi +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_common/__init__.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_common/string_util.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/__init__.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/algorithm.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/atomic.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/boost_regex.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/cctype.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/cstdlib.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/cstring.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/dlfcn.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/errno.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/lexbor.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/lz4frame.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/lz4hc.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/pthread.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/re2.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/stdio.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/string_view.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/time.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/uchardet.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/unistd.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/utility.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/resiliparse_inc/zlib.pxd +0 -0
- {fastwarc-1.0.7 → fastwarc-1.0.9}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: FastWARC
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.9
|
|
4
4
|
Summary: The world's fastest WARC parsing library written in Rust with bindings for Python.
|
|
5
5
|
Author: Janek Bevendorff
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -12,7 +12,7 @@ Description-Content-Type: text/markdown
|
|
|
12
12
|
Requires-Dist: brotli
|
|
13
13
|
Requires-Dist: click
|
|
14
14
|
Requires-Dist: tqdm
|
|
15
|
-
Requires-Dist: typing_extensions>=4.
|
|
15
|
+
Requires-Dist: typing_extensions>=4.15; python_version < "3.13"
|
|
16
16
|
Provides-Extra: all
|
|
17
17
|
Requires-Dist: fastwarc[fsspec]; extra == "all"
|
|
18
18
|
Provides-Extra: fsspec
|
|
@@ -8,8 +8,10 @@ FastWARC.egg-info/dependency_links.txt
|
|
|
8
8
|
FastWARC.egg-info/entry_points.txt
|
|
9
9
|
FastWARC.egg-info/requires.txt
|
|
10
10
|
FastWARC.egg-info/top_level.txt
|
|
11
|
+
fastwarc/Cargo.toml
|
|
11
12
|
fastwarc/__init__.py
|
|
12
13
|
fastwarc/__init__.pyi
|
|
14
|
+
fastwarc/build.rs
|
|
13
15
|
fastwarc/cli.py
|
|
14
16
|
fastwarc/py.typed
|
|
15
17
|
fastwarc/stream_io.pyi
|
|
@@ -26,6 +28,14 @@ fastwarc/legacy/tools.pyx
|
|
|
26
28
|
fastwarc/legacy/warc.pxd
|
|
27
29
|
fastwarc/legacy/warc.pyi
|
|
28
30
|
fastwarc/legacy/warc.pyx
|
|
31
|
+
fastwarc/src/lib.rs
|
|
32
|
+
fastwarc/src/warc.rs
|
|
33
|
+
fastwarc/src/stream_io/brotli.rs
|
|
34
|
+
fastwarc/src/stream_io/chunked.rs
|
|
35
|
+
fastwarc/src/stream_io/gzip.rs
|
|
36
|
+
fastwarc/src/stream_io/lz4.rs
|
|
37
|
+
fastwarc/src/stream_io/mod.rs
|
|
38
|
+
fastwarc/src/stream_io/zstd.rs
|
|
29
39
|
resiliparse_common/__init__.pxd
|
|
30
40
|
resiliparse_common/string_util.pxd
|
|
31
41
|
resiliparse_inc/__init__.pxd
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: FastWARC
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.9
|
|
4
4
|
Summary: The world's fastest WARC parsing library written in Rust with bindings for Python.
|
|
5
5
|
Author: Janek Bevendorff
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -12,7 +12,7 @@ Description-Content-Type: text/markdown
|
|
|
12
12
|
Requires-Dist: brotli
|
|
13
13
|
Requires-Dist: click
|
|
14
14
|
Requires-Dist: tqdm
|
|
15
|
-
Requires-Dist: typing_extensions>=4.
|
|
15
|
+
Requires-Dist: typing_extensions>=4.15; python_version < "3.13"
|
|
16
16
|
Provides-Extra: all
|
|
17
17
|
Requires-Dist: fastwarc[fsspec]; extra == "all"
|
|
18
18
|
Provides-Extra: fsspec
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "fastwarc-py"
|
|
3
|
+
edition = "2024"
|
|
4
|
+
publish = false
|
|
5
|
+
|
|
6
|
+
[lib]
|
|
7
|
+
name = "fastwarc"
|
|
8
|
+
path = "src/lib.rs"
|
|
9
|
+
crate-type = [
|
|
10
|
+
"cdylib",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[dependencies]
|
|
14
|
+
fastwarc = "1.0.9"
|
|
15
|
+
pyo3 = "0.28"
|
|
16
|
+
|
|
17
|
+
[dependencies.time]
|
|
18
|
+
version = "0.3"
|
|
19
|
+
features = [
|
|
20
|
+
"formatting",
|
|
21
|
+
"parsing",
|
|
22
|
+
"macros",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[build-dependencies]
|
|
26
|
+
pyo3-build-config = "0.28"
|
|
27
|
+
|
|
28
|
+
[workspace]
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
from .warc import ArchiveIterator, WarcRecord, WarcRecordType
|
|
15
|
+
from .warc import ArchiveIterator, WarcHeader, WarcRecord, WarcRecordType
|
|
16
16
|
from .stream_io import (
|
|
17
17
|
GzipReader,
|
|
18
18
|
GzipWriter,
|
|
@@ -31,6 +31,7 @@ from .stream_io import (
|
|
|
31
31
|
|
|
32
32
|
__all__ = [
|
|
33
33
|
'ArchiveIterator',
|
|
34
|
+
'WarcHeader',
|
|
34
35
|
'WarcRecord',
|
|
35
36
|
'WarcRecordType',
|
|
36
37
|
'GzipReader',
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Copyright 2026 Janek Bevendorff
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
fn main() {
|
|
16
|
+
// https://pyo3.rs/v0.28.3/building-and-distribution.html?utm_source=chatgpt.com#macos
|
|
17
|
+
pyo3_build_config::add_extension_module_link_args();
|
|
18
|
+
}
|
|
@@ -14,9 +14,11 @@
|
|
|
14
14
|
|
|
15
15
|
# Legacy shims for making the new Rust extension work with the old type names
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
import sys
|
|
18
|
+
|
|
19
|
+
if sys.version_info >= (3, 13):
|
|
18
20
|
from warnings import deprecated
|
|
19
|
-
|
|
21
|
+
else:
|
|
20
22
|
from typing_extensions import deprecated
|
|
21
23
|
|
|
22
24
|
__all__ = [
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Copyright 2026 Janek Bevendorff
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
mod stream_io;
|
|
16
|
+
mod warc;
|
|
17
|
+
|
|
18
|
+
use pyo3::prelude::*;
|
|
19
|
+
use pyo3::types::PyModule;
|
|
20
|
+
|
|
21
|
+
#[pymodule]
|
|
22
|
+
pub mod _fastwarc {
|
|
23
|
+
use super::*;
|
|
24
|
+
|
|
25
|
+
#[pymodule_init]
|
|
26
|
+
pub fn __init__(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
27
|
+
let py = m.py();
|
|
28
|
+
|
|
29
|
+
// Load submodules.
|
|
30
|
+
// Don't add with add_submodule(), otherwise they end up in __all__.
|
|
31
|
+
let stream_io = pyo3::wrap_pymodule!(_stream_io)(py);
|
|
32
|
+
let stream_io = stream_io.bind(py);
|
|
33
|
+
|
|
34
|
+
let warc = pyo3::wrap_pymodule!(_warc)(py);
|
|
35
|
+
let warc = warc.bind(py);
|
|
36
|
+
|
|
37
|
+
// Register submodules to make them importable.
|
|
38
|
+
let sys = PyModule::import(py, "sys")?;
|
|
39
|
+
let modules = sys.getattr("modules")?;
|
|
40
|
+
modules.set_item("fastwarc.stream_io", stream_io)?;
|
|
41
|
+
modules.set_item("fastwarc.warc", warc)?;
|
|
42
|
+
|
|
43
|
+
// Top-level exports
|
|
44
|
+
m.add("WarcRecordType", warc.getattr("WarcRecordType")?)?;
|
|
45
|
+
m.add("WarcHeader", warc.getattr("WarcHeader")?)?;
|
|
46
|
+
m.add("WarcRecord", warc.getattr("WarcRecord")?)?;
|
|
47
|
+
m.add("ArchiveIterator", warc.getattr("ArchiveIterator")?)?;
|
|
48
|
+
|
|
49
|
+
m.add("GzipReader", stream_io.getattr("GzipReader")?)?;
|
|
50
|
+
m.add("GzipWriter", stream_io.getattr("GzipWriter")?)?;
|
|
51
|
+
m.add("Lz4Reader", stream_io.getattr("Lz4Reader")?)?;
|
|
52
|
+
m.add("Lz4Writer", stream_io.getattr("Lz4Writer")?)?;
|
|
53
|
+
m.add("ZstdReader", stream_io.getattr("ZstdReader")?)?;
|
|
54
|
+
m.add("ZstdWriter", stream_io.getattr("ZstdWriter")?)?;
|
|
55
|
+
|
|
56
|
+
Ok(())
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#[pymodule(name = "stream_io", module = "fastwarc")]
|
|
61
|
+
pub mod _stream_io {
|
|
62
|
+
#[pymodule_export]
|
|
63
|
+
pub use crate::stream_io::{WarcReaderPy, WarcWriterPy};
|
|
64
|
+
|
|
65
|
+
#[pymodule_export]
|
|
66
|
+
pub use crate::stream_io::gzip::{GzipReaderPy, GzipWriterPy};
|
|
67
|
+
|
|
68
|
+
#[pymodule_export]
|
|
69
|
+
pub use crate::stream_io::zstd::{
|
|
70
|
+
ZstdReaderPy, ZstdWriterPy, zstd_train_dictionary_from_continuous, zstd_train_dictionary_from_files,
|
|
71
|
+
zstd_train_dictionary_from_samples,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
#[pymodule_export]
|
|
75
|
+
pub use crate::stream_io::lz4::{Lz4ReaderPy, Lz4WriterPy};
|
|
76
|
+
|
|
77
|
+
#[pymodule_export]
|
|
78
|
+
pub use crate::stream_io::brotli::{BrotliReaderPy, BrotliWriterPy};
|
|
79
|
+
|
|
80
|
+
#[pymodule_export]
|
|
81
|
+
pub use crate::stream_io::chunked::{ChunkedReaderPy, ChunkedWriterPy};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#[pymodule(name = "warc", module = "fastwarc")]
|
|
85
|
+
pub mod _warc {
|
|
86
|
+
use super::*;
|
|
87
|
+
|
|
88
|
+
#[pymodule_init]
|
|
89
|
+
fn __init__(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
90
|
+
// Re-export enum members as individual constants.
|
|
91
|
+
m.add("warcinfo", WarcRecordTypePy::warcinfo)?;
|
|
92
|
+
m.add("response", WarcRecordTypePy::response)?;
|
|
93
|
+
m.add("resource", WarcRecordTypePy::resource)?;
|
|
94
|
+
m.add("request", WarcRecordTypePy::request)?;
|
|
95
|
+
m.add("metadata", WarcRecordTypePy::metadata)?;
|
|
96
|
+
m.add("revisit", WarcRecordTypePy::revisit)?;
|
|
97
|
+
m.add("conversion", WarcRecordTypePy::conversion)?;
|
|
98
|
+
m.add("continuation", WarcRecordTypePy::continuation)?;
|
|
99
|
+
m.add("unknown", WarcRecordTypePy::unknown)?;
|
|
100
|
+
m.add("no_type", WarcRecordTypePy::no_type)?;
|
|
101
|
+
m.add("any_type", WarcRecordTypePy::any_type)?;
|
|
102
|
+
|
|
103
|
+
// Legacy name
|
|
104
|
+
m.add("WarcHeaderMap", m.getattr("HeaderMap")?)?;
|
|
105
|
+
|
|
106
|
+
Ok(())
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
#[pymodule_export]
|
|
110
|
+
pub use crate::warc::{
|
|
111
|
+
ArchiveIteratorPy, HeaderMapPy, WarcHeaderPy, WarcRecordPayloadReaderPy, WarcRecordPy, WarcRecordTypePy,
|
|
112
|
+
has_block_digest_py, has_content_length_gte_py, has_content_length_lte_py, has_payload_digest_py,
|
|
113
|
+
has_record_type_py, has_valid_block_digest_py, has_valid_payload_digest_py, is_concurrent_py, is_http_py,
|
|
114
|
+
is_warc_10_py, is_warc_11_py,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// Copyright 2026 Janek Bevendorff
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
use super::impl_macros::*;
|
|
16
|
+
use crate::stream_io::{WarcReaderPy, WarcWriterPy, wrap_reader_stream, wrap_writer_stream};
|
|
17
|
+
use fastwarc::stream_io::brotli::{self, BrotliWriterOptions};
|
|
18
|
+
use fastwarc::stream_io::traits::{WarcRead, WarcWrite};
|
|
19
|
+
use pyo3::exceptions::PyValueError;
|
|
20
|
+
use pyo3::prelude::*;
|
|
21
|
+
use pyo3::types::PyBytes;
|
|
22
|
+
use std::io::{self, Read, Seek, Write};
|
|
23
|
+
use std::sync::Mutex;
|
|
24
|
+
|
|
25
|
+
/// Brotli reader.
|
|
26
|
+
///
|
|
27
|
+
/// :param inner: raw input stream, file-like object, file name, or URL
|
|
28
|
+
/// :param buffer_size: input buffer size
|
|
29
|
+
/// :param fsspec_args: arguments for :mod:`fsspec`, or ``False`` to disable it
|
|
30
|
+
#[pyclass(name = "BrotliReader", module = "fastwarc.stream_io", extends = WarcReaderPy, subclass)]
|
|
31
|
+
pub struct BrotliReaderPy {
|
|
32
|
+
pub(crate) inner: Mutex<Option<Box<dyn WarcRead + Send>>>,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// noinspection DuplicatedCode
|
|
36
|
+
#[pymethods]
|
|
37
|
+
impl BrotliReaderPy {
|
|
38
|
+
#[new]
|
|
39
|
+
#[pyo3(signature = (inner, buffer_size=64 << 10, fsspec_args=None))]
|
|
40
|
+
pub fn __new__(
|
|
41
|
+
py: Python<'_>,
|
|
42
|
+
inner: Py<PyAny>,
|
|
43
|
+
buffer_size: usize,
|
|
44
|
+
fsspec_args: Option<Py<PyAny>>,
|
|
45
|
+
) -> PyResult<PyClassInitializer<Self>> {
|
|
46
|
+
let options = brotli::BrotliReaderOptions { capacity: buffer_size };
|
|
47
|
+
let inner = wrap_reader_stream(
|
|
48
|
+
py,
|
|
49
|
+
inner,
|
|
50
|
+
fsspec_args,
|
|
51
|
+
|reader| -> io::Result<Box<dyn WarcRead + Send>> {
|
|
52
|
+
Ok(Box::new(brotli::BrotliReader::with_options(reader, options)))
|
|
53
|
+
},
|
|
54
|
+
|path| Ok(Box::new(brotli::BrotliReader::from_path_with_options(path, options)?)),
|
|
55
|
+
)?;
|
|
56
|
+
Ok(PyClassInitializer::from(WarcReaderPy::__new__()).add_subclass(Self {
|
|
57
|
+
inner: Mutex::new(Some(inner)),
|
|
58
|
+
}))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#[pyo3(signature = (size=-1))]
|
|
62
|
+
pub fn read<'py>(&self, py: Python<'py>, size: i128) -> PyResult<Bound<'py, PyBytes>> {
|
|
63
|
+
impl_reader_read!(self, py, size)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#[pyo3(signature = (offset, whence=0))]
|
|
67
|
+
pub fn seek(&self, offset: i128, whence: u8) -> PyResult<u64> {
|
|
68
|
+
impl_reader_seek!(self, offset, whence, seek)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#[pyo3(signature = (offset, whence=0))]
|
|
72
|
+
pub fn inner_seek(&self, offset: i128, whence: u8) -> PyResult<u64> {
|
|
73
|
+
impl_reader_seek!(self, offset, whence, inner_seek)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
pub fn tell(&self) -> PyResult<u64> {
|
|
77
|
+
forward_fn_call!(self, stream_position)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pub fn inner_tell(&self) -> PyResult<u64> {
|
|
81
|
+
forward_fn_call!(self, inner_stream_position)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#[allow(clippy::needless_question_mark)]
|
|
85
|
+
pub fn frame_start_position(&mut self) -> io::Result<Option<u64>> {
|
|
86
|
+
forward_fn_call!(self, frame_start_position)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
pub fn close(&self) -> PyResult<()> {
|
|
90
|
+
impl_reader_close!(self)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// Brotli writer.
|
|
95
|
+
///
|
|
96
|
+
/// :param inner: raw output stream, file-like object, file name, or URL
|
|
97
|
+
/// :param buffer_size: compression buffer size
|
|
98
|
+
/// :param fsspec_args: arguments for :mod:`fsspec`, or ``False`` to disable it
|
|
99
|
+
#[pyclass(name = "BrotliWriter", module = "fastwarc.stream_io", extends = WarcWriterPy, subclass)]
|
|
100
|
+
pub struct BrotliWriterPy {
|
|
101
|
+
pub(crate) inner: Mutex<Option<Box<dyn WarcWrite + Send>>>,
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// noinspection DuplicatedCode
|
|
105
|
+
#[pymethods]
|
|
106
|
+
impl BrotliWriterPy {
|
|
107
|
+
#[new]
|
|
108
|
+
#[pyo3(signature = (inner, buffer_size=8192, fsspec_args=None,))]
|
|
109
|
+
pub fn __new__(
|
|
110
|
+
py: Python<'_>,
|
|
111
|
+
inner: Py<PyAny>,
|
|
112
|
+
buffer_size: usize,
|
|
113
|
+
fsspec_args: Option<Py<PyAny>>,
|
|
114
|
+
) -> PyResult<PyClassInitializer<Self>> {
|
|
115
|
+
let options = brotli::BrotliWriterOptions {
|
|
116
|
+
capacity: buffer_size,
|
|
117
|
+
..BrotliWriterOptions::default()
|
|
118
|
+
};
|
|
119
|
+
let inner = wrap_writer_stream(
|
|
120
|
+
py,
|
|
121
|
+
inner,
|
|
122
|
+
fsspec_args,
|
|
123
|
+
|reader| -> io::Result<Box<dyn WarcWrite + Send>> {
|
|
124
|
+
Ok(Box::new(brotli::BrotliWriter::with_options(reader, options)))
|
|
125
|
+
},
|
|
126
|
+
|path| Ok(Box::new(brotli::BrotliWriter::from_path_with_options(path, options)?)),
|
|
127
|
+
)?;
|
|
128
|
+
Ok(PyClassInitializer::from(WarcWriterPy::__new__()).add_subclass(Self {
|
|
129
|
+
inner: Mutex::new(Some(inner)),
|
|
130
|
+
}))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
pub fn write(&self, data: &[u8]) -> PyResult<usize> {
|
|
134
|
+
impl_writer_write!(self, data)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
pub fn flush(&self) -> PyResult<()> {
|
|
138
|
+
forward_fn_call!(self, flush)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
pub fn finish(&self) -> PyResult<()> {
|
|
142
|
+
forward_fn_call!(self, finish)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
pub fn close(&self) -> PyResult<()> {
|
|
146
|
+
impl_writer_close!(self)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Copyright 2026 Janek Bevendorff
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
use super::impl_macros::*;
|
|
16
|
+
use crate::stream_io::{WarcReaderPy, WarcWriterPy, wrap_reader_stream, wrap_writer_stream};
|
|
17
|
+
use fastwarc::stream_io::chunked;
|
|
18
|
+
use fastwarc::stream_io::traits::{WarcRead, WarcWrite};
|
|
19
|
+
use pyo3::exceptions::PyValueError;
|
|
20
|
+
use pyo3::prelude::*;
|
|
21
|
+
use pyo3::types::PyBytes;
|
|
22
|
+
use std::io::{self, Read, Seek, Write};
|
|
23
|
+
use std::sync::Mutex;
|
|
24
|
+
|
|
25
|
+
/// HTTP chunked-transfer reader.
|
|
26
|
+
///
|
|
27
|
+
/// :param inner: raw input stream, file-like object, file name, or URL
|
|
28
|
+
/// :param buffer_size: input buffer size
|
|
29
|
+
/// :param fsspec_args: arguments for :mod:`fsspec`, or ``False`` to disable it
|
|
30
|
+
#[pyclass(name = "ChunkedReader", module = "fastwarc.stream_io", extends = WarcReaderPy, subclass)]
|
|
31
|
+
pub struct ChunkedReaderPy {
|
|
32
|
+
pub(crate) inner: Mutex<Option<Box<dyn WarcRead + Send>>>,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// noinspection DuplicatedCode
|
|
36
|
+
#[pymethods]
|
|
37
|
+
impl ChunkedReaderPy {
|
|
38
|
+
#[new]
|
|
39
|
+
#[pyo3(signature = (inner, buffer_size=4096, fsspec_args=None))]
|
|
40
|
+
pub fn __new__(
|
|
41
|
+
py: Python<'_>,
|
|
42
|
+
inner: Py<PyAny>,
|
|
43
|
+
buffer_size: usize,
|
|
44
|
+
fsspec_args: Option<Py<PyAny>>,
|
|
45
|
+
) -> PyResult<PyClassInitializer<Self>> {
|
|
46
|
+
let options = chunked::ChunkedReaderOptions { capacity: buffer_size };
|
|
47
|
+
let inner = wrap_reader_stream(
|
|
48
|
+
py,
|
|
49
|
+
inner,
|
|
50
|
+
fsspec_args,
|
|
51
|
+
|reader| -> io::Result<Box<dyn WarcRead + Send>> {
|
|
52
|
+
Ok(Box::new(chunked::ChunkedReader::with_options(reader, options)))
|
|
53
|
+
},
|
|
54
|
+
|path| Ok(Box::new(chunked::ChunkedReader::from_path_with_options(path, options)?)),
|
|
55
|
+
)?;
|
|
56
|
+
Ok(PyClassInitializer::from(WarcReaderPy::__new__()).add_subclass(Self {
|
|
57
|
+
inner: Mutex::new(Some(inner)),
|
|
58
|
+
}))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#[pyo3(signature = (size=-1))]
|
|
62
|
+
pub fn read<'py>(&self, py: Python<'py>, size: i128) -> PyResult<Bound<'py, PyBytes>> {
|
|
63
|
+
impl_reader_read!(self, py, size)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#[pyo3(signature = (offset, whence=0))]
|
|
67
|
+
pub fn seek(&self, offset: i128, whence: u8) -> PyResult<u64> {
|
|
68
|
+
impl_reader_seek!(self, offset, whence, seek)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#[pyo3(signature = (offset, whence=0))]
|
|
72
|
+
pub fn inner_seek(&self, offset: i128, whence: u8) -> PyResult<u64> {
|
|
73
|
+
impl_reader_seek!(self, offset, whence, inner_seek)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
pub fn tell(&self) -> PyResult<u64> {
|
|
77
|
+
forward_fn_call!(self, stream_position)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pub fn inner_tell(&self) -> PyResult<u64> {
|
|
81
|
+
forward_fn_call!(self, inner_stream_position)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
pub fn close(&self) -> PyResult<()> {
|
|
85
|
+
impl_reader_close!(self)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// HTTP chunked-transfer writer.
|
|
90
|
+
///
|
|
91
|
+
/// :param inner: raw output stream, file-like object, file name, or URL
|
|
92
|
+
/// :param min_chunk_size: minimum chunk size
|
|
93
|
+
/// :param fsspec_args: arguments for :mod:`fsspec`, or ``False`` to disable it
|
|
94
|
+
#[pyclass(name = "ChunkedWriter", module = "fastwarc.stream_io", extends = WarcWriterPy, subclass)]
|
|
95
|
+
pub struct ChunkedWriterPy {
|
|
96
|
+
pub(crate) inner: Mutex<Option<Box<dyn WarcWrite + Send>>>,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// noinspection DuplicatedCode
|
|
100
|
+
#[pymethods]
|
|
101
|
+
impl ChunkedWriterPy {
|
|
102
|
+
#[new]
|
|
103
|
+
#[pyo3(signature = (inner, min_chunk_size=1024, fsspec_args=None,))]
|
|
104
|
+
pub fn __new__(
|
|
105
|
+
py: Python<'_>,
|
|
106
|
+
inner: Py<PyAny>,
|
|
107
|
+
min_chunk_size: usize,
|
|
108
|
+
fsspec_args: Option<Py<PyAny>>,
|
|
109
|
+
) -> PyResult<PyClassInitializer<Self>> {
|
|
110
|
+
let options = chunked::ChunkedWriterOptions { min_chunk_size };
|
|
111
|
+
let inner = wrap_writer_stream(
|
|
112
|
+
py,
|
|
113
|
+
inner,
|
|
114
|
+
fsspec_args,
|
|
115
|
+
|reader| -> io::Result<Box<dyn WarcWrite + Send>> {
|
|
116
|
+
Ok(Box::new(chunked::ChunkedWriter::with_options(reader, options)))
|
|
117
|
+
},
|
|
118
|
+
|path| Ok(Box::new(chunked::ChunkedWriter::from_path_with_options(path, options)?)),
|
|
119
|
+
)?;
|
|
120
|
+
Ok(PyClassInitializer::from(WarcWriterPy::__new__()).add_subclass(Self {
|
|
121
|
+
inner: Mutex::new(Some(inner)),
|
|
122
|
+
}))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
pub fn write(&self, data: &[u8]) -> PyResult<usize> {
|
|
126
|
+
impl_writer_write!(self, data)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
pub fn flush(&self) -> PyResult<()> {
|
|
130
|
+
forward_fn_call!(self, flush)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
pub fn finish(&self) -> PyResult<()> {
|
|
134
|
+
forward_fn_call!(self, finish)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
pub fn close(&self) -> PyResult<()> {
|
|
138
|
+
impl_writer_close!(self)
|
|
139
|
+
}
|
|
140
|
+
}
|