Generates random Python functions using Markov chains
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

107 lines
3.4 KiB

  1. import opcode
  2. import sys
  3. import func_smash
  4. OP_HASLOAD = ("LOAD_FAST", "LOAD_CONST", "LOAD_GLOBAL")
  5. OP_HASBINARY = {"BINARY_ADD": "+", "BINARY_SUBTRACT": "-",
  6. "BINARY_MULTIPLY": "*", "BINARY_DIVIDE": "/",
  7. "BINARY_POWER": "**", "BINARY_MODULO": "%"}
  8. OP_HASBUILD = ("BUILD_TUPLE", "BUILD_LIST", "BUILD_MAP", "BUILD_SET")
  9. def prettify_function(func, indent=0):
  10. args = _get_func_args(func)
  11. _print(indent, "def {0}({1}):".format(func.func_name, args))
  12. prettify_code(func.__code__, indent=indent+4)
  13. def prettify_code(codeobj, indent=0):
  14. codes = []
  15. codestring = codeobj.co_code
  16. length = len(codestring)
  17. i = 0
  18. while i < length:
  19. code = ord(codestring[i])
  20. i += 1
  21. if code >= opcode.HAVE_ARGUMENT:
  22. arg = func_smash._get_argument(codeobj, codestring, i, code)
  23. i += 2
  24. codes.append((code, arg))
  25. else:
  26. codes.append((code, None))
  27. _print_codestring(codes, indent)
  28. def _print_codestring(codes, indent):
  29. stack = []
  30. print_buffer = []
  31. for instruction in codes:
  32. code, arg = instruction
  33. opname = opcode.opname[code]
  34. _print(indent, opname, arg, "; stack ==", stack, debug=True)
  35. if opname in OP_HASLOAD:
  36. stack.append(arg)
  37. elif opname in OP_HASBINARY:
  38. tos, tos1 = str(stack.pop()), str(stack.pop())
  39. stack.append(" ".join((tos1, OP_HASBINARY[opname], tos)))
  40. elif opname in OP_HASBUILD:
  41. args = []
  42. for i in xrange(arg):
  43. args.append(str(stack.pop()))
  44. if opname == "BUILD_TUPLE":
  45. stack.append("(" + ", ".join(args) + ")")
  46. elif opname == "STORE_FAST":
  47. _print(indent, arg, "=", stack.pop())
  48. elif opname == "POP_TOP":
  49. _print(indent, stack.pop())
  50. elif opname == "CALL_FUNCTION":
  51. numargs, numkwargs = arg
  52. args = []
  53. for i in xrange(numkwargs):
  54. value = str(stack.pop())
  55. key = str(stack.pop())
  56. args.append("=".join((key, value)))
  57. for i in xrange(numargs):
  58. args.append(str(stack.pop()))
  59. args.reverse()
  60. funcname = stack.pop()
  61. stack.append("{0}({1})".format(funcname, ", ".join(args)))
  62. elif opname == "PRINT_ITEM":
  63. print_buffer.append(stack.pop())
  64. elif opname == "PRINT_NEWLINE":
  65. _print(indent, "print", ", ".join(print_buffer))
  66. print_buffer = []
  67. elif opname == "RETURN_VALUE":
  68. _print(indent, "return", stack.pop())
  69. else:
  70. raise NotImplementedError(opname, arg, stack)
  71. def _get_func_args(func):
  72. codeobj = func.__code__
  73. count = codeobj.co_argcount
  74. if count == 0:
  75. return ""
  76. return ", ".join([arg for arg in codeobj.co_varnames[:count]])
  77. def _print(indentation, *args, **kwargs):
  78. argstring = " ".join([str(arg) for arg in args if str(arg)])
  79. if kwargs.get("debug"):
  80. # Ignore debug messages without -d flag
  81. if len(sys.argv) > 1 and sys.argv[1] == "-d":
  82. print " " * 40 + "#", argstring
  83. return
  84. print " " * indentation + argstring
  85. if __name__ == "__main__":
  86. def f1(a):
  87. b = a + 10 / 7.0
  88. d = long_func(a, b+9, c=42, d=43)
  89. print b, d
  90. v, z
  91. return d % 10
  92. def f2():
  93. pass
  94. prettify_function(f1)
  95. print
  96. prettify_function(f2)