Skip to content
Snippets Groups Projects

MAINT: refactor _Messages class

Merged Henrik tom Wörden requested to merge f-messages into dev
All threads resolved!
Files
2
+ 119
95
@@ -32,8 +32,8 @@ All additional classes are either important for the entities or the
transactions.
"""
from __future__ import print_function, unicode_literals
from __future__ import annotations # Can be removed with 3.10.
from __future__ import print_function, unicode_literals
import re
import sys
@@ -51,8 +51,8 @@ from warnings import warn
from caosdb.common.datatype import (BOOLEAN, DATETIME, DOUBLE, INTEGER, TEXT,
is_list_datatype, is_reference)
from caosdb.common.state import State
from caosdb.common.utils import uuid, xml2str
from caosdb.common.timezone import TimeZone
from caosdb.common.utils import uuid, xml2str
from caosdb.common.versioning import Version
from caosdb.configuration import get_config
from caosdb.connection.connection import get_connection
@@ -112,7 +112,7 @@ class Entity:
self.__datatype = None
self.datatype = datatype
self.value = value
self.messages = _Messages()
self.messages = Messages()
self.properties = _Properties()
self.parents = _ParentList()
self.path = None
@@ -419,7 +419,7 @@ class Entity:
self.acl.is_permitted(permission=permission)
def get_all_messages(self):
ret = _Messages()
ret = Messages()
ret.append(self.messages)
for p in self.properties:
@@ -1056,7 +1056,7 @@ out: List[Entity]
def get_messages(self):
"""Get all messages of this entity.
@return: _Messages(list)
@return: Messages(list)
"""
return self.messages
@@ -1064,9 +1064,9 @@ out: List[Entity]
def get_warnings(self):
"""Get all warning messages of this entity.
@return _Messages(list): Warning messages.
@return Messages(list): Warning messages.
"""
ret = _Messages()
ret = Messages()
for m in self.messages:
if m.type.lower() == "warning":
@@ -1077,9 +1077,9 @@ out: List[Entity]
def get_errors(self):
"""Get all error messages of this entity.
@return _Messages(list): Error messages.
@return Messages(list): Error messages.
"""
ret = _Messages()
ret = Messages()
for m in self.messages:
if m.type.lower() == "error":
@@ -1599,7 +1599,7 @@ class QueryTemplate():
self._cuid = None
self.value = None
self.datatype = None
self.messages = _Messages()
self.messages = Messages()
self.properties = None
self.parents = None
self.path = None
@@ -1719,7 +1719,7 @@ class QueryTemplate():
return self.id is not None
def get_errors(self):
ret = _Messages()
ret = Messages()
for m in self.messages:
if m.type.lower() == "error":
@@ -2399,7 +2399,7 @@ class _ParentList(list):
raise KeyError(str(parent) + " not found.")
class _Messages(dict):
class Messages(list):
"""This 'kind of dictionary' stores error, warning, info, and other
messages. The mentioned three messages types are messages of special use.
@@ -2417,21 +2417,21 @@ class _Messages(dict):
and the same code (or no code). Every message
MUST NOT occur more than once per entity (to which the message in question belongs).
If a message m2 is added while a messages m1 is already in this _Message object m2 will
If a message m2 is added while a messages m1 is already in this Message object m2 will
OVERRIDE m1.
Error, warning, and info messages will be deleted before any transaction.
Examples:
<<< msgs = _Messages()
<<< msgs = Messages()
<<< # create Message
<<< msg = Message(type="HelloWorld", code=1, description="Greeting the world", body="Hello, world!")
<<< # append it to the _Messages
<<< # append it to the Messages
<<< msgs.append(msg)
<<< # use _Messages as list of Message objects
<<< # use Messages as list of Message objects
<<< for m in msgs:
... assert isinstance(m,Message)
@@ -2464,7 +2464,8 @@ class _Messages(dict):
"""
def clear_server_messages(self):
"""Removes all error, warning and info messages."""
"""Removes all messages of type error, warning and info."""
# TODO Why do we not remove ALL messages?
rem = []
for m in self:
@@ -2474,9 +2475,18 @@ class _Messages(dict):
for m in rem:
self.remove(m)
return self
#######################################################################
# can be removed after 01.07.24
# default implementation of list is sufficient
def __setitem__(self, key, value): # @ReservedAssignment
if not isinstance(value, Message):
warn("__setitem__ will in future only accept Message objects as second argument. "
"You will no longe be"
" able to pass bodys such that Message object is created on the fly",
DeprecationWarning)
if not isinstance(key, int):
warn("__setitem__ will in future only accept int as first argument",
DeprecationWarning)
if isinstance(key, tuple):
if len(key) == 2:
type = key[0] # @ReservedAssignment
@@ -2487,7 +2497,7 @@ class _Messages(dict):
else:
raise TypeError(
"('type', 'code'), ('type'), or 'type' expected.")
elif isinstance(key, _Messages._msg_key):
elif isinstance(key, Messages._msg_key):
type = key._type # @ReservedAssignment
code = key._code
else:
@@ -2508,13 +2518,19 @@ class _Messages(dict):
if isinstance(value, Message):
body = value.body
description = value.description
m = Message
else:
body = value
description = None
m = Message(type=type, code=code, description=description, body=body)
dict.__setitem__(self, _Messages._msg_key(type, code), m)
m = Message(type=type, code=code, description=description, body=body)
if isinstance(key, int):
super().__setitem__(key, m)
else:
self.append(m)
def __getitem__(self, key):
if not isinstance(key, int):
warn("__getitem__ only supports integer keys in future.", DeprecationWarning)
if isinstance(key, tuple):
if len(key) == 2:
type = key[0] # @ReservedAssignment
@@ -2525,113 +2541,117 @@ class _Messages(dict):
else:
raise TypeError(
"('type', 'code'), ('type'), or 'type' expected.")
elif isinstance(key, int) and int(key) >= 0:
for m in self.values():
if key == 0:
return m
else:
key -= 1
type = key # @ReservedAssignment
code = None
elif isinstance(key, int) and key >= 0:
return super().__getitem__(key)
else:
type = key # @ReservedAssignment
code = None
m = dict.__getitem__(self, _Messages._msg_key(type, code))
m = self.get(type, code)
if m is None:
return
if m.description:
return (m.description, m.body)
else:
return m.body
def __init__(self):
dict.__init__(self)
def __delitem__(self, key):
if isinstance(key, tuple):
if len(key) == 2:
type = key[0] # @ReservedAssignment
code = key[1]
elif len(key) == 1:
type = key[0] # @ReservedAssignment
code = None
else:
raise TypeError(
"('type', 'code'), ('type'), or 'type' expected.")
warn("__delitem__ only supports integer keys in future.", DeprecationWarning)
if self.get(key[0], key[1]) is not None:
self.remove(self.get(key[0], key[1]))
else:
type = key # @ReservedAssignment
code = None
return dict.__delitem__(self, _Messages._msg_key(type, code))
super().__delitem__(key)
def remove(self, obj, obj2=None):
if isinstance(obj, Message):
return dict.__delitem__(self, _Messages._msg_key.get(obj))
if obj2 is not None:
warn("Supplying a second argument to remove is deprecated.",
DeprecationWarning)
super().remove(self.get(obj, obj2))
else:
super().remove(obj)
return self.__delitem__((obj, obj2))
def append(self, msg):
if isinstance(msg, Messages) or isinstance(msg, list):
warn("Supplying a list-like object to append is deprecated. Please use extend"
" instead.", DeprecationWarning)
for m in msg:
self.append(m)
return
def get(self, type, code=None, default=None): # @ReservedAssignment
try:
return dict.__getitem__(self, _Messages._msg_key(type, code))
except KeyError:
return default
super().append(msg)
def extend(self, messages):
self.append(messages)
@staticmethod
def _hash(t, c):
return hash(str(t).lower() + (str(",") + str(c) if c is not None else ''))
# end remove
#######################################################################
return self
def get(self, type, code=None, default=None, exact=False): # @ReservedAssignment
"""
returns a message from the list that kind of matches type and code
def append(self, msg):
if hasattr(msg, "__iter__"):
for m in msg:
self.append(m)
case and types (str/int) are ignored
return self
If no suitable message is found, the default argument is returned
If exact=True, the message has to match code and type exactly
"""
if not exact:
warn("The fuzzy mode (exact=False) is deprecated. Please use exact in future.",
DeprecationWarning)
for msg in self:
if exact:
if msg.type == type and msg.code == code:
return msg
else:
if self._hash(msg.t, msg.c) == self._hash(type, code):
return msg
if isinstance(msg, Message):
dict.__setitem__(self, _Messages._msg_key.get(msg), msg)
return default
return self
else:
raise TypeError("Argument was not a Message")
def to_xml(self, add_to_element):
for m in self:
melem = m.to_xml()
add_to_element.append(melem)
return self
def __repr__(self):
xml = etree.Element("Messages")
self.to_xml(xml)
def __iter__(self):
return dict.values(self).__iter__()
return xml2str(xml)
#######################################################################
# can be removed after 01.07.24
class _msg_key:
def __init__(self, type, code): # @ReservedAssignment
warn("This class is deprecated.", DeprecationWarning)
self._type = type
self._code = code
@staticmethod
def get(msg):
return _Messages._msg_key(msg.type, msg.code)
return Messages._msg_key(msg.type, msg.code)
def __eq__(self, obj):
return self.__hash__() == obj.__hash__()
def __hash__(self):
return hash(str(self._type).lower() + (str(",") +
str(self._code) if self._code is not None else ''))
return
def __repr__(self):
return str(self._type) + (str(",") + str(self._code)
if self._code is not None else '')
# end remove
#######################################################################
def to_xml(self, add_to_element):
for m in self:
melem = m.to_xml()
add_to_element.append(melem)
return self
def __repr__(self):
xml = etree.Element("Messages")
self.to_xml(xml)
return xml2str(xml)
class _Messages(Messages):
def __init__(self, *args, **kwargs):
warn("_Messages is deprecated. "
"Use class Messages instead and beware of the slightly different API of the new"
" Messages class", DeprecationWarning)
super().__init__(*args, **kwargs)
def _basic_sync(e_local, e_remote):
@@ -2834,7 +2854,7 @@ class Container(list):
list.__init__(self)
self._timestamp = None
self._srid = None
self.messages = _Messages()
self.messages = Messages()
def extend(self, entities):
"""Extend this Container by appending all single entities in the given
@@ -2927,11 +2947,11 @@ class Container(list):
def get_errors(self):
"""Get all error messages of this container.
@return _Messages: Error messages.
@return Messages: Error messages.
"""
if self.has_errors():
ret = _Messages()
ret = Messages()
for m in self.messages:
if m.type.lower() == "error":
@@ -2944,11 +2964,11 @@ class Container(list):
def get_warnings(self):
"""Get all warning messages of this container.
@return _Messages: Warning messages.
@return Messages: Warning messages.
"""
if self.has_warnings():
ret = _Messages()
ret = Messages()
for m in self.messages:
if m.type.lower() == "warning":
@@ -2959,7 +2979,7 @@ class Container(list):
return None
def get_all_messages(self):
ret = _Messages()
ret = Messages()
for e in self:
ret.extend(e.get_all_messages())
@@ -4309,7 +4329,7 @@ class Query():
The query string.
flags : dict of str
A dictionary of flags to be send with the query request.
messages : _Messages()
messages : Messages()
A container of messages included in the last query response.
cached : bool
indicates whether the server used the query cache for the execution of
@@ -4332,7 +4352,7 @@ class Query():
def __init__(self, q):
self.flags = dict()
self.messages = _Messages()
self.messages = Messages()
self.cached = None
self.etag = None
@@ -4456,6 +4476,10 @@ def execute_query(q, unique=False, raise_exception_on_error=True, cache=True, fl
class DropOffBox(list):
def __init__(self, *args, **kwargs):
warn(DeprecationWarning(
"The DropOffBox is deprecated and will be removed in future."))
super().__init__(*args, **kwargs)
path = None
@@ -4499,7 +4523,7 @@ class UserInfo():
class Info():
def __init__(self):
self.messages = _Messages()
self.messages = Messages()
self.sync()
def sync(self):
Loading