From b8363447da89cb57d70ab85d30b96f9c012d47c2 Mon Sep 17 00:00:00 2001 From: Carson Date: Thu, 14 Apr 2022 19:18:47 -0500 Subject: [PATCH 01/13] wip --- shiny/ui/__init__.py | 1 + shiny/ui/_include_helpers.py | 224 +++++++++++++++++++++++++++++++++++ 2 files changed, 225 insertions(+) create mode 100644 shiny/ui/_include_helpers.py diff --git a/shiny/ui/__init__.py b/shiny/ui/__init__.py index a7f83138b..17874c55c 100644 --- a/shiny/ui/__init__.py +++ b/shiny/ui/__init__.py @@ -6,6 +6,7 @@ # All objects imported into this scope will be available as shiny.ui.foo from ._bootstrap import * from ._download_button import * +from ._include_helpers import * from ._input_action_button import * from ._input_check_radio import * from ._input_date import * diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py new file mode 100644 index 000000000..92bb3b30a --- /dev/null +++ b/shiny/ui/_include_helpers.py @@ -0,0 +1,224 @@ +__all__ = ("include_javascript", "include_css", "include_html", "include_font_face") + +import os +import shutil +import tempfile +from typing import Dict, Tuple + +from htmltools import tags, Tag, TagAttrArg, HTMLDependency, HTMLDependencySource +from htmltools._util import hash_deterministic + + +def include_javascript( + path: str, + *, + include_files: bool = True, + inline: bool = False, + attrs: Dict[str, TagAttrArg] = {} +) -> HTMLDependency: + """ + Include a JavaScript file as a :func:`~ui.tags.script` tag. + + Parameters + ---------- + path + A path to a JS file. + include_files + If ``True``, the JS file may reference other files in the same directory (e.g., + use ``fetch()`` to fetch another file). + inline + Whether to link to the file path or inline its contents. + attrs + Additional attributes to add to the :func:`~ui.tags.script` tag. + + Note + ---- + This produces a :func:`~ui.tags.script` tag in the :func:`~ui.tags.head` of the + document, which means it executes before any of the document's :func:`~ui.tags.body` + has been parsed, and so DOM manipulation code may need to be wrapped in + ``document.addEventListener("DOMContentLoaded", () => {});`` (or similar). + Alternatively, to get :func:`~ui.tags.script` tag included in the + :func:`~ui.tags.body`, you can do something like this: + + .. code-block:: python + + with open("custom.html", "r", encoding="utf-8") as f: + custom_js = ui.tags.script(f.read(), type="text/javascript") + + app_ui = ui.page_fluid(..., custom_js, ...) + + See Also + -------- + ~ui.tags.script + ~include_css + ~include_html + """ + + newpath, hash, contents = read_and_copy(path, include_files) + + name = "include-script-" + hash + source = HTMLDependencySource(subdir=os.path.dirname(newpath)) + + if inline: + return HTMLDependency( + name, + DEFAULT_VERSION, + source=source, + all_files=include_files, + head=tags.script(contents, type="text/javascript", **attrs), + ) + else: + return HTMLDependency( + name, + DEFAULT_VERSION, + source=source, + all_files=include_files, + script=dict(src=os.path.basename(path), **attrs), + ) + + +def include_css( + path: str, + *, + include_files: bool = True, + inline: bool = False, + attrs: Dict[str, TagAttrArg] = {} +) -> HTMLDependency: + """ + Include CSS + + Parameters + ---------- + path + A path to a CSS file. + include_files + If ``True``, the CSS file may reference other files in the same directory + (e.g., use ``@import()`` to import another file). + inline + If ``True``, the contents are inlined into a :func:`~ui.tags.style` tag; + otherwise, a :func:`~ui.tags.link` tag is generated. + attrs + Additional attributes to add to the :func:`~ui.tags.style` (or + :func:`~ui.tags.link`) tag. + + Note + ---- + This produces a :func:`~ui.tags.style` tag in the :func:`~ui.tags.head` of the + document, which is advantageous for performance. + + See Also + -------- + ~ui.tags.style + ~ui.tags.link + ~include_javascript + ~include_html + """ + + newpath, hash, css = read_and_copy(path, include_files) + + name = "include-script-" + hash + source = HTMLDependencySource(subdir=os.path.dirname(newpath)) + + if inline: + return HTMLDependency( + name, + DEFAULT_VERSION, + source=source, + all_files=include_files, + head=tags.style(css, type="text/css", **attrs), + ) + else: + return HTMLDependency( + name, + DEFAULT_VERSION, + source=source, + all_files=include_files, + stylesheet=dict(href=os.path.basename(path), **attrs), + ) + + +def include_html( + path: str, *, include_files: bool = True, attrs: Dict[str, TagAttrArg] = {} +) -> Tag: + """ + Include an HTML file as an :func:`~ui.tags.iframe` + + Parameters + ---------- + path + A path to an HTML file. + include_files + Whether to make other files in the ``path``'s directory available. + attrs + Additional attributes to add to the :func:`~ui.tags.iframe` tag. + + Returns + ------- + An :func:`~ui.tags.iframe` tag. + + Note + ---- + For safety reasons, this function includes the HTML file as a + :func:`~ui.tags.iframe`, which means it's 'isolated' from the rest of the parent + document. If instead, you don't want to isolate (meaning, among other things, + you want the HTML inherit CSS styles from the parent document), you can do + something like this: + + .. code-block:: python + from shiny import ui + + with open("custom.html", "r", encoding="utf-8") as f: + custom_html = ui.HTML(f.read()) + + app_ui = ui.page_fluid(..., custom_html, ...) + + See also + -------- + ~ui.tags.iframe + ~ui.HTML + ~include_javascript + ~include_css + """ + + newpath, hash, _ = read_and_copy(path, include_files) + + return tags.iframe( + dict(src=os.path.basename(newpath), class_="shiny-iframe", **attrs), + HTMLDependency( + "include-iframe-" + hash, + DEFAULT_VERSION, + source=HTMLDependencySource(subdir=os.path.dirname(newpath)), + ), + ) + + +def include_font_face(path: str): + pass + + +def read_and_copy(path: str, include_files: bool) -> Tuple[str, str, str]: + contents = read_utf8(path) + + tmpdir = tempfile.mkdtemp() + if include_files: + # Copy all the directory contents except for path, which we copy later + # TODO: verify this actually works + shutil.copytree( + os.path.dirname(path), tmpdir, ignore=shutil.ignore_patterns(path) + ) + + hash = hash_deterministic(contents) + basename = os.path.splitext(os.path.basename(path)) + newpath = os.path.join(tmpdir, basename[0] + "-" + hash + basename[1]) + + shutil.copy(path, newpath) + + return newpath, hash, contents + + +def read_utf8(path: str) -> str: + with open(path, "r", encoding="utf-8") as f: + return f.read() + + +DEFAULT_VERSION = "0.0" From ecb3ce3e9632fd46364c6adbeec5861bbcb622c1 Mon Sep 17 00:00:00 2001 From: Carson Date: Fri, 15 Apr 2022 18:53:07 -0500 Subject: [PATCH 02/13] wip2 --- shiny/_app.py | 14 +- shiny/examples/include_css/app.py | 16 + .../include_css/css/SquarePeg-Regular.ttf | Bin 0 -> 126688 bytes shiny/examples/include_css/css/styles.css | 10 + shiny/examples/include_html/app.py | 10 + shiny/examples/include_javascript/app.py | 16 + shiny/examples/include_javascript/js/app.js | 1 + shiny/session/_session.py | 2 +- shiny/ui/_include_helpers.py | 303 +++++++++++------- 9 files changed, 248 insertions(+), 124 deletions(-) create mode 100644 shiny/examples/include_css/app.py create mode 100644 shiny/examples/include_css/css/SquarePeg-Regular.ttf create mode 100644 shiny/examples/include_css/css/styles.css create mode 100644 shiny/examples/include_html/app.py create mode 100644 shiny/examples/include_javascript/app.py create mode 100644 shiny/examples/include_javascript/js/app.js diff --git a/shiny/_app.py b/shiny/_app.py index 4363c8884..621f19465 100644 --- a/shiny/_app.py +++ b/shiny/_app.py @@ -47,12 +47,6 @@ def server(input: Inputs, output: Outputs, session: Session): app = App(app_ui, server) """ - LIB_PREFIX: str = "lib/" - """ - A path prefix to place before all HTML dependencies processed by - ``register_web_dependency()``. - """ - SANITIZE_ERRORS: bool = False """ Whether or not to show a generic message (``SANITIZE_ERRORS=True``) or the actual @@ -72,7 +66,7 @@ def __init__( *, debug: bool = False, ) -> None: - self.ui: RenderedHTML = _render_page(ui, lib_prefix=self.LIB_PREFIX) + self.ui: RenderedHTML = _render_page(ui) self.server: Callable[[Inputs, Outputs, Session], None] = server self._debug: bool = debug @@ -263,7 +257,7 @@ def _register_web_dependency(self, dep: HTMLDependency) -> None: # For HTMLDependencies that have sources on disk, mount the source dir. # (Some HTMLDependencies only carry head content, and have no source on disk.) if dep.source: - paths = dep.source_path_map(lib_prefix=self.LIB_PREFIX) + paths = dep.source_path_map() self._dependency_handler.mount( "/" + paths["href"], StaticFiles(directory=paths["source"]), @@ -273,6 +267,6 @@ def _register_web_dependency(self, dep: HTMLDependency) -> None: self._registered_dependencies[dep.name] = dep -def _render_page(ui: Union[Tag, TagList], lib_prefix: str) -> RenderedHTML: +def _render_page(ui: Union[Tag, TagList]) -> RenderedHTML: doc = HTMLDocument(TagList(jquery_deps(), shiny_deps(), ui)) - return doc.render(lib_prefix=lib_prefix) + return doc.render() diff --git a/shiny/examples/include_css/app.py b/shiny/examples/include_css/app.py new file mode 100644 index 000000000..1b179574d --- /dev/null +++ b/shiny/examples/include_css/app.py @@ -0,0 +1,16 @@ +import os +from shiny import * + +css_file = os.path.join(os.path.dirname(__file__), "css/styles.css") + +app_ui = ui.page_fluid( + "Almost before we knew it, we had left the ground!!!", + ui.include_css(css_file, method="link_files"), +) + + +def server(input: Inputs, output: Outputs, session: Session): + pass + + +app = App(app_ui, server) diff --git a/shiny/examples/include_css/css/SquarePeg-Regular.ttf b/shiny/examples/include_css/css/SquarePeg-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..dfc6f841ad60671e65fac9e6f7128638a202741a GIT binary patch literal 126688 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 literal 0 HcmV?d00001 diff --git a/shiny/examples/include_css/css/styles.css b/shiny/examples/include_css/css/styles.css new file mode 100644 index 000000000..f66696ac0 --- /dev/null +++ b/shiny/examples/include_css/css/styles.css @@ -0,0 +1,10 @@ +@font-face { + font-family: 'Square Peg'; + src: url('SquarePeg-Regular.ttf'); +} + +body { + font-family: 'Square Peg', sans-serif; + font-size: 3rem; + background-color: pink +} diff --git a/shiny/examples/include_html/app.py b/shiny/examples/include_html/app.py new file mode 100644 index 000000000..3079aedd9 --- /dev/null +++ b/shiny/examples/include_html/app.py @@ -0,0 +1,10 @@ +from shiny import * + +app_ui = ui.page_fluid() + + +def server(input: Inputs, output: Outputs, session: Session): + pass + + +app = App(app_ui, server) diff --git a/shiny/examples/include_javascript/app.py b/shiny/examples/include_javascript/app.py new file mode 100644 index 000000000..bb4ae0a8f --- /dev/null +++ b/shiny/examples/include_javascript/app.py @@ -0,0 +1,16 @@ +import os +from shiny import * + +js_file = os.path.join(os.path.dirname(__file__), "js/app.js") + +app_ui = ui.page_fluid( + "If you see this page before 'OK'-ing the alert box, something went wrong", + ui.include_javascript(js_file), +) + + +def server(input: Inputs, output: Outputs, session: Session): + pass + + +app = App(app_ui, server) diff --git a/shiny/examples/include_javascript/js/app.js b/shiny/examples/include_javascript/js/app.js new file mode 100644 index 000000000..964cab451 --- /dev/null +++ b/shiny/examples/include_javascript/js/app.js @@ -0,0 +1 @@ +alert("If you're seeing this, the javascript file was included successfully."); diff --git a/shiny/session/_session.py b/shiny/session/_session.py index 46ebf7d99..0f428cc1b 100644 --- a/shiny/session/_session.py +++ b/shiny/session/_session.py @@ -644,7 +644,7 @@ def _process_ui(self, ui: TagChildArg) -> RenderedDeps: deps: List[Dict[str, Any]] = [] for dep in res["dependencies"]: self.app._register_web_dependency(dep) - dep_dict = dep.as_dict(lib_prefix=self.app.LIB_PREFIX) + dep_dict = dep.as_dict() deps.append(dep_dict) return {"deps": deps, "html": res["html"]} diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py index 92bb3b30a..4f4a866fb 100644 --- a/shiny/ui/_include_helpers.py +++ b/shiny/ui/_include_helpers.py @@ -1,51 +1,69 @@ -__all__ = ("include_javascript", "include_css", "include_html", "include_font_face") +__all__ = ("include_javascript", "include_css", "include_html") +import glob import os import shutil import tempfile -from typing import Dict, Tuple +from typing import Dict, Literal, Tuple -from htmltools import tags, Tag, TagAttrArg, HTMLDependency, HTMLDependencySource +# TODO: maybe these include_*() functions should actually live in htmltools? +from htmltools import tags, Tag, TagAttrArg, HTMLDependency from htmltools._util import hash_deterministic +from .._docstring import add_example +# TODO: it's bummer that, when method="link_files" and path is in the same directory +# as the app, the app's source will be included. Should we just not copy .py/.r files? + + +@add_example() def include_javascript( - path: str, - *, - include_files: bool = True, - inline: bool = False, - attrs: Dict[str, TagAttrArg] = {} -) -> HTMLDependency: + path: str, *, method: Literal["link", "link_files", "inline"] = "link" +) -> Tag: """ - Include a JavaScript file as a :func:`~ui.tags.script` tag. + Include a JavaScript file Parameters ---------- path A path to a JS file. - include_files - If ``True``, the JS file may reference other files in the same directory (e.g., - use ``fetch()`` to fetch another file). - inline - Whether to link to the file path or inline its contents. - attrs - Additional attributes to add to the :func:`~ui.tags.script` tag. + method + One of the following: + * ``"link"``: Link to the JS file via a :func:`~ui.tags.script` tag. This + method is generally preferrable to ``"inline"`` since it allows the browser + to cache the file. + * ``"link_files"``: Same as ``"link"``, but also allow for the CSS file to + request other files within ``path``'s immediate parent directory (e.g., + ``fetch()`` the contents of another file). Note that this isn't the default + behavior because you should **be careful not to include files in the same + directory as ``path`` that contain sensitive information**. A good general + rule of thumb to follow is to have ``path`` be located in a subdirectory of + the app directory. For example, if the app's source is located at + ``/app/app.py``, then ``path`` should be somewhere like + ``/app/js/custom.js`` (and all the other relevant accompanying 'safe' files + should be located under ``/app/js/``). + * ``"inline"``: Inline the JS file contents within a :func:`~ui.tags.script` + tag. + + + Returns + ------- + A :func:`~ui.tags.script` tag. Note ---- - This produces a :func:`~ui.tags.script` tag in the :func:`~ui.tags.head` of the - document, which means it executes before any of the document's :func:`~ui.tags.body` - has been parsed, and so DOM manipulation code may need to be wrapped in - ``document.addEventListener("DOMContentLoaded", () => {});`` (or similar). - Alternatively, to get :func:`~ui.tags.script` tag included in the - :func:`~ui.tags.body`, you can do something like this: + This places a :func:`~ui.tags.script` tag in the :func:`~ui.tags.body` of the + document. If instead, you want to place the tag in the :func:`~ui.tags.head` of the + document, you can wrap it in ``head_content`` (in this case, just make sure you're + aware that the DOM probably won't be ready when the script is executed). .. code-block:: python - with open("custom.html", "r", encoding="utf-8") as f: - custom_js = ui.tags.script(f.read(), type="text/javascript") + from htmltools import head_content from shiny import ui - app_ui = ui.page_fluid(..., custom_js, ...) + ui.fluidPage( + head_content(ui.include_javascript("custom.js")) + ) See Also -------- @@ -54,57 +72,65 @@ def include_javascript( ~include_html """ - newpath, hash, contents = read_and_copy(path, include_files) + if method == "inline": + return tags.script(read_utf8(path), type="text/javascript") - name = "include-script-" + hash - source = HTMLDependencySource(subdir=os.path.dirname(newpath)) + include_files = method == "link_files" + path_dest, hash = maybe_copy_files(path, include_files) - if inline: - return HTMLDependency( - name, - DEFAULT_VERSION, - source=source, - all_files=include_files, - head=tags.script(contents, type="text/javascript", **attrs), - ) - else: - return HTMLDependency( - name, - DEFAULT_VERSION, - source=source, - all_files=include_files, - script=dict(src=os.path.basename(path), **attrs), - ) + dep, src = create_include_dependency("include-js-" + hash, path_dest, include_files) + return tags.script(dep, src=src) + +@add_example() def include_css( - path: str, - *, - include_files: bool = True, - inline: bool = False, - attrs: Dict[str, TagAttrArg] = {} -) -> HTMLDependency: + path: str, *, method: Literal["link", "link_files", "inline"] = "link" +) -> Tag: """ - Include CSS + Include a CSS file Parameters ---------- path A path to a CSS file. - include_files - If ``True``, the CSS file may reference other files in the same directory - (e.g., use ``@import()`` to import another file). - inline - If ``True``, the contents are inlined into a :func:`~ui.tags.style` tag; - otherwise, a :func:`~ui.tags.link` tag is generated. - attrs - Additional attributes to add to the :func:`~ui.tags.style` (or - :func:`~ui.tags.link`) tag. + method + One of the following: + * ``"link"``: Link to the CSS file via a :func:`~ui.tags.link` tag. This + method is generally preferrable to ``"inline"`` since it allows the browser + to cache the file. + * ``"link_files"``: Same as ``"link"``, but also allow for the CSS file to + request other files within ``path``'s immediate parent directory (e.g., + ``@import()`` another file). Note that this isn't the default behavior + because you should **be careful not to include files in the same directory + as ``path`` that contain sensitive information**. A good general rule of + thumb to follow is to have ``path`` be located in a subdirectory of the app + directory. For example, if the app's source is located at ``/app/app.py``, + then ``path`` should be somewhere like ``/app/css/custom.css`` (and all the + other relevant accompanying 'safe' files should be located under + ``/app/css/``). + * ``"inline"``: Inline the CSS file contents within a :func:`~ui.tags.style` + tag. + + Returns + ------- + If ``method="inline"``, returns a :func:`~ui.tags.style` tag; otherwise, returns a + :func:`~ui.tags.link` tag. Note ---- - This produces a :func:`~ui.tags.style` tag in the :func:`~ui.tags.head` of the - document, which is advantageous for performance. + By default this places a :func:`~ui.tags.link` (or :func:`~ui.tags.style`) tag in + the :func:`~ui.tags.body` of the document, which isn't optimal for performance, and + may result in a Flash of Unstyled Content (FOUC). To instead place the CSS in the + :func:`~ui.tags.head` of the document, you can wrap it in ``head_content``: + + .. code-block:: python + + from htmltools import head_content from shiny import ui + + ui.fluidPage( + head_content(ui.include_css("custom.css")) + ) See Also -------- @@ -114,55 +140,61 @@ def include_css( ~include_html """ - newpath, hash, css = read_and_copy(path, include_files) + if method == "inline": + return tags.style(read_utf8(path), type="text/css") - name = "include-script-" + hash - source = HTMLDependencySource(subdir=os.path.dirname(newpath)) + include_files = method == "link_files" + path_dest, hash = maybe_copy_files(path, include_files) - if inline: - return HTMLDependency( - name, - DEFAULT_VERSION, - source=source, - all_files=include_files, - head=tags.style(css, type="text/css", **attrs), - ) - else: - return HTMLDependency( - name, - DEFAULT_VERSION, - source=source, - all_files=include_files, - stylesheet=dict(href=os.path.basename(path), **attrs), - ) + dep, src = create_include_dependency( + "include-css-" + hash, path_dest, include_files + ) + + return tags.link(dep, href=src, rel="stylesheet") +# TODO: maybe support remote URLs? +@add_example() def include_html( - path: str, *, include_files: bool = True, attrs: Dict[str, TagAttrArg] = {} + path: str, + *, + method: Literal["link", "link_files"] = "link", + attrs: Dict[str, TagAttrArg] = {}, ) -> Tag: """ - Include an HTML file as an :func:`~ui.tags.iframe` + Include an HTML file Parameters ---------- path A path to an HTML file. - include_files - Whether to make other files in the ``path``'s directory available. + method + One of the following: + * ``"link"``: Link to the CSS file via a :func:`~ui.tags.link` tag. + * ``"link_files"``: Same as ``"link"``, but also allow for the HTML file to + request other files within ``path``'s immediate parent directory (e.g., + include an HTML ```` that points to another local file). This isn't the + default behavior because you should **be careful not to include files in the + same directory as ``path`` that contain sensitive information**. A good + general rule of thumb to follow is to have ``path`` be located in a + subdirectory of the app directory. For example, if the app's source is + located at ``/app/app.py``, then ``path`` should be somewhere like + ``/app/html/index.html`` (and all the other relevant accompanying 'safe' + files should be located under ``/app/html/``). attrs Additional attributes to add to the :func:`~ui.tags.iframe` tag. Returns ------- - An :func:`~ui.tags.iframe` tag. + A :func:`~ui.tags.iframe` tag. Note ---- For safety reasons, this function includes the HTML file as a :func:`~ui.tags.iframe`, which means it's 'isolated' from the rest of the parent - document. If instead, you don't want to isolate (meaning, among other things, - you want the HTML inherit CSS styles from the parent document), you can do - something like this: + document. If instead, you don't want to isolate (meaning, among other things, you + want the HTML inherit CSS styles from the parent document), you can do something + like this: .. code-block:: python from shiny import ui @@ -180,40 +212,85 @@ def include_html( ~include_css """ - newpath, hash, _ = read_and_copy(path, include_files) + include_files = method == "link_files" + path_dest, hash = maybe_copy_files(path, include_files) - return tags.iframe( - dict(src=os.path.basename(newpath), class_="shiny-iframe", **attrs), - HTMLDependency( - "include-iframe-" + hash, - DEFAULT_VERSION, - source=HTMLDependencySource(subdir=os.path.dirname(newpath)), - ), + dep, src = create_include_dependency( + "include-html-" + hash, path_dest, include_files ) + default_attrs: Dict[str, TagAttrArg] = { + "src": src, + "scrolling": "no", + "seamless": "seamless", + "frameBorder": "0", + } + + return tags.iframe(default_attrs, dep, **attrs) + -def include_font_face(path: str): - pass +# --------------------------------------------------------------------------- +# Include helpers +# --------------------------------------------------------------------------- -def read_and_copy(path: str, include_files: bool) -> Tuple[str, str, str]: - contents = read_utf8(path) +def create_include_dependency( + name: str, path: str, include_files: bool +) -> Tuple[HTMLDependency, str]: + + dep = HTMLDependency( + name, + DEFAULT_VERSION, + source={"subdir": os.path.dirname(path)}, + all_files=include_files, + ) + + # source_path_map() tells us where the source subdir is mapped to on the client + # (i.e., session._register_web_dependency() uses the same thing to determine where + # to mount the subdir, but we can't assume an active session at this point). + src = os.path.join(dep.source_path_map()["href"], os.path.basename(path)) + + return dep, src + + +def maybe_copy_files(path: str, include_files: bool) -> Tuple[str, str]: + hash = get_hash(path, include_files) + + # To avoid unnecessary work when the same file is included multiple times, + # use a directory scoped by a hash of the file. + tmpdir = os.path.join(tempfile.gettempdir(), "shiny_include_files", hash) + path_dest = os.path.join(tmpdir, os.path.basename(path)) + + # Since the hash/tmpdir should represent all the files in the path's directory, + # we can simply return here + if os.path.exists(path_dest): + return path_dest, hash + + # Otherwise, make sure we have a clean slate + if os.path.exists(tmpdir): + shutil.rmtree(tmpdir) - tmpdir = tempfile.mkdtemp() if include_files: - # Copy all the directory contents except for path, which we copy later - # TODO: verify this actually works - shutil.copytree( - os.path.dirname(path), tmpdir, ignore=shutil.ignore_patterns(path) - ) + shutil.copytree(os.path.dirname(path), tmpdir) + else: + os.makedirs(tmpdir, exist_ok=True) + shutil.copy(path, path_dest) + + return path_dest, hash + - hash = hash_deterministic(contents) - basename = os.path.splitext(os.path.basename(path)) - newpath = os.path.join(tmpdir, basename[0] + "-" + hash + basename[1]) +def get_hash(path: str, include_files: bool) -> str: + if include_files: + key = get_file_key(path) + else: + dir = os.path.dirname(path) + files = glob.iglob(os.path.join(dir, "**"), recursive=True) + key = "\n".join([get_file_key(x) for x in files]) + return hash_deterministic(key) - shutil.copy(path, newpath) - return newpath, hash, contents +def get_file_key(path: str) -> str: + return path + "-" + str(os.path.getmtime(path)) def read_utf8(path: str) -> str: From 49d162b6ca87bb9587bae5ce027a4ceecb96866b Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Tue, 2 May 2023 12:59:47 -0300 Subject: [PATCH 03/13] Linting --- shiny/ui/_include_helpers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py index 4f4a866fb..fe717517b 100644 --- a/shiny/ui/_include_helpers.py +++ b/shiny/ui/_include_helpers.py @@ -7,7 +7,7 @@ from typing import Dict, Literal, Tuple # TODO: maybe these include_*() functions should actually live in htmltools? -from htmltools import tags, Tag, TagAttrArg, HTMLDependency +from htmltools import HTMLDependency, Tag, TagAttrArg, tags from htmltools._util import hash_deterministic from .._docstring import add_example @@ -237,7 +237,6 @@ def include_html( def create_include_dependency( name: str, path: str, include_files: bool ) -> Tuple[HTMLDependency, str]: - dep = HTMLDependency( name, DEFAULT_VERSION, From 0f85347e93f5188e8e5092da06e418df9e8b8f47 Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Fri, 12 May 2023 10:30:04 -0300 Subject: [PATCH 04/13] PR Comments --- shiny/examples/include_css/app.py | 13 +- .../include_css/css/SquarePeg-Regular.ttf | Bin 126688 -> 0 bytes shiny/examples/include_css/css/styles.css | 1 - shiny/examples/include_html/app.py | 10 -- shiny/examples/include_javascript/app.py | 9 +- shiny/ui/__init__.py | 3 + shiny/ui/_include_helpers.py | 120 +++++------------- 7 files changed, 44 insertions(+), 112 deletions(-) delete mode 100644 shiny/examples/include_css/css/SquarePeg-Regular.ttf delete mode 100644 shiny/examples/include_html/app.py diff --git a/shiny/examples/include_css/app.py b/shiny/examples/include_css/app.py index 1b179574d..515e809fd 100644 --- a/shiny/examples/include_css/app.py +++ b/shiny/examples/include_css/app.py @@ -1,4 +1,5 @@ import os + from shiny import * css_file = os.path.join(os.path.dirname(__file__), "css/styles.css") @@ -6,11 +7,11 @@ app_ui = ui.page_fluid( "Almost before we knew it, we had left the ground!!!", ui.include_css(css_file, method="link_files"), + ui.div( + # Style individual elements with an attribute dictionary. + {"style": "font-weight: bold"}, + ui.p("Bold text"), + ), ) - -def server(input: Inputs, output: Outputs, session: Session): - pass - - -app = App(app_ui, server) +app = App(app_ui, None) diff --git a/shiny/examples/include_css/css/SquarePeg-Regular.ttf b/shiny/examples/include_css/css/SquarePeg-Regular.ttf deleted file mode 100644 index dfc6f841ad60671e65fac9e6f7128638a202741a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 126688 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 diff --git a/shiny/examples/include_css/css/styles.css b/shiny/examples/include_css/css/styles.css index f66696ac0..77d1e5297 100644 --- a/shiny/examples/include_css/css/styles.css +++ b/shiny/examples/include_css/css/styles.css @@ -4,7 +4,6 @@ } body { - font-family: 'Square Peg', sans-serif; font-size: 3rem; background-color: pink } diff --git a/shiny/examples/include_html/app.py b/shiny/examples/include_html/app.py deleted file mode 100644 index 3079aedd9..000000000 --- a/shiny/examples/include_html/app.py +++ /dev/null @@ -1,10 +0,0 @@ -from shiny import * - -app_ui = ui.page_fluid() - - -def server(input: Inputs, output: Outputs, session: Session): - pass - - -app = App(app_ui, server) diff --git a/shiny/examples/include_javascript/app.py b/shiny/examples/include_javascript/app.py index bb4ae0a8f..84ce68ef0 100644 --- a/shiny/examples/include_javascript/app.py +++ b/shiny/examples/include_javascript/app.py @@ -1,16 +1,13 @@ import os + from shiny import * js_file = os.path.join(os.path.dirname(__file__), "js/app.js") app_ui = ui.page_fluid( "If you see this page before 'OK'-ing the alert box, something went wrong", - ui.include_javascript(js_file), + ui.include_js(js_file), ) -def server(input: Inputs, output: Outputs, session: Session): - pass - - -app = App(app_ui, server) +app = App(app_ui, None) diff --git a/shiny/ui/__init__.py b/shiny/ui/__init__.py index d3f420f68..3464b9d3c 100644 --- a/shiny/ui/__init__.py +++ b/shiny/ui/__init__.py @@ -18,6 +18,7 @@ ) from ._download_button import download_button, download_link from ._plot_output_opts import brush_opts, click_opts, dblclick_opts, hover_opts +from ._include_helpers import include_css, include_js from ._input_action_button import input_action_button, input_action_link from ._input_check_radio import ( input_checkbox, @@ -124,6 +125,8 @@ "click_opts", "dblclick_opts", "hover_opts", + "include_css", + "include_js", "input_action_button", "input_action_link", "input_checkbox", diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py index fe717517b..756c8436b 100644 --- a/shiny/ui/_include_helpers.py +++ b/shiny/ui/_include_helpers.py @@ -1,23 +1,25 @@ -__all__ = ("include_javascript", "include_css", "include_html") +from __future__ import annotations + +__all__ = ("include_js", "include_css") import glob +import hashlib import os import shutil import tempfile -from typing import Dict, Literal, Tuple # TODO: maybe these include_*() functions should actually live in htmltools? -from htmltools import HTMLDependency, Tag, TagAttrArg, tags -from htmltools._util import hash_deterministic +from htmltools import HTMLDependency, Tag, TagAttrs, tags from .._docstring import add_example +from .._typing_extensions import Literal # TODO: it's bummer that, when method="link_files" and path is in the same directory # as the app, the app's source will be included. Should we just not copy .py/.r files? @add_example() -def include_javascript( +def include_js( path: str, *, method: Literal["link", "link_files", "inline"] = "link" ) -> Tag: """ @@ -59,17 +61,21 @@ def include_javascript( .. code-block:: python - from htmltools import head_content from shiny import ui + + ui.fluidPage( + head_content(ui.include_js("custom.js")), + ) + + # Alternately you can inline Javscript by changing the method. ui.fluidPage( - head_content(ui.include_javascript("custom.js")) + head_content(ui.include_js("custom.js", method = "inline")), ) See Also -------- ~ui.tags.script ~include_css - ~include_html """ if method == "inline": @@ -129,15 +135,20 @@ def include_css( from htmltools import head_content from shiny import ui ui.fluidPage( - head_content(ui.include_css("custom.css")) + head_content(ui.include_css("custom.css")), + + # You can also inline css by passing a dictionary with a `style` element. + ui.div( + {"style": "font-weight: bold;"}, + ui.p("Some text!"), + ) ) See Also -------- ~ui.tags.style ~ui.tags.link - ~include_javascript - ~include_html + ~include_js """ if method == "inline": @@ -153,82 +164,6 @@ def include_css( return tags.link(dep, href=src, rel="stylesheet") -# TODO: maybe support remote URLs? -@add_example() -def include_html( - path: str, - *, - method: Literal["link", "link_files"] = "link", - attrs: Dict[str, TagAttrArg] = {}, -) -> Tag: - """ - Include an HTML file - - Parameters - ---------- - path - A path to an HTML file. - method - One of the following: - * ``"link"``: Link to the CSS file via a :func:`~ui.tags.link` tag. - * ``"link_files"``: Same as ``"link"``, but also allow for the HTML file to - request other files within ``path``'s immediate parent directory (e.g., - include an HTML ```` that points to another local file). This isn't the - default behavior because you should **be careful not to include files in the - same directory as ``path`` that contain sensitive information**. A good - general rule of thumb to follow is to have ``path`` be located in a - subdirectory of the app directory. For example, if the app's source is - located at ``/app/app.py``, then ``path`` should be somewhere like - ``/app/html/index.html`` (and all the other relevant accompanying 'safe' - files should be located under ``/app/html/``). - attrs - Additional attributes to add to the :func:`~ui.tags.iframe` tag. - - Returns - ------- - A :func:`~ui.tags.iframe` tag. - - Note - ---- - For safety reasons, this function includes the HTML file as a - :func:`~ui.tags.iframe`, which means it's 'isolated' from the rest of the parent - document. If instead, you don't want to isolate (meaning, among other things, you - want the HTML inherit CSS styles from the parent document), you can do something - like this: - - .. code-block:: python - from shiny import ui - - with open("custom.html", "r", encoding="utf-8") as f: - custom_html = ui.HTML(f.read()) - - app_ui = ui.page_fluid(..., custom_html, ...) - - See also - -------- - ~ui.tags.iframe - ~ui.HTML - ~include_javascript - ~include_css - """ - - include_files = method == "link_files" - path_dest, hash = maybe_copy_files(path, include_files) - - dep, src = create_include_dependency( - "include-html-" + hash, path_dest, include_files - ) - - default_attrs: Dict[str, TagAttrArg] = { - "src": src, - "scrolling": "no", - "seamless": "seamless", - "frameBorder": "0", - } - - return tags.iframe(default_attrs, dep, **attrs) - - # --------------------------------------------------------------------------- # Include helpers # --------------------------------------------------------------------------- @@ -236,7 +171,7 @@ def include_html( def create_include_dependency( name: str, path: str, include_files: bool -) -> Tuple[HTMLDependency, str]: +) -> tuple[HTMLDependency, str]: dep = HTMLDependency( name, DEFAULT_VERSION, @@ -252,7 +187,7 @@ def create_include_dependency( return dep, src -def maybe_copy_files(path: str, include_files: bool) -> Tuple[str, str]: +def maybe_copy_files(path: str, include_files: bool) -> tuple[str, str]: hash = get_hash(path, include_files) # To avoid unnecessary work when the same file is included multiple times, @@ -292,6 +227,13 @@ def get_file_key(path: str) -> str: return path + "-" + str(os.path.getmtime(path)) +def hash_deterministic(s: str) -> str: + """ + Returns a deterministic hash of the given string. + """ + return hashlib.sha1(s.encode("utf-8")).hexdigest() + + def read_utf8(path: str) -> str: with open(path, "r", encoding="utf-8") as f: return f.read() From 06de6d3a60c1a1ed9a07e5bfeaa80af54d532253 Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Fri, 12 May 2023 13:26:28 -0300 Subject: [PATCH 05/13] Pass attrributes to tags.script --- shiny/ui/_include_helpers.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py index 756c8436b..5ccac3c92 100644 --- a/shiny/ui/_include_helpers.py +++ b/shiny/ui/_include_helpers.py @@ -20,7 +20,7 @@ @add_example() def include_js( - path: str, *, method: Literal["link", "link_files", "inline"] = "link" + path: str, *, method: Literal["link", "link_files", "inline"] = "link", **kwargs ) -> Tag: """ Include a JavaScript file @@ -46,6 +46,8 @@ def include_js( should be located under ``/app/js/``). * ``"inline"``: Inline the JS file contents within a :func:`~ui.tags.script` tag. + *kwargs + Attributes which are passed on to `~ui.tags.script` Returns @@ -79,14 +81,14 @@ def include_js( """ if method == "inline": - return tags.script(read_utf8(path), type="text/javascript") + return tags.script(read_utf8(path), type="text/javascript", **kwargs) include_files = method == "link_files" path_dest, hash = maybe_copy_files(path, include_files) dep, src = create_include_dependency("include-js-" + hash, path_dest, include_files) - return tags.script(dep, src=src) + return tags.script(dep, src=src, **kwargs) @add_example() From 8c33228a000397fc887f2d24cdad7238448bd473 Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Fri, 12 May 2023 13:50:14 -0300 Subject: [PATCH 06/13] linting --- shiny/ui/_include_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py index 5ccac3c92..5c81108d9 100644 --- a/shiny/ui/_include_helpers.py +++ b/shiny/ui/_include_helpers.py @@ -9,7 +9,7 @@ import tempfile # TODO: maybe these include_*() functions should actually live in htmltools? -from htmltools import HTMLDependency, Tag, TagAttrs, tags +from htmltools import HTMLDependency, Tag, tags from .._docstring import add_example from .._typing_extensions import Literal From 5427d77c70b0edd088862b7f0049793a0e340d0a Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Fri, 12 May 2023 15:00:55 -0300 Subject: [PATCH 07/13] Typing --- shiny/ui/_include_helpers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py index 5c81108d9..152ae2b29 100644 --- a/shiny/ui/_include_helpers.py +++ b/shiny/ui/_include_helpers.py @@ -9,7 +9,7 @@ import tempfile # TODO: maybe these include_*() functions should actually live in htmltools? -from htmltools import HTMLDependency, Tag, tags +from htmltools import HTMLDependency, Tag, TagAttrValue, tags from .._docstring import add_example from .._typing_extensions import Literal @@ -20,7 +20,10 @@ @add_example() def include_js( - path: str, *, method: Literal["link", "link_files", "inline"] = "link", **kwargs + path: str, + *, + method: Literal["link", "link_files", "inline"] = "link", + **kwargs: TagAttrValue, ) -> Tag: """ Include a JavaScript file From 3b5b9c09af9df13aa6a3eee5eef7074dc3513357 Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Mon, 15 May 2023 14:55:19 -0300 Subject: [PATCH 08/13] Update shiny/ui/_include_helpers.py Co-authored-by: Winston Chang --- shiny/ui/_include_helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py index 152ae2b29..120cfeb32 100644 --- a/shiny/ui/_include_helpers.py +++ b/shiny/ui/_include_helpers.py @@ -37,9 +37,9 @@ def include_js( * ``"link"``: Link to the JS file via a :func:`~ui.tags.script` tag. This method is generally preferrable to ``"inline"`` since it allows the browser to cache the file. - * ``"link_files"``: Same as ``"link"``, but also allow for the CSS file to + * ``"link_files"``: Same as ``"link"``, but also allow for the JS file to request other files within ``path``'s immediate parent directory (e.g., - ``fetch()`` the contents of another file). Note that this isn't the default + ``import()` another file, if it is loaded with `type="module"`). Note that this isn't the default behavior because you should **be careful not to include files in the same directory as ``path`` that contain sensitive information**. A good general rule of thumb to follow is to have ``path`` be located in a subdirectory of From 7de397806e2f058e3c9dfb086220c307ef25bfc9 Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Mon, 15 May 2023 14:55:26 -0300 Subject: [PATCH 09/13] Update shiny/ui/_include_helpers.py Co-authored-by: Winston Chang --- shiny/ui/_include_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py index 120cfeb32..559069066 100644 --- a/shiny/ui/_include_helpers.py +++ b/shiny/ui/_include_helpers.py @@ -49,7 +49,7 @@ def include_js( should be located under ``/app/js/``). * ``"inline"``: Inline the JS file contents within a :func:`~ui.tags.script` tag. - *kwargs + **kwargs Attributes which are passed on to `~ui.tags.script` From ee30073d3db83d38fd85e40260c81fbd50a02a5e Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Mon, 15 May 2023 14:55:40 -0300 Subject: [PATCH 10/13] Update shiny/ui/_include_helpers.py Co-authored-by: Winston Chang --- shiny/ui/_include_helpers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py index 559069066..f48c74f62 100644 --- a/shiny/ui/_include_helpers.py +++ b/shiny/ui/_include_helpers.py @@ -66,8 +66,6 @@ def include_js( .. code-block:: python - - ui.fluidPage( head_content(ui.include_js("custom.js")), ) From 2b5010fa49206432ace84bb51621102cff3fb7c5 Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Mon, 15 May 2023 14:55:54 -0300 Subject: [PATCH 11/13] Update shiny/ui/_include_helpers.py Co-authored-by: Winston Chang --- shiny/ui/_include_helpers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py index f48c74f62..087456a02 100644 --- a/shiny/ui/_include_helpers.py +++ b/shiny/ui/_include_helpers.py @@ -55,7 +55,8 @@ def include_js( Returns ------- - A :func:`~ui.tags.script` tag. + : + A :func:`~ui.tags.script` tag. Note ---- From 2c3bd1ffb9dff2d4a34e532363bca74b3a7f3fab Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Mon, 15 May 2023 15:09:44 -0300 Subject: [PATCH 12/13] PR comments --- shiny/examples/include_css/css/styles.css | 5 --- shiny/ui/_include_helpers.py | 53 +++++++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/shiny/examples/include_css/css/styles.css b/shiny/examples/include_css/css/styles.css index 77d1e5297..4eec9c349 100644 --- a/shiny/examples/include_css/css/styles.css +++ b/shiny/examples/include_css/css/styles.css @@ -1,8 +1,3 @@ -@font-face { - font-family: 'Square Peg'; - src: url('SquarePeg-Regular.ttf'); -} - body { font-size: 3rem; background-color: pink diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py index 087456a02..75494493a 100644 --- a/shiny/ui/_include_helpers.py +++ b/shiny/ui/_include_helpers.py @@ -7,6 +7,7 @@ import os import shutil import tempfile +from pathlib import Path # TODO: maybe these include_*() functions should actually live in htmltools? from htmltools import HTMLDependency, Tag, TagAttrValue, tags @@ -20,7 +21,7 @@ @add_example() def include_js( - path: str, + path: Path | str, *, method: Literal["link", "link_files", "inline"] = "link", **kwargs: TagAttrValue, @@ -67,14 +68,14 @@ def include_js( .. code-block:: python - ui.fluidPage( - head_content(ui.include_js("custom.js")), - ) + ui.fluidPage( + head_content(ui.include_js("custom.js")), + ) - # Alternately you can inline Javscript by changing the method. - ui.fluidPage( - head_content(ui.include_js("custom.js", method = "inline")), - ) + # Alternately you can inline Javscript by changing the method. + ui.fluidPage( + head_content(ui.include_js("custom.js", method = "inline")), + ) See Also -------- @@ -83,7 +84,7 @@ def include_js( """ if method == "inline": - return tags.script(read_utf8(path), type="text/javascript", **kwargs) + return tags.script(read_utf8(path), **kwargs) include_files = method == "link_files" path_dest, hash = maybe_copy_files(path, include_files) @@ -122,10 +123,13 @@ def include_css( * ``"inline"``: Inline the CSS file contents within a :func:`~ui.tags.style` tag. + Returns ------- - If ``method="inline"``, returns a :func:`~ui.tags.style` tag; otherwise, returns a - :func:`~ui.tags.link` tag. + : + + If ``method="inline"``, returns a :func:`~ui.tags.style` tag; otherwise, returns a + :func:`~ui.tags.link` tag. Note ---- @@ -136,17 +140,17 @@ def include_css( .. code-block:: python - from htmltools import head_content from shiny import ui + from htmltools import head_content from shiny import ui - ui.fluidPage( - head_content(ui.include_css("custom.css")), + ui.fluidPage( + head_content(ui.include_css("custom.css")), - # You can also inline css by passing a dictionary with a `style` element. - ui.div( - {"style": "font-weight: bold;"}, - ui.p("Some text!"), + # You can also inline css by passing a dictionary with a `style` element. + ui.div( + {"style": "font-weight: bold;"}, + ui.p("Some text!"), + ) ) - ) See Also -------- @@ -191,7 +195,7 @@ def create_include_dependency( return dep, src -def maybe_copy_files(path: str, include_files: bool) -> tuple[str, str]: +def maybe_copy_files(path: Path | str, include_files: bool) -> tuple[str, str]: hash = get_hash(path, include_files) # To avoid unnecessary work when the same file is included multiple times, @@ -217,7 +221,7 @@ def maybe_copy_files(path: str, include_files: bool) -> tuple[str, str]: return path_dest, hash -def get_hash(path: str, include_files: bool) -> str: +def get_hash(path: Path | str, include_files: bool) -> str: if include_files: key = get_file_key(path) else: @@ -227,8 +231,9 @@ def get_hash(path: str, include_files: bool) -> str: return hash_deterministic(key) -def get_file_key(path: str) -> str: - return path + "-" + str(os.path.getmtime(path)) +def get_file_key(path: Path | str) -> str: + path_str = str(path) if isinstance(path, Path) else path + return path_str + "-" + str(os.path.getmtime(path_str)) def hash_deterministic(s: str) -> str: @@ -238,7 +243,7 @@ def hash_deterministic(s: str) -> str: return hashlib.sha1(s.encode("utf-8")).hexdigest() -def read_utf8(path: str) -> str: +def read_utf8(path: Path | str) -> str: with open(path, "r", encoding="utf-8") as f: return f.read() From e325acbeafaf3a3065194ff357e67248c9d25eaf Mon Sep 17 00:00:00 2001 From: Gordon Shotwell Date: Tue, 16 May 2023 16:12:23 -0300 Subject: [PATCH 13/13] Check if asset files exist. --- shiny/examples/include_css/app.py | 6 +++--- shiny/examples/include_javascript/app.py | 4 ++-- shiny/ui/_include_helpers.py | 25 ++++++++++++++++++------ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/shiny/examples/include_css/app.py b/shiny/examples/include_css/app.py index 515e809fd..31e510cd3 100644 --- a/shiny/examples/include_css/app.py +++ b/shiny/examples/include_css/app.py @@ -1,12 +1,12 @@ -import os +from pathlib import Path from shiny import * -css_file = os.path.join(os.path.dirname(__file__), "css/styles.css") +css_file = Path(__file__).parent / "css" / "styles.css" app_ui = ui.page_fluid( "Almost before we knew it, we had left the ground!!!", - ui.include_css(css_file, method="link_files"), + ui.include_css(css_file), ui.div( # Style individual elements with an attribute dictionary. {"style": "font-weight: bold"}, diff --git a/shiny/examples/include_javascript/app.py b/shiny/examples/include_javascript/app.py index 84ce68ef0..00c475704 100644 --- a/shiny/examples/include_javascript/app.py +++ b/shiny/examples/include_javascript/app.py @@ -1,8 +1,8 @@ -import os +from pathlib import Path from shiny import * -js_file = os.path.join(os.path.dirname(__file__), "js/app.js") +js_file = Path(__file__).parent / "js" / "app.js" app_ui = ui.page_fluid( "If you see this page before 'OK'-ing the alert box, something went wrong", diff --git a/shiny/ui/_include_helpers.py b/shiny/ui/_include_helpers.py index 75494493a..8c95e9ba3 100644 --- a/shiny/ui/_include_helpers.py +++ b/shiny/ui/_include_helpers.py @@ -82,12 +82,13 @@ def include_js( ~ui.tags.script ~include_css """ + file_path = check_path(path) if method == "inline": - return tags.script(read_utf8(path), **kwargs) + return tags.script(read_utf8(file_path), **kwargs) include_files = method == "link_files" - path_dest, hash = maybe_copy_files(path, include_files) + path_dest, hash = maybe_copy_files(file_path, include_files) dep, src = create_include_dependency("include-js-" + hash, path_dest, include_files) @@ -159,11 +160,12 @@ def include_css( ~include_js """ + file_path = check_path(path) if method == "inline": - return tags.style(read_utf8(path), type="text/css") + return tags.style(read_utf8(file_path), type="text/css") include_files = method == "link_files" - path_dest, hash = maybe_copy_files(path, include_files) + path_dest, hash = maybe_copy_files(file_path, include_files) dep, src = create_include_dependency( "include-css-" + hash, path_dest, include_files @@ -177,6 +179,17 @@ def include_css( # --------------------------------------------------------------------------- +def check_path(path: Path | str) -> Path: + path = Path(path) + if not path.exists(): + err = f""" + {path.absolute()} does not exist. + Files are typically placed in the app directory and refered to with 'Path(__file__) / {path.name}' + """ + raise RuntimeError(err) + return path + + def create_include_dependency( name: str, path: str, include_files: bool ) -> tuple[HTMLDependency, str]: @@ -232,8 +245,8 @@ def get_hash(path: Path | str, include_files: bool) -> str: def get_file_key(path: Path | str) -> str: - path_str = str(path) if isinstance(path, Path) else path - return path_str + "-" + str(os.path.getmtime(path_str)) + path = Path(path) + return str(path) + "-" + str(path.stat().st_mtime) def hash_deterministic(s: str) -> str: