You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
89 lines
2.7 KiB
89 lines
2.7 KiB
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()
|