@@ -170,20 +170,22 @@ class ContextMeta(type):
170170 """Functionality for objects that put themselves in a context using
171171 the `with` statement.
172172 """
173- _context_class = None # type: Union[Type, str]
174173
175174 def __new__ (cls , name , bases , dct , ** kargs ):
175+ # this serves only to strip off keyword args, per the warning from
176+ # StackExchange:
176177 # DO NOT send "**kargs" to "type.__new__". It won't catch them and
177178 # you'll get a "TypeError: type() takes 1 or 3 arguments" exception.
178- # dct['get_context'] = classmethod(_get_context)
179- # dct['get_contexts'] = classmethod(_get_contexts)
180179 return super ().__new__ (cls , name , bases , dct )
181180
181+ # FIXME: is there a more elegant way to automatically add methods to the class that
182+ # are instance methods instead of class methods?
182183 def __init__ (cls , name , bases , nmspc , context_class : Optional [Type ]= None , ** kwargs ):
184+ """Add ``__enter__`` and ``__exit__`` methods to the new class automatically."""
183185 if context_class is not None :
184186 cls ._context_class = context_class
185187 super ().__init__ (name , bases , nmspc )
186- cls . contexts = threading . local ()
188+
187189 def __enter__ (self ):
188190 self .__class__ .context_class .get_contexts ().append (self )
189191 # self._theano_config is set in Model.__new__
@@ -200,9 +202,11 @@ def __exit__(self, typ, value, traceback):
200202 cls .__enter__ = __enter__
201203 cls .__exit__ = __exit__
202204
205+
203206 def get_context (cls , error_if_none = True ) -> Optional [T ]:
204207 """Return the most recently pushed context object of type ``cls``
205- on the stack, or ``None``."""
208+ on the stack, or ``None``. If ``error_if_none`` is True (default),
209+ raise a ``TypeError`` instead of returning ``None``."""
206210 idx = - 1
207211 while True :
208212 try :
@@ -217,12 +221,32 @@ def get_context(cls, error_if_none=True) -> Optional[T]:
217221 idx = idx - 1
218222
219223 def get_contexts (cls ) -> List [T ]:
220- # no race-condition here, cls.contexts is a thread-local object
224+ """Return a stack of context instances for the ``context_class``
225+ of ``cls``."""
226+ # This lazily creates the context class's contexts
227+ # thread-local object, as needed. This seems inelegant to me,
228+ # but since the context class is not guaranteed to exist when
229+ # the metaclass is being instantiated, I couldn't figure out a
230+ # better way. [2019/10/11:rpg]
231+
232+ # no race-condition here, contexts is a thread-local object
221233 # be sure not to override contexts in a subclass however!
222- if not hasattr (cls .context_class , 'stack' ):
223- cls .context_class .stack = []
224- return cls .context_class .stack
225-
234+ context_class = cls .context_class
235+ assert isinstance (context_class , type ), \
236+ "Name of context class, %s was not resolvable to a class" % context_class
237+ if not hasattr (context_class , 'contexts' ):
238+ context_class .contexts = threading .local ()
239+
240+ contexts = context_class .contexts
241+
242+ if not hasattr (contexts , 'stack' ):
243+ contexts .stack = []
244+ return contexts .stack
245+
246+ # the following complex property accessor is necessary because the
247+ # context_class may not have been created at the point it is
248+ # specified, so the context_class may be a class *name* rather
249+ # than a class.
226250 @property
227251 def context_class (cls ) -> Type :
228252 def resolve_type (c : Union [Type , str ]) -> Type :
@@ -239,11 +263,13 @@ def resolve_type(c: Union[Type, str]) -> Type:
239263 (cls .__name__ , cls ._context_class ))
240264 return cls ._context_class
241265
266+ # Inherit context class from parent
242267 def __init_subclass__ (cls , ** kwargs ):
243268 super ().__init_subclass__ (** kwargs )
244269 cls .context_class = super ().context_class
245270
246271 # Initialize object in its own context...
272+ # Merged from InitContextMeta in the original.
247273 def __call__ (cls , * args , ** kwargs ):
248274 instance = cls .__new__ (cls , * args , ** kwargs )
249275 with instance : # appends context
0 commit comments