First implementation of bindings

This commit is contained in:
2025-11-09 19:23:18 +01:00
parent b5c1c15198
commit 86dfff812b
51 changed files with 5971 additions and 1080 deletions

View File

@@ -1,5 +1,16 @@
import logging
from bs4 import Tag
from fastcore.xml import FT
from fasthtml.fastapp import fast_app
from starlette.routing import Mount
from myfasthtml.core.constants import Routes, ROUTE_ROOT
from myfasthtml.test.MyFT import MyFT
utils_app, utils_rt = fast_app()
logger = logging.getLogger("Commands")
def mount_if_not_exists(app, path: str, sub_app):
"""
@@ -17,3 +28,166 @@ def mount_if_not_exists(app, path: str, sub_app):
if not is_mounted:
app.mount(path, app=sub_app)
def merge_classes(*args):
all_elements = []
for element in args:
if element is None or element == '':
continue
if isinstance(element, (tuple, list, set)):
all_elements.extend(element)
elif isinstance(element, dict):
if "cls" in element:
all_elements.append(element.pop("cls"))
elif "class" in element:
all_elements.append(element.pop("class"))
elif isinstance(element, str):
all_elements.append(element)
else:
raise ValueError(f"Cannot merge {element} of type {type(element)}")
if all_elements:
# Remove duplicates while preserving order
unique_elements = list(dict.fromkeys(all_elements))
return " ".join(unique_elements)
else:
return None
def debug_routes(app):
def _debug_routes(_app, _route, prefix=""):
if isinstance(_route, Mount):
for sub_route in _route.app.router.routes:
_debug_routes(_app, sub_route, prefix=_route.path)
else:
print(f"path={prefix}{_route.path}, methods={_route.methods}, endpoint={_route.endpoint}")
for route in app.router.routes:
_debug_routes(app, route)
def mount_utils(app):
"""
Mounts the commands_app to the given application instance if the route does not already exist.
:param app: The application instance to which the commands_app will be mounted.
:type app: Any
:return: Returns the result of the mount operation performed by mount_if_not_exists.
:rtype: Any
"""
return mount_if_not_exists(app, ROUTE_ROOT, utils_app)
def get_default_ft_attr(ft):
"""
for every type of HTML element (ft) gives the default attribute to use for binding
:param ft:
:return:
"""
if ft.tag == "input":
if ft.attrs.get("type") == "checkbox":
return "checked"
elif ft.attrs.get("type") == "radio":
return "checked"
elif ft.attrs.get("type") == "file":
return "files"
else:
return "value"
else:
return None # indicate that the content of the FT should be updated
def get_default_attr(data):
all_attrs = data.__dict__.keys()
return next(iter(all_attrs))
def is_checkbox(elt):
if isinstance(elt, (FT, MyFT)):
return elt.tag == "input" and elt.attrs.get("type", None) == "checkbox"
elif isinstance(elt, Tag):
return elt.name == "input" and elt.attrs.get("type", None) == "checkbox"
else:
return False
def is_radio(elt):
if isinstance(elt, (FT, MyFT)):
return elt.tag == "input" and elt.attrs.get("type", None) == "radio"
elif isinstance(elt, Tag):
return elt.name == "input" and elt.attrs.get("type", None) == "radio"
else:
return False
def is_select(elt):
if isinstance(elt, (FT, MyFT)):
return elt.tag == "select"
elif isinstance(elt, Tag):
return elt.name == "select"
else:
return False
def is_datalist(elt):
if isinstance(elt, (FT, MyFT)):
return elt.tag == "datalist"
elif isinstance(elt, Tag):
return elt.name == "datalist"
else:
return False
def quoted_str(s):
if s is None:
return "None"
if isinstance(s, str):
if "'" in s and '"' in s:
return f'"{s.replace('"', '\\"')}"'
elif '"' in s:
return f"'{s}'"
else:
return f'"{s}"'
return str(s)
@utils_rt(Routes.Commands)
def post(session, c_id: str):
"""
Default routes for all commands.
:param session:
:param c_id:
:return:
"""
logger.debug(f"Entering {Routes.Commands} with {session=}, {c_id=}")
from myfasthtml.core.commands import CommandsManager
command = CommandsManager.get_command(c_id)
if command:
return command.execute()
raise ValueError(f"Command with ID '{c_id}' not found.")
@utils_rt(Routes.Bindings)
def post(session, b_id: str, values: dict):
"""
Default routes for all bindings.
:param session:
:param b_id:
:param values:
:return:
"""
logger.debug(f"Entering {Routes.Bindings} with {session=}, {b_id=}, {values=}")
from myfasthtml.core.bindings import BindingsManager
binding = BindingsManager.get_binding(b_id)
if binding:
return binding.update(values)
raise ValueError(f"Binding with ID '{b_id}' not found.")