123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772 |
- #-*- coding: utf-8 -*-
- ##
- # @package lodel.leapi.datahandlers.base_classes Defines all base/abstract
- # classes for DataHandlers
- #
- # Contains custom exceptions too
-
- import copy
- import importlib
- import inspect
- import warnings
-
- from lodel.context import LodelContext
-
- LodelContext.expose_modules(globals(), {
- 'lodel.exceptions': [
- 'LodelException',
- 'LodelExceptions',
- 'LodelFatalError',
- 'DataNoneValid',
- 'FieldValidationError'
- ],
- 'lodel.mlnamedobject.mlnamedobject': ['MlNamedObject'],
- 'lodel.leapi.datahandlers.exceptions': [
- 'LodelDataHandlerConsistencyException',
- 'LodelDataHandlerException'
- ],
- 'lodel.validator.validator': [
- 'ValidationError'
- ],
- 'lodel.logger': 'logger',
- 'lodel.utils.mlstring': ['MlString']})
-
- ##
- # @brief Base class for all DataHandlers
- # @ingroup lodel2_datahandlers
- #
- # @remarks Some of the methods and properties in this "abstract" class are
- # bounded to its children. This implies that the parent
- # is aware of its children, which is an absolute anti-pattern
- # (Liskov / OC violation), a source of confusion and a decrased
- # maintainability. Aggregation =/= Inheritance
- # Concerned methods are: is_reference; is_singlereference.
- # Concerned properties are __custom_datahandlers; base_handlers.
- # @remarks What is the purpose of an internal property being set to a
- # string (namely 'automatic')
- # @remarks Two sets of methods appears a little strange in regards to their
- # visibility.
- # - @ref _construct_data / @ref construct_data
- # - @ref _check_data_consistency / @ref check_data_consistency
- class DataHandler(MlNamedObject):
- base_type = "type"
- _HANDLERS_MODULES = ('datas_base', 'datas', 'references')
-
- ##
- # @brief Stores the DataHandler child classes indexed by name
- _base_handlers = None
-
- ##
- # @brief Stores custom DataHandlers classes indexed by name
- # @todo do it ! (like plugins, register handlers... blablabla)
- __custom_handlers = dict()
-
- help_text = 'Generic Field Data Handler'
- display_name = "Generic Field"
- options_spec = dict()
- options_values = dict()
-
- ##
- # @brief Lists fields that will be exposed to the construct_data method
- _construct_datas_deps = []
-
- directly_editable = True
-
-
- ##
- # @brief constructor
- #
- # @param internal False | str : define whether or not a field is internal
- # @param immutable bool : Indicates if the fieldtype has to be defined in child classes of
- # LeObject or if it is designed globally and immutable
- # @throw NotImplementedError If it is instantiated directly
- # @remarks Shouldn't the class be declared abstract? No need to check if it
- # is instantiated directly, no exception to throw, cleaner code.
- def __init__(self, **kwargs):
- if self.__class__ == DataHandler:
- raise NotImplementedError("Abstract class")
- self.__arguments = kwargs
- self.nullable = True
- self.uniq = False
- self.immutable = False
- self.primary_key = False
- self.internal = False
- if 'default' in kwargs:
- self.default, error = self.check_data_value(kwargs['default'])
- if error:
- raise error
- del kwargs['default']
- for argname, argval in kwargs.items():
- setattr(self, argname, argval)
- self.check_options()
-
- display_name = kwargs.get('display_name', MlString(self.display_name))
- help_text = kwargs.get('help_text', MlString(self.help_text))
- super().__init__(display_name, help_text)
-
-
- ##
- # @brief Sets properly cast and checked options for the DataHandler
- #
- # @throw LodelDataHandlerNotAllowedOptionException when a passed option
- # is not in the option specifications of the DataHandler
- def check_options(self):
- for option_name, option_datas in self.options_spec.items():
- if option_name in self.options_values:
- # There is a configured option, we check its value
- try:
- self.options_values[option_name] = option_datas[1].check_value(
- self.options_values[option_name])
- except ValueError:
- pass # TODO Deal with the case where the value used for an option is invalid
- else:
- # This option was not configured, we get the default value from the specs
- self.options_values[option_name] = option_datas[0]
-
-
- ##
- # @return string: Field type name
- @classmethod
- def name(cls):
- return cls.__module__.split('.')[-1]
-
-
- ##
- # @return bool: True if subclass is of Reference type, False otherwise.
- @classmethod
- def is_reference(cls):
- return issubclass(cls, Reference)
-
-
- ##
- # @return bool: True if subclass is of SingleRef type, False otherwise.
- @classmethod
- def is_singlereference(cls):
- return issubclass(cls, SingleRef)
-
-
- ##
- # @return bool: True if the field is a primary_key, False otherwise.
- def is_primary_key(self):
- return self.primary_key
-
-
- ##
- # @brief checks if a field type is internal
- # @return bool: True if the field is internal, False otherwise.
- def is_internal(self):
- return self.internal is not False
-
-
- ##
- # @brief check if a value can be nullable
- #
- # @param value *
- # @throw DataNoneValid if value is None and nullable.
- # @throw LodelExceptions if not nullable
- # @return value (if not None)
- # @return value
- #
- # @remarks why are there an thrown exception if it is allowed?
- # Exceptions are no message brokers
- def _check_data_value(self, value):
- if value is None:
- if not self.nullable:
- raise LodelExceptions("None value is forbidden for this data field")
- raise DataNoneValid("None with a nullable. This exception is allowed")
- return value
-
-
- ##
- # @brief calls the data_field (defined in derived class) _check_data_value() method
- # @param value *
- # @return tuple (value|None, None|error) value can be cast if NoneError
- # @remarks Consider renaming this method, such as '_is_data_nullable'.
- # @remarks Exceptions ARE NOT message brokers! Moreover, those two methods
- # are more complicated than required. In case 'value' is None,
- # the returned value is the same as the input value. This is the
- # same behavior as when the value is not None!
- # @return What's a "NoneError"? Value can be cast to what?
- def check_data_value(self, value):
- try:
- value = self._check_data_value(value)
- except DataNoneValid as expt:
- return value, None
- except (LodelExceptions, FieldValidationError) as expt:
- return None, expt
- return value, None
-
- ##
- # @brief Checks if this class can override the given data handler.
- # i.e. both class having the same base_type.
- # @param data_handler DataHandler
- # @return bool
- # @remarks Simplify by "return data_handler.__class__.base_type == self.__class__.base_type"?
- def can_override(self, data_handler):
- if data_handler.__class__.base_type != self.__class__.base_type:
- return False
- return True
-
-
- ##
- # @brief Build field value
- #
- # @ingroup lodel2_dh_checks
- # @ref _construct_data() and @ref lodel2_dh_check_impl )
- #
- # @param emcomponent EmComponent : An EmComponent child class instance
- # @param fname str : The field name
- # @param datas dict : dict storing fields values (from the component)
- # @param cur_value : the value from the current field (identified by fieldname)
- # @return the value
- # @throw RunTimeError if data construction fails
- #
- # @warning DO NOT REIMPLEMENT THIS METHOD IN A CUSTOM DATAHANDLER (see
- # @todo raise something else
- #
- # @remarks What the todo up right here means? Raise what? When?
- # @remarks Nothing is being raised in this method, should it?
- def construct_data(self, emcomponent, fname, datas, cur_value):
- emcomponent_fields = emcomponent.fields()
- data_handler = None
- if fname in emcomponent_fields:
- data_handler = emcomponent_fields[fname]
- new_val = cur_value
- if fname in datas.keys():
- pass
- elif data_handler is not None and hasattr(data_handler, 'default'):
- new_val = data_handler.default
- elif data_handler is not None and data_handler.nullable:
- new_val = None
- return self._construct_data(emcomponent, fname, datas, new_val)
-
-
- ##
- # @brief Designed to be reimplemented by child classes
- #
- # @param emcomponent EmComponent : An EmComponent child class instance
- # @param fname str : The field name
- # @param datas dict : dict storing fields values (from the component)
- # @param cur_value : the value from the current field (identified by fieldname)
- # @return the value
- # @see construct_data() lodel2_dh_check_impl
- def _construct_data(self, emcomponent, fname, datas, cur_value):
- return cur_value
-
-
- ##
- # @brief Check data consistency
- # @ingroup lodel2_dh_checks
- #
- # @ref lodel2_dh_datas_construction "Data construction section"
- # @param emcomponent EmComponent : An EmComponent child class instance
- # @param fname : the field name
- # @param datas dict : dict storing fields values
- # @return an Exception instance if fails else True
- #
- # @warning DO NOT REIMPLEMENT THIS METHOD IN A CUSTOM DATAHANDLER (see
- # @ref _construct_data() and @ref lodel2_dh_check_impl )
- # @warning the data argument looks like a dict but is not a dict
- # see @ref base_classes.DatasConstructor "DatasConstructor" and
- # @todo A implémenter
- def check_data_consistency(self, emcomponent, fname, datas):
- return self._check_data_consistency(emcomponent, fname, datas)
-
-
- ##
- # @brief Designed to be reimplemented by child classes
- #
- # @param emcomponent EmComponent : An EmComponent child class instance
- # @param fname : the field name
- # @param datas dict : dict storing fields values
- # @return an Exception instance if fails else True
- #
- # @see check_data_consistency() lodel2_dh_check_impl
- def _check_data_consistency(self, emcomponent, fname, datas):
- return True
-
-
- ##
- # @brief Makes consistency after a query
- #
- # @param emcomponent EmComponent : An EmComponent child class instance
- # @param fname : the field name
- # @param datas dict : dict storing fields values
- # @return an Exception instance if fails else True
- #
- # @todo To be implemented
- # @remarks It not clear what is the intent of this method...
- def make_consistency(self, emcomponent, fname, datas):
- pass
-
-
- ##
- # @brief Registers a new data handlers
- #
- # @note Used by plugins.
- # @remarks This method is actually never used anywhere. May consider removing it.
- @classmethod
- def register_new_handler(cls, name, data_handler):
- if not inspect.isclass(data_handler):
- raise ValueError("A class was expected but %s given" % type(data_handler))
- if not issubclass(data_handler, DataHandler):
- raise ValueError("A data handler HAS TO be a child class of DataHandler")
- cls.__custom_handlers[name] = data_handler
-
-
- ##
- # @brief Loads all DataHandlers
- @classmethod
- def load_base_handlers(cls):
- if cls._base_handlers is None:
- cls._base_handlers = dict()
- for module_name in cls._HANDLERS_MODULES:
- module = importlib.import_module('lodel.leapi.datahandlers.%s' % module_name)
- for name, obj in inspect.getmembers(module):
- if inspect.isclass(obj):
- logger.debug("Load data handler %s.%s" % (obj.__module__, obj.__name__))
- cls._base_handlers[name.lower()] = obj
- return copy.copy(cls._base_handlers)
-
-
- ##
- # @brief given a field type name, returns the associated python class
- #
- # @param name str : A field type name (not case sensitive)
- # @return DataField child class
- # @throw NameError
- #
- # @note Would not it be better to prefix the DataHandler name with the
- # plugin's one so that it is ensured names are unique?
- # @remarks "do/get what from name?" Consider renaming this method (e.g.
- # 'get_datafield_from_name')
- @classmethod
- def from_name(cls, name):
- cls.load_base_handlers()
- all_handlers = dict(cls._base_handlers, **cls.__custom_handlers)
- name = name.lower()
-
- if name not in all_handlers:
- raise NameError("No data handlers named '%s'" % (name,))
- return all_handlers[name]
-
-
- ##
- # @brief List all DataHandlers
- # @return a dict with, display_name for keys, and a dict for value
- # @remarks ATM, solely used by the EditorialModel.
- # @remarks EditorialModel own class does nothing but calls this class.
- # Moreover, nothing calls it anyway.
- # @remarks It also seems like it is an EM related concern, and has
- # nothing to do with this class. That list appears to be doing
- # a purely presentational job. Isn't that a serialization instead?
- @classmethod
- def list_data_handlers(cls):
- cls.load_base_handlers()
- all_handlers = dict(cls._base_handlers, **cls.__custom_handlers)
- list_dh = dict()
- for hdl in all_handlers:
- options = dict({'nullable': hdl.nullable,
- 'internal': hdl.internal,
- 'immutable': hdl.immutable,
- 'primary_key': hdl.primary_key}, hdl.options_spec)
- list_dh[hdl.display_name] = {'help_text': hdl.help_text, 'options': options}
-
- return list_dh
-
-
- ##
- # @brief Return the module name to import in order to use the DataHandler
- # @param datahandler_name str : Data handler name
- # @return str
- # @remarks consider renaming this (e.g. "datahandler_module_name")
- @classmethod
- def module_name(cls, datahandler_name):
- datahandler_name = datahandler_name.lower()
- handler_class = cls.from_name(datahandler_name)
- return '{module_name}.{class_name}'.format(
- module_name=handler_class.__module__,
- class_name=handler_class.__name__
- )
-
-
- ##
- # @brief __hash__ implementation for field types
- def __hash__(self):
- hash_dats = [self.__class__.__module__]
- for kdic in sorted([k for k in self.__dict__.keys() if not k.startswith('_')]):
- hash_dats.append((kdic, getattr(self, kdic)))
- return hash(tuple(hash_dats))
-
-
- ##
- # @brief Base class for data data handler (by opposition with references)
- # @ingroup lodel2_datahandlers
- class DataField(DataHandler):
- pass
-
-
- ##
- # @brief Abstract class for all references
- # @ingroup lodel2_datahandlers
- #
- # References are fields that stores a reference to another
- # editorial object
- # @todo Construct data implementation : transform the data into a LeObject instance
- class Reference(DataHandler):
-
- base_type = "ref"
-
- ##
- # @brief Instantiation
- # @param allowed_classes list | None : list of allowed em classes if None no restriction
- # @param back_reference tuple | None : tuple containing (LeObject child class, field name)
- # @param internal bool | string: if False, the field is not internal
- # @param **kwargs : other arguments
- # @throw ValueError
- # @remarks internal may hold the string value 'automatic'. So far, nothing
- # mentions what that means, and nothing seems to be aware
- # of an 'automatic' value (at least not in leapi package)
- def __init__(self, allowed_classes=None, back_reference=None, internal=False, **kwargs):
- self.__allowed_classes = set() if allowed_classes is None else set(allowed_classes)
- ##
- # @note what is "useful to Jinja 2"?
- # For now useful to jinja 2
- self.allowed_classes = list() if allowed_classes is None else allowed_classes
- if back_reference is not None:
- if len(back_reference) != 2:
- raise ValueError(
- "A tuple (classname, fieldname) expected but got '%s'" % back_reference)
- ##
- # @note Why is there commented out code? Should it be deleted? Ractivated?
- # if not issubclass(lodel.leapi.leobject.LeObject, back_reference[0])
- # or not isinstance(back_reference[1], str):
- # raise TypeError("Back reference was expected to be a tuple(<class LeObject>, str)
- # but got : (%s, %s)" % (back_reference[0], back_reference[1]))
- self.__back_reference = back_reference
- super().__init__(internal=internal, **kwargs)
-
-
- ##
- # @brief Method designed to return an empty value for this kind of
- # multipleref
- # @remarks purpose!?
- @classmethod
- def empty(cls):
- return None
-
-
- ##
- # @brief Property that takes value of a copy of the back_reference tuple
- @property
- def back_reference(self):
- return copy.copy(self.__back_reference)
-
-
- ##
- # @brief Property that takes value of datahandler of the backreference or
- # None
- @property
- def back_ref_datahandler(self):
- if self.__back_reference is None:
- return None
- return self.__back_reference[0].data_handler(self.__back_reference[1])
-
- @property
- def linked_classes(self):
- return copy.copy(self.__allowed_classes)
-
-
- ##
- # @brief Sets a back reference.
- def _set_back_reference(self, back_reference):
- self.__back_reference = back_reference
-
-
- ##
- # @brief Check and cast value in the appropriate type
- #
- # @param value
- # @throw FieldValidationError if value is an appropriate type
- # @return value
- # @todo implement the check when we have LeObject uid check value
- def _check_data_value(self, value):
- from lodel.leapi.leobject import LeObject
- value = super()._check_data_value(value)
- if not (hasattr(value, '__class__') and
- issubclass(value.__class__, LeObject)):
- if self.__allowed_classes:
- rcls = list(self.__allowed_classes)[0]
- uidname = rcls.uid_fieldname()[0] # TODO multiple uid is broken
- uiddh = rcls.data_handler(uidname)
- value = uiddh._check_data_value(value)
- else:
- raise FieldValidationError(
- "Reference datahandler can not check this value %s if any allowed_class is allowed. " % value)
- return value
-
-
- ##
- # @brief Check data consistency
- #
- # @param emcomponent EmComponent :
- # @param fname string : the field name
- # @param datas dict : dict storing fields values
- # @return bool | Exception :
- #
- # @todo check for performance issues and checks logic
- # @warning composed uid capabilities are broken
- # @remarks Is that really a legitimate case of retuning an Exception object?
- def check_data_consistency(self, emcomponent, fname, datas):
- rep = super().check_data_consistency(emcomponent, fname, datas)
- if isinstance(rep, Exception):
- return rep
- if self.back_reference is None:
- return True
- ##
- # @todo Reimplement instance fetching in construct data
- # @remarks Set the previous todo as one, looked like it was intended to be.
- target_class = self.back_reference[0]
- if target_class not in self.__allowed_classes:
- logger.warning('Class of the back_reference given is not an allowed class')
- return False
- value = datas[fname]
- ##
- # @warning multi uid broken here
- # @remarks Why is that broken? Any clue? Set as a warning.
- target_uidfield = target_class.uid_fieldname()[0]
- obj = target_class.get([(target_uidfield, '=', value)])
- if len(obj) == 0:
- logger.warning('Object referenced does not exist')
- return False
- return True
-
-
- ##
- # @brief Utility method designed to fetch referenced objects
- #
- # @param value mixed : the field value
- # @throw NotImplementedError
- # @remarks Not implemented? Consider renaming?
- def get_referenced(self, value):
- raise NotImplementedError
-
-
- ##
- # @brief DataHandler for single reference to another object
- #
- # An instance of this class acts like a "foreign key" to another object
- class SingleRef(Reference):
-
-
- def __init__(self, allowed_classes=None, **kwargs):
- super().__init__(allowed_classes=allowed_classes, **kwargs)
-
-
- ##
- # @brief Checks and casts value to the appropriate type
- #
- # @param value: mixed
- # @throw FieldValidationError if value is inappropriate or can not be cast
- # @return mixed
- def _check_data_value(self, value):
- value = super()._check_data_value(value)
- return value
-
-
- ##
- # @brief Utility method to fetch referenced objects
- #
- # @param value mixed : the field value
- # @return A LeObject child class instance
- # @throw LodelDataHandlerConsistencyException if no referenced object found
- # @remarks Consider renaming (e.g. get_referenced_object)?
- def get_referenced(self, value):
- for leo_cls in self.linked_classes:
- res = leo_cls.get_from_uid(value)
- if res is not None:
- return res
- raise LodelDataHandlerConsistencyException("Unable to find \
- referenced object with uid %s" % value)
-
-
- ##
- # @brief DataHandler for multiple references to another object
- # @ingroup lodel2_datahandlers
- #
- # The fields using this data handlers are like SingleRef but can store multiple
- # references in one field.
- # @note for the moment split on ',' chars
- class MultipleRef(Reference):
-
- ##
- # @brief Constructor
- #
- # @param max_item int | None : indicate the maximum number of item referenced
- # by this field, None mean no limit
- def __init__(self, max_item=None, **kwargs):
- self.max_item = max_item
- super().__init__(**kwargs)
-
- ##
- # @brief Method designed to return an empty value for this kind of
- # multipleref
- # @remarks Purpose!?
- @classmethod
- def empty(cls):
- return []
-
-
- ##
- # @brief Check and cast value in appropriate type
- # @param value mixed
- # @throw FieldValidationError if value is unappropriate or can not be cast
- # @return value
- # @todo Writing test error for errors when stored multiple references in one field
- def _check_data_value(self, value):
- value = DataHandler._check_data_value(self, value)
- if not hasattr(value, '__iter__'):
- raise FieldValidationError(
- "MultipleRef has to be an iterable or a string, '%s' found" % value)
- if self.max_item is not None:
- if self.max_item < len(value):
- raise FieldValidationError("Too many items")
- new_val = list()
- error_list = list()
- for i, v in enumerate(value):
- try:
- v = super()._check_data_value(v)
- new_val.append(v)
- except (FieldValidationError):
- error_list.append(repr(v))
- if len(error_list) > 0:
- raise FieldValidationError(
- "MultipleRef have for invalid values [%s] :" % (",".join(error_list)))
- return new_val
-
- ##
- # @brief Utility method designed to fetch referenced objects
- #
- # @param values mixed : the field values
- # @return A list of LeObject child class instance
- # @throw LodelDataHandlerConsistencyException if some referenced objects
- # were not found
- def get_referenced(self, values):
- if values is None or len(values) == 0:
- return list()
- left = set(values)
- values = set(values)
- res = list()
- for leo_cls in self.linked_classes:
- uidname = leo_cls.uid_fieldname()[0] # MULTIPLE UID BROKEN HERE
- tmp_res = leo_cls.get(('%s in (%s)' % (uidname, ','.join(
- [str(l) for l in left]))))
- left ^= set((leo.uid() for leo in tmp_res))
- res += tmp_res
- if len(left) == 0:
- return res
- raise LodelDataHandlerConsistencyException("Unable to find \
- some referenced objects. Following uids were not found : %s" % ','.join(left))
-
-
- ##
- # @brief Class designed to handle data access while field types are constructing data
- # @ingroup lodel2_datahandlers
- #
- # This class is designed to allow automatic scheduling of construct_data calls.
- #
- # In theory it has the ability to detect circular dependencies
- # @todo test circular deps detection
- # @todo test circular deps false positive
- # @remarks Would not it be better to make sure what the code actually is doing?
- class DatasConstructor(object):
-
- ##
- # @brief Init a DatasConstructor
- #
- # @param leobject LeObject
- # @param datas dict : dict with field name as key and field values as value
- # @param fields_handler dict : dict with field name as key and data handler instance as value
- def __init__(self, leobject, datas, fields_handler):
- self._leobject = leobject
- self._datas = copy.copy(datas)
- # Stores fieldtypes
- self._fields_handler = fields_handler
- # Stores list of fieldname for constructed
- self._constructed = []
- # Stores construct calls list
- self._construct_calls = []
-
-
- ##
- # @brief Implements the dict.keys() method on instance
- #
- # @return list
- def keys(self):
- return self._datas.keys()
-
-
- ##
- # @brief Allows to access the instance like a dict
- #
- # @param fname string: The field name
- # @return field values
- # @throw RuntimeError
- #
- # @note Determine return type
- def __getitem__(self, fname):
- if fname not in self._constructed:
- if fname in self._construct_calls:
- raise RuntimeError('Probably circular dependencies in fieldtypes')
- cur_value = self._datas[fname] if fname in self._datas else None
- self._datas[fname] = self._fields_handler[fname].construct_data(
- self._leobject, fname, self, cur_value)
- self._constructed.append(fname)
- return self._datas[fname]
-
-
- ##
- # @brief Allows to set instance values like a dict
- #
- # @warning Should not append in theory
- #
- # @remarks Why is a warning issued any time we call this method?
- def __setitem__(self, fname, value):
- self._datas[fname] = value
- warnings.warn("Setting value of an DatasConstructor instance")
-
-
- ##
- # @brief Class designed to handle a DataHandler option
- class DatahandlerOption(MlNamedObject):
-
-
- ##
- # @brief instantiates a new DataHandlerOption object
- #
- # @param id str
- # @param display_name MlString
- # @param help_text MlString
- # @param validator function
- def __init__(self, id, display_name, help_text, validator):
- self.__id = id
- self.__validator = validator
- super().__init__(display_name, help_text)
-
- ##
- # @brief Accessor to the id property.
- @property
- def id(self):
- return self.__id
-
- ##
- # @brief checks a value corresponding to this option is valid
- #
- # @param value mixed
- # @return cast value
- # @throw ValueError
- def check_value(self, value):
- try:
- return self.__validator(value)
- except ValidationError:
- raise ValueError()
|