mbuali's picture
Upload folder using huggingface_hub
d1ceb73 verified
"""Tornado handlers for dynamic theme loading."""
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
from __future__ import annotations
import os
import re
from glob import glob
from typing import Any, Generator
from urllib.parse import urlparse
from jupyter_server.base.handlers import FileFindHandler
from jupyter_server.utils import url_path_join as ujoin
class ThemesHandler(FileFindHandler):
"""A file handler that mangles local urls in CSS files."""
def initialize(
self,
path: str | list[str],
default_filename: str | None = None,
no_cache_paths: list[str] | None = None,
themes_url: str | None = None,
labextensions_path: list[str] | None = None,
**kwargs: Any, # noqa: ARG002
) -> None:
"""Initialize the handler."""
# Get all of the available theme paths in order
labextensions_path = labextensions_path or []
ext_paths: list[str] = []
for ext_dir in labextensions_path:
theme_pattern = ext_dir + "/**/themes"
ext_paths.extend(path for path in glob(theme_pattern, recursive=True))
# Add the core theme path last
if not isinstance(path, list):
path = [path]
path = ext_paths + path
FileFindHandler.initialize(
self, path, default_filename=default_filename, no_cache_paths=no_cache_paths
)
self.themes_url = themes_url
def get_content( # type:ignore[override]
self, abspath: str, start: int | None = None, end: int | None = None
) -> bytes | Generator[bytes, None, None]:
"""Retrieve the content of the requested resource which is located
at the given absolute path.
This method should either return a byte string or an iterator
of byte strings.
"""
base, ext = os.path.splitext(abspath)
if ext != ".css":
return FileFindHandler.get_content(abspath, start, end)
return self._get_css()
def get_content_size(self) -> int:
"""Retrieve the total size of the resource at the given path."""
assert self.absolute_path is not None
base, ext = os.path.splitext(self.absolute_path)
if ext != ".css":
return FileFindHandler.get_content_size(self)
return len(self._get_css())
def _get_css(self) -> bytes:
"""Get the mangled css file contents."""
assert self.absolute_path is not None
with open(self.absolute_path, "rb") as fid:
data = fid.read().decode("utf-8")
if not self.themes_url:
return b""
basedir = os.path.dirname(self.path).replace(os.sep, "/")
basepath = ujoin(self.themes_url, basedir)
# Replace local paths with mangled paths.
# We only match strings that are local urls,
# e.g. `url('../foo.css')`, `url('images/foo.png')`
pattern = r"url\('(.*)'\)|url\('(.*)'\)"
def replacer(m: Any) -> Any:
"""Replace the matched relative url with the mangled url."""
group = m.group()
# Get the part that matched
part = next(g for g in m.groups() if g)
# Ignore urls that start with `/` or have a protocol like `http`.
parsed = urlparse(part)
if part.startswith("/") or parsed.scheme:
return group
return group.replace(part, ujoin(basepath, part))
return re.sub(pattern, replacer, data).encode("utf-8")