Skip to content

fix: align custom .tagify() annotations with htmltools Tagified contract#226

Draft
schloerke wants to merge 5 commits into
mainfrom
schloerke/htmltools-105-tagified-types
Draft

fix: align custom .tagify() annotations with htmltools Tagified contract#226
schloerke wants to merge 5 commits into
mainfrom
schloerke/htmltools-105-tagified-types

Conversation

@schloerke
Copy link
Copy Markdown
Contributor

@schloerke schloerke commented May 15, 2026

Summary

The upcoming htmltools release (posit-dev/py-htmltools#106 implementing #105) tightens the Tagifiable.tagify() Protocol return type to Tagified. Tagified is a tighter union than the previous bare TagList | Tag | MetadataNode | str | HTML: it excludes the un-resolved Tagifiable arm and is the only tagified-shape alias exported by htmltools — downstream .tagify() implementations should annotate -> Tagified exclusively. Custom .tagify() implementations whose return is wider than Tagified no longer satisfy the protocol in strict-mode type checkers.

Changes:

  • _chat_bookmark.py: change def tagify(self) -> TagChild: return "" to -> Tagified. The previous TagChild annotation was wider than the protocol return type (TagChild includes float/None/Sequence/etc.).
  • _chat_normalize_chatlas.py: annotate both def tagify(self): methods as -> Tagified and call .tagify() on the returned Tag(…) so the value is properly tagified (matches the recursive invariant the protocol now expresses).

Pyright (basic mode, as configured) is already clean with the released htmltools; these changes make the code also correct under the upcoming protocol tightening.

Test plan

  • uv run --with-editable <path>/py-htmltools pyright → 0 errors
  • Functional behavior is unchanged — .tagify() on a leaf-only Tag is a no-op at runtime

The upcoming htmltools release (posit-dev/py-htmltools#105) tightens the
`Tagifiable.tagify()` Protocol return type from a bare
`TagList | Tag | MetadataNode | str | HTML` to the new `Tagified` union
(`TagifiedTag | TagifiedTagList | TagLeaf`). Custom .tagify()
implementations whose return is wider than `Tagified` no longer satisfy
the protocol in strict-mode type checkers.

* `_chat_bookmark.py`: change `def tagify(self) -> TagChild: return ""`
  to `-> Tagified`. The previous `TagChild` annotation was wider than
  the protocol return type (TagChild includes float/None/Sequence/etc.).
* `_chat_normalize_chatlas.py`: annotate both `def tagify(self):`
  methods as `-> Tagified` and call `.tagify()` on the returned `Tag(…)`
  so the value is properly tagified (matches the recursive invariant
  the protocol now expresses).
schloerke added a commit to posit-dev/py-htmltools that referenced this pull request May 15, 2026
…ype changes

* py-shiny@schloerke/htmltools-105-tagified-types — posit-dev/py-shiny#2244
* shinychat@schloerke/htmltools-105-tagified-types — posit-dev/shinychat#226
* chatlas@schloerke/htmltools-105-tagified-types — posit-dev/chatlas#311
* brand-yml@schloerke/htmltools-105-tagified-types — posit-dev/brand-yml#115

When each downstream PR merges, flip its `ref` back to `main` (or remove
the explicit ref). The matrix is restructured to use `include:` so each
entry can carry its own `ref`.
schloerke added 3 commits May 18, 2026 09:53
The `Tagified` type alias is only available in htmltools >= 0.7.0
(posit-dev/py-htmltools#105). Importing it at module load time breaks
`from htmltools import Tagified` against the currently-released
htmltools 0.6.x.

Move the import under `if TYPE_CHECKING:`. The existing
`from __future__ import annotations` defers all annotations to lazy
strings, so the name only needs to resolve at type-check time, not at
runtime. This makes the branch importable today and type-checks under
both the released htmltools and the upcoming one.
The `Tagified` alias used by this PR is only available in htmltools
>= 0.7.0 (posit-dev/py-htmltools#105). Until that release lands, pin
htmltools to the PR branch via `[tool.uv.sources]` so CI installs
the version that exports `Tagified` and pyright is happy.

\*\*Remove this override before merging\*\* once htmltools 0.7.0 is on
PyPI.
The PR introduces `Tagified`, only available in htmltools 0.7.0+
(posit-dev/py-htmltools#105). Bump the declared minimum so consumers
who skip the PR-branch pin still resolve a compatible version.
The schloerke/tagify-tag-class-issue branch was deleted after
posit-dev/py-htmltools#106 merged. Point at main so CI resolves
again while we wait for the htmltools 0.7.0 PyPI release. The
[tool.uv.sources] block will be dropped entirely once 0.7.0 ships
(see posit-dev/py-htmltools#113).
@cpsievert cpsievert marked this pull request as draft May 18, 2026 20:56
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.

1 participant