util.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. # Much of this is borrowed from Python's ``Lib/dis.py``.
  2. from math import copysign
  3. from typing import Dict
  4. def code2num(code, i):
  5. if isinstance(code, str):
  6. return ord(code[i])
  7. else:
  8. return code[i]
  9. def num2code(num):
  10. return num & 0xFF, num >> 8
  11. # The `inspect` module interrogates this dictionary to build its
  12. # list of CO_* constants. It is also used by pretty_flags to
  13. # turn the co_flags field into a human-readable list.
  14. COMPILER_FLAG_NAMES = {
  15. 0x00000001: "OPTIMIZED",
  16. 0x00000002: "NEWLOCALS",
  17. 0x00000004: "VARARGS",
  18. 0x00000008: "VARKEYWORDS",
  19. 0x00000010: "NESTED",
  20. 0x00000020: "GENERATOR",
  21. 0x00000040: "NOFREE",
  22. # These are in Python 3.5 +
  23. 0x00000080: "COROUTINE",
  24. 0x00000100: "ITERABLE_COROUTINE",
  25. # These are in Python 3.6+
  26. 0x00000200: "ASYNC_GENERATOR",
  27. # These are used only in Python 2.x */
  28. 0x00001000: "GENERATOR_ALLOWED",
  29. 0x00002000: "FUTURE_DIVISION",
  30. 0x00004000: "ABSOLUTE_IMPORT",
  31. 0x00008000: "FUTURE_WITH_STATEMENT",
  32. 0x00010000: "FUTURE_PRINT_FUNCTION",
  33. 0x00020000: "FUTURE_UNICODE_LITERALS",
  34. 0x00040000: "FUTURE_BARRY_AS_DBFL",
  35. 0x00080000: "FUTURE_GENERATOR_STOP",
  36. 0x00100000: "FUTURE_ANNOTATIONS",
  37. }
  38. # These are PYPY specific
  39. PYPY_COMPILER_FLAG_NAMES = {
  40. 0x00100000: "PYPY_KILL_DOCSTRING",
  41. 0x00200000: "PYPY_YIELD_INSIDE_TRY",
  42. 0x00000400: "PYPY_ONLY_AST",
  43. 0x00000800: "PYPY_IGNORE_COOKIE",
  44. 0x10000000: "PYPY_ACCEPT_NULL_BYTES",
  45. }
  46. # Invert above dictionary, so we can look up a bit value
  47. # from the "compile" flag name
  48. COMPILER_FLAG_BIT: Dict[str, int] = dict(
  49. (v, k) for (k, v) in COMPILER_FLAG_NAMES.items()
  50. )
  51. # Allow us to access by just name, prefixed with CO., e.g.,
  52. # CO_OPTIMIZED, CO_NOFREE.
  53. globals().update(dict(("CO_" + k, v) for (k, v) in COMPILER_FLAG_BIT.items()))
  54. def co_flags_is_async(co_flags):
  55. """
  56. Return True iff co_flags indicates an async function.
  57. """
  58. return co_flags & (
  59. COMPILER_FLAG_BIT["COROUTINE"]
  60. | COMPILER_FLAG_BIT["ITERABLE_COROUTINE"]
  61. | COMPILER_FLAG_BIT["ASYNC_GENERATOR"]
  62. )
  63. def code_has_star_arg(code) -> bool:
  64. """
  65. Return True iff the code object has a variable positional parameter
  66. (*args-like)
  67. """
  68. return (code.co_flags & 4) != 0
  69. def code_has_star_star_arg(code):
  70. """Return True iff
  71. The code object has a variable keyword parameter (**kwargs-like)."""
  72. return (code.co_flags & 8) != 0
  73. def is_negative_zero(n: float) -> bool:
  74. """Returns true if n is -0.0"""
  75. return n == 0.0 and copysign(1, n) == -1
  76. def better_repr(v: float) -> str:
  77. """Work around Python's not orthogonal and unhelpful repr() for primitive float
  78. and complex."""
  79. if isinstance(v, float):
  80. # Float values 'nan' and 'inf' are not directly
  81. # representable in Python before Python 3.5. In Python 3.5
  82. # it is accessible via a library constant math.inf. We
  83. # will canonicalize representation of these value as
  84. # float('nan') and float('inf')
  85. if str(v) in frozenset(["nan", "-nan", "inf", "-inf"]):
  86. return "float('%s')" % v
  87. elif is_negative_zero(v):
  88. return "-0.0"
  89. return repr(v)
  90. elif isinstance(v, complex):
  91. real = better_repr(v.real)
  92. imag = better_repr(v.imag)
  93. # FIXME: we could probably use repr() in most cases
  94. # sort out when that's possible.
  95. # The below round trips, and Python's repr() isn't.
  96. return "complex(%s, %s)" % (real, imag)
  97. elif isinstance(v, tuple):
  98. if len(v) == 1:
  99. return "(%s,)" % better_repr(v[0])
  100. return "(%s)" % ", ".join(better_repr(i) for i in v)
  101. elif isinstance(v, list):
  102. if len(v) == 1:
  103. return "[%s,]" % better_repr(v[0])
  104. return "[%s]" % ", ".join(better_repr(i) for i in v)
  105. # TODO: elif deal with sets and dicts
  106. else:
  107. return repr(v)