You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

manager.py 5.6 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. # Copyright (c) OpenMMLab. All rights reserved.
  2. import inspect
  3. import threading
  4. import warnings
  5. from collections import OrderedDict
  6. from typing import Type, TypeVar
  7. _lock = threading.RLock()
  8. T = TypeVar("T")
  9. def _accquire_lock() -> None:
  10. """Acquire the module-level lock for serializing access to shared data.
  11. This should be released with _release_lock().
  12. """
  13. if _lock:
  14. _lock.acquire()
  15. def _release_lock() -> None:
  16. """Release the module-level lock acquired by calling _accquire_lock()."""
  17. if _lock:
  18. _lock.release()
  19. class ManagerMeta(type):
  20. """The metaclass for global accessible class.
  21. The subclasses inheriting from ``ManagerMeta`` will manage their
  22. own ``_instance_dict`` and root instances. The constructors of subclasses
  23. must contain the ``name`` argument.
  24. Examples:
  25. >>> class SubClass1(metaclass=ManagerMeta):
  26. >>> def __init__(self, *args, **kwargs):
  27. >>> pass
  28. AssertionError: <class '__main__.SubClass1'>.__init__ must have the
  29. name argument.
  30. >>> class SubClass2(metaclass=ManagerMeta):
  31. >>> def __init__(self, name):
  32. >>> pass
  33. >>> # valid format.
  34. """
  35. def __init__(cls, *args):
  36. cls._instance_dict = OrderedDict()
  37. params = inspect.getfullargspec(cls)
  38. params_names = params[0] if params[0] else []
  39. assert "name" in params_names, f"{cls} must have the `name` argument"
  40. super().__init__(*args)
  41. class ManagerMixin(metaclass=ManagerMeta):
  42. """``ManagerMixin`` is the base class for classes that have global access
  43. requirements.
  44. The subclasses inheriting from ``ManagerMixin`` can get their
  45. global instances.
  46. Examples:
  47. >>> class GlobalAccessible(ManagerMixin):
  48. >>> def __init__(self, name=''):
  49. >>> super().__init__(name)
  50. >>>
  51. >>> GlobalAccessible.get_instance('name')
  52. >>> instance_1 = GlobalAccessible.get_instance('name')
  53. >>> instance_2 = GlobalAccessible.get_instance('name')
  54. >>> assert id(instance_1) == id(instance_2)
  55. Args:
  56. name (str): Name of the instance. Defaults to ''.
  57. """
  58. def __init__(self, name: str = "", **kwargs):
  59. assert isinstance(name, str) and name, "name argument must be an non-empty string."
  60. self._instance_name = name
  61. @classmethod
  62. def get_instance(cls: Type[T], name: str, **kwargs) -> T:
  63. """Get subclass instance by name if the name exists.
  64. If corresponding name instance has not been created, ``get_instance``
  65. will create an instance, otherwise ``get_instance`` will return the
  66. corresponding instance.
  67. Examples
  68. >>> instance1 = GlobalAccessible.get_instance('name1')
  69. >>> # Create name1 instance.
  70. >>> instance.instance_name
  71. name1
  72. >>> instance2 = GlobalAccessible.get_instance('name1')
  73. >>> # Get name1 instance.
  74. >>> assert id(instance1) == id(instance2)
  75. Args:
  76. name (str): Name of instance. Defaults to ''.
  77. Returns:
  78. object: Corresponding name instance, the latest instance, or root
  79. instance.
  80. """
  81. _accquire_lock()
  82. assert isinstance(name, str), f"type of name should be str, but got {type(cls)}"
  83. instance_dict = cls._instance_dict # type: ignore
  84. # Get the instance by name.
  85. if name not in instance_dict:
  86. instance = cls(name=name, **kwargs) # type: ignore
  87. instance_dict[name] = instance # type: ignore
  88. elif kwargs:
  89. warnings.warn(
  90. f"{cls} instance named of {name} has been created, "
  91. "the method `get_instance` should not accept any other "
  92. "arguments"
  93. )
  94. # Get latest instantiated instance or root instance.
  95. _release_lock()
  96. return instance_dict[name]
  97. @classmethod
  98. def get_current_instance(cls):
  99. """Get latest created instance.
  100. Before calling ``get_current_instance``, The subclass must have called
  101. ``get_instance(xxx)`` at least once.
  102. Examples
  103. >>> instance = GlobalAccessible.get_current_instance()
  104. AssertionError: At least one of name and current needs to be set
  105. >>> instance = GlobalAccessible.get_instance('name1')
  106. >>> instance.instance_name
  107. name1
  108. >>> instance = GlobalAccessible.get_current_instance()
  109. >>> instance.instance_name
  110. name1
  111. Returns:
  112. object: Latest created instance.
  113. """
  114. _accquire_lock()
  115. if not cls._instance_dict:
  116. raise RuntimeError(
  117. f"Before calling {cls.__name__}.get_current_instance(), you "
  118. "should call get_instance(name=xxx) at least once."
  119. )
  120. name = next(iter(reversed(cls._instance_dict)))
  121. _release_lock()
  122. return cls._instance_dict[name]
  123. @classmethod
  124. def check_instance_created(cls, name: str) -> bool:
  125. """Check whether the name corresponding instance exists.
  126. Args:
  127. name (str): Name of instance.
  128. Returns:
  129. bool: Whether the name corresponding instance exists.
  130. """
  131. return name in cls._instance_dict
  132. @property
  133. def instance_name(self) -> str:
  134. """Get the name of instance.
  135. Returns:
  136. str: Name of instance.
  137. """
  138. return self._instance_name

An efficient Python toolkit for Abductive Learning (ABL), a novel paradigm that integrates machine learning and logical reasoning in a unified framework.