12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- """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 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")
- class Container(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
- 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)
- def append(self: C, block: T) -> C:
- self._structure.append(block)
- return self
|