innertext 0.2.2__tar.gz → 0.2.4__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.
- {innertext-0.2.2 → innertext-0.2.4}/Cargo.lock +4 -4
- {innertext-0.2.2 → innertext-0.2.4}/Cargo.toml +1 -1
- {innertext-0.2.2 → innertext-0.2.4}/PKG-INFO +21 -1
- {innertext-0.2.2/bindings/innertext-python → innertext-0.2.4}/README.md +20 -0
- {innertext-0.2.2 → innertext-0.2.4/bindings/innertext-python}/README.md +20 -0
- {innertext-0.2.2 → innertext-0.2.4}/bindings/innertext-python/src/lib.rs +1 -1
- {innertext-0.2.2 → innertext-0.2.4}/crates/innertext-core/src/inner_text.rs +44 -0
- {innertext-0.2.2 → innertext-0.2.4}/crates/innertext-core/tests/spec_behavior.rs +42 -0
- {innertext-0.2.2 → innertext-0.2.4}/pyproject.toml +2 -1
- innertext-0.2.4/python/innertext/__init__.py +8 -0
- innertext-0.2.4/python/innertext/py.typed +0 -0
- {innertext-0.2.2 → innertext-0.2.4}/bindings/innertext-python/Cargo.toml +0 -0
- {innertext-0.2.2 → innertext-0.2.4}/crates/innertext-core/Cargo.toml +0 -0
- {innertext-0.2.2 → innertext-0.2.4}/crates/innertext-core/src/css.rs +0 -0
- {innertext-0.2.2 → innertext-0.2.4}/crates/innertext-core/src/lib.rs +0 -0
- {innertext-0.2.2 → innertext-0.2.4}/crates/innertext-core/src/model.rs +0 -0
- {innertext-0.2.2 → innertext-0.2.4}/crates/innertext-core/src/render.rs +0 -0
- {innertext-0.2.2 → innertext-0.2.4}/crates/innertext-core/src/text_content.rs +0 -0
- {innertext-0.2.2 → innertext-0.2.4}/crates/innertext-core/tests/chromium_parity.rs +0 -0
- {innertext-0.2.2 → innertext-0.2.4}/python/innertext/__init__.pyi +0 -0
|
@@ -115,7 +115,7 @@ dependencies = [
|
|
|
115
115
|
|
|
116
116
|
[[package]]
|
|
117
117
|
name = "innertext-core"
|
|
118
|
-
version = "0.2.
|
|
118
|
+
version = "0.2.4"
|
|
119
119
|
dependencies = [
|
|
120
120
|
"html5ever",
|
|
121
121
|
"markup5ever",
|
|
@@ -128,7 +128,7 @@ dependencies = [
|
|
|
128
128
|
|
|
129
129
|
[[package]]
|
|
130
130
|
name = "innertext-java"
|
|
131
|
-
version = "0.2.
|
|
131
|
+
version = "0.2.4"
|
|
132
132
|
dependencies = [
|
|
133
133
|
"innertext-core",
|
|
134
134
|
"jni",
|
|
@@ -137,7 +137,7 @@ dependencies = [
|
|
|
137
137
|
|
|
138
138
|
[[package]]
|
|
139
139
|
name = "innertext-node"
|
|
140
|
-
version = "0.2.
|
|
140
|
+
version = "0.2.4"
|
|
141
141
|
dependencies = [
|
|
142
142
|
"innertext-core",
|
|
143
143
|
"napi",
|
|
@@ -146,7 +146,7 @@ dependencies = [
|
|
|
146
146
|
|
|
147
147
|
[[package]]
|
|
148
148
|
name = "innertext-python"
|
|
149
|
-
version = "0.2.
|
|
149
|
+
version = "0.2.4"
|
|
150
150
|
dependencies = [
|
|
151
151
|
"innertext-core",
|
|
152
152
|
"pyo3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: innertext
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Environment :: Console
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -55,6 +55,26 @@ print(innertext.inner_text(html)) # "Hello World"
|
|
|
55
55
|
print(innertext.text_content(html)) # "Hello hidden console.log("not shown")"
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
## Packaging Notes
|
|
59
|
+
|
|
60
|
+
The published Python package uses a two-layer layout:
|
|
61
|
+
- `innertext` is a regular Python package with a runtime `__init__.py`
|
|
62
|
+
- the compiled Rust extension is loaded as `innertext._innertext`
|
|
63
|
+
|
|
64
|
+
This avoids namespace-package import issues and ensures `import innertext`
|
|
65
|
+
always exposes `inner_text`, `outer_text`, and `text_content`.
|
|
66
|
+
|
|
67
|
+
## Type Hints
|
|
68
|
+
|
|
69
|
+
This package ships with PEP 561-compatible typing metadata:
|
|
70
|
+
- `innertext/__init__.pyi` defines the public API types
|
|
71
|
+
- `innertext/py.typed` marks the package as typed
|
|
72
|
+
|
|
73
|
+
Type checkers such as mypy and pyright can therefore infer:
|
|
74
|
+
- `inner_text(html: str) -> str`
|
|
75
|
+
- `outer_text(html: str) -> str`
|
|
76
|
+
- `text_content(html: str) -> str`
|
|
77
|
+
|
|
58
78
|
## API
|
|
59
79
|
|
|
60
80
|
### Functions
|
|
@@ -27,6 +27,26 @@ print(innertext.inner_text(html)) # "Hello World"
|
|
|
27
27
|
print(innertext.text_content(html)) # "Hello hidden console.log("not shown")"
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
## Packaging Notes
|
|
31
|
+
|
|
32
|
+
The published Python package uses a two-layer layout:
|
|
33
|
+
- `innertext` is a regular Python package with a runtime `__init__.py`
|
|
34
|
+
- the compiled Rust extension is loaded as `innertext._innertext`
|
|
35
|
+
|
|
36
|
+
This avoids namespace-package import issues and ensures `import innertext`
|
|
37
|
+
always exposes `inner_text`, `outer_text`, and `text_content`.
|
|
38
|
+
|
|
39
|
+
## Type Hints
|
|
40
|
+
|
|
41
|
+
This package ships with PEP 561-compatible typing metadata:
|
|
42
|
+
- `innertext/__init__.pyi` defines the public API types
|
|
43
|
+
- `innertext/py.typed` marks the package as typed
|
|
44
|
+
|
|
45
|
+
Type checkers such as mypy and pyright can therefore infer:
|
|
46
|
+
- `inner_text(html: str) -> str`
|
|
47
|
+
- `outer_text(html: str) -> str`
|
|
48
|
+
- `text_content(html: str) -> str`
|
|
49
|
+
|
|
30
50
|
## API
|
|
31
51
|
|
|
32
52
|
### Functions
|
|
@@ -27,6 +27,26 @@ print(innertext.inner_text(html)) # "Hello World"
|
|
|
27
27
|
print(innertext.text_content(html)) # "Hello hidden console.log("not shown")"
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
## Packaging Notes
|
|
31
|
+
|
|
32
|
+
The published Python package uses a two-layer layout:
|
|
33
|
+
- `innertext` is a regular Python package with a runtime `__init__.py`
|
|
34
|
+
- the compiled Rust extension is loaded as `innertext._innertext`
|
|
35
|
+
|
|
36
|
+
This avoids namespace-package import issues and ensures `import innertext`
|
|
37
|
+
always exposes `inner_text`, `outer_text`, and `text_content`.
|
|
38
|
+
|
|
39
|
+
## Type Hints
|
|
40
|
+
|
|
41
|
+
This package ships with PEP 561-compatible typing metadata:
|
|
42
|
+
- `innertext/__init__.pyi` defines the public API types
|
|
43
|
+
- `innertext/py.typed` marks the package as typed
|
|
44
|
+
|
|
45
|
+
Type checkers such as mypy and pyright can therefore infer:
|
|
46
|
+
- `inner_text(html: str) -> str`
|
|
47
|
+
- `outer_text(html: str) -> str`
|
|
48
|
+
- `text_content(html: str) -> str`
|
|
49
|
+
|
|
30
50
|
## API
|
|
31
51
|
|
|
32
52
|
### Functions
|
|
@@ -29,7 +29,7 @@ fn text_content(html: &str) -> Result<String, PyErr> {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
#[pymodule]
|
|
32
|
-
fn
|
|
32
|
+
fn _innertext(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
33
33
|
m.add_function(wrap_pyfunction!(inner_text, m)?)?;
|
|
34
34
|
m.add_function(wrap_pyfunction!(outer_text, m)?)?;
|
|
35
35
|
m.add_function(wrap_pyfunction!(text_content, m)?)?;
|
|
@@ -22,6 +22,8 @@ pub(crate) fn extract(dom: &RcDom) -> String {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
let normalized = normalize_tokens(tokens);
|
|
25
|
+
let normalized = drop_inter_block_separator_spaces(normalized);
|
|
26
|
+
let normalized = drop_boundaries_between_required_breaks(normalized);
|
|
25
27
|
let folded = fold_required_break_runs(normalized);
|
|
26
28
|
serialize_tokens(folded)
|
|
27
29
|
}
|
|
@@ -133,6 +135,48 @@ fn normalize_tokens(mut tokens: Vec<RenderToken>) -> Vec<RenderToken> {
|
|
|
133
135
|
tokens
|
|
134
136
|
}
|
|
135
137
|
|
|
138
|
+
fn drop_inter_block_separator_spaces(tokens: Vec<RenderToken>) -> Vec<RenderToken> {
|
|
139
|
+
let mut out = Vec::with_capacity(tokens.len());
|
|
140
|
+
|
|
141
|
+
for i in 0..tokens.len() {
|
|
142
|
+
if let RenderToken::Text(text) = &tokens[i] {
|
|
143
|
+
let prev_is_marker = i > 0
|
|
144
|
+
&& matches!(
|
|
145
|
+
tokens.get(i - 1),
|
|
146
|
+
Some(RenderToken::RequiredBreak(_) | RenderToken::CollapseBoundary)
|
|
147
|
+
);
|
|
148
|
+
let next_is_marker = matches!(
|
|
149
|
+
tokens.get(i + 1),
|
|
150
|
+
Some(RenderToken::RequiredBreak(_) | RenderToken::CollapseBoundary)
|
|
151
|
+
);
|
|
152
|
+
if text.chars().all(|ch| ch == ' ') && prev_is_marker && next_is_marker {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
out.push(tokens[i].clone());
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
out
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
fn drop_boundaries_between_required_breaks(tokens: Vec<RenderToken>) -> Vec<RenderToken> {
|
|
163
|
+
let mut out = Vec::with_capacity(tokens.len());
|
|
164
|
+
|
|
165
|
+
for i in 0..tokens.len() {
|
|
166
|
+
if matches!(tokens[i], RenderToken::CollapseBoundary) {
|
|
167
|
+
let prev_is_break =
|
|
168
|
+
i > 0 && matches!(tokens.get(i - 1), Some(RenderToken::RequiredBreak(_)));
|
|
169
|
+
let next_is_break = matches!(tokens.get(i + 1), Some(RenderToken::RequiredBreak(_)));
|
|
170
|
+
if prev_is_break && next_is_break {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
out.push(tokens[i].clone());
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
out
|
|
178
|
+
}
|
|
179
|
+
|
|
136
180
|
fn fold_required_break_runs(tokens: Vec<RenderToken>) -> Vec<RenderToken> {
|
|
137
181
|
let mut out = Vec::new();
|
|
138
182
|
let mut i = 0;
|
|
@@ -51,6 +51,48 @@ fn sibling_blocks_have_two_newlines_between_them() {
|
|
|
51
51
|
assert_eq!(got, "Block A\nBlock B");
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
#[test]
|
|
55
|
+
fn formatting_whitespace_between_p_tags_does_not_change_inner_text() {
|
|
56
|
+
let compact = "<p>Some text</p><p>Some more text which <span>is inside a span</span></p><p style='display: none'>This text is hidden</p>";
|
|
57
|
+
let spaced = "<p>Some text</p>\n\n<p>Some more text which <span>is inside a span</span></p><p style='display: none'>This text is hidden</p>";
|
|
58
|
+
let heavily_spaced = "<p>Some text</p>\n\n\n\n <p>Some more text which <span>is inside a span</span></p><p style='display: none'>This text is hidden</p>";
|
|
59
|
+
|
|
60
|
+
let expected = inner_text_from_html(compact).expect("must extract compact text");
|
|
61
|
+
let spaced_actual = inner_text_from_html(spaced).expect("must extract spaced text");
|
|
62
|
+
let heavily_spaced_actual =
|
|
63
|
+
inner_text_from_html(heavily_spaced).expect("must extract heavily spaced text");
|
|
64
|
+
|
|
65
|
+
assert_eq!(spaced_actual, expected);
|
|
66
|
+
assert_eq!(heavily_spaced_actual, expected);
|
|
67
|
+
assert_eq!(
|
|
68
|
+
expected,
|
|
69
|
+
"Some text\n\nSome more text which is inside a span"
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#[test]
|
|
74
|
+
fn formatting_whitespace_between_div_tags_does_not_change_inner_text() {
|
|
75
|
+
let compact = "<div>Block A</div><div>Block B</div>";
|
|
76
|
+
let spaced = "<div>Block A</div>\n\n\n <div>Block B</div>";
|
|
77
|
+
|
|
78
|
+
let expected = inner_text_from_html(compact).expect("must extract compact text");
|
|
79
|
+
let actual = inner_text_from_html(spaced).expect("must extract spaced text");
|
|
80
|
+
|
|
81
|
+
assert_eq!(actual, expected);
|
|
82
|
+
assert_eq!(expected, "Block A\nBlock B");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
#[test]
|
|
86
|
+
fn hidden_block_with_separator_whitespace_does_not_add_extra_gaps() {
|
|
87
|
+
let compact = "<p>One</p><p style='display:none'>Hidden</p><p>Two</p>";
|
|
88
|
+
let spaced = "<p>One</p>\n\n\n<p style='display:none'>Hidden</p>\n\n\n<p>Two</p>";
|
|
89
|
+
|
|
90
|
+
let expected = inner_text_from_html(compact).expect("must extract compact text");
|
|
91
|
+
let actual = inner_text_from_html(spaced).expect("must extract spaced text");
|
|
92
|
+
|
|
93
|
+
assert_eq!(actual, expected);
|
|
94
|
+
}
|
|
95
|
+
|
|
54
96
|
// ── outerText getter ───────────────────────────────────────────────────────
|
|
55
97
|
|
|
56
98
|
#[test]
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "innertext"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.4"
|
|
8
8
|
description = "Pure Rust WHATWG-compliant innerText, outerText, and textContent extraction"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -38,5 +38,6 @@ Homepage = "https://github.com/joshfayer/innertext"
|
|
|
38
38
|
|
|
39
39
|
[tool.maturin]
|
|
40
40
|
features = ["pyo3/extension-module", "pyo3/abi3-py37"]
|
|
41
|
+
module-name = "innertext._innertext"
|
|
41
42
|
manifest-path = "bindings/innertext-python/Cargo.toml"
|
|
42
43
|
python-source = "python"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""innertext - Pure Rust WHATWG-compliant text extraction from HTML.
|
|
2
|
+
|
|
3
|
+
This package re-exports functions from the compiled Rust extension.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from ._innertext import inner_text, outer_text, text_content
|
|
7
|
+
|
|
8
|
+
__all__ = ["inner_text", "outer_text", "text_content"]
|
|
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
|