2025, Dec 02 00:02
Как починить input в модуле Shiny for Python при expressify
Почему при expressify реактивный input в модуле Shiny for Python «теряется», и как это исправить: передать session input в модуль и использовать его в render.
Когда приложение Shiny for Python разрастается и выходит за рамки одного файла, разумно разбивать его на модули. Однако при использовании expressify и попытке обратиться к реактивным вводам из отдельного модуля часто появляется скрытая проблема: интерфейс отрисовывается, статический текст видно, а реактивные значения упорно не показываются. Суть в тонкости: объект input внутри модуля — это не тот же самый input, который привязан к текущей сессии.
Постановка проблемы
Приложение разделено на два файла. В главном описаны набор вкладок и базовый контрол, а во втором добавляется ещё одна вкладка с числовым полем и попыткой вывести его значение. Если вычислять или ссылаться на значение в той же функции, вывод остаётся пустым.
Ниже — минимальный пример, который воспроизводит проблему.
main_app.py
import section_view
from shiny import ui
from shiny.express import input, ui
with ui.navset_tab(id="active_tab"):
with ui.nav_panel("Welcome", value="home_view"):
with ui.card():
ui.input_selectize(
"dummy", "Filler",
["list", "of", "items", "here"],
multiple=False
)
section_view.build_other()
section_view.py
from shiny import render
from shiny.express import input, ui, expressify
@expressify
def build_other():
with ui.nav_panel("Detached page", value="aux_view"):
ui.input_numeric("num_val", "I need this inpt value!", 1, min=1, max=1000)
@render.express
def show_text():
val = input.num_val()
val
Что здесь не так
Input, используемый внутри модуля, — отдельный объект, отличный от того, что живёт в главном приложении. В главном приложении уже есть session‑bound input, и именно он должен быть единственным источником истины. Повторный импорт input внутри модуля создаёт другой объект, о котором сессия не знает, поэтому выражения, зависящие от него, не дают результата. Это легко проверить: замените тело функции render на статическую строку — строка отобразится, а обращение к числовому значению — нет. В зависимости от того, как запускается код, это может даже проявиться как ошибка доступа к атрибуту у «не того» input.
Решение
Не импортируйте input в модуле. Вместо этого принимайте основной input параметром функции модуля и используйте его внутри блока render. Затем передайте session input из главного файла в модуль.
section_view.py
from shiny import render
from shiny.express import ui, expressify
@expressify
def build_other(inlet):
with ui.nav_panel("Detached page", value="aux_view"):
ui.input_numeric("num_val", "I need this input value!", 1, min=1, max=1000)
@render.express
def show_text():
current = inlet.num_val()
f"input value: {current}"
main_app.py
import section_view
from shiny import ui
from shiny.express import input, ui
with ui.navset_tab(id="active_tab"):
with ui.nav_panel("Welcome", value="home_view"):
with ui.card():
ui.input_selectize(
"dummy", "Filler",
["list", "of", "items", "here"],
multiple=False
)
section_view.build_other(inlet=input)
Почему это важно
Когда вы делите приложение Shiny на несколько файлов, критично сохранять согласованность реактивных объектов, привязанных к сессии. Если модуль незаметно создаёт собственный input, он оказывается отделён от текущей сессии. UI отрисуется, статический текст появится, но реактивный вывод — нет, потому что чтение происходит из объекта, который сессия не обновляет. Передача session input в модуль сохраняет синхронизацию и позволяет пользоваться чистой, многофайловой структурой без компромиссов.
Итоги
Сохраняйте модульность, но направляйте корректный input в каждый модуль, которому он нужен. Не импортируйте новый input во вспомогательных файлах: принимайте его из главного приложения и используйте в render.express, чтобы отображать значения, задаваемые пользователем. Эта небольшая «проводка» предотвращает загадочные состояния без вывода и помогает вашему приложению Shiny for Python оставаться реактивным и удобным в сопровождении по мере роста.