import threading
from typing import Any
from typing import Callable
from typing import MutableMapping
import weakref
class NameRegistry ( object ) :
""" Generates and return an object, keeping it as a
singleton for a certain identifier for as long as its
strongly referenced .
e . g . : :
class MyFoo ( object ) :
" some important object. "
def __init__ ( self , identifier ) :
self . identifier = identifier
registry = NameRegistry ( MyFoo )
# thread 1:
my_foo = registry . get ( " foo1 " )
# thread 2
my_foo = registry . get ( " foo1 " )
Above , ` ` my_foo ` ` in both thread #1 and #2 will
be * the same object * . The constructor for
` ` MyFoo ` ` will be called once , passing the
identifier ` ` foo1 ` ` as the argument .
When thread 1 and thread 2 both complete or
otherwise delete references to ` ` my_foo ` ` , the
object is * removed * from the : class : ` . NameRegistry ` as
a result of Python garbage collection .
: param creator : A function that will create a new
value , given the identifier passed to the : meth : ` . NameRegistry . get `
method .
"""
_mutex = threading . RLock ( )
def __init__ ( self , creator : Callable [ . . . , Any ] ) :
""" Create a new :class:`.NameRegistry`. """
self . _values : MutableMapping [ str , Any ] = weakref . WeakValueDictionary ( )
self . _mutex = threading . RLock ( )
self . creator = creator
def get ( self , identifier : str , * args : Any , * * kw : Any ) - > Any :
r """ Get and possibly create the value.
: param identifier : Hash key for the value .
If the creation function is called , this identifier
will also be passed to the creation function .
: param \* args , \* * kw : Additional arguments which will
also be passed to the creation function if it is
called .
"""
try :
if identifier in self . _values :
return self . _values [ identifier ]
else :
return self . _sync_get ( identifier , * args , * * kw )
except KeyError :
return self . _sync_get ( identifier , * args , * * kw )
def _sync_get ( self , identifier : str , * args : Any , * * kw : Any ) - > Any :
self . _mutex . acquire ( )
try :
try :
if identifier in self . _values :
return self . _values [ identifier ]
else :
self . _values [ identifier ] = value = self . creator (
identifier , * args , * * kw
)
return value
except KeyError :
self . _values [ identifier ] = value = self . creator (
identifier , * args , * * kw
)
return value
finally :
self . _mutex . release ( )