posit-dev / posit-dev/py-shinylive

`_find_packages_in_requirements()` silently strips extras, so `shiny[theme]` never bundles libsass

Open
#65 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
56
Forks
6
Avg merge
3m
Merged PRs (30d)
1

Description

Summary

_find_packages_in_requirements() (shinylive/_deps.py:608) strips extras from a requirement line, so shiny[theme] is recorded as plain shiny. The packages the extra asks for are therefore never copied into the published assets, and the app fails at runtime with a 404 — even though the wheels are bundled with Shinylive and would have worked.

The failure is silent at build time: nothing warns, and the export/render succeeds.

Reproduction

>>> from shinylive._deps import _find_packages_in_requirements
>>> _find_packages_in_requirements("shiny[theme]")
['shiny']
>>> _find_packages_in_requirements("libsass")
['libsass']

The offending line is the version-suffix regex, which treats [theme] as trailing junk:

pkg_name = re.sub(r"([a-zA-Z0-9._-]+)(.*)", r"\1", line).strip()

End-to-end, with requirements.txt = shiny[theme] and an app that builds a customized ui.Theme():

preload error: The following error occurred while loading libsass: Failed to fetch
preload error: ... ruamel.yaml ... pydantic ... annotated-types ... pydantic_core: Failed to fetch
ImportError: The 'libsass' package is required to compile custom themes.

Switching the same file to a bare libsass fixes it: the wheel is copied, 0 console errors, app runs.

Why this is worth fixing rather than documenting around

The two halves of Shinylive disagree about extras:

  • The runtime installer handles them correctly. _install_requirements_from_dir() in shinylive/src/hooks/usePyodide.tsx explicitly resolves extras against the already-installed dist's metadata — its own source comment uses this very case as the worked example: # Convert requires records : 'libsass>=0.23.0; extra == "theme"'.
  • The build-time asset copier does not. So the installer dutifully tries to install what the extra asks for, and every fetch 404s because those wheels were never published next to the app.

shiny[theme] is not a hypothetical spelling — it is what Shiny's own ui.Theme docstring instructs users to install:

Note: Compiling custom themes requires the libsass package, which is not installed by default with Shiny. Use pip install libsass or pip install "shiny[theme]" to install it.

So a user who follows the documentation into a Shinylive app gets a broken app with no build-time signal. We just hit this in the Shiny for Python docs site: posit-dev/py-shiny#2386 / posit-dev/py-shiny#2387 had to pin a bare libsass instead of the nicer shiny[theme] specifically to work around this.

Scope

Both consumers of find_package_deps() are affected, since both go through this parser:

  • shinylive_app_resources() (_deps.py:350) — the Quarto extension's app-resources path, used by Quarto sites.
  • shinylive export (_export.py:99).

Suggested fix

Options, roughly in increasing order of effort:

  1. Fail loudly. At minimum, don't silently drop the extra — warn (or error) when a requirement carries one, so the problem surfaces at build time instead of in the user's browser. This alone would have saved the debugging round-trip.
  2. Parse properly. Replace the regex with packaging.requirements.Requirement, which gives .name and .extras directly and also handles markers/specifiers correctly.
  3. Resolve the extras. For a package present in the assets dir, read Provides-Extra/Requires-Dist from the wheel's METADATA and add each selected extra's requirements to the dependency set — mirroring what usePyodide.tsx already does at runtime. Note the pyodide lock's depends won't help here: it lists runtime deps only, not extras.

Even (1) + (2) would be a real improvement; (3) makes shiny[theme] work as users reasonably expect.

Related: #4 (wheel not bundled unless explicitly imported) and #17 (only inspect requirements.txt packages for dependencies) — same general area of "what gets bundled", different trigger.

Environment

  • shinylive Python package 0.8.11, web assets 0.10.14
  • Verified against main (shinylive/_deps.py is byte-identical there today)

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in shinylive/_deps.py at _find_packages_in_requirements() and trace its callers in shinylive_app_resources() and shinylive/_export.py. Compare the parser’s behavior with extras handling in shinylive/src/hooks/usePyodide.tsx, then run the existing dependency or export checks if available. Done means requirements such as shiny[theme] are handled deliberately at build time, with selected extra dependencies bundled or the limitation reported clearly.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
build-system
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.