Source code for pycsamt.jones.components
# Author: LKouadio <etanoyau@gmail.com>
# License: LGPL-3.0-or-later
from __future__ import annotations
from collections.abc import Iterable
from typing import Any
__all__ = ["JComponentMixin"]
[docs]
class JComponentMixin:
r"""
Lightweight facade to manage Jones (J-format) components on a
host object. The host is expected to expose a mapping-like
container (``self.sections`` or ``self.components``). Keys are
normalized to lowercase for stable lookups.
The mixin centralizes small CRUD helpers (``cget/cset/chas/
cdrop/snapshot``) and offers typed accessors for frequent J
parts such as ``banner``, ``info``, ``head``, ``heads``,
``blocks``, and derived data objects (``z``, ``tip``, ``res``).
It also provides ``compose_headers`` which serializes the
header portion of a J file by calling ``write()`` on available
components. If a combined ``heads`` component is present, it
takes precedence over separate ``banner``/``head``/``info``
parts to avoid duplication.
"""
@staticmethod
def _norm(key: str) -> str:
return str(key).strip().lower()
def _bag(self) -> dict[str, Any]:
# Prefer `sections`, fall back to `components`.
bag = getattr(self, "sections", None)
if not isinstance(bag, dict):
bag = getattr(self, "components", None)
if not isinstance(bag, dict):
bag = {}
self.sections = bag
return bag
[docs]
def cget(self, key: str, default: Any = None) -> Any:
return self._bag().get(self._norm(key), default)
[docs]
def cset(self, key: str, obj: Any) -> None:
self._bag()[self._norm(key)] = obj
[docs]
def chas(self, key: str) -> bool:
return self._norm(key) in self._bag()
[docs]
def cdrop(self, key: str) -> None:
self._bag().pop(self._norm(key), None)
[docs]
def snapshot(self, keys: Iterable[str] | None = None) -> dict[str, Any]:
bag = self._bag()
if keys is None:
keys = list(bag.keys())
out: dict[str, Any] = {}
for k in keys:
kk = self._norm(k)
out[kk] = bag.get(kk, None)
return out
# Banner
[docs]
def get_banner(self) -> Any:
return self.cget("banner")
[docs]
def set_banner(self, obj: Any) -> None:
self.cset("banner", obj)
# Info (>KEY=VALUE)
[docs]
def get_info(self) -> Any:
return self.cget("info")
[docs]
def set_info(self, obj: Any) -> None:
self.cset("info", obj)
# Single head (station / dtype / count)
[docs]
def get_head(self) -> Any:
return self.cget("head")
[docs]
def set_head(self, obj: Any) -> None:
self.cset("head", obj)
# Combined Heads facade (banner + head + info)
[docs]
def get_heads(self) -> Any:
return self.cget("heads")
[docs]
def set_heads(self, obj: Any) -> None:
self.cset("heads", obj)
# Data blocks collection
[docs]
def get_blocks(self) -> Any:
return self.cget("blocks")
[docs]
def set_blocks(self, obj: Any) -> None:
self.cset("blocks", obj)
# Derived data objects (from pycsamt.z.*)
[docs]
def get_z(self) -> Any:
# impedance tensor
return self.cget("z")
[docs]
def set_z(self, obj: Any) -> None:
self.cset("z", obj)
[docs]
def get_tip(self) -> Any:
# tipper
return self.cget("tip")
[docs]
def set_tip(self, obj: Any) -> None:
self.cset("tip", obj)
[docs]
def get_res(self) -> Any:
# resistivity/phase
return self.cget("res")
[docs]
def set_res(self, obj: Any) -> None:
self.cset("res", obj)