fix: adapt to htmltools tagified-type changes (#105)#2244
Open
schloerke wants to merge 11 commits into
Open
Conversation
The upcoming htmltools release (posit-dev/py-htmltools#105) tightens the `Tagifiable.tagify()` Protocol return type to the new `Tagified` union (`TagifiedTag | TagifiedTagList | TagLeaf`), and `Tag` / `TagList` become generic in their child type (`ChildT`, defaulting to `TagNode`). Because `ChildT` is invariant, `TagifiedTag` (= `Tag[TagifiedNode]`) is no longer a subtype of bare `Tag` (= `Tag[TagNode]`), and the same for TagList. This commit makes the minimum changes so py-shiny's strict-mode pyright run stays green against the new htmltools: * `_app.py`: widen `App.__init__`'s `ui` parameter and `is_uifunc`'s `x` parameter to accept `TagifiedTag` / `TagifiedTagList` in addition to bare `Tag` / `TagList`. Callable variants accept either too. * `_accordion.py` / `_card.py` / `_sidebar.py`: change `tagify()` return annotations from `Tag` / `TagList` to `TagifiedTag` / `TagifiedTagList` to match what `.tagify()` actually produces. * `_navs.py`: same plus narrow `content.children` items via `cast("Tag[TagNode]", child)` after `isinstance(Tag)` so the downstream generic-Tag helpers compose. * `_page.py`: same `cast("Tag[TagNode]", body)` narrow before passing to `as_fillable_container`. * `_toolbar.py` / `_utils.py`: cast narrowed Tag values back to `Tag[TagNode]` so `.children` doesn't leak `Unknown` into recursive helpers. * `_func_displayhook.py`: suppress two narrow-induced `Unknown` reports on the `object -> object` decorator (the public signature doesn't carry the narrowing).
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`.
`Sidebar.tagify()` now returns `TagifiedTagList` (per the htmltools type changes); its items narrow via isinstance(Tag) to `Tag[Unknown] | Tag[TagifiedNode]`, which doesn't satisfy the bare `Tag` return type of `get_sidebar_tags`. Add a `cast(Tag, …)` on each item to re-widen to the default `Tag[TagNode]`.
4 tasks
…and guard import Per htmltools maintainer guidance, downstreams should only reference the single `Tagified` alias rather than the more specific `TagifiedTag` / `TagifiedTagList`. Substitute throughout. Also move the `Tagified` import under `if TYPE_CHECKING:` so each module still loads against the currently-released htmltools (0.6.x), which doesn't export the new alias yet. The existing `from __future__ import annotations` line in each file defers all annotations to strings, so `Tagified` only needs to resolve at type-check time. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The `Tagified` alias used by this PR is only available in htmltools >= 0.7.0 (posit-dev/py-htmltools#105). Pin htmltools directly to the PR branch's git ref so `uv pip install -e ".[dev,test]"` (used by py-shiny's CI via `make ci-install-deps`) picks up the new alias. **Revert this dependency pin 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). Document the post-release version constraint that the temporary git-url pin should be flipped to.
The is_uifunc signature was below the line-length threshold after collapsing TagifiedTag/TagifiedTagList down to Tagified, and the imports needed re-wrapping.
`Sidebar.tagify()` returns `Tagified`, which includes non-iterable arms (Tag, MetadataNode, ReprHtml). The unpack `sidebar, collapse = ...` made pyright complain about every non-iterable arm. Narrow to `TagList` via cast before unpacking; the runtime shape is unchanged.
uv refuses to resolve `py-shiny-templates/requirements.txt` while py-shiny's own `pyproject.toml` pins htmltools to a git URL, because URL-pinned transitive deps must be repeated as direct requirements. Pass the URL inline to satisfy uv. Remove this workaround once htmltools 0.7.0 ships.
4 tasks
`TagifiedNode` is no longer exported from htmltools (it lives in `htmltools._core` for internal use). Rewrite the two cast-narrowing comments to describe the situation without naming the internal alias.
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 direct git URL dep and the Makefile workaround will be removed once 0.7.0 ships (see posit-dev/py-htmltools#113 and #2245).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adapts py-shiny to the upcoming htmltools changes in posit-dev/py-htmltools#106 (implementing posit-dev/py-htmltools#105).
In that release:
Tagifiable.tagify()'s Protocol return is tightened to the newTagifiedunion — the only tagified-shape alias htmltools exports.Tagifiedexcludes the un-resolvedTagifiablearm ofTagNode, so it's a proper subtype of the old return.TagandTagListbecome generic in their child type (ChildT, defaulting toTagNode). BecauseChildTis invariant, a tagifiedTag(internallyTag[TagifiedNode]) is no longer a subtype of bareTag(=Tag[TagNode]); same for TagList.Without this PR, strict-mode pyright on py-shiny reports ~20 errors against the new htmltools. With this PR, py-shiny is clean again (modulo a single pre-existing unrelated matplotlib
Figure | SubFigure | Noneerror inshiny/render/_try_render_plot.py:272).Changes
_app.py: widenApp.__init__'suiparameter andis_uifunc'sxparameter to also acceptTagifiedin addition to bareTag/TagList. Callable variants accept either too._accordion.py/_card.py/_sidebar.py: changetagify()return annotations toTagifiedto match the new protocol contract._navs.py: same as above plus narrowcontent.childrenitems viacast("Tag[TagNode]", child)afterisinstance(Tag)so the downstream generic-Tag helpers compose._page.py: samecast("Tag[TagNode]", body)narrow before passing toas_fillable_container._toolbar.py/_utils.py: cast narrowed Tag values back toTag[TagNode]so.childrendoesn't leakUnknowninto recursive helpers._func_displayhook.py: suppress two narrow-inducedUnknownreports on theobject -> objectdecorator (the public signature doesn't carry the narrowing).tests/pytest/test_sidebar.py: castsb.tagify()toTagListbefore unpacking — theTagifiedunion includes non-iterable arms.Makefile:ci-install-py-shiny-templates-depspre-installs htmltools from the PR branch as a workaround for uv's URL-dep resolution. Tracked by Remove temporary htmltools git URL workaround in Makefile once htmltools 0.7.0 ships #2245 for removal post-release.Test plan
uv run --with-editable <path>/py-htmltools pyright shiny→ 1 error, unrelated to htmltools (pre-existing matplotlibFigure | SubFigureissue in_try_render_plot.py:272).Dependency
This PR should land alongside (or shortly after) posit-dev/py-htmltools#106. The
Tagifiedalias referenced here is new in that release; this PR requires htmltools 0.7.0+. The pyproject.toml git pin and Makefile workaround (#2245) should be reverted in favor ofhtmltools>=0.7.0once 0.7.0 ships on PyPI.