From 27c767e2edeee14e366d5c9e6dd91157aea81cfb Mon Sep 17 00:00:00 2001 From: Timm Fitschen <t.fitschen@indiscale.com> Date: Fri, 25 Nov 2022 14:26:42 +0100 Subject: [PATCH] TST: fix integration tests --- src/caosdb/common/models.py | 131 +++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/src/caosdb/common/models.py b/src/caosdb/common/models.py index 49315031..0c196f9c 100644 --- a/src/caosdb/common/models.py +++ b/src/caosdb/common/models.py @@ -46,6 +46,7 @@ from os.path import isdir from random import randint from tempfile import NamedTemporaryFile from warnings import warn +from lxml import etree from caosdb.common.datatype import (BOOLEAN, DATETIME, DOUBLE, INTEGER, TEXT, is_list_datatype, is_reference) @@ -66,7 +67,6 @@ from caosdb.exceptions import (AmbiguousEntityError, AuthorizationError, TransactionError, UniqueNamesError, UnqualifiedParentsError, UnqualifiedPropertiesError) -from lxml import etree _ENTITY_URI_SEGMENT = "Entity" @@ -198,6 +198,10 @@ class Entity(object): return self._wrapped_entity.size + @size.setter + def size(self, new_size): + self._size = new_size if new_size is None else int(new_size) + @property def id(self): if self.__id is not None: @@ -246,14 +250,24 @@ class Entity(object): return self._wrapped_entity.description - @property - def checksum(self): - return self._checksum - @description.setter def description(self, new_description): self.__description = new_description + @property + def checksum(self): + if self._checksum is not None: + return self._checksum + + if self._wrapped_entity is None: + return None + + return self._wrapped_entity._checksum + + @checksum.setter + def checksum(self, new_checksum): + self._checksum = new_checksum + @property def unit(self): if self.__unit is not None or self._wrapped_entity is None: @@ -1049,8 +1063,8 @@ class Entity(object): if self.file is not None and local_serialization: xml.set("file", self.file) - if self._checksum is not None: - xml.set("checksum", self._checksum) + if self.checksum is not None: + xml.set("checksum", self.checksum) if self.size is not None: xml.set("size", str(self.size)) @@ -1818,19 +1832,26 @@ class File(Record): look at `test_files.py` in the Python integration tests of the `load_files.py` script in the advanced user tools. - @param name: A name for this file record (That's an entity name - not to be - confused with the last segment of the files path). - @param id: An ID. - @param description: A description for this file record. - @param path: The complete path, including the file name, of the file in the - server's "caosroot" file system. - @param file: A local path or python file object. The file designated by - this argument will be uploaded to the server via HTTP. - @param pickup: deprecated - @param thumbnail: deprecated. - @param properties: A list of properties for this file record. @todo is this - implemented? - + Parameters + ---------- + name : str + A name for this file record (That's an entity name - not to be confused + with the last segment of the files path). + id : int + An ID. + description : str + A description for this file record. + path : str + The complete path, including the file name, of the file in the server's + virtual file system. + file : str or readable + A local path or python file object. The file designated by this + argument will be uploaded to the server via HTTP. + pickup : str + A file/folder in the DropOffBox (the server will move that file into + its "caosroot" file system). (DEPRECATED, use import feature) + import_file : bool + Import the file (don't upload it, its already there). Default: `False` """ def __init__(self, name=None, id=None, description=None, @@ -1867,7 +1888,7 @@ class File(Record): return Entity.to_xml(self, xml=xml, add_properties=add_properties, local_serialization=local_serialization) - def download(self, target=None): + def download(self, target=None, check_hash=True): """Download this file-entity's actual file from the file server. It will be stored to the target or will be hold as a temporary file. @@ -1880,16 +1901,16 @@ class File(Record): file_ = open(target, 'wb') else: file_ = NamedTemporaryFile(mode='wb', delete=False) - checksum = File.download_from_path(file_, self.path) + checksum = File._download_from_path(file_, self.path) - if self._checksum is not None and self._checksum.lower() != checksum.hexdigest().lower(): + if check_hash and self.checksum is not None and self.checksum.lower() != checksum: raise ConsistencyError( "The downloaded file had an invalid checksum. Maybe the download did not finish?") return file_.name @staticmethod - def download_from_path(target_file, path): + def _download_from_path(target_file, path): _log_request("GET (download): " + path) response = get_connection().download_file(path) @@ -1903,46 +1924,27 @@ class File(Record): data = response.read(8000) target_file.close() - return checksum + return checksum.hexdigest().lower() @staticmethod - def _get_checksum(files): - import locale - - if hasattr(files, "name"): - return File._get_checksum_single_file(files.name) + def _get_checksum(file): + if hasattr(file, "name"): + file_name = file.name else: - if isdir(files): - checksumappend = "" - - for child in sorted(listdir(files), - key=cmp_to_key(locale.strcoll)): - - if isdir(files + '/' + child): - checksumappend += child - checksumappend += File._get_checksum(files + "/" + child) - checksum = sha512() - checksum.update(checksumappend.encode('utf-8')) - - return checksum.hexdigest() - else: - return File._get_checksum_single_file(files) + file_name = file - @staticmethod - def _get_checksum_single_file(single_file): - _file = open(single_file, 'rb') - data = _file.read(1000) checksum = sha512() - - while data: - checksum.update(data) + with open(file_name, 'rb') as _file: data = _file.read(1000) - _file.close() - return checksum.hexdigest() + while data: + checksum.update(data) + data = _file.read(1000) + + return checksum.hexdigest().lower() def add_property(self, property=None, id=None, name=None, description=None, datatype=None, - value=None, unit=None, importance=FIX, inheritance=FIX): # @ReservedAssignment + value=None, unit=None, importance=FIX, inheritance=FIX): return super().add_property( property=property, id=id, name=name, description=description, datatype=datatype, @@ -3323,10 +3325,10 @@ class Container(list): uri1, uri2 = Container._split_uri_string(entities) except ValueError as val_e: raise uri_e from val_e - c1 = self._retrieve(entities=uri1, flags=flags) - c2 = self._retrieve(entities=uri2, flags=flags) - c1.extend(c2) - c1.messages.extend(c2.messages) + c1 = self._retrieve(entities=uri1, flags=flags) + c2 = self._retrieve(entities=uri2, flags=flags) + c1.extend(c2) + c1.messages.extend(c2.messages) return c1 @@ -3473,15 +3475,18 @@ class Container(list): part.filename = entity._upload http_parts.append(part) + elif isinstance(entity, File) and hasattr(entity, "file") and entity.file is None: + entity._upload = None else: + entity._upload = None entity._checksum = None def insert(self, strict=False, raise_exception_on_error=True, unique=True, sync=True, flags=None): - """Insert this file entity into CaosDB. 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 - been deleted. + """Insert these entities into CaosDB. A successful insertion will + generate a persistent ID for the new entities. These entities can be + identified, retrieved, updated, and deleted via their ID until they + have been deleted. If the insertion fails, a CaosDBException will be raised. The server will have returned at least one error-message describing the reason why it failed in that case (call -- GitLab