diff --git a/src/linkahead/common/models.py b/src/linkahead/common/models.py index 0cefbbb3adfca8ab12cac39fb87648b59acc48d1..0ce0c56032fc11c4f8269d97b20134b5bb1cc891 100644 --- a/src/linkahead/common/models.py +++ b/src/linkahead/common/models.py @@ -482,7 +482,6 @@ class Entity: raise EntityHasNoAclError("This entity does not have an ACL (yet).") self.acl.is_permitted(role=role, permission=permission) - def get_all_messages(self) -> Messages: ret = Messages() ret.append(self.messages) @@ -561,7 +560,7 @@ class Entity: property = self.get_property(property_name) if property is None: return self - + if property.value is None: remove_if_empty_afterwards = False @@ -1107,9 +1106,12 @@ out: List[Entity] else: ref = prop - # if we saved a special selector before, apply it + # if we saved a special selector before, apply it if special_selector is None: - return prop.value + if prop is None: + return None + else: + return prop.value else: return getattr(ref, special_selector.lower()) @@ -1708,7 +1710,13 @@ def _log_response(body): class QueryTemplate(): - def __init__(self, id=None, name=None, query=None, description=None): # @ReservedAssignment + def __init__( + self, + id: Optional[int] = None, + name: Optional[str] = None, + query: Optional[str] = None, + description: Optional[str] = None, + ): # @ReservedAssignment self.id = (int(id) if id is not None else None) self.role = "QueryTemplate" @@ -1727,15 +1735,20 @@ class QueryTemplate(): self._size = None self._upload = None self.unit = None - self.acl = None - self.permissions = None + self.acl: Optional[ACL] = None + self.permissions: Optional[Permissions] = None self.is_valid = lambda: False self.is_deleted = lambda: False self.version = None self.state = None - def retrieve(self, raise_exception_on_error=True, unique=True, sync=True, - flags=None): + def retrieve( + self, + raise_exception_on_error: bool = True, + unique: bool = True, + sync: bool = True, + flags: Optional[Dict[str, Optional[str]]] = None, + ): return Container().append(self).retrieve( raise_exception_on_error=raise_exception_on_error, @@ -1743,8 +1756,14 @@ class QueryTemplate(): sync=sync, flags=flags)[0] - def insert(self, strict=True, raise_exception_on_error=True, - unique=True, sync=True, flags=None): + def insert( + self, + strict: bool = True, + raise_exception_on_error: bool = True, + unique: bool = True, + sync: bool = True, + flags: Optional[Dict[str, Optional[str]]] = None, + ): return Container().append(self).insert( strict=strict, @@ -1753,8 +1772,14 @@ class QueryTemplate(): sync=sync, flags=flags)[0] - def update(self, strict=True, raise_exception_on_error=True, - unique=True, sync=True, flags=None): + def update( + self, + strict: bool = True, + raise_exception_on_error: bool = True, + unique: bool = True, + sync: bool = True, + flags: Optional[Dict[str, Optional[str]]] = None, + ): return Container().append(self).update( strict=strict, @@ -1770,7 +1795,7 @@ class QueryTemplate(): def __repr__(self): return xml2str(self.to_xml()) - def to_xml(self, xml=None): + def to_xml(self, xml: Optional[etree._Element] = None): if xml is None: xml = etree.Element("QueryTemplate") @@ -1800,7 +1825,7 @@ class QueryTemplate(): return xml @staticmethod - def _from_xml(xml): + def _from_xml(xml: etree._Element): if xml.tag.lower() == "querytemplate": q = QueryTemplate(name=xml.get("name"), description=xml.get("description"), query=None) @@ -1810,16 +1835,18 @@ class QueryTemplate(): q.query = e.text else: child = _parse_single_xml_element(e) - + if child is None: + continue if isinstance(child, Message): q.messages.append(child) elif isinstance(child, ACL): q.acl = child elif isinstance(child, Version): - q.version = child + q.version = child # type: ignore elif isinstance(child, Permissions): q.permissions = child - q.id = int(xml.get("id")) + id = xml.get("id") + q.id = int(id) if id is not None else None return q else: @@ -2273,7 +2300,9 @@ class File(Record): return file_.name @staticmethod - def download_from_path(target_file, path): + def download_from_path( + target_file: Union[BufferedWriter, _TemporaryFileWrapper], path: str + ): _log_request("GET (download): " + path) response = get_connection().download_file(path) @@ -2339,19 +2368,21 @@ class _Properties(list): def __init__(self): list.__init__(self) - self._importance = dict() - self._inheritance = dict() - self._element_by_name = dict() - self._element_by_id = dict() + self._importance: Dict[Property, IMPORTANCE] = dict() + self._inheritance: Dict[Property, INHERITANCE] = dict() + self._element_by_name: Dict[str, Property] = dict() + self._element_by_id: Dict[str, Property] = dict() - def get_importance(self, property): # @ReservedAssignment + def get_importance( + self, property: Union[Property, str, None] + ): # @ReservedAssignment if property is not None: - if hasattr(property, "encode"): + if isinstance(property, str): property = self.get_by_name(property) # @ReservedAssignment return self._importance.get(property) - def set_importance(self, property, importance): # @ReservedAssignment + def set_importance(self, property: Optional[Property], importance: IMPORTANCE): # @ReservedAssignment if property is not None: self._importance[property] = importance @@ -2372,9 +2403,9 @@ class _Properties(list): def append( self, - property: Union[List[Entity], Entity], - importance=None, - inheritance: Union[str, INHERITANCE, None] = None, + property: Union[List[Entity], Entity, Property], + importance: Optional[IMPORTANCE] = None, + inheritance: Optional[INHERITANCE] = None, ): # @ReservedAssignment if isinstance(property, list): for p in property: @@ -3750,7 +3781,7 @@ class Container(list): return (entities[0:hl], entities[hl:len(entities)]) - def _retrieve(self, entities, flags): + def _retrieve(self, entities, flags: Dict[str, Optional[str]]): c = get_connection() try: _log_request("GET: " + _ENTITY_URI_SEGMENT + str(entities) + @@ -3804,8 +3835,14 @@ class Container(list): return ret - def update(self, strict=False, raise_exception_on_error=True, - unique=True, sync=True, flags=None): + def update( + self, + strict: bool = False, + raise_exception_on_error: bool = True, + unique: bool = True, + sync: bool = True, + flags: Optional[Dict[str, Any]] = None, + ): """Update these entites.""" if len(self) < 1: @@ -3816,7 +3853,7 @@ class Container(list): self.clear_server_messages() insert_xml = etree.Element("Update") - http_parts = [] + http_parts: List[MultipartParam] = [] if flags is None: flags = {} @@ -3886,7 +3923,9 @@ class Container(list): return cresp @staticmethod - def _process_file_if_present_and_add_to_http_parts(http_parts, entity): + def _process_file_if_present_and_add_to_http_parts( + http_parts: List[MultipartParam], entity: Union[File, Entity] + ): if isinstance(entity, File) and hasattr( entity, 'file') and entity.file is not None: new_checksum = File._get_checksum(entity.file) @@ -3927,8 +3966,16 @@ class Container(list): else: entity._checksum = None - def insert(self, strict=False, raise_exception_on_error=True, - unique=True, sync=True, flags=None): + # FIXME: The signature of Conatiner.insert is completely different than the superclass' + # list.insert method. This may be a problem in the future, but is ignored for now. + def insert( # type: ignore + self, + strict: bool = False, + raise_exception_on_error: bool = True, + unique: bool = True, + sync: bool = True, + flags: Optional[Dict[str, Optional[str]]] = None, + ): """Insert this file entity into LinkAhead. A successful insertion will generate a new persistent ID for this entity. This entity can be identified, retrieved, updated, and deleted via this ID until it has @@ -3944,12 +3991,15 @@ class Container(list): @param strict=False: Flag for strict mode. @param sync=True: synchronize this container with the response from the server. Otherwise, - this method returns a new container with the inserted entities and leaves this container untouched. + this method returns a new container with the inserted entities and leaves this container untouched. + @param unique=True: Flag for unique mode. If set to True, the server will check if the name of the + entity is unique. If not, the server will return an error. + @param flags=None: Additional flags for the server. """ self.clear_server_messages() insert_xml = etree.Element("Insert") - http_parts = [] + http_parts: List[MultipartParam] = [] if flags is None: flags = {} @@ -4030,7 +4080,6 @@ class Container(list): cresp = Container._response_to_entities(http_response) if sync: - self._sync(cresp, unique=unique, raise_exception_on_error=raise_exception_on_error) @@ -4045,7 +4094,7 @@ class Container(list): return cresp @staticmethod - def _get_smallest_tmpid(entity): + def _get_smallest_tmpid(entity: Entity): tmpid = 0 if entity.id is not None: @@ -4110,7 +4159,9 @@ class Container(list): return self - def get_property_values(self, *selectors): + def get_property_values( + self, *selectors: Union[str, Tuple[str]] + ) -> List[Tuple[str]]: """ Return a list of tuples with values of the given selectors. I.e. a tabular representation of the container's content. @@ -4356,8 +4407,14 @@ class ACL(): if item in self._grants: self._grants.remove(item) - def revoke_denial(self, username=None, realm=None, - role=None, permission=None, priority=False): + def revoke_denial( + self, + username: Optional[str] = None, + realm: Optional[str] = None, + role: Optional[str] = None, + permission: Optional[str] = None, + priority: bool = False, + ): priority = self._get_boolean_priority(priority) item = ACI(role=role, username=username, realm=realm, permission=permission) @@ -4518,7 +4575,7 @@ class ACL(): return ret - def get_acl_for_user(self, username, realm=None): + def get_acl_for_user(self, username: str, realm: Optional[str] = None): ret = ACL() for aci in self._grants: @@ -4611,7 +4668,7 @@ class Query(): with the resulting entities. """ - def putFlag(self, key, value=None): + def putFlag(self, key: str, value: Optional[str] = None): self.flags[key] = value return self @@ -4623,7 +4680,7 @@ class Query(): return self.flags.get(key) def __init__(self, q: Union[str, etree._Element]): - self.flags: Dict[str, str] = dict() + self.flags: Dict[str, Optional[str]] = dict() self.messages = Messages() self.cached: Optional[bool] = None self.etag = None @@ -4650,7 +4707,7 @@ class Query(): else: self.q = q - def _query_request(self, query_dict): + def _query_request(self, query_dict: Dict[str, Optional[str]]): """Used internally to execute the query request...""" _log_request("GET Entity?" + str(query_dict), None) connection = get_connection() @@ -4660,7 +4717,12 @@ class Query(): cresp = Container._response_to_entities(http_response) return cresp - def _paging_generator(self, first_page, query_dict, page_length): + def _paging_generator( + self, + first_page: Container, + query_dict: Dict[str, Optional[str]], + page_length: int, + ): """Used internally to create a generator of pages instead instead of a container which contais all the results.""" if len(first_page) == 0: @@ -4763,7 +4825,7 @@ class Query(): return r self.messages = cresp.messages - if has_paging: + if has_paging and page_length is not None: return self._paging_generator(cresp, query_dict, page_length) else: return cresp @@ -4774,7 +4836,7 @@ def execute_query( unique: bool = False, raise_exception_on_error: bool = True, cache: bool = True, - flags: Optional[Dict[str, str]] = None, + flags: Optional[Dict[str, Optional[str]]] = None, page_length: Optional[int] = None, ) -> Union[Container, int]: """Execute a query (via a server-requests) and return the results. @@ -4878,8 +4940,8 @@ class Info(): def __init__(self): self.messages = Messages() - self.user_info = None - self.time_zone = None + self.user_info: Optional[UserInfo] = None + self.time_zone: Optional[TimeZone] = None self.sync() def sync(self): @@ -4942,7 +5004,7 @@ class Permission(): class Permissions(): - known_permissions = None + known_permissions: Optional[List[Permissions]] = None def __init__(self, xml: etree._Element): self.parse_xml(xml) @@ -5187,7 +5249,7 @@ def raise_errors(arg0: Union[Entity, QueryTemplate, Container]): raise transaction_error -def delete(ids: Union[List[int], range], raise_exception_on_error=True): +def delete(ids: Union[List[int], range], raise_exception_on_error: bool = True): c = Container() if isinstance(ids, list) or isinstance(ids, range):