Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Bug fixes

* Fixed several issues related to including custom js and css files
* JS and CSS file loaded with `include_js()` or `include_css()` will be loaded into a new temporary directory and cleaned up properly on program exit. (#2061)
* JS and CSS files in the same directory will be loaded correctly. (#729)

* Fixed numerous issues related to programmatically updating selectize options. (#2053)
* `update_selectize(options=...)` no longer gets ignored when `server=False` (the default).
* `update_selectize(options=...)` now works as expected in a module.
Expand Down
31 changes: 31 additions & 0 deletions shiny/_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import copy
import os
import secrets
import shutil
import tempfile
from contextlib import AsyncExitStack, asynccontextmanager
from inspect import signature
from pathlib import Path
Expand Down Expand Up @@ -214,6 +216,35 @@ def __init__(
cast("Tag | TagList", ui), lib_prefix=self.lib_prefix
)

def __del__(self) -> None:
deps = self._registered_dependencies.values()
self._cleanup_temp_source_dirs(list(deps))

@staticmethod
def _cleanup_temp_source_dirs(deps: list[HTMLDependency]) -> None:
# include_css()/include_js() create temporary directories to hold files that
# persist across user sessions, but also need to be cleaned up at some point,
# and Python (unlike R) does not cleanup tempdirs on process exit. So, our next
# best option is to clean them up when the App object is deleted. It's not
# perfect (the App object might be deleted while the process is still running,
# and there might be multiple App objects using the same UI). However, it still
# seems worth doing since that is such a hypothetical edge case. More generally,
# if _any_ HTMLDependency with a source directory that is a _subdirectory_ of
# the (system-wide) temp directory, we should remove it.
current_temp_dir = os.path.realpath(tempfile.gettempdir())
for dep in deps:
src = dep.source_path_map()["source"]
if not src:
continue
src = os.path.realpath(src)
if not os.path.exists(src):
continue
if src == current_temp_dir:
continue
common = os.path.commonprefix([src, current_temp_dir])
if common == current_temp_dir:
shutil.rmtree(src)

def init_starlette_app(self) -> starlette.applications.Starlette:
routes: list[starlette.routing.BaseRoute] = [
starlette.routing.WebSocketRoute("/websocket/", self._on_connect_cb),
Expand Down
Loading