feedparser-rs 0.7.2__cp315-cp315-win_amd64.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.
- feedparser_rs/__init__.py +39 -0
- feedparser_rs/_feedparser_rs.cp315-win_amd64.pyd +0 -0
- feedparser_rs/py.typed +1 -0
- feedparser_rs-0.7.2.dist-info/METADATA +276 -0
- feedparser_rs-0.7.2.dist-info/RECORD +7 -0
- feedparser_rs-0.7.2.dist-info/WHEEL +4 -0
- feedparser_rs-0.7.2.dist-info/sboms/feedparser-rs-py.cyclonedx.json +6684 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""
|
|
2
|
+
feedparser_rs: High-performance RSS/Atom/JSON Feed parser
|
|
3
|
+
|
|
4
|
+
Drop-in replacement for Python's feedparser library with 10-100x performance.
|
|
5
|
+
Written in Rust with PyO3 bindings for maximum speed and safety.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
>>> import feedparser_rs
|
|
9
|
+
>>> d = feedparser_rs.parse('<rss>...</rss>')
|
|
10
|
+
>>> print(d.feed.title)
|
|
11
|
+
>>> print(d.entries[0].published_parsed)
|
|
12
|
+
|
|
13
|
+
For full documentation, see: https://github.com/bug-ops/feedparser-rs
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from ._feedparser_rs import (
|
|
17
|
+
FeedParserDict,
|
|
18
|
+
ParserLimits,
|
|
19
|
+
__version__,
|
|
20
|
+
detect_format,
|
|
21
|
+
parse,
|
|
22
|
+
parse_url,
|
|
23
|
+
parse_url_with_limits,
|
|
24
|
+
parse_with_limits,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"FeedParserDict",
|
|
29
|
+
"ParserLimits",
|
|
30
|
+
"__version__",
|
|
31
|
+
"detect_format",
|
|
32
|
+
"parse",
|
|
33
|
+
"parse_url",
|
|
34
|
+
"parse_url_with_limits",
|
|
35
|
+
"parse_with_limits",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
# Type alias for better IDE support
|
|
39
|
+
ParseResult = FeedParserDict
|
|
Binary file
|
feedparser_rs/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# PEP 561 marker for type checking
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: feedparser-rs
|
|
3
|
+
Version: 0.7.2
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Rust
|
|
14
|
+
Classifier: Topic :: Text Processing :: Markup :: XML
|
|
15
|
+
Summary: High-performance RSS/Atom/JSON Feed parser with feedparser-compatible API
|
|
16
|
+
Keywords: rss,atom,feed,parser,feedparser,rust
|
|
17
|
+
License: MIT OR Apache-2.0
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
20
|
+
Project-URL: Documentation, https://github.com/bug-ops/feedparser-rs#readme
|
|
21
|
+
Project-URL: Homepage, https://github.com/bug-ops/feedparser-rs
|
|
22
|
+
Project-URL: Repository, https://github.com/bug-ops/feedparser-rs
|
|
23
|
+
|
|
24
|
+
# feedparser-rs
|
|
25
|
+
|
|
26
|
+
[](https://pypi.org/project/feedparser-rs/)
|
|
27
|
+
[](https://pypi.org/project/feedparser-rs/)
|
|
28
|
+
[](LICENSE-MIT)
|
|
29
|
+
|
|
30
|
+
High-performance RSS/Atom/JSON Feed parser for Python with feedparser-compatible API.
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- **Fast**: Native Rust implementation via PyO3
|
|
35
|
+
- **HTTP fetching**: Built-in URL fetching with compression (gzip, deflate, brotli)
|
|
36
|
+
- **Conditional GET**: ETag/Last-Modified support for efficient polling
|
|
37
|
+
- **Tolerant parsing**: Bozo flag for graceful handling of malformed feeds
|
|
38
|
+
- **Sanitized by default**: HTML-bearing fields are sanitized against XSS unless explicitly disabled via `sanitize_html=False`
|
|
39
|
+
- **Multi-format**: RSS 0.9x/1.0/2.0, Atom 0.3/1.0, JSON Feed 1.0/1.1
|
|
40
|
+
- **Podcast support**: iTunes and Podcast 2.0 namespace extensions
|
|
41
|
+
- **JSON Feed extensions**: `_`-prefixed custom objects (e.g. `_cast`) captured as `feed.json_extensions`/`entry.json_extensions` dicts
|
|
42
|
+
- **feedparser-compatible**: Dict-style access, field aliases, same API patterns
|
|
43
|
+
- **DoS protection**: Built-in resource limits
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install feedparser-rs
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> **Important:** Requires Python 3.10 or later.
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
### Basic Parsing
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
import feedparser_rs
|
|
59
|
+
|
|
60
|
+
# Parse from string, bytes, or URL (auto-detected)
|
|
61
|
+
d = feedparser_rs.parse("<rss>...</rss>")
|
|
62
|
+
d = feedparser_rs.parse(b"<rss>...</rss>")
|
|
63
|
+
d = feedparser_rs.parse("https://example.com/feed.xml") # URL auto-detected
|
|
64
|
+
|
|
65
|
+
# Attribute-style access (feedparser-compatible)
|
|
66
|
+
print(d.feed.title)
|
|
67
|
+
print(d.version) # "rss20", "atom10", etc.
|
|
68
|
+
print(d.bozo) # True if parsing errors occurred
|
|
69
|
+
|
|
70
|
+
# Dict-style access (feedparser-compatible)
|
|
71
|
+
print(d["feed"]["title"])
|
|
72
|
+
print(d["entries"][0]["link"])
|
|
73
|
+
|
|
74
|
+
for entry in d.entries:
|
|
75
|
+
print(entry.title)
|
|
76
|
+
print(entry.published_parsed) # time.struct_time
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
> **Note:** Date fields like `published_parsed` return `time.struct_time` for feedparser compatibility.
|
|
80
|
+
|
|
81
|
+
### Fetching from URL
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import feedparser_rs
|
|
85
|
+
|
|
86
|
+
# Option 1: Auto-detection (recommended)
|
|
87
|
+
d = feedparser_rs.parse("https://example.com/feed.xml")
|
|
88
|
+
|
|
89
|
+
# Option 2: Explicit URL function
|
|
90
|
+
d = feedparser_rs.parse_url("https://example.com/feed.xml")
|
|
91
|
+
|
|
92
|
+
# With conditional GET for efficient polling
|
|
93
|
+
d = feedparser_rs.parse("https://example.com/feed.xml", etag=cached_etag, modified=cached_modified)
|
|
94
|
+
if d.status == 304:
|
|
95
|
+
print("Feed not modified")
|
|
96
|
+
|
|
97
|
+
# With custom limits
|
|
98
|
+
limits = feedparser_rs.ParserLimits(max_entries=100)
|
|
99
|
+
d = feedparser_rs.parse_with_limits("https://example.com/feed.xml", limits=limits)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
> **Tip:** URL fetching supports automatic compression (gzip, deflate, brotli) and follows redirects.
|
|
103
|
+
|
|
104
|
+
### HTML Sanitization
|
|
105
|
+
|
|
106
|
+
HTML-bearing fields (titles, summaries, `content:encoded`, etc.) are sanitized by default to
|
|
107
|
+
strip dangerous markup, matching feedparser's sanitizer coverage:
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
import feedparser_rs
|
|
111
|
+
|
|
112
|
+
# Default: sanitize_html=True, resolve_relative_uris=True
|
|
113
|
+
d = feedparser_rs.parse(feed_data)
|
|
114
|
+
|
|
115
|
+
# Trust this feed source; skip the sanitizer pass
|
|
116
|
+
d = feedparser_rs.parse(feed_data, sanitize_html=False)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
> **Important:** Disabling `sanitize_html` is **not recommended** unless you fully trust the feed
|
|
120
|
+
> source and have other security measures in place.
|
|
121
|
+
|
|
122
|
+
## Migration from feedparser
|
|
123
|
+
|
|
124
|
+
feedparser-rs is designed as a drop-in replacement for Python feedparser:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
# Drop-in replacement
|
|
128
|
+
import feedparser_rs as feedparser
|
|
129
|
+
|
|
130
|
+
# Same API patterns work
|
|
131
|
+
d = feedparser.parse("https://example.com/feed.xml")
|
|
132
|
+
print(d.feed.title)
|
|
133
|
+
print(d["feed"]["title"]) # Dict-style access works too
|
|
134
|
+
print(d.entries[0].link)
|
|
135
|
+
|
|
136
|
+
# Deprecated field names supported
|
|
137
|
+
print(d.feed.description) # → d.feed.subtitle
|
|
138
|
+
print(d.channel.title) # → d.feed.title
|
|
139
|
+
print(d.items[0].guid) # → d.entries[0].id
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Supported Field Aliases
|
|
143
|
+
|
|
144
|
+
| Old Name | Maps To |
|
|
145
|
+
|----------|---------|
|
|
146
|
+
| `feed.description` | `feed.subtitle` or `feed.summary` |
|
|
147
|
+
| `feed.tagline` | `feed.subtitle` |
|
|
148
|
+
| `feed.copyright` | `feed.rights` |
|
|
149
|
+
| `feed.modified` | `feed.updated` |
|
|
150
|
+
| `channel` | `feed` |
|
|
151
|
+
| `items` | `entries` |
|
|
152
|
+
| `entry.guid` | `entry.id` |
|
|
153
|
+
| `entry.description` | `entry.summary` |
|
|
154
|
+
| `entry.issued` | `entry.published` |
|
|
155
|
+
|
|
156
|
+
## Advanced Usage
|
|
157
|
+
|
|
158
|
+
### Custom Resource Limits
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
import feedparser_rs
|
|
162
|
+
|
|
163
|
+
limits = feedparser_rs.ParserLimits(
|
|
164
|
+
max_feed_size_bytes=50_000_000, # 50 MB
|
|
165
|
+
max_entries=5_000,
|
|
166
|
+
max_authors=20,
|
|
167
|
+
max_links_per_entry=50,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
d = feedparser_rs.parse_with_limits(feed_data, limits=limits)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Format Detection
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
import feedparser_rs
|
|
177
|
+
|
|
178
|
+
version = feedparser_rs.detect_format(feed_data)
|
|
179
|
+
print(version) # "rss20", "atom10", "json11", etc.
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Podcast Support
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
import feedparser_rs
|
|
186
|
+
|
|
187
|
+
d = feedparser_rs.parse(podcast_feed)
|
|
188
|
+
|
|
189
|
+
# iTunes metadata
|
|
190
|
+
if d.feed.itunes:
|
|
191
|
+
print(d.feed.itunes.author)
|
|
192
|
+
print(d.feed.itunes.categories)
|
|
193
|
+
|
|
194
|
+
# Episode metadata
|
|
195
|
+
for entry in d.entries:
|
|
196
|
+
if entry.itunes:
|
|
197
|
+
print(f"Duration: {entry.itunes.duration}") # e.g. "01:30:00"
|
|
198
|
+
|
|
199
|
+
# Podcast 2.0: episode-level value split, chat rooms, etc.
|
|
200
|
+
if entry.podcast and entry.podcast.value:
|
|
201
|
+
print(f"Time splits: {len(entry.podcast.value.time_splits)}")
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## API Reference
|
|
205
|
+
|
|
206
|
+
### Functions
|
|
207
|
+
|
|
208
|
+
- `parse(source, etag=None, modified=None, user_agent=None, sanitize_html=True, resolve_relative_uris=True)` — Parse feed from bytes, str, or URL (auto-detected)
|
|
209
|
+
- `parse_url(url, etag=None, modified=None, user_agent=None, sanitize_html=True, resolve_relative_uris=True)` — Fetch and parse feed from URL
|
|
210
|
+
- `parse_with_limits(source, etag=None, modified=None, user_agent=None, limits=None, sanitize_html=True, resolve_relative_uris=True)` — Parse with custom resource limits
|
|
211
|
+
- `parse_url_with_limits(url, etag=None, modified=None, user_agent=None, limits=None, sanitize_html=True, resolve_relative_uris=True)` — Fetch and parse with custom limits
|
|
212
|
+
- `detect_format(source)` — Detect feed format without full parsing
|
|
213
|
+
|
|
214
|
+
### Classes
|
|
215
|
+
|
|
216
|
+
- `FeedParserDict` — Parsed feed result (supports both attribute and dict-style access)
|
|
217
|
+
- `.feed` / `['feed']` — Feed metadata
|
|
218
|
+
- `.entries` / `['entries']` — List of entries
|
|
219
|
+
- `.bozo` — True if parsing errors occurred
|
|
220
|
+
- `.version` — Feed version string
|
|
221
|
+
- `.encoding` — Character encoding
|
|
222
|
+
- `.status` — HTTP status code (for URL fetches)
|
|
223
|
+
- `.etag` — ETag header (for conditional GET)
|
|
224
|
+
- `.modified` — Last-Modified header (for conditional GET)
|
|
225
|
+
|
|
226
|
+
- `ParserLimits` — Resource limits configuration
|
|
227
|
+
|
|
228
|
+
## Performance
|
|
229
|
+
|
|
230
|
+
Benchmarks vs Python feedparser on Apple M1 Pro:
|
|
231
|
+
|
|
232
|
+
| Operation | feedparser-rs | Python feedparser | Speedup |
|
|
233
|
+
|-----------|---------------|-------------------|---------|
|
|
234
|
+
| Parse 2 KB RSS | 0.01 ms | 0.9 ms | **90x** |
|
|
235
|
+
| Parse 20 KB RSS | 0.09 ms | 8.5 ms | **94x** |
|
|
236
|
+
| Parse 200 KB RSS | 0.94 ms | 85 ms | **90x** |
|
|
237
|
+
|
|
238
|
+
> **Tip:** For maximum performance, pass `bytes` instead of `str` to avoid UTF-8 re-encoding.
|
|
239
|
+
|
|
240
|
+
## Platform Support
|
|
241
|
+
|
|
242
|
+
Pre-built wheels available for:
|
|
243
|
+
|
|
244
|
+
| Platform | Architecture |
|
|
245
|
+
|----------|--------------|
|
|
246
|
+
| macOS | Intel (x64), Apple Silicon (arm64) |
|
|
247
|
+
| Linux | x64, arm64 |
|
|
248
|
+
| Windows | x64 |
|
|
249
|
+
|
|
250
|
+
Supported Python versions: 3.10, 3.11, 3.12, 3.13, 3.14
|
|
251
|
+
|
|
252
|
+
## Development
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
git clone https://github.com/bug-ops/feedparser-rs
|
|
256
|
+
cd feedparser-rs/crates/feedparser-rs-py
|
|
257
|
+
pip install maturin
|
|
258
|
+
maturin develop
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## License
|
|
262
|
+
|
|
263
|
+
Licensed under either of:
|
|
264
|
+
|
|
265
|
+
- [Apache License, Version 2.0](../../LICENSE-APACHE)
|
|
266
|
+
- [MIT License](../../LICENSE-MIT)
|
|
267
|
+
|
|
268
|
+
at your option.
|
|
269
|
+
|
|
270
|
+
## Links
|
|
271
|
+
|
|
272
|
+
- [GitHub](https://github.com/bug-ops/feedparser-rs)
|
|
273
|
+
- [PyPI](https://pypi.org/project/feedparser-rs/)
|
|
274
|
+
- [Rust API Documentation](https://docs.rs/feedparser-rs)
|
|
275
|
+
- [Changelog](../../CHANGELOG.md)
|
|
276
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
feedparser_rs/__init__.py,sha256=qDKdYY65hMg4BP8uI7Z3byvMmrKqo5oyGYuarY0ahPA,910
|
|
2
|
+
feedparser_rs/_feedparser_rs.cp315-win_amd64.pyd,sha256=qQrrJ0uKQQLWDHjQfyqEfsh4_q6bO1XV0-VAqS5C5FU,6905856
|
|
3
|
+
feedparser_rs/py.typed,sha256=H2JJLtf0lnw9xdu8JDmV7kAT90Z8HJnavchzQ5jnSek,36
|
|
4
|
+
feedparser_rs-0.7.2.dist-info/METADATA,sha256=sfkXp2u3_EQI8NsKYjDd838MJLluAHBRyHBr4b8yOqU,9074
|
|
5
|
+
feedparser_rs-0.7.2.dist-info/WHEEL,sha256=Q5QdGvjBfn8x6ihg-B4VQuuGKk7DT67L9DVrwEH_qj8,97
|
|
6
|
+
feedparser_rs-0.7.2.dist-info/sboms/feedparser-rs-py.cyclonedx.json,sha256=Fk3sLmyQoqgtP0dhLFiZETgtsMthfDhEmR6tSDPMtJY,215588
|
|
7
|
+
feedparser_rs-0.7.2.dist-info/RECORD,,
|