conversions.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. from typing import (
  2. Callable,
  3. Optional,
  4. TypeVar,
  5. Union,
  6. cast,
  7. )
  8. from eth_typing import (
  9. HexStr,
  10. Primitives,
  11. )
  12. from .decorators import (
  13. validate_conversion_arguments,
  14. )
  15. from .encoding import (
  16. big_endian_to_int,
  17. int_to_big_endian,
  18. )
  19. from .hexadecimal import (
  20. add_0x_prefix,
  21. decode_hex,
  22. encode_hex,
  23. is_hexstr,
  24. remove_0x_prefix,
  25. )
  26. from .types import (
  27. is_boolean,
  28. is_integer,
  29. is_string,
  30. )
  31. T = TypeVar("T")
  32. BytesLike = Union[Primitives, bytearray, memoryview]
  33. @validate_conversion_arguments
  34. def to_hex(
  35. primitive: Optional[BytesLike] = None,
  36. hexstr: Optional[HexStr] = None,
  37. text: Optional[str] = None,
  38. ) -> HexStr:
  39. """
  40. Auto converts any supported value into its hex representation.
  41. Trims leading zeros, as defined in:
  42. https://github.com/ethereum/wiki/wiki/JSON-RPC#hex-value-encoding
  43. """
  44. if hexstr is not None:
  45. return add_0x_prefix(HexStr(hexstr.lower()))
  46. if text is not None:
  47. return encode_hex(text.encode("utf-8"))
  48. if is_boolean(primitive):
  49. return HexStr("0x1") if primitive else HexStr("0x0")
  50. if isinstance(primitive, (bytes, bytearray)):
  51. return encode_hex(primitive)
  52. if isinstance(primitive, memoryview):
  53. return encode_hex(bytes(primitive))
  54. elif is_string(primitive):
  55. raise TypeError(
  56. "Unsupported type: The primitive argument must be one of: bytes,"
  57. "bytearray, int or bool and not str"
  58. )
  59. if is_integer(primitive):
  60. return HexStr(hex(cast(int, primitive)))
  61. raise TypeError(
  62. f"Unsupported type: '{repr(type(primitive))}'. Must be one of: bool, str, "
  63. "bytes, bytearray or int."
  64. )
  65. @validate_conversion_arguments
  66. def to_int(
  67. primitive: Optional[BytesLike] = None,
  68. hexstr: Optional[HexStr] = None,
  69. text: Optional[str] = None,
  70. ) -> int:
  71. """
  72. Converts value to its integer representation.
  73. Values are converted this way:
  74. * primitive:
  75. * bytes, bytearray, memoryview: big-endian integer
  76. * bool: True => 1, False => 0
  77. * int: unchanged
  78. * hexstr: interpret hex as integer
  79. * text: interpret as string of digits, like '12' => 12
  80. """
  81. if hexstr is not None:
  82. return int(hexstr, 16)
  83. elif text is not None:
  84. return int(text)
  85. elif isinstance(primitive, (bytes, bytearray)):
  86. return big_endian_to_int(primitive)
  87. elif isinstance(primitive, memoryview):
  88. return big_endian_to_int(bytes(primitive))
  89. elif isinstance(primitive, str):
  90. raise TypeError("Pass in strings with keyword hexstr or text")
  91. elif isinstance(primitive, (int, bool)):
  92. return int(primitive)
  93. else:
  94. raise TypeError(
  95. "Invalid type. Expected one of int/bool/str/bytes/bytearray. Got "
  96. f"{type(primitive)}"
  97. )
  98. @validate_conversion_arguments
  99. def to_bytes(
  100. primitive: Optional[BytesLike] = None,
  101. hexstr: Optional[HexStr] = None,
  102. text: Optional[str] = None,
  103. ) -> bytes:
  104. if is_boolean(primitive):
  105. return b"\x01" if primitive else b"\x00"
  106. elif isinstance(primitive, (bytearray, memoryview)):
  107. return bytes(primitive)
  108. elif isinstance(primitive, bytes):
  109. return primitive
  110. elif is_integer(primitive):
  111. return to_bytes(hexstr=to_hex(primitive))
  112. elif hexstr is not None:
  113. if len(hexstr) % 2:
  114. hexstr = cast(HexStr, "0x0" + remove_0x_prefix(hexstr))
  115. return decode_hex(hexstr)
  116. elif text is not None:
  117. return text.encode("utf-8")
  118. raise TypeError(
  119. "expected a bool, int, byte or bytearray in first arg, "
  120. "or keyword of hexstr or text"
  121. )
  122. @validate_conversion_arguments
  123. def to_text(
  124. primitive: Optional[BytesLike] = None,
  125. hexstr: Optional[HexStr] = None,
  126. text: Optional[str] = None,
  127. ) -> str:
  128. if hexstr is not None:
  129. return to_bytes(hexstr=hexstr).decode("utf-8")
  130. elif text is not None:
  131. return text
  132. elif isinstance(primitive, str):
  133. return to_text(hexstr=primitive)
  134. elif isinstance(primitive, (bytes, bytearray)):
  135. return primitive.decode("utf-8")
  136. elif isinstance(primitive, memoryview):
  137. return bytes(primitive).decode("utf-8")
  138. elif is_integer(primitive):
  139. byte_encoding = int_to_big_endian(cast(int, primitive))
  140. return to_text(byte_encoding)
  141. raise TypeError("Expected an int, bytes, bytearray or hexstr.")
  142. def text_if_str(
  143. to_type: Callable[..., T], text_or_primitive: Union[bytes, int, str]
  144. ) -> T:
  145. """
  146. Convert to a type, assuming that strings can be only unicode text (not a hexstr).
  147. :param to_type function: takes the arguments (primitive, hexstr=hexstr, text=text),
  148. eg~ to_bytes, to_text, to_hex, to_int, etc
  149. :param text_or_primitive bytes, str, int: value to convert
  150. """
  151. if isinstance(text_or_primitive, str):
  152. return to_type(text=text_or_primitive)
  153. else:
  154. return to_type(text_or_primitive)
  155. def hexstr_if_str(
  156. to_type: Callable[..., T], hexstr_or_primitive: Union[bytes, int, str]
  157. ) -> T:
  158. """
  159. Convert to a type, assuming that strings can be only hexstr (not unicode text).
  160. :param to_type function: takes the arguments (primitive, hexstr=hexstr, text=text),
  161. eg~ to_bytes, to_text, to_hex, to_int, etc
  162. :param hexstr_or_primitive bytes, str, int: value to convert
  163. """
  164. if isinstance(hexstr_or_primitive, str):
  165. if remove_0x_prefix(HexStr(hexstr_or_primitive)) and not is_hexstr(
  166. hexstr_or_primitive
  167. ):
  168. raise ValueError(
  169. "when sending a str, it must be a hex string. "
  170. f"Got: {repr(hexstr_or_primitive)}"
  171. )
  172. return to_type(hexstr=hexstr_or_primitive)
  173. else:
  174. return to_type(hexstr_or_primitive)