@@ -100,15 +100,24 @@ class RuntimeTypeCheckWrapperDoFn(AbstractDoFnWrapper):
100100 def __init__ (self , dofn , type_hints , full_label ):
101101 super ().__init__ (dofn )
102102 self .full_label = full_label
103- # Note that the bound process method must not be cached on the instance:
103+ # Note that a * bound* process method must not be cached on the instance:
104104 # an attribute holding a bound method is visible to the stateful DoFn
105105 # reflection in userstate.get_dofn_specs, and a cached copy can diverge
106106 # from self.dofn.process across (de)serialization, in which case stateful
107- # DoFn validation would see duplicate StateSpecs/TimerSpecs.
107+ # DoFn validation would see duplicate StateSpecs/TimerSpecs. Caching the
108+ # underlying (unbound) function is safe: plain functions stored on an
109+ # instance are not bound methods, so DoFn reflection ignores them.
110+ process_fn = dofn ._process_argspec_fn ()
111+ if hasattr (process_fn , '__func__' ):
112+ self ._process_fn = process_fn .__func__
113+ self ._process_fn_needs_self = True
114+ else :
115+ self ._process_fn = process_fn
116+ self ._process_fn_needs_self = False
108117 if type_hints .input_types :
109118 input_args , input_kwargs = type_hints .input_types
110119 self ._input_hints = getcallargs_forhints (
111- dofn . _process_argspec_fn () , * input_args , ** input_kwargs )
120+ process_fn , * input_args , ** input_kwargs )
112121 else :
113122 self ._input_hints = None
114123 # TODO(robertwb): Multi-output.
@@ -125,8 +134,11 @@ def wrapper(self, method, args, kwargs):
125134 def process (self , * args , ** kwargs ):
126135 try :
127136 if self ._input_hints :
128- actual_inputs = inspect .getcallargs (
129- self .dofn ._process_argspec_fn (), * args , ** kwargs ) # pylint: disable=deprecated-method
137+ if self ._process_fn_needs_self :
138+ actual_inputs = inspect .getcallargs (
139+ self ._process_fn , self .dofn , * args , ** kwargs ) # pylint: disable=deprecated-method
140+ else :
141+ actual_inputs = inspect .getcallargs (self ._process_fn , * args , ** kwargs ) # pylint: disable=deprecated-method
130142 for var , hint in self ._input_hints .items ():
131143 if hint is actual_inputs [var ]:
132144 # self parameter
0 commit comments