diff --git a/frontend/css/style.scss b/frontend/css/style.scss index b4986f5b..9471809c 100644 --- a/frontend/css/style.scss +++ b/frontend/css/style.scss @@ -2719,8 +2719,7 @@ textarea:not(:placeholder-shown) ~ .rio-input-box-label, // Code Explorer .rio-code-explorer { - // pointer-events: none; - pointer-events: auto; // REMOVE just for debugging + pointer-events: none; display: flex; align-items: center; diff --git a/pyproject.toml b/pyproject.toml index 753a742d..bae79ee6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ fastapi = "^0.109.1" fuzzywuzzy = "^0.18.0" gitignore-parser = "^0.1.9" httpx = "^0.25.1" -imy = "^0.2.3" +imy = "^0.2.4" introspection = "^1.7.14" keyring = "^24.3.0" pillow = "^10.2.0" diff --git a/rio/components/color_picker.py b/rio/components/color_picker.py index f15b9142..ab4032a1 100644 --- a/rio/components/color_picker.py +++ b/rio/components/color_picker.py @@ -26,7 +26,7 @@ class ColorPicker(FundamentalComponent): """ # ColorPicker - Allows the user to pick an RGB(A) color. + Allows the user to pick a RGB(A) color. `ColorPicker` is a component that allows the user to pick a color. It displays a combination of colorful areas and sliders that the user can diff --git a/rio/components/plot.py b/rio/components/plot.py index 4c9d76b6..6d44de88 100644 --- a/rio/components/plot.py +++ b/rio/components/plot.py @@ -25,7 +25,7 @@ class Plot(FundamentalComponent): """ # Plot - Displays a matplotlib, seaborn or plotly plot. + Displays a `matplotlib`, `seaborn` or `plotly` plot. ## Attributes diff --git a/rio/debug/client_side_debugger/component_details.py b/rio/debug/client_side_debugger/component_details.py index 74008368..59f078bd 100644 --- a/rio/debug/client_side_debugger/component_details.py +++ b/rio/debug/client_side_debugger/component_details.py @@ -2,6 +2,7 @@ from pathlib import Path from typing import * # type: ignore import rio +import rio.docs from ... import common @@ -308,7 +309,7 @@ class ComponentDetails(rio.Component): # Link to docs if type(target)._rio_builtin_: - docs_url = f"https://rio.dev/docs/{type(target).__name__.lower()}" + docs_url = rio.docs.documentation_url(type(target).__name__) link_color = self.session.theme.secondary_color result.add( diff --git a/rio/docs/__init__.py b/rio/docs/__init__.py index 4f69ff4d..b0b09e12 100644 --- a/rio/docs/__init__.py +++ b/rio/docs/__init__.py @@ -1 +1,25 @@ +import rio + from . import custom as custom + + +def build_documentation_url( + object_name: str, + *, + relative: bool = False, +) -> rio.URL: + """ + Returns the URL to the documentation for the given Rio object. This doesn't + perform any checks on whether the object actually exists and has + documentation. It relies solely on the passed values. + """ + + # Build the relative URL + result_string = f"/docs/object/{object_name.lower()}" + + # Make it absolute, if requested + if not relative: + result_string = "https://rio.dev" + result_string + + # Done + return rio.URL(result_string) diff --git a/rio/docs/custom.py b/rio/docs/custom.py index 1309ba0d..1ae365ad 100644 --- a/rio/docs/custom.py +++ b/rio/docs/custom.py @@ -10,7 +10,7 @@ import imy.docstrings import rio -def find_items_needing_documentation() -> Iterable[Type | Callable]: +def find_objects_possibly_needing_documentation() -> Iterable[Type | Callable]: """ Find all classes and functions in `Rio` that need to be documented. """ diff --git a/rio/icon_registry.py b/rio/icon_registry.py index 463f27b8..b2e40de0 100644 --- a/rio/icon_registry.py +++ b/rio/icon_registry.py @@ -65,10 +65,6 @@ class IconRegistry: sections = icon_name.split("/") if len(sections) == 1: - raise ValueError( - f"Missing icon set in icon `{icon_name}`" - ) # TODO: Decide on a default icon set - icon_set = "material" icon_name = sections[0] elif len(sections) == 2: diff --git a/rio/session.py b/rio/session.py index 47c65ba9..2f4107a5 100644 --- a/rio/session.py +++ b/rio/session.py @@ -114,6 +114,22 @@ class SessionAttachments: def add(self, value: Any) -> None: self._add(value, synchronize=True) + def remove(self, typ: type) -> None: + # Remove the attachment, propagating any `KeyError` + old_value = self._attachments.pop(typ) + + # User settings need special care + if not isinstance(old_value, user_settings_module.UserSettings): + return + + # Unlink the value from the session + old_value._rio_session_ = None + + # Trigger a resync + # + # TODO: `_save_settings_soon` doesn't currently delete any settings + self._session._save_settings_soon() + class Session(unicall.Unicall): """ @@ -345,6 +361,26 @@ class Session(unicall.Unicall): """ return self._attachments[typ] + def __delete__(self, typ: type) -> None: + """ + Removes an attachment from this session. + + ## Raises + + `KeyError`: If no attachment of this type is attached to the session. + """ + self._attachments.remove(typ) + + def detach(self, typ: type) -> None: + """ + Removes an attachment from this session. + + ## Raises + + `KeyError`: If no attachment of this type is attached to the session. + """ + self._attachments.remove(typ) + def close(self) -> None: """ Ends the session, closing any window or browser tab. diff --git a/scripts/check_docs.py b/scripts/check_docs.py index d5acbcb9..6e074678 100644 --- a/scripts/check_docs.py +++ b/scripts/check_docs.py @@ -8,6 +8,8 @@ import sys from pathlib import Path from typing import * # type: ignore +import imy.docstrings + # Some rio modules optionally depend on libraries and evaling their type # annotations can fail if they're not installed. Import them explicitly here to produce more obvious error messages from revel import * # type: ignore @@ -21,7 +23,7 @@ import rio.docs def check_function( - docs: rio.docs.FunctionDocs, + docs: imy.docstrings.FunctionDocs, owning_cls: type | None, ) -> None: qualname = ( @@ -61,7 +63,7 @@ def check_function( ) -def check_class(cls: type, docs: rio.docs.ClassDocs) -> None: +def check_class(cls: type, docs: imy.docstrings.ClassDocs) -> None: # Run checks if docs.summary is None: warning(f"Docstring for `{docs.name}` is missing a short description") @@ -92,20 +94,24 @@ def main() -> None: ) # Find all items that should be documented - print_chapter("Looking for items needing documentation") - target_items: list[type | Callable[..., Any]] = list( - rio.docs.custom.find_items_needing_documentation() + print_chapter("Looking for objects in the Rio module") + candidate_objects: list[type | Callable[..., Any]] = list( + rio.docs.custom.find_objects_possibly_needing_documentation() ) - print(f"Found {len(target_items)} items") + print(f"Found {len(candidate_objects)} items") # Make sure they're all properly documented print_chapter("Making you depressed") - for item in target_items: + for item in candidate_objects: # Classes / Components if inspect.isclass(item): # Fetch the docs - docs = rio.docs.ClassDocs.parse(item) + docs = imy.docstrings.ClassDocs.from_class(item) + + # Drop internals + if not docs.metadata.public: + continue # Post-process them as needed if isinstance(item, rio.Component): @@ -118,7 +124,12 @@ def main() -> None: else: assert inspect.isfunction(item), item - docs = rio.docs.FunctionDocs.parse(item) + # Fetch the docs + docs = imy.docstrings.FunctionDocs.from_function(item) + + # Drop internals + if not docs.metadata.public: + continue check_function(docs, None) @@ -133,7 +144,7 @@ def main() -> None: visited_item_names.add(builder.component_class.__name__) - for item in target_items: + for item in candidate_objects: if item.__name__ not in visited_item_names: warning(f"Item `{item.__name__}` is not displayed in the documentation")