-
Notifications
You must be signed in to change notification settings - Fork 260
/
elpy-profile.el
114 lines (94 loc) · 4.57 KB
/
elpy-profile.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
;;; elpy-profile.el --- Profiling capabilitiss for elpy
;; Copyright (C) 2013-2019 Jorgen Schaefer
;; Author: Gaby Launay <[email protected]>
;; URL: https://github.com/jorgenschaefer/elpy
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License
;; as published by the Free Software Foundation; either version 3
;; of the License, or (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This file serves as an extension to elpy by adding profiling capabilities
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;
;;; User customization
(defcustom elpy-profile-visualizer "snakeviz"
"Visualizer for elpy profile results."
:type '(choice (const :tag "Snakeviz" "snakeviz")
(const :tag "RunSnakeRun" "runsnake")
(const :tag "pyprof2calltree" "pyprof2calltree -k -i")
(string :tag "Other"))
:group 'elpy)
;;;;;;;;;;;;;;;;;;;;;;
;;; Helper Functions
(defun elpy-profile--display-profiling (file)
"Display the profile result FILE using `elpy-profile-visualizer'."
(let ((exec (car (split-string elpy-profile-visualizer " " t)))
(args (append (cdr (split-string elpy-profile-visualizer " " t)) (list file))))
(if (executable-find exec)
(apply 'call-process exec nil 0 nil args)
(message "Elpy profile visualizer '%s' not found" exec))))
(defun elpy-profile--sentinel (process string)
"Elpy profile sentinel."
(let ((filename (file-name-nondirectory (process-get process 'file)))
(prof-file (process-get process 'prof-file))
(dont-display (process-get process 'dont-display)))
(with-current-buffer "*elpy-profile-log*"
(view-mode))
(if (not (string-equal string "finished\n"))
(progn
(message "[%s] Profiling failed" filename)
(display-buffer "*elpy-profile-log*"))
(message "[%s] Profiling succeeded" filename)
(unless dont-display
(elpy-profile--display-profiling prof-file)))))
(defun elpy-profile--file (file &optional in-dir dont-display)
"Profile asynchronously FILE and display the result using
`elpy-profile-visualizer'.
If IN-DIR is non nil, profile result is saved in the same
directory as the script.
If DONT-DISPLAY is non nil, don't display the profile results."
(ignore-errors (kill-buffer "*elpy-profile-log*"))
(let* ((prof-file (if in-dir
(concat (file-name-sans-extension file) ".profile")
(concat (make-temp-file "elpy-profile-" nil ".profile"))))
(proc-name (format "elpy-profile-%s" file))
(proc-cmd (list elpy-rpc-python-command "-m" "cProfile" "-o" prof-file file))
(proc (make-process :name proc-name
:buffer "*elpy-profile-log*"
:sentinel 'elpy-profile--sentinel
:command proc-cmd)))
(message "[%s] Profiling ..." (file-name-nondirectory file))
(process-put proc 'prof-file prof-file)
(process-put proc 'file file)
(process-put proc 'dont-display dont-display)
prof-file))
;;;;;;;;;;;;;;;;;;;;;;
;;; User Functions
(defun elpy-profile-buffer-or-region (&optional in-dir dont-display)
"Profile asynchronously the active region or the current buffer
and display the result using `elpy-profile-visualizer'.
If IN-DIR is non nil, profile result is saved in the same
directory as the script.
If DONT-DISPLAY is non nil, don't display the profile results."
(interactive "P")
(let* ((file-name (buffer-name))
(file-dir (file-name-directory (buffer-file-name)))
(beg (if (region-active-p) (region-beginning) (point-min)))
(end (if (region-active-p) (region-end) (point-max)))
(tmp-file-prefix (if (region-active-p) "_region_" ""))
(tmp-file (if in-dir
(concat file-dir "/" tmp-file-prefix file-name)
(concat (make-temp-file "elpy-profile-" t) "/" tmp-file-prefix file-name)))
(region (python-shell-buffer-substring beg end)))
(with-temp-buffer
(insert region)
(write-region (point-min) (point-max) tmp-file nil t))
(elpy-profile--file tmp-file t dont-display)))
(provide 'elpy-profile)
;;; elpy-profile.el ends here