1
1
from __future__ import unicode_literals
2
2
3
3
from builtins import object
4
+ import copy
4
5
import hashlib
5
- import json
6
6
7
7
8
- class Node (object ):
9
- """Node base"""
10
- def __init__ (self , parents , name , * args , ** kwargs ):
8
+ def _recursive_repr (item ):
9
+ """Hack around python `repr` to deterministically represent dictionaries.
10
+
11
+ This is able to represent more things than json.dumps, since it does not require things to be JSON serializable
12
+ (e.g. datetimes).
13
+ """
14
+ if isinstance (item , basestring ):
15
+ result = str (item )
16
+ elif isinstance (item , list ):
17
+ result = '[{}]' .format (', ' .join ([_recursive_repr (x ) for x in item ]))
18
+ elif isinstance (item , dict ):
19
+ kv_pairs = ['{}: {}' .format (_recursive_repr (k ), _recursive_repr (item [k ])) for k in sorted (item )]
20
+ result = '{' + ', ' .join (kv_pairs ) + '}'
21
+ else :
22
+ result = repr (item )
23
+ return result
24
+
25
+
26
+ def _create_hash (item ):
27
+ hasher = hashlib .sha224 ()
28
+ repr_ = _recursive_repr (item )
29
+ hasher .update (repr_ .encode ('utf-8' ))
30
+ return hasher .hexdigest ()
31
+
32
+
33
+ class _NodeBase (object ):
34
+ @property
35
+ def hash (self ):
36
+ if self ._hash is None :
37
+ self ._update_hash ()
38
+ return self ._hash
39
+
40
+ def __init__ (self , parents , name ):
11
41
parent_hashes = [hash (parent ) for parent in parents ]
12
42
assert len (parent_hashes ) == len (set (parent_hashes )), 'Same node cannot be included as parent multiple times'
13
43
self ._parents = parents
14
44
self ._hash = None
15
45
self ._name = name
16
- self ._args = args
17
- self ._kwargs = kwargs
46
+
47
+ def _transplant (self , new_parents ):
48
+ other = copy .copy (self )
49
+ other ._parents = copy .copy (new_parents )
50
+ return other
51
+
52
+ @property
53
+ def _repr_args (self ):
54
+ raise NotImplementedError ()
55
+
56
+ @property
57
+ def _repr_kwargs (self ):
58
+ raise NotImplementedError ()
59
+
60
+ @property
61
+ def _short_hash (self ):
62
+ return '{:x}' .format (abs (hash (self )))[:12 ]
18
63
19
64
def __repr__ (self ):
20
- formatted_props = ['{}' .format (arg ) for arg in self ._args ]
21
- formatted_props += ['{}={!r}' .format (key , self ._kwargs [key ]) for key in sorted (self ._kwargs )]
22
- return '{}({})' .format (self ._name , ',' .join (formatted_props ))
65
+ args = self ._repr_args
66
+ kwargs = self ._repr_kwargs
67
+ formatted_props = ['{!r}' .format (arg ) for arg in args ]
68
+ formatted_props += ['{}={!r}' .format (key , kwargs [key ]) for key in sorted (kwargs )]
69
+ return '{}({}) <{}>' .format (self ._name , ', ' .join (formatted_props ), self ._short_hash )
23
70
24
71
def __hash__ (self ):
25
72
if self ._hash is None :
@@ -30,16 +77,31 @@ def __eq__(self, other):
30
77
return hash (self ) == hash (other )
31
78
32
79
def _update_hash (self ):
33
- props = {'args' : self ._args , 'kwargs' : self ._kwargs }
34
- props_str = json .dumps (props , sort_keys = True ).encode ('utf-8' )
35
- my_hash = hashlib .md5 (props_str ).hexdigest ()
80
+ props = {'args' : self ._repr_args , 'kwargs' : self ._repr_kwargs }
81
+ my_hash = _create_hash (props )
36
82
parent_hashes = [str (hash (parent )) for parent in self ._parents ]
37
83
hashes = parent_hashes + [my_hash ]
38
84
hashes_str = ',' .join (hashes ).encode ('utf-8' )
39
85
hash_str = hashlib .md5 (hashes_str ).hexdigest ()
40
86
self ._hash = int (hash_str , base = 16 )
41
87
42
88
89
+ class Node (_NodeBase ):
90
+ """Node base"""
91
+ def __init__ (self , parents , name , * args , ** kwargs ):
92
+ super (Node , self ).__init__ (parents , name )
93
+ self ._args = args
94
+ self ._kwargs = kwargs
95
+
96
+ @property
97
+ def _repr_args (self ):
98
+ return self ._args
99
+
100
+ @property
101
+ def _repr_kwargs (self ):
102
+ return self ._kwargs
103
+
104
+
43
105
class InputNode (Node ):
44
106
"""InputNode type"""
45
107
def __init__ (self , name , * args , ** kwargs ):
0 commit comments