Coverage for src/bioimageio/core/_description_serializer.py: 0%

44 statements  

« prev     ^ index     » next       coverage.py v7.14.2, created at 2026-06-22 16:54 +0000

1import base64 

2import hashlib 

3from io import BytesIO 

4from typing import Tuple 

5from zipfile import ZipFile 

6 

7from bioimageio.spec import ( 

8 InvalidDescr, 

9 ResourceDescr, 

10 load_description, 

11 save_bioimageio_package_to_stream, 

12) 

13from bioimageio.spec.common import Sha256 

14 

15 

16class DescriptionSerializer: 

17 """Description serializer intended for client/server communication, NOT for sharing resource descriptions. 

18 

19 This serializer only includes local files to keep the serialized package small. 

20 """ 

21 

22 STRING_ENCODING = "ascii" 

23 

24 @staticmethod 

25 def serialize(rd: ResourceDescr) -> bytes: 

26 stream = save_bioimageio_package_to_stream(rd, local_files_only=True) 

27 _ = stream.seek(0) 

28 return stream.read() 

29 

30 @classmethod 

31 def serialize_to_string(cls, rd: ResourceDescr) -> str: 

32 package_bytes = cls.serialize(rd) 

33 

34 safe_bytes = cls._get_safe_bytes(package_bytes) 

35 serialized_str = safe_bytes.decode(cls.STRING_ENCODING) 

36 if len(serialized_str) <= 2083: 

37 raise RuntimeError( 

38 "Serialized model description should be longer than 2083 characters to not be treated as a URL on the server side." 

39 ) 

40 return serialized_str 

41 

42 @staticmethod 

43 def _get_safe_bytes(raw_bytes: bytes) -> bytes: 

44 return base64.b64encode(raw_bytes) 

45 

46 @classmethod 

47 def deserialize_from_string(cls, serialized: str) -> ResourceDescr: 

48 package_bytes = base64.b64decode(serialized.encode(cls.STRING_ENCODING)) 

49 return cls.deserialize(package_bytes) 

50 

51 @staticmethod 

52 def deserialize(serialized: bytes) -> ResourceDescr: 

53 descr = load_description(ZipFile(BytesIO(serialized)), perform_io_checks=False) 

54 if isinstance(descr, InvalidDescr): 

55 raise ValueError(f"invalid serialized model package: {descr.get_reason()}") 

56 

57 return descr 

58 

59 @classmethod 

60 def serialize_to_string_and_hash(cls, rd: ResourceDescr) -> Tuple[str, Sha256]: 

61 package_bytes = cls.serialize(rd) 

62 safe_bytes = cls._get_safe_bytes(package_bytes) 

63 serialized_str = safe_bytes.decode(cls.STRING_ENCODING) 

64 if len(serialized_str) <= 2083: 

65 raise RuntimeError( 

66 "Serialized model description should be longer than 2083 characters to not be treated as a URL on the server side." 

67 ) 

68 sha256 = Sha256(hashlib.sha256(package_bytes).hexdigest()) 

69 return serialized_str, sha256