Parent is now mandatory when creating a new BaseInstance class
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user