Repository URL to install this package:
|
Version:
0.4.6 ▾
|
# -*- coding: utf-8 -*-
from typing import TYPE_CHECKING, Any, Dict, Iterable, Mapping, Set, Type, Union
from jinja2 import Template
from kiara.models.module.pipeline.pipeline import Pipeline
from kiara.renderers import RenderInputsSchema, SourceTransformer
from kiara.renderers.included_renderers.pipeline import PipelineTransformer
from kiara.renderers.jinja import BaseJinjaRenderer, JinjaEnv
from kiara.utils import log_message
from kiara_plugin.streamlit.components.input import (
InputComponent,
)
from kiara_plugin.streamlit.streamlit import KiaraStreamlit
if TYPE_CHECKING:
from kiara.context import Kiara
class PipelineRendererStreamlit(BaseJinjaRenderer[Pipeline, RenderInputsSchema]):
"""Renders a basic streamlit app from a pipeline structure."""
_renderer_name = "pipeline_streamlit_app"
def retrieve_jinja_env(self) -> JinjaEnv:
jinja_env = JinjaEnv(template_base="kiara_plugin.streamlit")
return jinja_env
def retrieve_supported_render_sources(self) -> str:
return "pipeline"
def retrieve_supported_render_targets(cls) -> Union[Iterable[str], str]:
return "streamlit_app"
def retrieve_source_transformers(self) -> Iterable[SourceTransformer]:
return [PipelineTransformer(kiara=self._kiara)]
def get_template(self, render_config: RenderInputsSchema) -> Template:
return self.get_jinja_env().get_template("pipeline/streamlit_app.py.j2")
def assemble_render_inputs(
self, instance: Any, render_config: RenderInputsSchema
) -> Mapping[str, Any]:
inputs = render_config.dict()
inputs["pipeline"] = instance
return inputs
def _post_process(self, rendered: str) -> str:
try:
import black
from black import Mode # type: ignore
cleaned = black.format_str(rendered, mode=Mode())
return cleaned
except Exception as e:
log_message(
f"Could not format python code, 'black' not in virtual environment: {e}."
)
return rendered
class KiaraStreamlitTransformer(SourceTransformer):
def __init__(self, kiara: "Kiara"):
self._kiara: "Kiara" = kiara
super().__init__()
def retrieve_supported_python_classes(self) -> Iterable[Type]:
return [str]
def retrieve_supported_inputs_descs(self) -> Union[str, Iterable[str]]:
return []
def validate_and_transform(self, source: Any) -> Union[KiaraStreamlit, None]:
if not source or source == "kiara_streamlit":
context_config = self._kiara.context_config
runtime_config = self._kiara.runtime_config
return KiaraStreamlit(
context_config=context_config, runtime_config=runtime_config
)
else:
return None
class KiaraStreamlitAPIClassRenderer(
BaseJinjaRenderer[KiaraStreamlit, RenderInputsSchema]
):
"""Renders a Python module containing a Protocol Class.
This is useful for development, so all the dynamic components can be picked up by IDEs.
"""
_renderer_name = "streamlit_api_class"
def retrieve_jinja_env(self) -> JinjaEnv:
jinja_env = JinjaEnv(template_base="kiara_plugin.streamlit")
return jinja_env
def retrieve_supported_render_sources(self) -> str:
return "kiara_streamlit"
def retrieve_supported_render_targets(cls) -> Union[Iterable[str], str]:
return "python_code"
def retrieve_source_transformers(self) -> Iterable[SourceTransformer]:
return [KiaraStreamlitTransformer(kiara=self._kiara)]
def get_template(self, render_config: RenderInputsSchema) -> Template:
return self.get_jinja_env().get_template("code_gen/kiara_streamlit_api.py.j2")
def assemble_render_inputs(
self, instance: Any, render_config: RenderInputsSchema
) -> Mapping[str, Any]:
render_config.dict()
kiara_streamlit: KiaraStreamlit = instance
components = kiara_streamlit.components
all_methods = {}
imports: Dict[str, Set] = {
"typing": set(),
"streamlit": set(),
"kiara": set(),
"uuid": set(),
}
# imports["kiara_plugin.streamlit.streamlit"] = {"KiaraStreamlit"}
for comp_name in sorted(components.keys()):
comp = components[comp_name]
comp_info = comp.info
doc = comp_info.documentation
is_type_specific_select_comp = (
isinstance(comp, InputComponent)
and comp.component_name != "select_value"
)
params = []
for arg_name, arg in comp_info.arguments.items():
# better hide this component, otherwise it might be confusing
if is_type_specific_select_comp and arg_name in [
"value_schema",
"data_type",
]:
continue
if isinstance(arg.default, str):
arg_default = f'"{arg.default}"'
else:
arg_default = arg.default
arg_type = arg.python_type
arg_type_string = arg.python_type_string
module = arg_type.__module__
if module != "builtins":
imports.setdefault(module, set()).add(arg_type.__name__)
# sorry, this is dodgy as...
if arg_default is not None:
params.append(
{
"name": arg_name,
"type": arg_type_string,
"default": arg_default,
}
)
elif "Union" in arg_type_string and "None" in arg_type_string:
params.append(
{
"name": arg_name,
"type": arg_type_string,
"default": arg_default,
}
)
else:
params.append({"name": arg_name, "type": arg_type_string})
imports["typing"] = set()
signature = ["self"]
for param in params:
if "default" in param.keys():
continue
token = f"{param['name']}: \"{param['type']}\""
signature.append(token)
for param in params:
if "default" not in param.keys():
continue
token = f"{param['name']}: \"{param['type']}\""
token += f" = {param['default']}"
signature.append(token)
signature_string = f"def {comp_name}({', '.join(signature)})"
signature_string = signature_string.replace("NoneType", "None")
signature_string = signature_string.replace("Literal", "str")
all_methods[signature_string] = doc
data = {"imports": imports, "methods": all_methods}
return data
def _post_process(self, rendered: str) -> str:
try:
import black
from black import Mode # type: ignore
cleaned = black.format_str(rendered, mode=Mode())
return cleaned
except Exception as e:
log_message(
f"Could not format python code, 'black' not in virtual environment: {e}."
)
return rendered