Parent is now mandatory when creating a new BaseInstance class

This commit is contained in:
2025-11-16 17:46:44 +01:00
parent edcd3ae1a8
commit e286b60348
14 changed files with 55 additions and 38 deletions

View File

@@ -7,7 +7,7 @@ from myfasthtml.controls.Layout import Layout
from myfasthtml.controls.TabsManager import TabsManager
from myfasthtml.controls.helpers import Ids, mk
from myfasthtml.core.commands import Command
from myfasthtml.core.instances import InstancesManager
from myfasthtml.core.instances import InstancesManager, RootInstance
from myfasthtml.myfastapp import create_app
with open('logging.yaml', 'r') as f:
@@ -27,10 +27,10 @@ app, rt = create_app(protect_routes=True,
@rt("/")
def index(session):
layout = InstancesManager.get(session, Ids.Layout, Layout, "Testing Layout")
layout = InstancesManager.get(session, Ids.Layout, Layout, RootInstance, "Testing Layout")
layout.set_footer("Goodbye World")
tabs_manager = TabsManager(session, _id="main")
tabs_manager = TabsManager(layout, _id=f"{Ids.TabsManager}-main")
btn_show_right_drawer = mk.button("show",
command=Command("ShowRightDrawer",

View File

@@ -27,7 +27,7 @@ class Boundaries(SingleInstance):
"""
def __init__(self, session, owner, container_id: str = None, on_resize=None):
super().__init__(session, Ids.Boundaries)
super().__init__(session, Ids.Boundaries, owner)
self._owner = owner
self._container_id = container_id or owner.get_id()
self._on_resize = on_resize

View File

@@ -84,7 +84,7 @@ class Layout(SingleInstance):
def get_content(self):
return self._content
def __init__(self, session, app_name):
def __init__(self, session, app_name, parent=None):
"""
Initialize the Layout component.
@@ -93,7 +93,7 @@ class Layout(SingleInstance):
left_drawer (bool): Enable left drawer. Default is True.
right_drawer (bool): Enable right drawer. Default is True.
"""
super().__init__(session, Ids.Layout)
super().__init__(session, Ids.Layout, parent)
self.app_name = app_name
# Content storage

View File

@@ -6,7 +6,7 @@ from fasthtml.components import *
from myfasthtml.controls.BaseCommands import BaseCommands
from myfasthtml.controls.helpers import Ids, mk
from myfasthtml.core.commands import Command
from myfasthtml.core.instances import MultipleInstance
from myfasthtml.core.instances import MultipleInstance, BaseInstance
from myfasthtml.core.matching_utils import subsequence_matching, fuzzy_matching
logger = logging.getLogger("Search")
@@ -22,7 +22,7 @@ class Commands(BaseCommands):
class Search(MultipleInstance):
def __init__(self,
session,
parent: BaseInstance,
_id=None,
items_names=None, # what is the name of the items to filter
items=None, # first set of items to filter
@@ -42,7 +42,7 @@ class Search(MultipleInstance):
function that returns the item as is.
:param template: Callable function to render the filtered items. Defaults to a Div rendering function.
"""
super().__init__(session, Ids.Search, _id=_id)
super().__init__(Ids.Search, parent, _id=_id)
self.items_names = items_names or ''
self.items = items or []
self.filtered = self.items.copy()

View File

@@ -77,12 +77,12 @@ class Commands(BaseCommands):
class TabsManager(MultipleInstance):
_tab_count = 0
def __init__(self, session, _id=None):
super().__init__(session, Ids.TabsManager, _id=_id)
def __init__(self, parent, _id=None):
super().__init__(Ids.TabsManager, parent, _id=_id)
self._state = TabsManagerState(self)
self.commands = Commands(self)
self._boundaries = Boundaries()
self._search = Search(self._session,
self._search = Search(self,
items=self._get_tab_list(),
get_attr=lambda x: x["label"],
template=self._mk_tab_button)
@@ -102,7 +102,7 @@ class TabsManager(MultipleInstance):
tab_config = self._state.tabs[tab_id]
if tab_config["component_type"] is None:
return None
return InstancesHelper.dynamic_get(self._session, tab_config["component_type"], tab_config["component_id"])
return InstancesHelper.dynamic_get(self, tab_config["component_type"], tab_config["component_id"])
@staticmethod
def _get_tab_count():
@@ -114,7 +114,7 @@ class TabsManager(MultipleInstance):
logger.debug(f"on_new_tab {label=}, {component=}, {auto_increment=}")
if auto_increment:
label = f"{label}_{self._get_tab_count()}"
component = component or VisNetwork(self._session, nodes=vis_nodes, edges=vis_edges)
component = component or VisNetwork(self, nodes=vis_nodes, edges=vis_edges)
tab_id = self.add_tab(label, component)
return (
self._mk_tabs_controller(),
@@ -328,7 +328,7 @@ class TabsManager(MultipleInstance):
tab_content = self._mk_tab_content(active_tab, content)
self._state._tabs_content[active_tab] = tab_content
else:
tab_content = self._mk_tab_content("", None)
tab_content = self._mk_tab_content(None, None)
return Div(
tab_content,

View File

@@ -35,8 +35,8 @@ class Commands(BaseCommands):
class UserProfile(SingleInstance):
def __init__(self, session):
super().__init__(session, Ids.UserProfile)
def __init__(self, session, parent=None):
super().__init__(session, Ids.UserProfile, parent)
self._state = UserProfileState(self)
self._commands = Commands(self)

View File

@@ -28,8 +28,8 @@ class VisNetworkState(DbObject):
class VisNetwork(MultipleInstance):
def __init__(self, session, _id=None, nodes=None, edges=None, options=None):
super().__init__(session, Ids.VisNetwork, _id=_id)
def __init__(self, parent, _id=None, nodes=None, edges=None, options=None):
super().__init__(Ids.VisNetwork, parent, _id=_id)
logger.debug(f"VisNetwork created with id: {self._id}")
self._state = VisNetworkState(self)

View File

@@ -11,6 +11,7 @@ class Ids:
Boundaries = "mf-boundaries"
DbManager = "mf-dbmanager"
Layout = "mf-layout"
Root = "mf-root"
Search = "mf-search"
TabsManager = "mf-tabs-manager"
UserProfile = "mf-user-profile"

View File

@@ -1,11 +1,11 @@
from myfasthtml.auth.utils import login_user, save_user_info, register_user
from myfasthtml.controls.helpers import Ids
from myfasthtml.core.instances import special_session, UniqueInstance
from myfasthtml.core.instances import UniqueInstance, RootInstance
class AuthProxy(UniqueInstance):
def __init__(self, base_url: str = None):
super().__init__(special_session, Ids.AuthProxy)
super().__init__(Ids.AuthProxy, RootInstance)
self._base_url = base_url
def login_user(self, email: str, password: str):

View File

@@ -9,8 +9,8 @@ from myfasthtml.core.utils import retrieve_user_info
class DbManager(SingleInstance):
def __init__(self, session, root=".myFastHtmlDb", auto_register: bool = True):
super().__init__(session, Ids.DbManager, auto_register=auto_register)
def __init__(self, session, parent=None, root=".myFastHtmlDb", auto_register: bool = True):
super().__init__(session, Ids.DbManager, parent, auto_register=auto_register)
self.db = DbEngine(root=root)
def save(self, entry, obj):

View File

@@ -1,4 +1,5 @@
import uuid
from typing import Self
from myfasthtml.controls.helpers import Ids
@@ -17,10 +18,11 @@ class BaseInstance:
Base class for all instances (manageable by InstancesManager)
"""
def __init__(self, session: dict, prefix: str, _id: str, auto_register: bool = True):
def __init__(self, session: dict, prefix: str, _id: str, parent: Self, auto_register: bool = True):
self._session = session
self._id = _id
self._prefix = prefix
self._parent = parent
if auto_register:
InstancesManager.register(session, self)
@@ -39,8 +41,8 @@ class SingleInstance(BaseInstance):
Base class for instances that can only have one instance at a time.
"""
def __init__(self, session: dict, prefix: str, auto_register: bool = True):
super().__init__(session, prefix, prefix, auto_register)
def __init__(self, session: dict, prefix: str, parent, auto_register: bool = True):
super().__init__(session, prefix, prefix, parent, auto_register)
class UniqueInstance(BaseInstance):
@@ -49,8 +51,8 @@ class UniqueInstance(BaseInstance):
Does not throw exception if the instance already exists, it simply overwrites it.
"""
def __init__(self, session: dict, prefix: str, auto_register: bool = True):
super().__init__(session, prefix, prefix, auto_register)
def __init__(self, prefix: str, parent: BaseInstance, auto_register: bool = True):
super().__init__(parent.get_session(), prefix, prefix, parent, auto_register)
self._prefix = prefix
@@ -59,8 +61,8 @@ class MultipleInstance(BaseInstance):
Base class for instances that can have multiple instances at a time.
"""
def __init__(self, session: dict, prefix: str, auto_register: bool = True, _id=None):
super().__init__(session, prefix, _id or f"{prefix}-{str(uuid.uuid4())}", auto_register)
def __init__(self, prefix: str, parent: BaseInstance, auto_register: bool = True, _id=None):
super().__init__(parent.get_session(), prefix, _id or f"{prefix}-{str(uuid.uuid4())}", parent, auto_register)
self._prefix = prefix
@@ -84,12 +86,13 @@ class InstancesManager:
return instance
@staticmethod
def get(session: dict, instance_id: str, instance_type: type = None, *args, **kwargs):
def get(session: dict, instance_id: str, instance_type: type = None, parent: BaseInstance = None, *args, **kwargs):
"""
Get or create an instance of the given type (from its id)
:param session:
:param instance_id:
:param instance_type:
:param parent:
:param args:
:param kwargs:
:return:
@@ -100,7 +103,9 @@ class InstancesManager:
return InstancesManager.instances[key]
except KeyError:
if instance_type:
return instance_type(session, *args, **kwargs) # it will be automatically registered
if not issubclass(instance_type, SingleInstance):
assert parent is not None, "Parent instance must be provided if not SingleInstance"
return instance_type(session, parent=parent, *args, **kwargs) # it will be automatically registered
else:
raise
@@ -119,3 +124,6 @@ class InstancesManager:
@staticmethod
def reset():
return InstancesManager.instances.clear()
RootInstance = SingleInstance(special_session, Ids.Root, None)

View File

@@ -1,11 +1,12 @@
from myfasthtml.controls.VisNetwork import VisNetwork
from myfasthtml.controls.helpers import Ids
from myfasthtml.core.instances import BaseInstance
class InstancesHelper:
@staticmethod
def dynamic_get(session, component_type: str, instance_id: str):
def dynamic_get(parent: BaseInstance, component_type: str, instance_id: str):
if component_type == Ids.VisNetwork:
return VisNetwork(session, _id=instance_id)
return VisNetwork(parent, _id=instance_id)
return None

View File

@@ -1,5 +1,7 @@
import pytest
from myfasthtml.core.instances import SingleInstance
@pytest.fixture(scope="session")
def session():
@@ -14,3 +16,8 @@ def session():
'updated_at': '2025-11-10T15:52:59.006213'
}
}
@pytest.fixture(scope="session")
def root_instance(session):
return SingleInstance(session, "TestRoot", None)

View File

@@ -4,13 +4,13 @@ from fasthtml.xtend import Script
from myfasthtml.controls.TabsManager import TabsManager
from myfasthtml.core.instances import InstancesManager
from myfasthtml.test.matcher import matches, NoChildren, StartsWith
from myfasthtml.test.matcher import matches, NoChildren
from .conftest import session
@pytest.fixture()
def tabs_manager(session):
yield TabsManager(session)
def tabs_manager(root_instance):
yield TabsManager(root_instance)
InstancesManager.reset()
@@ -113,7 +113,7 @@ class TestTabsManagerRender:
id=f"{tabs_manager.get_id()}-header-wrapper"
),
Div(
Div(id=StartsWith(tabs_manager.get_id())),
Div("Content 3"), # active tab content
# Lasy loading for the other contents
id=f"{tabs_manager.get_id()}-content-wrapper"
),