Source code for configupdater.container
"""Together with :mod:`~configupdater.block` this module forms the basis of
the class hierarchy in **ConfigUpdater**.
The :class:`Container` is the parent class of everything that can contain configuration
blocks, e.g. a section or the entire file itself.
"""
import sys
from abc import ABC
from copy import deepcopy
from textwrap import indent
from typing import TYPE_CHECKING, Generic, Optional, TypeVar
if sys.version_info[:2] >= (3, 9): # pragma: no cover
from collections.abc import Iterator
List = list
else: # pragma: no cover
from typing import Iterator, List
if TYPE_CHECKING:
from .block import Block # noqa
T = TypeVar("T", bound="Block")
C = TypeVar("C", bound="Container")
[docs]class Container(ABC, Generic[T]):
"""Abstract Mixin Class describing a container that holds blocks of type ``T``"""
def __init__(self):
self._structure: List[T] = []
def _repr_blocks(self) -> str:
blocks = "\n".join(repr(block) for block in self._structure)
blocks = indent(blocks, " " * 4)
return f"[\n{blocks.rstrip()}\n]" if blocks.strip() else "[]"
def __repr__(self) -> str:
return f"<{self.__class__.__name__} {self._repr_blocks()}>"
def __deepcopy__(self: C, memo: dict) -> C:
clone = self._instantiate_copy()
memo[id(self)] = clone
return clone._copy_structure(self._structure, memo)
def _copy_structure(self: C, structure: List[T], memo: dict) -> C:
"""``__deepcopy__`` auxiliary method also useful with multi-inheritance"""
self._structure = [b.attach(self) for b in deepcopy(structure, memo)]
return self
def _instantiate_copy(self: C) -> C:
"""Auxiliary method that allows subclasses calling ``__deepcopy__``"""
return self.__class__() # allow overwrite for different init args
@property
def structure(self) -> List[T]:
return self._structure
@property
def first_block(self) -> Optional[T]:
if self._structure:
return self._structure[0]
else:
return None
@property
def last_block(self) -> Optional[T]:
if self._structure:
return self._structure[-1]
else:
return None
def _remove_block(self: C, idx: int) -> C:
"""Remove block at index idx within container
Use `.container_idx` of a block to get the index.
Not meant for users, rather use block.detach() instead!
"""
del self._structure[idx]
return self
[docs] def iter_blocks(self) -> Iterator[T]:
"""Iterate over all blocks inside container."""
return iter(self._structure)
def __len__(self) -> int:
"""Number of blocks in container"""
return len(self._structure)
[docs] def append(self: C, block: T) -> C:
self._structure.append(block)
return self