"""
Common code for DaCHS's base package.
"""
#c Copyright 2008-2023, 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 warnings
from gavo.utils.excs import ( #noflake: exceptions are historically in base
Error,
BadCode, DataError, EmptyData, ExecutiveAction, LiteralParseError,
MultiplicityError, NotFoundError, ParseException, RDNotFound,
ReportableError, RestrictedElement, SkipThis, StructureError,
SourceParseError, ValidationError)
[docs]class NotGivenType(type):
def __str__(self):
raise StructureError(
"%s cannot be stringified"%self.__class__.__name__)
def __repr__(self):
return "<Not given/empty>"
def __bool__(self):
return False
[docs]class NotGiven(object, metaclass=NotGivenType):
"""A sentinel class for defaultless values that can remain undefined.
"""
[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, newName=None):
self.newOb, self.newName = newOb, newName
[docs]class Parser(object):
"""is an object that routes events.
It is constructed with up to three functions for handling start,
value, and end events; these would override methods ``start_``, ``end_``,
or ``value_``. Thus, you can simply implement when inheriting from
Parser. In that case, no call the the constructor is necessary
(i.e., Parser works as a mixin as well).
"""
def __init__(self, start=None, value=None, end=None):
self.start, self.value, self.end = start, value, end
[docs] def feedEvent(self, ctx, type, name, value):
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]class StructParseDebugMixin(object):
"""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, type, name, value):
print(type, name, value, self)
return Parser.feedEvent(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.
"""
def __warnTermination(self, methname):
if hasattr(super(), methname):
args = (self.name_, " ".join(c.__name__ for c in self.__class__.__mro__))
warnings.warn("Discarding validate method while calling up"
" from {}, mro {} ".format(*args))
[docs] def validate(self):
self.__warnTermination("validate")
[docs] def completeElement(self, ctx):
self.__warnTermination("completeElement")
[docs] def onElementComplete(self):
self.__warnTermination("onElementComplete")
[docs] def setParent(self, parent):
self.__warnTermination("setParent")