Skip to content

Navigation Menu

Sign in
Sign up

fix: fall back to sniffed encoding when a page omits an explicit charset - #127

Open
Smengerl wants to merge 1 commit into
j6k4m8:master from
Smengerl:fix/rss-encoding-fallback
Open

fix: fall back to sniffed encoding when a page omits an explicit charset #127
Smengerl wants to merge 1 commit into
j6k4m8:master from
Smengerl:fix/rss-encoding-fallback

Conversation

@Smengerl

@Smengerl Smengerl commented Aug 3, 2026
edited
Loading

Copy link
Copy Markdown
Contributor

Summary

  • requests defaults undeclared text/* charsets to ISO-8859-1 per RFC 2616. When a fetched article page's Content-Type header doesn't declare a charset, this mangles UTF-8 pages (accented characters etc. get decoded as the wrong encoding).
  • Fall back to response.apparent_encoding (requests' own content-sniffing) in that case, before the body is read - so _story_from_response sees correctly decoded text.
  • No behavior change for pages that do declare a charset in their header.

Test plan

  • pytest goosepaper/storyprovider/test_rss.py - 11 passed (9 existing + 2 new: an undeclared charset gets sniffed and decoded correctly; a declared charset is left untouched, even when sniffing would've suggested something different)
  • Full suite (pytest) - 80 passed

Merge overlap note

Verified by locally merging every pairwise combination of my currently open PRs against master. This one produces a mechanical (non-semantic) merge conflict with #121, #128, #135 - all touch rss.py/test_rss.py at nearby insertion points. No logic overlap in any case checked.

Smengerl added a commit to Smengerl/goosepaper-logicpuzzles that referenced this pull request Aug 5, 2026
Backport of j6k4m8#127's own tests - the fix (response.apparent_encoding
fallback in rss.py) was already in mainline, but its two tests had
been lost somewhere along the way (likely during one of the several
test_rss.py consolidation passes). Ported verbatim from PR j6k4m8#127.

Smengerl commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Part of a merge-order check across all my open PRs (full breakdown on #124). This PR conflicts with #135, #128, and #121 — the most contested spot in storyprovider/rss.py's _story_from_response. Suggested order: last among the rss.py-only group, after #129#134#135#128.

j6k4m8 added a commit that referenced this pull request Aug 11, 2026
...es (#135)
## What
Absolutizes relative URLs (`<img src>`, `<source src>`, `<a href>`) in
the HTML body that `readability-lxml` extracts from a fetched article,
before it becomes a `Story`'s `body_html`.
## Why
`readability.Document.summary()` returns the extracted article body more
or less as-is from the source page's own markup. That markup can (and
often does) use relative URLs - e.g. `<img src="/assets/img/foo.svg">`
or a page-relative `<a href="../other-story/">`.
goosepaper renders the whole newspaper - every story from every source,
concatenated - as a single HTML document with one `base_url` for the
entire thing (`Goosepaper.to_pdf`, which sets it to the local
filesystem's `cwd`). There's no single correct base for a multi-origin
document, so a relative URL that survives into `body_html` resolves
against the wrong thing and silently fails. The most visible symptom is
a missing image and a WeasyPrint log line like:
```
Failed to load image at 'file:///assets/img/foo.svg': ... No such file or directory
```
## Fix
Right after `doc.summary()` extracts the body, absolutize `src`/`href`
on `<img>`, `<source>`, and `<a>` tags against the article's own final
URL (`response.url`, i.e. after any redirects) - the one point where a
single correct base is actually known, for that specific story.
Already-absolute URLs, protocol-relative URLs (`//cdn.example.com/...`),
and `data:` URIs are left untouched.
## Evidence
Reproduced live against current, publicly reachable caranddriver.com
review pages (fetched via the RSS feed, same code path this fix
touches):
-
https://www.caranddriver.com/reviews/a73298183/2026-lexus-rx-350h-hybrid-f-sport-test/
-
https://www.caranddriver.com/reviews/a73297467/2027-kia-telluride-x-pro-test/
Both extract body markup containing root-relative `src`/`href` values
(site design-token icon SVGs, author profile links). Before the fix
these are left relative in `body_html`; after the fix they resolve to
absolute `https://www.caranddriver.com/...` URLs, and nothing relative
is left behind.
## Testing
- New `TestMakeUrlsAbsolute` test class (7 cases): root-relative image
`src`, page-relative link `href`, already-absolute URLs left alone,
`data:` URIs left alone, no synthetic `<body>` wrapper leaked into the
result (bs4's `lxml` parser always wraps a bare fragment in
`<html><body>`, so the implementation uses `.decode_contents()` rather
than `str()` on the container), a true no-op returns the original string
object unchanged, and empty/missing input handled.
- Full existing test suite passes (85/85), including the `_FakeResponse`
fixture update needed to give it a realistic `.url` now that
`_story_from_response` reads it.
## Scope note
This is deliberately independent of #134 (duplicate embedded headline) -
different bug, different code path, no shared logic - so it's a separate
PR rather than bundled in.
## Merge overlap note
Verified by locally merging against `master` alongside my other
currently open PRs. This one produces a mechanical (non-semantic) merge
conflict with **#127, #134** - all touch `rss.py`/`test_rss.py` at
nearby points (#134 at the exact same insertion line in
`_story_from_response`, see its own description). No logic overlap in
any case checked.
Smengerl force-pushed the fix/rss-encoding-fallback branch from bb54f26 to 78b9dcc Compare August 14, 2026 20:21
requests defaults undeclared text/* charsets to ISO-8859-1 per RFC 2616,
mangling UTF-8 article pages that don't send a charset in Content-Type.
Use response.apparent_encoding instead whenever the header didn't declare one.
Smengerl force-pushed the fix/rss-encoding-fallback branch from 78b9dcc to f40cf82 Compare August 18, 2026 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

1 participant

AltStyle によって変換されたページ (->オリジナル) /