from functools import singledispatch
[docs]class BaseWriter:
"""Base class for recursive writers.
Usage:
- Create an instance of this class.
- Use :meth:`register` in the same manner as Python's built-in
:func:`functools.singledispatch` to decorate functions that certain types of
:mod:`sdmx.model` or :mod:`sdmx.message` objects.
- Call :meth:`recurse` to kick off recursive writing of objects, including from
inside other functions.
Example
-------
>>> MyWriter = BaseWriter('my')
>>> @MyWriter.register
>>> def _(obj: sdmx.model.ItemScheme):
>>> ... code to write an ItemScheme ...
>>> return result
>>> @MyWriter.register
>>> def _(obj: sdmx.model.Codelist):
>>> ... code to write a Codelist ...
>>> return result
"""
def __init__(self, format_name):
# Create the single-dispatch function
@singledispatch
def func(obj, *args, **kwargs):
raise NotImplementedError(
f"write {obj.__class__.__name__} to " f"{format_name}"
)
self._dispatcher = func
[docs] def recurse(self, obj, *args, **kwargs):
"""Recursively write *obj*.
If there is no :meth:`register` 'ed function to write the class of `obj`, then
the parent class of `obj` is used to find a method.
"""
# TODO use a cache to speed up, so the MRO does not need to be traversed for
# every object instance
dispatcher = getattr(self, "_dispatcher")
try:
# Let the single dispatch function choose the overload
return dispatcher(obj, *args, **kwargs)
except NotImplementedError as exc:
try:
# Use the object's parent class to get a different overload
func = dispatcher.registry[obj.__class__.mro()[1]]
except KeyError:
# Overload for the parent class did not exist
raise exc
return func(obj, *args, **kwargs)
def __call__(self, func):
"""Register *func* as a writer for a particular object type."""
dispatcher = getattr(self, "_dispatcher")
dispatcher.register(func)
return func