Source code for sections.section

"""
Flexible tree data structures for organizing lists and dicts into sections.

https://github.com/trevorpogue/sections
"""

from typing import Any
from typing import List
from typing import Type
from typing import Union

from .attr_parser import SectionAttrParser
from .dict import SectionDict
from .meta import MetaSection
from .node import SectionNode
from .string_parser import SectionStringParser
from .types import GetType
from .types import SectionAttrs
from .types import SectionNone
from .types import SectionType


[docs]class Section(SectionNode, SectionDict, SectionAttrParser, SectionStringParser, metaclass=MetaSection): """ Objects instantiated by :class:`Section <Section>` are nodes in a sections tree structure. Each node has useful methods and properties for organizing lists/dicts into sections and for conveniently accessing/modifying the sub-list/dicts from each section/subsection. """ ########################################################################## # tree-structure-wide attributes for every node # # class attributes act as tree-structure-wide attributes across all nodes. # This is possible because each sections() module call returns a unique # copy of the Section class, giving each individual structure its own class # Choose whether to use a cache in each node. The cache contains # quickly-readable references to attribute iterables parsed from manually # traversing through descendant nodes in a previous read. The caches are # invalidated when the tree structure or node attribute values change. # Using the cache can often make structure attribute reading faster by 5x # and even much more. The downside is that it also increases memory used by # roughly 5x as well. This is not a concern on a general-purpose computer # for structures containing less than 1000 nodes or 10,000 nodes, although # further testing is required to confirm this. After 10,000 nodes, it may # be recommended to turn the structure class attribute `use_cache` to # False. use_cache = True # See method Section.get_nearest_attr's doctring for a full description of # gettype and their default value. 'hybrid' returns a list if more # than 1 element is found, else return the non-iterable raw form of the # element default_gettype = 'hybrid' default_attr = 'names' list_attr_prefix = '_' # See https://sections.readthedocs.io/ for usage: use_pluralsingular = True ########################################################################## __private_prefix = '_Section' def __init__(self, **kwds: SectionAttrs) -> None: """Set object attr for every attr in kwds and init attr cache.""" SectionAttrParser.__init__(self) self._SectionAttrParser__cache = {} for name, value in kwds.items(): self.__setattr__(name, value, _invalidate_cache=False) # def post_init(self) -> None: # """Called after nodes children (if any) have been constructed.""" # pass @property def cls(self) -> Type[SectionType]: """The unique structure-wide class of each node.""" return self.__class__ @ property def sections(self) -> SectionType: """A synonym for property :meth:`children <Section.children>`.""" return self.children @ property def entries(self) -> SectionType: """A synonym for property :meth:`leaves <Section.leaves>`.""" return self.leaves
[docs] def structure_change(self): """ Will be called every time there is a change in structure, i.e. whenever a node is added or removed or rearranged in child order. Meant for use when overriding. """ pass
[docs] def __call__( self, name: str = SectionNone, gettype: GetType = 'default', default: Any = SectionNone, ) -> Union[Any, List[Any]]: """ Run :meth:`get_nearest_attr <Section.get_nearest_attr>`. This returns attribute `name` from self if self contains the attribute in either the singular or plural form for `name`. Else, try the same pattern for each of self's children, putting the returned results from each child into a list. Else, raise AttributeError. :param name: The name of the attribute to find in self or self's descendants. :param gettype: Valid values are `'default'`, `'hybrid'` `list`, `iter`, `dict`, `'self'`. Setting to `'default'` uses the value of self.default_gettype for gettype (its default is 'hybrid'). Setting to `'hybrid'` returns a list if more than 1 element is found, else returns the non-iterable raw form of the element. Setting to `list` returns a list containing the attribute values. Setting to `iter` returns an iterable iterating through the attribute values. Setting to `dict` returns a dict containing pairs of the containing node's name with the attribute value. Setting to `'self'` will only search for attrs in self, and will never wrap the attr in an iterable form like the dict/list/iter options. searches for attributes only in self. Setting to `'nearest'` also searches through :param default: If not provided, AttributeError will be raised if attr `name` not found. If given, return default if attr `name` not found. :return: An iterable or non-iterable form of the attribute `name` formed from self or descendant nodes. Depends on the value given to `gettype`. """ if name is SectionNone: name = self.default_attr attrs = self._get_nearest_attr(name) return self._parse_top_getattr(name, attrs, gettype=gettype, default=default)