emacspy.el 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. ;;; emacspy.el --- emacspy enables you to use python modules from Elisp or program Emacs in Python instead of ELisp. -*- lexical-binding: t; -*-
  2. ;; Copyright (C) 2024 zielmicha, 813gan
  3. ;; URL: https://github.com/zielmicha/emacspy
  4. ;; Author: zielmicha, 813gan
  5. ;; Keywords: python
  6. ;; Version: 1.0
  7. ;; Package: emacspy
  8. ;; Package-Requires: ((python-environment))
  9. ;;; Commentary:
  10. ;; # emacspy.el
  11. ;;; Code:
  12. (require 'cl-lib)
  13. (eval-when-compile (require 'subr-x))
  14. (require 'python-environment)
  15. (defvar emacspy-python-name "python3"
  16. "Name of python executable.")
  17. (defun emacspy--hash-table-to-lists (hash)
  18. "Utility function that convert `HASH' into (list keys values)."
  19. (let ((keys (hash-table-keys hash))
  20. (values nil))
  21. (setq values
  22. (mapcar (lambda (key) (gethash key hash)) keys))
  23. (list keys values) ))
  24. (defun emacspy--lists-to-hash-table (keys values)
  25. "Utility function that convert lists of `KEYS' and `VALUES' to hash."
  26. (let ((hash (make-hash-table :test 'equal)))
  27. (cl-mapcar (lambda (k v) (puthash k v hash))
  28. keys values)
  29. hash))
  30. (require 'emacspy-module)
  31. (defun python-environment-packages-paths (&optional root)
  32. (let* ((ret nil)
  33. (dirs (directory-files (python-environment-lib "" root)
  34. 't directory-files-no-dot-files-regexp 't)))
  35. (dolist (dir dirs ret)
  36. (push (format "%s/site-packages" dir) ret)) ))
  37. ;; emacspy public API
  38. (defun emacspy-alist2hash (alist)
  39. "Convert `ALIST' to hash for easy creation of Python dicts."
  40. (let ((hash (make-hash-table) ))
  41. (dolist (kv alist)
  42. (puthash (car kv) (cdr kv) hash))
  43. hash))
  44. (defun emacspy-python-pip-install (subinterpreter packages &optional virtualenv)
  45. "Install packages for `SUBINTERPRETER' from string list `PACKAGES'."
  46. (let* ((packages-shell-arg (mapcar 'shell-quote-argument packages)))
  47. (python-environment-run-block (append '("pip" "install" "--") packages-shell-arg)
  48. subinterpreter virtualenv)))
  49. (defun emacspy-get-base-prefix (subinterpreter)
  50. "Get base prefix for `SUBINTERPRETER'.
  51. https://docs.python.org/3/library/sys.html#sys.base_prefix"
  52. (emacspy-import subinterpreter "sys" "__emacspy_sys")
  53. (emacspy-get-object-attr subinterpreter "__emacspy_sys" "base_prefix"))
  54. (defun emacspy-get-base-prefix-bin (subinterpreter)
  55. (python-environment-bin "" (emacspy-get-base-prefix subinterpreter)))
  56. (defun emacspy-python-environment-make (subinterpreter &optional packages virtualenv)
  57. "Create venv for `SUBINTERPRETER' and install modules from string list `PACKAGES'."
  58. (py-make-interpreter subinterpreter)
  59. (let ((python-environment-virtualenv
  60. (list (expand-file-name emacspy-python-name (emacspy-get-base-prefix-bin subinterpreter)) "-m" "venv")))
  61. (python-environment-make-block subinterpreter virtualenv)
  62. (when packages
  63. (emacspy-python-pip-install subinterpreter packages virtualenv) )))
  64. (defun emacspy-setup-subinterpreter (subinterpreter &rest pythonpaths)
  65. "Create Python subinterpreter called `SUBINTERPRETER' and add strings `PYTHONPATHS' to `sys.path'."
  66. (py-make-interpreter subinterpreter)
  67. (emacspy-import subinterpreter "sys")
  68. (py-get-object-attr subinterpreter "sys" "path" "__emacspy_syspath")
  69. (when (python-environment-exists-p subinterpreter)
  70. (dolist (path (python-environment-packages-paths subinterpreter))
  71. (emacspy-call-method subinterpreter "__emacspy_syspath" "append" nil path)))
  72. (dolist (path pythonpaths)
  73. (emacspy-call-method subinterpreter "__emacspy_syspath" "append" nil path)) )
  74. (defun emacspy--import-py-multipe-objs (subinterpreter module objs)
  75. (let ((out (list 'progn))
  76. (temp_mod_symobol (format "_emacspy_import_%s" module)))
  77. (push `(emacspy-import ,subinterpreter ,module ,temp_mod_symobol) out)
  78. (dolist (obj objs)
  79. (push (list 'emacspy-get-object-attr subinterpreter temp_mod_symobol obj obj) out) )
  80. (nreverse out)))
  81. (defun emacspy--import-py-get-import (subinterpreter import-def)
  82. "Make import sexp from `IMPORT-DEF'. This is utility function for emacspy-import-py."
  83. (let ((module) (objs) (as)
  84. (reading-module) (reading-obj)
  85. (head) (tail import-def))
  86. (while tail
  87. (setq head (car tail)
  88. tail (cdr tail))
  89. (cond
  90. ((eq head 'from)
  91. (setq reading-module 't) )
  92. ((eq head 'import)
  93. (setq reading-obj 't
  94. reading-module nil))
  95. ;;;;;;;;;
  96. (reading-module
  97. (setq module head
  98. reading-module nil))
  99. (reading-obj
  100. (push head objs)) ))
  101. (emacspy--import-py-multipe-objs subinterpreter module objs) ))
  102. (defmacro emacspy-import-py (subinterpreter &rest import-defs)
  103. "Execute imports inside `SUBINTERPRETER' according to `IMPORT-DEFS'."
  104. (cons 'progn (mapcar (apply-partially 'emacspy--import-py-get-import subinterpreter)
  105. import-defs)))
  106. (defun emacspy-get-object-attr (subinterpreter obj_name attr_name &optional target_name)
  107. (py-get-object-attr subinterpreter obj_name attr_name target_name))
  108. (defun emacspy-import (subinterpreter module &optional as)
  109. "Import modules `MODULE' in `SUBINTERPRETER' and optionally bind it as `AS'."
  110. (if as
  111. (py-import subinterpreter module as)
  112. (py-import subinterpreter module)))
  113. (defun emacspy-call (subinterpreter obj_name method_name &optional as &rest args)
  114. (apply 'emacspy--call subinterpreter obj_name method_name as args nil))
  115. (provide 'emacspy)
  116. ;;; emacspy.el ends here