team-10/env/Lib/site-packages/streamlit/components/v1/component_registry.py
2025-08-02 07:34:44 +02:00

147 lines
5.6 KiB
Python

# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2025)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
import inspect
import os
from pathlib import Path
from typing import TYPE_CHECKING
from streamlit import config
from streamlit.components.v1.custom_component import CustomComponent
from streamlit.runtime import get_instance
from streamlit.runtime.scriptrunner_utils.script_run_context import get_script_run_ctx
if TYPE_CHECKING:
from types import FrameType
from streamlit.components.types.base_component_registry import BaseComponentRegistry
def _get_module_name(caller_frame: FrameType) -> str:
# Get the caller's module name. `__name__` gives us the module's
# fully-qualified name, which includes its package.
module = inspect.getmodule(caller_frame)
if module is None:
raise RuntimeError("module is None. This should never happen.")
module_name = module.__name__
# If the caller was the main module that was executed (that is, if the
# user executed `python my_component.py`), then this name will be
# "__main__" instead of the actual package name. In this case, we use
# the main module's filename, sans `.py` extension, as the component name.
if module_name == "__main__":
file_path = inspect.getfile(caller_frame)
filename = os.path.basename(file_path)
module_name, _ = os.path.splitext(filename)
return module_name
def declare_component(
name: str,
path: str | Path | None = None,
url: str | None = None,
) -> CustomComponent:
"""Create a custom component and register it if there is a ``ScriptRunContext``.
The component is not registered when there is no ``ScriptRunContext``.
This can happen when a ``CustomComponent`` is executed as standalone
command (e.g. for testing).
To use this function, import it from the ``streamlit.components.v1``
module.
.. warning::
Using ``st.components.v1.declare_component`` directly (instead of
importing its module) is deprecated and will be disallowed in a later
version.
Parameters
----------
name : str
A short, descriptive name for the component, like "slider".
path: str, Path, or None
The path to serve the component's frontend files from. The path should
be absolute. If ``path`` is ``None`` (default), Streamlit will serve
the component from the location in ``url``. Either ``path`` or ``url``
must be specified. If both are specified, then ``url`` will take
precedence.
url: str or None
The URL that the component is served from. If ``url`` is ``None``
(default), Streamlit will serve the component from the location in
``path``. Either ``path`` or ``url`` must be specified. If both are
specified, then ``url`` will take precedence.
Returns
-------
CustomComponent
A ``CustomComponent`` that can be called like a function.
Calling the component will create a new instance of the component
in the Streamlit app.
"""
if path is not None and isinstance(path, Path):
path = str(path)
# Get our stack frame.
current_frame: FrameType | None = inspect.currentframe()
if current_frame is None:
raise RuntimeError("current_frame is None. This should never happen.")
# Get the stack frame of our calling function.
caller_frame = current_frame.f_back
if caller_frame is None:
raise RuntimeError("caller_frame is None. This should never happen.")
module_name = _get_module_name(caller_frame)
# Build the component name.
component_name = f"{module_name}.{name}"
# NOTE: We intentionally don't mention this behavior in this function's
# docstring as we're only using it internally for now (the config option
# is also hidden). This should be properly documented if/when we do decide
# to expose it more publicly.
if not url and (
component_base_path := config.get_option("server.customComponentBaseUrlPath")
):
url = f"{component_base_path}/{component_name}/"
# Create our component object, and register it.
component = CustomComponent(
name=component_name, path=path, url=url, module_name=module_name
)
# the ctx can be None if a custom component script is run outside of Streamlit, e.g. via 'python ...'
ctx = get_script_run_ctx()
if ctx is not None:
get_instance().component_registry.register_component(component)
return component
# Keep for backwards-compatibility for now as we don't know whether existing custom
# components use this method. We made significant refactors to the custom component
# registry code in https://github.com/streamlit/streamlit/pull/8193 and after
# that is out in the wild, we can follow-up with more refactorings, e.g. remove
# the following class and method. When we do that, we should conduct some testing with
# popular custom components.
class ComponentRegistry:
@classmethod
def instance(cls) -> BaseComponentRegistry:
"""Returns the ComponentRegistry of the runtime instance."""
return get_instance().component_registry