From e37580cc304e0a1c93877ebfa2ecaf76098726f9 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Tue, 1 Apr 2025 09:29:22 -0400 Subject: [PATCH 1/4] Remove uv source of uvicorn --- pyproject.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c8c323c91..34e8f238f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -134,9 +134,6 @@ doc = [ "griffe>=1.3.2", ] -[tool.uv.sources] -# https://github.com/encode/uvicorn/pull/2602 -uvicorn = { git = "https://github.com/schloerke/uvicorn", branch = "reload-exclude-abs-path" } [project.urls] Homepage = "https://github.com/posit-dev/py-shiny" From 513eabffec2e9883b190e9f354bae04b54c424b8 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Tue, 1 Apr 2025 11:16:44 -0400 Subject: [PATCH 2/4] Add workaround for uvicorn reload excludes bug --- shiny/_main.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/shiny/_main.py b/shiny/_main.py index a691aaf41..30db3232c 100644 --- a/shiny/_main.py +++ b/shiny/_main.py @@ -359,6 +359,32 @@ def run_app( reload_args: ReloadArgs = {} if reload: + if shiny_bookmarks_folder_name in reload_excludes: + # Related: https://github.com/posit-dev/py-shiny/pull/1950 + # + # Temp hack to work around uvicorn + # https://github.com/encode/uvicorn/pull/2602 which will incorrectly reload + # an app if any matching files in `RELOAD_INCLUDES_DEFAULT` (e.g. `*.png`) + # are uploaded within `shiny_bookmarks` (an excluded relative directory). + # + # By extending `reload_excludes` to ignore everything under the bookmarks folder, we + # can prevent the unexpected reload from happening for the root session. File + # matches are performed via `pathlib.PurePath.match`, which is a right-match and + # only supports `*` glob. + # + # Ignore up to five modules deep. This should cover most cases. + # + # Note: file uploads are always in the root session, so they are always + # stored in the root bookmark dir of `shiny_bookmarks_folder_name / *`. + reload_excludes = [ + *reload_excludes, + str(Path(shiny_bookmarks_folder_name) / "*"), + str(Path(shiny_bookmarks_folder_name) / "*" / "*"), + str(Path(shiny_bookmarks_folder_name) / "*" / "*" / "*"), + str(Path(shiny_bookmarks_folder_name) / "*" / "*" / "*" / "*"), + str(Path(shiny_bookmarks_folder_name) / "*" / "*" / "*" / "*" / "*"), + ] + reload_args = { "reload": reload, # Adding `reload_includes` param while `reload=False` produces an warning From 181665d70a91f96154d5d397d26dee6f72c896ed Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Tue, 1 Apr 2025 11:25:30 -0400 Subject: [PATCH 3/4] independent lints --- shiny/api-examples/notification_show/app-core.py | 2 -- shiny/api-examples/notification_show/app-express.py | 2 -- tests/pytest/test_reactives.py | 12 ------------ 3 files changed, 16 deletions(-) diff --git a/shiny/api-examples/notification_show/app-core.py b/shiny/api-examples/notification_show/app-core.py index 2f8e3a056..f3c0683ac 100644 --- a/shiny/api-examples/notification_show/app-core.py +++ b/shiny/api-examples/notification_show/app-core.py @@ -14,7 +14,6 @@ def server(input: Inputs, output: Outputs, session: Session): @reactive.effect @reactive.event(input.show) def _(): - nonlocal ids nonlocal n # Save the ID for removal later id = ui.notification_show("Message " + str(n), duration=None) @@ -24,7 +23,6 @@ def _(): @reactive.effect @reactive.event(input.remove) def _(): - nonlocal ids if ids: ui.notification_remove(ids.pop()) diff --git a/shiny/api-examples/notification_show/app-express.py b/shiny/api-examples/notification_show/app-express.py index d64a57431..a6de781a8 100644 --- a/shiny/api-examples/notification_show/app-express.py +++ b/shiny/api-examples/notification_show/app-express.py @@ -11,7 +11,6 @@ @reactive.effect @reactive.event(input.show) def _(): - global ids global n # Save the ID for removal later id = ui.notification_show("Message " + str(n), duration=None) @@ -22,6 +21,5 @@ def _(): @reactive.effect @reactive.event(input.remove) def _(): - global ids if ids: ui.notification_remove(ids.pop()) diff --git a/tests/pytest/test_reactives.py b/tests/pytest/test_reactives.py index 4edaa4841..44ef9b48a 100644 --- a/tests/pytest/test_reactives.py +++ b/tests/pytest/test_reactives.py @@ -235,7 +235,6 @@ async def test_async_sequential(): async def react_chain(n: int): @calc() async def r(): - nonlocal exec_order exec_order.append(f"r{n}-1") await asyncio.sleep(0) exec_order.append(f"r{n}-2") @@ -243,7 +242,6 @@ async def r(): @effect() async def _(): - nonlocal exec_order exec_order.append(f"o{n}-1") await asyncio.sleep(0) exec_order.append(f"o{n}-2") @@ -402,19 +400,16 @@ async def test_effect_priority(): @effect(priority=1) def o1(): - nonlocal results v() results.append(1) @effect(priority=2) def o2(): - nonlocal results v() results.append(2) @effect(priority=1) def o3(): - nonlocal results v() results.append(3) @@ -425,7 +420,6 @@ def o3(): # invalidate others by changing v). @effect(priority=2) def o4(): - nonlocal results v() results.append(4) @@ -453,19 +447,16 @@ async def test_async_effect_priority(): @effect(priority=1) async def o1(): - nonlocal results v() results.append(1) @effect(priority=2) async def o2(): - nonlocal results v() results.append(2) @effect(priority=1) async def o3(): - nonlocal results v() results.append(3) @@ -476,7 +467,6 @@ async def o3(): # invalidate others by changing v). @effect(priority=2) async def o4(): - nonlocal results v() results.append(4) @@ -506,7 +496,6 @@ async def test_effect_destroy(): @effect() def o1(): - nonlocal results v() results.append(1) @@ -524,7 +513,6 @@ def o1(): @effect() def o2(): - nonlocal results v() results.append(1) From c43be97df7f4f7c59f514e686b5bbcf48b352870 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Tue, 1 Apr 2025 11:25:39 -0400 Subject: [PATCH 4/4] Update comment --- shiny/bookmark/_bookmark.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shiny/bookmark/_bookmark.py b/shiny/bookmark/_bookmark.py index f76ed9620..28abdb139 100644 --- a/shiny/bookmark/_bookmark.py +++ b/shiny/bookmark/_bookmark.py @@ -610,7 +610,8 @@ async def _scoped_on_bookmark(self, root_state: BookmarkState) -> None: ) # Make subdir for scope - # TODO: Barret; Is this for uploaded files?!? + # + # Folder only used by author callbacks. File uploads are handled by root session if root_state.dir is not None: scope_subpath = self._ns scoped_state.dir = Path(root_state.dir) / scope_subpath