@@ -170,20 +170,22 @@ class ContextMeta(type):
170
170
"""Functionality for objects that put themselves in a context using
171
171
the `with` statement.
172
172
"""
173
- _context_class = None # type: Union[Type, str]
174
173
175
174
def __new__ (cls , name , bases , dct , ** kargs ):
175
+ # this serves only to strip off keyword args, per the warning from
176
+ # StackExchange:
176
177
# DO NOT send "**kargs" to "type.__new__". It won't catch them and
177
178
# 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)
180
179
return super ().__new__ (cls , name , bases , dct )
181
180
181
+ # FIXME: is there a more elegant way to automatically add methods to the class that
182
+ # are instance methods instead of class methods?
182
183
def __init__ (cls , name , bases , nmspc , context_class : Optional [Type ]= None , ** kwargs ):
184
+ """Add ``__enter__`` and ``__exit__`` methods to the new class automatically."""
183
185
if context_class is not None :
184
186
cls ._context_class = context_class
185
187
super ().__init__ (name , bases , nmspc )
186
- cls . contexts = threading . local ()
188
+
187
189
def __enter__ (self ):
188
190
self .__class__ .context_class .get_contexts ().append (self )
189
191
# self._theano_config is set in Model.__new__
@@ -200,9 +202,11 @@ def __exit__(self, typ, value, traceback):
200
202
cls .__enter__ = __enter__
201
203
cls .__exit__ = __exit__
202
204
205
+
203
206
def get_context (cls , error_if_none = True ) -> Optional [T ]:
204
207
"""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``."""
206
210
idx = - 1
207
211
while True :
208
212
try :
@@ -217,12 +221,32 @@ def get_context(cls, error_if_none=True) -> Optional[T]:
217
221
idx = idx - 1
218
222
219
223
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
221
233
# 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.
226
250
@property
227
251
def context_class (cls ) -> Type :
228
252
def resolve_type (c : Union [Type , str ]) -> Type :
@@ -239,11 +263,13 @@ def resolve_type(c: Union[Type, str]) -> Type:
239
263
(cls .__name__ , cls ._context_class ))
240
264
return cls ._context_class
241
265
266
+ # Inherit context class from parent
242
267
def __init_subclass__ (cls , ** kwargs ):
243
268
super ().__init_subclass__ (** kwargs )
244
269
cls .context_class = super ().context_class
245
270
246
271
# Initialize object in its own context...
272
+ # Merged from InitContextMeta in the original.
247
273
def __call__ (cls , * args , ** kwargs ):
248
274
instance = cls .__new__ (cls , * args , ** kwargs )
249
275
with instance : # appends context
0 commit comments