Source code for endf_parserpy.endf_parser_base

############################################################
#
# Author(s):       Georg Schnabel
# Email:           g.schnabel@iaea.org
# Creation date:   2025/06/01
# Last modified:   2026/05/17
# License:         MIT
# Copyright (c) 2025-2026 International Atomic Energy Agency (IAEA)
#
############################################################

import typing
import inspect
import functools
from typing import Optional, Union
from typing import Dict, List, Tuple
from abc import ABC, abstractmethod


StringInput = Union[str, List[str]]


def _record_init_kwargs(init):
    """Decorate an ``__init__`` so its arguments are recorded for pickling.

    The wrapped ``__init__`` stores the (fully defaulted) arguments it
    was called with on the instance as ``_init_kwargs``. This lets
    :class:`EndfParserBase` pickle a parser *by recipe* -- the arguments
    it was built with -- instead of by its live state, which is heavy
    and, for the C++ parser, not picklable at all. The parser is rebuilt
    on unpickling by re-running ``__init__`` with those arguments.

    A parser subclass must decorate its ``__init__`` with this for the
    parser to be picklable.
    """
    signature = inspect.signature(init)

    @functools.wraps(init)
    def wrapper(self, *args, **kwargs):
        bound = signature.bind(self, *args, **kwargs)
        bound.apply_defaults()
        self._init_kwargs = {
            name: value for name, value in bound.arguments.items() if name != "self"
        }
        return init(self, *args, **kwargs)

    return wrapper


MfNumberType = MtNumberType = int
MfMtTupleType = Tuple[MfNumberType, MtNumberType]
MfMtTuplesType = Tuple[Union[MfNumberType, MfMtTupleType]]

EndfValueType = Union[dict, list, int, float, str]
ParsedEndfSectionType = Dict[str, EndfValueType]
UnparsedEndfSectionType = List[str]
EndfSectionType = Union[ParsedEndfSectionType, UnparsedEndfSectionType]

MtDictType = Dict[int, EndfSectionType]
MfMtDictType = Dict[int, MtDictType]


[docs] class EndfParserBase(ABC): """Abstract base class for ENDF parsers. This abstract base class defines the basic interface that must be provided by ENDF parser classes, such as :class:`~endf_parserpy.EndfParserPy` and :class:`~endf_parserpy.EndfParserCpp`. Therefore, any ENDF parser class should be derived from this abstract base class. A parser is picklable: it is pickled *by recipe* -- the arguments it was constructed with -- and rebuilt by re-running ``__init__`` on unpickling. A subclass must decorate its ``__init__`` with :func:`_record_init_kwargs` for this to work. """ def __getstate__(self): """Return the picklable state of the parser: its constructor arguments. A parser is pickled by recipe rather than by its live state, which is heavy and, for the C++ parser, not picklable at all. """ try: return dict(self._init_kwargs) except AttributeError: raise TypeError( f"{type(self).__name__} cannot be pickled: its __init__ does " "not record its constructor arguments -- decorate it with " "_record_init_kwargs" ) from None def __setstate__(self, state): """Rebuild the parser by re-running ``__init__`` with the saved args.""" self.__init__(**state)
[docs] @abstractmethod def parse( self, lines: StringInput, exclude: Optional[MfMtTuplesType] = None, include: Optional[MfMtTuplesType] = None, ) -> MfMtDictType: pass
[docs] @abstractmethod def parsefile( self, filename: str, exclude: Optional[MfMtTuplesType] = None, include: Optional[MfMtTuplesType] = None, ) -> MfMtDictType: pass
[docs] @abstractmethod def write( self, endf_dict: MfMtDictType, exclude: Optional[MfMtTuplesType] = None, include: Optional[MfMtTuplesType] = None, ) -> List[str]: pass
[docs] @abstractmethod def writefile( self, filename: str, endf_dict: MfMtDictType, exclude: Optional[MfMtTuplesType] = None, include: Optional[MfMtTuplesType] = None, overwrite: bool = False, ) -> None: pass