3
3
4
4
# import stat
5
5
import struct
6
+ import typing as ty
6
7
from collections .abc import Mapping
7
8
from functools import singledispatch
8
9
from hashlib import blake2b
10
+ import logging
9
11
10
12
# from pathlib import Path
11
13
from typing import (
14
16
NewType ,
15
17
Sequence ,
16
18
Set ,
17
- _SpecialForm ,
18
19
)
19
20
import attrs .exceptions
20
21
22
+ logger = logging .getLogger ("pydra" )
23
+
21
24
try :
22
25
from typing import Protocol
23
26
except ImportError :
@@ -88,7 +91,8 @@ def hash_single(obj: object, cache: Cache) -> Hash:
88
91
h = blake2b (digest_size = 16 , person = b"pydra-hash" )
89
92
for chunk in bytes_repr (obj , cache ):
90
93
h .update (chunk )
91
- cache [objid ] = Hash (h .digest ())
94
+ hsh = cache [objid ] = Hash (h .digest ())
95
+ logger .debug ("Hash of %s object is %s" , obj , hsh )
92
96
return cache [objid ]
93
97
94
98
@@ -102,15 +106,14 @@ def __bytes_repr__(self, cache: Cache) -> Iterator[bytes]:
102
106
def bytes_repr (obj : object , cache : Cache ) -> Iterator [bytes ]:
103
107
cls = obj .__class__
104
108
yield f"{ cls .__module__ } .{ cls .__name__ } :{{" .encode ()
105
- try :
109
+ dct : Dict [str , ty .Any ]
110
+ if attrs .has (type (obj )):
111
+ # Drop any attributes that aren't used in comparisons by default
112
+ dct = attrs .asdict (obj , recurse = False , filter = lambda a , _ : bool (a .eq ))
113
+ elif hasattr (obj , "__slots__" ):
114
+ dct = {attr : getattr (obj , attr ) for attr in obj .__slots__ }
115
+ else :
106
116
dct = obj .__dict__
107
- except AttributeError as e :
108
- # Attrs creates slots classes by default, so we add this here to handle those
109
- # cases
110
- try :
111
- dct = attrs .asdict (obj , recurse = False ) # type: ignore
112
- except attrs .exceptions .NotAnAttrsClassError :
113
- raise TypeError (f"Cannot hash { obj } as it is a slots class" ) from e
114
117
yield from bytes_repr_mapping_contents (dct , cache )
115
118
yield b"}"
116
119
@@ -224,10 +227,34 @@ def bytes_repr_dict(obj: dict, cache: Cache) -> Iterator[bytes]:
224
227
yield b"}"
225
228
226
229
227
- @register_serializer (_SpecialForm )
230
+ @register_serializer (ty ._GenericAlias )
231
+ @register_serializer (ty ._SpecialForm )
228
232
@register_serializer (type )
229
233
def bytes_repr_type (klass : type , cache : Cache ) -> Iterator [bytes ]:
230
- yield f"type:({ klass .__module__ } .{ klass .__name__ } )" .encode ()
234
+ def type_name (tp ):
235
+ try :
236
+ name = tp .__name__
237
+ except AttributeError :
238
+ name = tp ._name
239
+ return name
240
+
241
+ yield b"type:("
242
+ origin = ty .get_origin (klass )
243
+ if origin :
244
+ yield f"{ origin .__module__ } .{ type_name (origin )} [" .encode ()
245
+ for arg in ty .get_args (klass ):
246
+ if isinstance (
247
+ arg , list
248
+ ): # sometimes (e.g. Callable) the args of a type is a list
249
+ yield b"["
250
+ yield from (b for t in arg for b in bytes_repr_type (t , cache ))
251
+ yield b"]"
252
+ else :
253
+ yield from bytes_repr_type (arg , cache )
254
+ yield b"]"
255
+ else :
256
+ yield f"{ klass .__module__ } .{ type_name (klass )} " .encode ()
257
+ yield b")"
231
258
232
259
233
260
@register_serializer (list )
0 commit comments