from lektor.context import get_ctx from typing import TYPE_CHECKING, Set, Union, Iterable, Iterator import weakref from .util import split_strip if TYPE_CHECKING: from lektor.builder import Builder from lektor.db import Record from .groupby import GroupBy from .model import FieldKeyPath from .vobj import GroupBySource class WeakVGroupsList(list): def add(self, strong: 'FieldKeyPath', weak: 'GroupBySource') -> None: super().append((strong, weakref.ref(weak))) # super().append((strong, weak)) # strong-ref class GroupByRef: @staticmethod def of(builder: 'Builder') -> 'GroupBy': ''' Get the GroupBy object of a builder. ''' return builder.__groupby # type:ignore[attr-defined,no-any-return] @staticmethod def set(builder: 'Builder', groupby: 'GroupBy') -> None: ''' Set the GroupBy object of a builder. ''' builder.__groupby = groupby # type: ignore[attr-defined] class VGroups: @staticmethod def of(record: 'Record') -> WeakVGroupsList: ''' Return the (weak) set of virtual objects of a page. Creates a new set if it does not exist yet. ''' try: wset = record.__vgroups # type: ignore[attr-defined] except AttributeError: wset = WeakVGroupsList() record.__vgroups = wset # type: ignore[attr-defined] return wset # type: ignore[no-any-return] @staticmethod def iter( record: 'Record', keys: Union[str, Iterable[str], None] = None, *, fields: Union[str, Iterable[str], None] = None, flows: Union[str, Iterable[str], None] = None, recursive: bool = False, order_by: Union[str, Iterable[str], None] = None, ) -> Iterator['GroupBySource']: ''' Extract all referencing groupby virtual objects from a page. ''' # prepare filter if isinstance(keys, str): keys = [keys] if isinstance(fields, str): fields = [fields] if isinstance(flows, str): flows = [flows] # get GroupBy object ctx = get_ctx() if not ctx: raise NotImplementedError("Shouldn't happen, where is my context?") builder = ctx.build_state.builder GroupByRef.of(builder).make_once(keys) # ensure did cluster before use # find groups proc_list = [record] done_list = set() # type: Set[GroupBySource] while proc_list: page = proc_list.pop(0) if recursive and hasattr(page, 'children'): proc_list.extend(page.children) for key, vobj in VGroups.of(page): if fields and key.fieldKey not in fields: continue if flows and key.flowKey not in flows: continue if keys and vobj().config.key not in keys: continue done_list.add(vobj()) # manage config dependencies deps = set() # type: Set[str] for vobj in done_list: deps.update(vobj.config.dependencies) for dep in deps: ctx.record_dependency(dep) if order_by: if isinstance(order_by, str): order = split_strip(order_by, ',') # type: Iterable[str] elif isinstance(order_by, (list, tuple)): order = order_by else: raise AttributeError('order_by must be str or list type.') # using get_sort_key() of GroupBySource yield from sorted(done_list, key=lambda x: x.get_sort_key(order)) else: yield from done_list