Personal emacs config
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.

229 lines
7.1 KiB

  1. # lispy-python.py --- lispy support for Python.
  2. # Copyright (C) 2016 Oleh Krehel
  3. # This file is not part of GNU Emacs
  4. # This file is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 3, or (at your option)
  7. # any later version.
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. # For a full copy of the GNU General Public License
  13. # see <http://www.gnu.org/licenses/>.
  14. #* Imports
  15. import ast
  16. import sys
  17. import inspect
  18. import re
  19. import platform
  20. import shlex
  21. import types
  22. try:
  23. import jedi
  24. except:
  25. print("failed to load jedi")
  26. #* Classes
  27. class Stack:
  28. def __init__(self, tb):
  29. self.stack = []
  30. while tb:
  31. self.stack.append((
  32. tb.tb_frame.f_code.co_filename,
  33. tb.tb_frame.f_code.co_firstlineno,
  34. tb.tb_frame))
  35. tb = tb.tb_next
  36. self.stack_top = len(self.stack) - 1
  37. self.set_frame(self.stack_top)
  38. def frame_string(self, i):
  39. f = self.stack[i][2]
  40. res = " File \"%s\", line %d, Frame [%d/%d]:" % \
  41. (f.f_code.co_filename, f.f_lineno, i, self.stack_top)
  42. return res
  43. def __repr__(self):
  44. frames = []
  45. for i in range(self.stack_top + 1):
  46. s = self.frame_string(i)
  47. if i == self.stack_idx:
  48. s += "*"
  49. frames.append(s)
  50. return "\n".join(frames)
  51. def set_frame(self, i):
  52. f = self.stack[i][2]
  53. self.stack_idx = i
  54. tf = top_level()
  55. tf.f_globals["lnames"] = f.f_locals.keys()
  56. for (k, v) in f.f_locals.items():
  57. tf.f_globals[k] = v
  58. for (k, v) in f.f_globals.items():
  59. tf.f_globals[k] = v
  60. print(self.frame_string(self.stack_idx))
  61. def up(self, delta = 1):
  62. if self.stack_idx <= 0:
  63. print("top frame already")
  64. print(self.frame_string(self.stack_idx))
  65. else:
  66. self.stack_idx = max(self.stack_idx - delta, 0)
  67. self.set_frame(self.stack_idx)
  68. def down(self, delta = 1):
  69. if self.stack_idx >= self.stack_top:
  70. print("bottom frame already")
  71. print(self.frame_string(self.stack_idx))
  72. else:
  73. self.stack_idx = min(self.stack_idx + delta, self.stack_top)
  74. self.set_frame(self.stack_idx)
  75. class Autocall:
  76. def __init__(self, f):
  77. self.f = f
  78. def __repr__(self):
  79. self.f()
  80. return ""
  81. #* Functions
  82. def arglist_retrieve_java(method):
  83. name = method.__name__
  84. if hasattr(method, "argslist"):
  85. # uses only the first args list...
  86. args = [x.__name__ for x in method.argslist[0].args]
  87. else:
  88. methods = eval("method.__self__.class.getDeclaredMethods()")
  89. methods_by_name = [m for m in methods if m.getName() == name]
  90. assert len(methods_by_name) == 1, "expected only a single method by name %s" % name
  91. meta = methods_by_name[0]
  92. args = [str(par.getType().__name__ + " " + par.getName()) for par in meta.getParameters()]
  93. return inspect.ArgSpec(args, None, None, None)
  94. def arglist_retrieve(sym):
  95. try:
  96. if hasattr(inspect, "getfullargspec"):
  97. res = inspect.getfullargspec(sym)
  98. return inspect.ArgSpec(args = res.args,
  99. varargs = res.varargs,
  100. defaults = res.defaults,
  101. keywords = res.kwonlydefaults)
  102. else:
  103. return inspect.getargspec(sym)
  104. except TypeError as er:
  105. if(re.search("is not a Python function$", er.message)
  106. and platform.system() == "Java"):
  107. if inspect.isclass(sym):
  108. return arglist_retrieve(sym.__init__)
  109. elif hasattr(sym, "argslist") or \
  110. hasattr(sym, "__self__") and hasattr(sym.__self__, "class"):
  111. return arglist_retrieve_java(sym)
  112. else:
  113. print(er.message)
  114. else:
  115. print(er.message)
  116. def format_arg(arg_pair):
  117. name, default_value = arg_pair
  118. if default_value:
  119. return name + " = " + default_value
  120. else:
  121. return name
  122. def delete(element, lst):
  123. return [x for x in lst if x != element]
  124. def mapcar(func, lst):
  125. """Compatibility function for Python3.
  126. In Python2 `map' returns a list, as expected. But in Python3
  127. `map' returns a map object that can be converted to a list.
  128. """
  129. return list(map(func, lst))
  130. def arglist(sym):
  131. arg_info = arglist_retrieve(sym)
  132. if "self" in arg_info.args:
  133. arg_info.args.remove("self")
  134. if arg_info.defaults:
  135. defaults = [None] *(len(arg_info.args) - len(arg_info.defaults)) + \
  136. mapcar(repr, arg_info.defaults)
  137. args = mapcar(format_arg, zip(arg_info.args, defaults))
  138. else:
  139. args = arg_info.args
  140. if arg_info.varargs:
  141. args += arg_info.varargs
  142. if arg_info.keywords:
  143. if type(arg_info.keywords) is dict:
  144. for k, v in arg_info.keywords.items():
  145. args.append("%s = %s" %(k, v))
  146. else:
  147. args.append("**" + arg_info.keywords)
  148. return args
  149. def arglist_jedi(line, column, filename):
  150. script = jedi.Script(None, line, column, filename)
  151. defs = script.goto_definitions()
  152. if len(defs) == 0:
  153. raise TypeError("0 definitions found")
  154. elif len(defs) > 1:
  155. raise TypeError(">1 definitions found")
  156. else:
  157. return delete('', mapcar(lambda x: str(x.name), defs[0].params))
  158. def is_assignment(code):
  159. ops = ast.parse(code).body
  160. return len(ops) == 1 and type(ops[0]) is ast.Assign
  161. def top_level():
  162. """Return the topmost frame."""
  163. f = sys._getframe()
  164. while f.f_back:
  165. f = f.f_back
  166. return f
  167. def list_step(varname, lst):
  168. f_globals = top_level().f_globals
  169. try:
  170. val = f_globals[varname]
  171. i =(lst.index(val) + 1) % len(lst)
  172. except:
  173. i = 0
  174. val = lst[i]
  175. print("[{}/{}]".format(i + 1, len(lst)))
  176. f_globals[varname] = val
  177. return val
  178. def argv(cmd):
  179. sys.argv = shlex.split(cmd)
  180. def find_global_vars(class_name):
  181. """Find global variables of type CLASS_NAME."""
  182. return [(k, v) for (k, v) in top_level().f_globals.items() if v.__class__.__name__ == class_name]
  183. def rebind(cls_name, fun_name):
  184. """Rebind FUN_NAME in all top level instances of CLS_NAME.
  185. Modifying a method is two-step:
  186. 1. eval the method as if it's a free top-level function,
  187. 2. modify all instances of the class with an adapter to this top-level function.
  188. """
  189. for (n, v) in find_global_vars(cls_name):
  190. print("rebind:", n)
  191. top_level().f_globals[n].__dict__[fun_name] = types.MethodType(top_level().f_globals[fun_name], v)
  192. def pm():
  193. """Post mortem: recover the locals and globals from the last traceback."""
  194. stack = Stack(sys.last_traceback)
  195. tl = top_level()
  196. tl.f_globals["up"] = Autocall(stack.up)
  197. tl.f_globals["dn"] = Autocall(stack.down)
  198. globals()["stack"] = stack