Source code for gavo.base.common

"""
Common code for DaCHS's base package.
"""

#c Copyright 2008-2025, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL.  See the
#c COPYING file in the source distribution.

import enum
import warnings

from gavo import utils
from gavo.utils import NotGiven #noflake: backwards compatiblity import

from gavo.utils.excs import (  #noflake: exceptions are historically in base	
	Error,
	BadCode, DataError, DBTableError, EmptyData, ExecutiveAction,
	LiteralParseError,
	MultiplicityError, NotFoundError, ParseException, RDNotFound,
	ReportableError, RestrictedElement, SkipThis, StructureError,
	SourceParseError, ValidationError)

from gavo.utils.dachstypes import (
	Any, NoReturn, Optional,
	Self, Structure, StructParseContext, StructParserValue, Type, Union)
	

[docs]class Ignore(ExecutiveAction): """An executive action causing an element to be not adopted by its parent. Raise this in -- typically -- onElementComplete if the element just built goes somewhere else but into its parent structure (or is somehow benignly unusable). Classic use case: Active Tags. """
[docs]class Replace(ExecutiveAction): """An executive action replacing the current child with the Exception's argument. Use this sparingly. I'd like to get rid of it. """ def __init__(self, newOb: "Structure", newName: Optional[str] = None) -> None: self.newOb, self.newName = newOb, newName
# the tree event types for our struct parsing ParserEventType = enum.StrEnum("ParserEventName", ["start", "end", "value"])
[docs]class Parser: """A mixin for a structure you can feed events to. The class mixing thing in must define methods ``start_``, ``end_``, and ``value_``, each accepting a parse context (a base.RDParseContext instance), the name of an element or attribute, and a value (which is an attribute of dicts in the case of start, and a string in the case of value. They all must return either None or the next parser; start_ may also return a string, which then names the attribute the next value is for. """ def __init__(self) -> NoReturn: raise StructureError("Parser is now a pure mixin")
[docs] def feedEvent(self, ctx: StructParseContext, type: ParserEventType, name: str, value: StructParserValue) -> Optional[Union["Parser", str]]: if type=="start": return self.start_(ctx, name, value) elif type=="value": return self.value_(ctx, name, value) elif type=="end": return self.end_(ctx, name, value) else: raise StructureError( "Illegal event type while building: '%s'"%type)
[docs] def start_(self, ctx: StructParseContext, name: str, value: StructParserValue) -> Optional[Union["Parser", str]]: pass
[docs] def end_(self, ctx: StructParseContext, name: str, value: StructParserValue) -> Optional["Parser"]: pass
[docs] def value_(self, ctx: StructParseContext, name: str, value: StructParserValue) -> Optional["Parser"]: pass
[docs]class ParserEvent(tuple): """a tuple consists of an event type (a ParserEventType) the name and value from common.Parser, and the position object. We probably should use a namedtuple here one day and actually check we get what we need, but for now, this is only to acquiesce the type checker. """ def __new__(cls, *args): return super().__new__(cls, args)
[docs]class StructParseDebugMixin: """put this before Parser in the parent class list of a struct, and you'll see the events coming in to your parser. """
[docs] def feedEvent(self, ctx: StructParseContext, type: ParserEventType, name: str, value: StructParserValue) -> Optional[Self]: print(type, name, value, self) return super().feedEvent( # type: ignore self, ctx, type, name, value)
[docs]class StructCallbacks: """A class terminating super() calls for the structure callbacks. Structs, when finished, call completeElement(ctx), validate(), and onElementComplete() in sequence. All these need to up-call. This mixin provides non-upcalling implementations of these methods, and it needs to be present in mixins for structure. This will only work properly if self actually is a struct, as we rely on the presence of name_ attributes in case of prematurely terminated method resolution. """ name_: Union[str, Type[utils.Undefined]] = "(StructCallbacks terminator)" def __warnTermination(self, methname: str) -> None: if hasattr(super(), methname): mro = " ".join(c.__name__ for c in self.__class__.__mro__) warnings.warn(f"Discarding {methname} method while calling up" f" from {self.name_}, mro {mro} ")
[docs] def validate(self) -> None: self.__warnTermination("validate")
[docs] def completeElement(self, ctx: StructParseContext) -> None: self.__warnTermination("completeElement")
[docs] def onElementComplete(self) -> None: self.__warnTermination("onElementComplete")
[docs] def setParent(self, parent: Any) -> None: self.__warnTermination("setParent")