Exemplo n.º 1
0
 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))
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
 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))
Exemplo n.º 4
0
    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'")
Exemplo n.º 5
0
    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