@@ -29,7 +29,7 @@ def isTypeFunctionType(type):
2929 If so, return a tuple of (func, args, kwargs). otherwise none
3030 """
3131 if type in _type_to_typefunction :
32- (func , ( args , kwargs ) ) = _type_to_typefunction [type ]
32+ (func , args , kwargs ) = _type_to_typefunction [type ]
3333 return (func , args , kwargs )
3434 return None
3535
@@ -42,23 +42,10 @@ def reconstructTypeFunctionType(typeFunction, args, kwargs):
4242 return typeFunction (* args , ** dict (kwargs ))
4343
4444
45- def makeTypeFunction (f ):
46- """Decorate 'f' to be a 'TypeFunction'.
45+ _typeFunctionMemo = {}
4746
48- The resulting function is expected to take a set of hashable arguments and
49- produce a type object. The function is memoized, so code in the
50- decorated function is executed once only for each distinct set of
51- arguments. The order of keyword arguments is not considered in the memo.
52- The function should not have sideeffects.
53-
54- TypeFunctions may call each other recursively and in self-referential
55- cycles. If the function calls back into itself, a Forward will
56- be returned instead of a concrete type, which lets you express recursive
57- types in a natural way.
5847
59- Don't stash the type you return, since the actual type returned by the
60- function may not be the one you returned.
61- """
48+ def _buildTypeFunction (TypeFunction_ , f , args , kwargs ):
6249 def nameFor (args , kwargs ):
6350 def toStr (x ):
6451 if isinstance (x , type ):
@@ -93,53 +80,71 @@ def mapArg(arg):
9380
9481 raise TypeError ("Instance of type '%s' is not a valid argument to a type function" % type (arg ))
9582
96- _memoForKey = {}
83+ args = tuple (mapArg (a ) for a in args )
84+ kwargs = tuple (sorted ([(k , mapArg (v )) for k , v in kwargs .items ()]))
9785
98- def buildType (* args , ** kwargs ):
99- args = tuple (mapArg (a ) for a in args )
100- kwargs = tuple (sorted ([(k , mapArg (v )) for k , v in kwargs .items ()]))
86+ key = (TypeFunction_ , args , kwargs )
10187
102- key = (args , kwargs )
88+ if key in _typeFunctionMemo :
89+ res = _typeFunctionMemo [key ]
90+ if isinstance (res , Exception ):
91+ raise res
10392
104- if key in _memoForKey :
105- res = _memoForKey [key ]
93+ if getattr (res , '__typed_python_category__' , None ) != 'Forward' :
94+ # only return fully resolved TypeFunction values without
95+ # locking.
96+ return res
97+
98+ with runtimeLock :
99+ if key in _typeFunctionMemo :
100+ res = _typeFunctionMemo [key ]
106101 if isinstance (res , Exception ):
107102 raise res
103+ return res
108104
109- if getattr (res , '__typed_python_category__' , None ) != 'Forward' :
110- # only return fully resolved TypeFunction values without
111- # locking.
112- return res
105+ forward = Forward (nameFor (args , kwargs ))
113106
114- with runtimeLock :
115- if key in _memoForKey :
116- res = _memoForKey [key ]
117- if isinstance (res , Exception ):
118- raise res
119- return res
107+ _typeFunctionMemo [key ] = forward
108+ _type_to_typefunction [forward ] = key
120109
121- forward = Forward (nameFor (args , kwargs ))
110+ try :
111+ resultType = f (* args , ** dict (kwargs ))
122112
123- _memoForKey [key ] = forward
124- _type_to_typefunction [forward ] = (TypeFunction_ , key )
113+ forward .define (resultType )
125114
126- try :
127- resultType = f (* args , ** dict (kwargs ))
115+ _type_to_typefunction .pop (forward )
128116
129- forward .define (resultType )
117+ if resultType not in _type_to_typefunction :
118+ _type_to_typefunction [resultType ] = key
130119
131- _type_to_typefunction . pop ( forward )
120+ _typeFunctionMemo [ key ] = resultType
132121
133- if resultType not in _type_to_typefunction :
134- _type_to_typefunction [resultType ] = (TypeFunction_ , key )
122+ return resultType
123+ except Exception as e :
124+ _typeFunctionMemo [key ] = e
125+ logging .exception ("TypeFunction errored" )
126+ raise
135127
136- _memoForKey [key ] = resultType
137128
138- return resultType
139- except Exception as e :
140- _memoForKey [key ] = e
141- logging .exception ("TypeFunction errored" )
142- raise
129+ def makeTypeFunction (f ):
130+ """Decorate 'f' to be a 'TypeFunction'.
131+
132+ The resulting function is expected to take a set of hashable arguments and
133+ produce a type object. The function is memoized, so code in the
134+ decorated function is executed once only for each distinct set of
135+ arguments. The order of keyword arguments is not considered in the memo.
136+ The function should not have sideeffects.
137+
138+ TypeFunctions may call each other recursively and in self-referential
139+ cycles. If the function calls back into itself, a Forward will
140+ be returned instead of a concrete type, which lets you express recursive
141+ types in a natural way.
142+
143+ Don't stash the type you return, since the actual type returned by the
144+ function may not be the one you returned.
145+ """
146+ def buildType (* args , ** kwargs ):
147+ return _buildTypeFunction (TypeFunction_ , f , args , kwargs )
143148
144149 class TypeFunction_ (TypeFunction ):
145150 __module__ = f .__module__
0 commit comments