def _check_required_key(self, where, msg, required_keys, data): "" for y in (required_keys, data): self._expect_iterable(where, y) for x in data: if x not in required_keys: raise exceptions.InvalidMessage(where, msg.format(x))
def from_json(cls, msg, ignore_empty=True, skip_updating_existing=True, skip_descriptors=False, _type=None): db_obj = None with db.no_autoflush(constants.db_session()) as sess: if not cls.db_type and _type is None: raise ValueError("A database type has not been set") db_type = _type or cls.db_type item_attrs = db.table_attribs(db_type, id=None, descriptors=True) if msg and not all((k in item_attrs.keys() for k in msg.keys())): m_keys = set(msg.keys()).difference(set(item_attrs.keys())) raise exceptions.InvalidMessage( utils.this_function(), f"Message object mismatch. Message contains keys that are not present in the corresponding database item: {m_keys}" ) if msg: obj_id = msg.get('id', False) if obj_id: db_obj = sess.query(db_type).get(obj_id) else: db_obj = db_type() if not (obj_id and db_obj and skip_updating_existing): for attr, value in msg.items(): if ignore_empty: if value is None: continue elif isinstance(value, (list, dict)) and not value: continue cls_attr = item_attrs[attr] if skip_descriptors and db.is_descriptor(cls_attr): continue obj_attr = getattr(db_obj, attr) try: col_model = db.column_model(cls_attr) except TypeError: # most likely a hybrid_property descriptor col_model = None if not issubclass(col_model, db.Base) if inspect.isclass( col_model) else not isinstance( col_model, db.Base): if isinstance( col_model, (db.Boolean, db.Integer, db.Password, db.String, db.Text, db.LowerCaseString, db.CapitalizedString)): setattr(db_obj, attr, value) elif isinstance(col_model, db.ArrowType): setattr(db_obj, attr, arrow.Arrow.fromtimestamp(value)) elif db.is_descriptor(cls_attr): if db.descriptor_has_setter(cls_attr): raise NotImplementedError else: raise NotImplementedError( f"Value deserializing for this database type does not exist ({col_model})" ) continue msg_obj = cls._get_message_object( cls, attr, col_model, check_recursive=False, class_type=True) if issubclass(col_model, db.MetaTag): setattr(db_obj, attr, msg_obj._pack_metatags(value)) elif db.is_list(cls_attr) or db.is_query(cls_attr): for v in value: obj_attr.append( msg_obj.from_json( v, _type=col_model, ignore_empty=ignore_empty, skip_updating_existing= skip_updating_existing, skip_descriptors=skip_descriptors)) elif db.is_descriptor(cls_attr): if db.descriptor_has_setter(cls_attr): raise NotImplementedError else: setattr( db_obj, attr, msg_obj.from_json( value, _type=col_model, ignore_empty=ignore_empty, skip_updating_existing= skip_updating_existing, skip_descriptors=skip_descriptors)) return db_obj
def _expect_iterable(self, where, data): if not isinstance(data, (list, dict, tuple)): raise exceptions.InvalidMessage( where, "A list/dict was expected, not: {}".format(data))
def parse(self, data): """ Parse data from client Params: data -- data from client Returns: list of (function, function_kwargs, context_neccesity) """ where = "Message parsing" log.d("Parsing incoming data") try: j_data = utils.convert_to_json(data, where) log.d("Check if required root keys are present") # {"name":name, "data":data, 'session':id} root_keys = ('name', 'data', 'session') self._check_both(where, "JSON dict", root_keys, j_data) except exceptions.ServerError as e: raise self._check_session(j_data.get('session')) cmd = self._server_command(j_data) if cmd: return cmd if not self._accepted: self.handshake(j_data) return # 'data': [ list of function dicts ] function_keys = ('fname', ) msg_data = j_data['data'] if isinstance(msg_data, list): function_tuples = [] for f in msg_data: try: log.d("Cheking parameters in:", f) self._check_missing(where, "Function message", function_keys, f) except exceptions.InvalidMessage as e: raise function_name = f['fname'] try: # check function if function_name not in self.api: e = exceptions.InvalidMessage( where, "Function not found: '{}'".format(function_name)) self.errors.append((function_name, e)) continue # check parameters func_failed = False func_args = tuple(arg for arg in f if arg not in function_keys) func_varnames = self.api[ function_name].__code__.co_varnames need_ctx = 'ctx' in func_varnames for arg in func_args: if arg not in func_varnames: e = exceptions.InvalidMessage( where, "Unexpected argument in function '{}': '{}'". format(function_name, arg)) self.errors.append((function_name, e)) func_failed = True break if func_failed: continue function_tuples.append((self.api[function_name], {x: f[x] for x in func_args}, need_ctx)) except exceptions.ServerError as e: self.errors.append((function_name, e)) return function_tuples else: raise exceptions.InvalidMessage( where, "No list of function objects found in 'data'")
def from_json(cls, msg, ignore_empty=True, skip_updating_existing=True, skip_descriptors=False, _type=None, ignore_private=True, _from_attr=''): db_obj = None if not msg: return db_obj if not isinstance(msg, dict): raise exceptions.InvalidMessage( utils.this_function(), f"Expected message object on '{_from_attr}' not '{type(msg).__name__}'") with db.no_autoflush(constants.db_session()) as sess: if not cls.db_type and _type is None: raise ValueError("A database type has not been set") db_type = _type or cls.db_type item_attrs = db.table_attribs(db_type, id=None, descriptors=True) if msg and not all((k in item_attrs.keys() for k in msg.keys())): m_keys = set(msg.keys()).difference(set(item_attrs.keys())) raise exceptions.InvalidMessage( utils.this_function(), f"Message object mismatch. '{_from_attr}' contains keys that are not present in the corresponding database item: {m_keys}") if msg: new_obj = True obj_id = msg.get('id', False) if obj_id: db_obj = sess.query(db_type).get(obj_id) if not db_obj: raise exceptions.CoreError(utils.this_function(), f"Existing item from message object with id '{obj_id}' was not found") new_obj = False else: db_obj = db_type() if new_obj and isinstance(db_obj, db.UniqueMixin): if isinstance(db_obj, db.NameMixin) and msg.get('name') is not None: db_obj = db_type.as_unique(name=msg.get('name')) new_obj = bool(db_obj.id) elif isinstance(db_obj, db.NamespaceTags): ns_name = None tag_name = None try: ns_name = utils.get_dict_value('namespace.name', msg) tag_name = utils.get_dict_value('tag.name', msg) except KeyError: pass if ns_name is not None and tag_name is not None: db_obj = db_type.as_unique(ns=ns_name, tag=tag_name) new_obj = bool(db_obj.id) elif isinstance(db_obj, (db.Artist, db.Parody)): a_names = set() if msg.get('preferred_name') and msg['preferred_name'].get('name') is not None: a_names.add(msg['preferred_name']['name']) if msg.get('names'): for n in msg['names']: if n.get('name') is not None: a_names.add(n['name']) if a_names: db_obj = db_type.as_unique(names=a_names) new_obj = bool(db_obj.id) if db_obj and not (obj_id and db_obj and skip_updating_existing): for attr, value in msg.items(): if attr == 'id': continue if ignore_private and attr.startswith('_'): continue if ignore_empty: if value is None: continue elif isinstance(value, (list, dict)) and not value: continue cls_attr = item_attrs[attr] if skip_descriptors and db.is_descriptor(cls_attr): continue obj_attr = getattr(db_obj, attr) # noqa: F841 try: col_model = db.column_model(cls_attr) except TypeError: # most likely a hybrid_property descriptor col_model = None if not issubclass(col_model, db.Base) if inspect.isclass( col_model) else not isinstance(col_model, db.Base): if isinstance(col_model, (db.Boolean, db.Integer, db.Password, db.String, db.Text, db.LowerCaseString, db.CapitalizedString)): setattr(db_obj, attr, value) elif isinstance(col_model, db.ArrowType): setattr(db_obj, attr, arrow.Arrow.fromtimestamp(value)) elif db.is_descriptor(cls_attr): if db.descriptor_has_setter(cls_attr): raise NotImplementedError else: raise NotImplementedError( f"Value deserializing for this database type does not exist ({col_model})") continue msg_obj = cls._get_message_object(cls, attr, col_model, check_recursive=False, class_type=True) if col_model == db.Taggable and isinstance(value, dict): if db_obj.taggable and db_obj.taggable.id: value['id'] = db_obj.taggable.id if issubclass(col_model, db.MetaTag): setattr(db_obj, attr, msg_obj._pack_metatags(value)) elif db.is_list(cls_attr) or db.is_query(cls_attr): n_l = [] for v in value: o = msg_obj.from_json( v, _type=col_model, ignore_empty=ignore_empty, skip_updating_existing=skip_updating_existing, skip_descriptors=skip_descriptors, _from_attr=_from_attr + '.' + attr if _from_attr else attr, ) if o is not None: n_l.append(o) setattr(db_obj, attr, n_l) elif db.is_descriptor(cls_attr): if db.descriptor_has_setter(cls_attr): raise NotImplementedError else: setattr( db_obj, attr, msg_obj.from_json( value, _type=col_model, _from_attr=_from_attr + '.' + attr if _from_attr else attr, ignore_empty=ignore_empty, skip_updating_existing=skip_updating_existing, skip_descriptors=skip_descriptors)) return db_obj