types.py 4.2 KB

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