Skip to content

feat(content): a rewrite_link hook and embedded image helpers for the reader (0.4.3) - #10

Merged
baraline merged 1 commit into
mainfrom
feat/inline-document-images
Oct 8, 2026
Merged

baraline merged 1 commit into
mainfrom
feat/inline-document-images

Conversation

@baraline

@baraline baraline commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Why

A caller that mirrors memos into another system has to recognise the images a memo embeds and decide what they become, without parsing HTML or Markdown itself. EasyVista's editor keeps a pasted image as an attachment of the request and embeds it as <img src="@@EMBEDDED_IMAGE_PATH@@<DOCUMENT_ID>">. The reader wrote that as an ordinary image, which displays nowhere but in EasyVista.

What

  • from_transport(..., rewrite_link=callback) calls the callback with a Link for each link and image met outside code. An image is offered before the link around it, which it names as enclosing_href, and an embedded image carries its document_id. The answer is written:
    • None: the reader's own output;
    • a str: literal text, escaped as a text node, so it cannot become markup;
    • a Link: replaces the target;
    • a Link with an empty href: drops a link (its content is kept), or writes an image as its text.
  • Without the argument a read is unchanged, byte for byte. A test checks that across the whole round-trip corpus.
  • The callback is held in a ContextVar. Threads and tasks each read with their own, and a read inside a callback has its own too.
  • What the callback raises reaches the caller unchanged. It is carried past the reader's ValueError text fallback, which would otherwise take a callback's ValueError for markdownify's and quietly drop the body's formatting. An answer of another type raises TypeError.
  • document_id_of(src) and document_image(document_id, alt=...). The second is spelled by the reader itself, so from_transport(to_transport(x)) == x.
  • Link, RewriteLink and EasyvistaContentConverter.EMBEDDED_IMAGE_PREFIX are now exported.
  • Docs (docs/content.rst) and CHANGELOG updated; the version is 0.4.3.

Measured on one instance on 2026-10-07: an image written that way through the API displays, both for an attachment pasted in the editor and for one uploaded with add_document. An upload's DOCUMENT_ID has a different shape from a pasted image's, and the accepted id characters cover both.

The GLPI twin is baraline/glpi_python_client#42 (0.6.3).

Verification

  • test_rewrite_link.py: 208 tests.
  • 21 mutants of the new guards, all caught.
  • Full suite: 2304 passed, coverage 98.96%.
  • mypy, ruff, the sync lint, the unasync check and sphinx -W all clean.

🤖 Generated with Claude Code

… reader (0.4.3)

A caller that mirrors memos to another system has to recognise the images a memo embeds and
decide what they become, without parsing HTML or Markdown itself. EasyVista's editor keeps a
pasted image as an attachment of the request and embeds it as
<img src="@@EMBEDDED_IMAGE_PATH@@<DOCUMENT_ID>">, which the reader wrote as an image that
displays nowhere else.

- from_transport(..., rewrite_link=callback) calls the callback with a Link for each link and
  image met outside code, an image before the link around it (enclosing_href). Its answer is
  written: None keeps the reader's output, a str is literal text escaped as a text node, a
  Link replaces the target, and an empty href drops a link (content kept) or writes an image
  as its text. Without the argument a read is unchanged, byte for byte.
- The callback lives in a ContextVar, so threads and tasks each read with their own, and a
  read inside a callback has its own. What it raises reaches the caller unchanged: carried past
  the reader's ValueError text fallback, which would otherwise have taken a callback's
  ValueError for markdownify's and quietly dropped the body's formatting.
- document_id_of(src) and document_image(document_id, alt=...): the second is spelled by the
  reader itself, so from_transport(to_transport(x)) == x.

Measured on one instance on 2026-10-07: an image written that way through the API displays,
for an attachment pasted in the editor and for one uploaded with add_document. The upload's id
has another shape than a pasted one's; the id class covers both.

Tests: test_rewrite_link.py (none-answer parity over the round-trip corpus, offer order, each
answer kind, code skipped, exceptions, threads, nesting, helpers). 21 mutants of the new guards,
all killed. Full suite 2304 passed; mypy, ruff, sync lint and sphinx -W clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.96%. Comparing base (071e867) to head (dc6ca7e).
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #10      +/-   ##
==========================================
+ Coverage   98.93%   98.96%   +0.03%     
==========================================
  Files          40       40              
  Lines        2617     2694      +77     
==========================================
+ Hits         2589     2666      +77     
  Misses         28       28              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@baraline
baraline merged commit ce7cf41 into main Oct 8, 2026
6 checks passed
@baraline
baraline deleted the feat/inline-document-images branch October 8, 2026 09:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants