types.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. """Contains custom types and methods to check types."""
  2. from __future__ import annotations
  3. import contextlib
  4. import typing
  5. from typing import Any, Callable, Type, Union, _GenericAlias # type: ignore
  6. from reflex.base import Base
  7. # Union of generic types.
  8. GenericType = Union[Type, _GenericAlias]
  9. # Valid state var types.
  10. PrimitiveType = Union[int, float, bool, str, list, dict, set, tuple]
  11. StateVar = Union[PrimitiveType, Base, None]
  12. StateIterVar = Union[list, set, tuple]
  13. def get_args(alias: _GenericAlias) -> tuple[Type, ...]:
  14. """Get the arguments of a type alias.
  15. Args:
  16. alias: The type alias.
  17. Returns:
  18. The arguments of the type alias.
  19. """
  20. return alias.__args__
  21. def is_generic_alias(cls: GenericType) -> bool:
  22. """Check whether the class is a generic alias.
  23. Args:
  24. cls: The class to check.
  25. Returns:
  26. Whether the class is a generic alias.
  27. """
  28. # For older versions of Python.
  29. if isinstance(cls, _GenericAlias):
  30. return True
  31. with contextlib.suppress(ImportError):
  32. from typing import _SpecialGenericAlias # type: ignore
  33. if isinstance(cls, _SpecialGenericAlias):
  34. return True
  35. # For newer versions of Python.
  36. try:
  37. from types import GenericAlias # type: ignore
  38. return isinstance(cls, GenericAlias)
  39. except ImportError:
  40. return False
  41. def is_union(cls: GenericType) -> bool:
  42. """Check if a class is a Union.
  43. Args:
  44. cls: The class to check.
  45. Returns:
  46. Whether the class is a Union.
  47. """
  48. with contextlib.suppress(ImportError):
  49. from typing import _UnionGenericAlias # type: ignore
  50. return isinstance(cls, _UnionGenericAlias)
  51. return cls.__origin__ == Union if is_generic_alias(cls) else False
  52. def get_base_class(cls: GenericType) -> Type:
  53. """Get the base class of a class.
  54. Args:
  55. cls: The class.
  56. Returns:
  57. The base class of the class.
  58. """
  59. if is_union(cls):
  60. return tuple(get_base_class(arg) for arg in get_args(cls))
  61. return get_base_class(cls.__origin__) if is_generic_alias(cls) else cls
  62. def _issubclass(cls: GenericType, cls_check: GenericType) -> bool:
  63. """Check if a class is a subclass of another class.
  64. Args:
  65. cls: The class to check.
  66. cls_check: The class to check against.
  67. Returns:
  68. Whether the class is a subclass of the other class.
  69. """
  70. # Special check for Any.
  71. if cls_check == Any:
  72. return True
  73. if cls in [Any, Callable]:
  74. return False
  75. # Get the base classes.
  76. cls_base = get_base_class(cls)
  77. cls_check_base = get_base_class(cls_check)
  78. # The class we're checking should not be a union.
  79. if isinstance(cls_base, tuple):
  80. return False
  81. # Check if the types match.
  82. return cls_check_base == Any or issubclass(cls_base, cls_check_base)
  83. def _isinstance(obj: Any, cls: GenericType) -> bool:
  84. """Check if an object is an instance of a class.
  85. Args:
  86. obj: The object to check.
  87. cls: The class to check against.
  88. Returns:
  89. Whether the object is an instance of the class.
  90. """
  91. return isinstance(obj, get_base_class(cls))
  92. def is_dataframe(value: Type) -> bool:
  93. """Check if the given value is a dataframe.
  94. Args:
  95. value: The value to check.
  96. Returns:
  97. Whether the value is a dataframe.
  98. """
  99. if is_generic_alias(value) or value == typing.Any:
  100. return False
  101. return value.__name__ == "DataFrame"
  102. def is_image(value: Type) -> bool:
  103. """Check if the given value is a pillow image. By checking if the value subclasses PIL.
  104. Args:
  105. value: The value to check.
  106. Returns:
  107. Whether the value is a pillow image.
  108. """
  109. if is_generic_alias(value) or value == typing.Any:
  110. return False
  111. return "PIL" in value.__module__
  112. def is_figure(value: Type) -> bool:
  113. """Check if the given value is a figure.
  114. Args:
  115. value: The value to check.
  116. Returns:
  117. Whether the value is a figure.
  118. """
  119. return value.__name__ == "Figure"
  120. def is_valid_var_type(var: Type) -> bool:
  121. """Check if the given value is a valid prop type.
  122. Args:
  123. var: The value to check.
  124. Returns:
  125. Whether the value is a valid prop type.
  126. """
  127. return (
  128. _issubclass(var, StateVar)
  129. or is_dataframe(var)
  130. or is_figure(var)
  131. or is_image(var)
  132. )
  133. def is_backend_variable(name: str) -> bool:
  134. """Check if this variable name correspond to a backend variable.
  135. Args:
  136. name: The name of the variable to check
  137. Returns:
  138. bool: The result of the check
  139. """
  140. return name.startswith("_") and not name.startswith("__")
  141. # Store this here for performance.
  142. StateBases = get_base_class(StateVar)
  143. StateIterBases = get_base_class(StateIterVar)