emacs `python-shell-send-defun` skips first line in buffer - python

There appears to be a pretty significant bug in C-M-x, a.k.a. python-shell-send-defun in the builtin python mode
start emacs
create a new file, let's call it test.py
C-c C-p to run-python
in the test.py buffer, type some simple function, like
def f(x):
return x+1
C-M-x for python-shell-send-defun
we get:
return x+1
SyntaxError: 'return' outside function
So what's happening here is that python-shell-send-defun is only sending one line, not both lines as we would expect it to (the corresponding command in emacs lisp major mode for instance works correctly in sending the entire multiline defun prior to the cursor). What's going on here? I tested this in both emacs 25 gui version in Mac OS as well as emacs 24 in Ubuntu. Both exhibit the same error.
Is this a bug, or am I misunderstanding what python-shell-send-defun actually does?
Update: with some further testing, it appears that this error only occurs for functions that start on the very first line. If we insert a blank line right above def f(x):, C-M-x works perfectly.

Yes, this looks like a bug and should be reported as one. That function should probably be rewritten to have a check as follows
(defun python-shell-send-defun (&optional arg msg)
"Send the current defun to inferior Python process.
When argument ARG is non-nil do not include decorators. When
optional argument MSG is non-nil, forces display of a
user-friendly message if there's no process running; defaults to
t when called interactively."
(interactive (list current-prefix-arg t))
(save-excursion
(python-shell-send-region
(progn
(end-of-line 1)
(while (and (or (python-nav-beginning-of-defun)
(beginning-of-line 1))
(> (current-indentation) 0)))
(when (not arg)
(while (and (forward-line -1)
(looking-at (python-rx decorator))))
;; add a check here to see if we are on the first line
(and (not (bobp)) (forward-line 1)))
(point-marker))
(progn
(or (python-nav-end-of-defun)
(end-of-line 1))
(point-marker))
nil ;; noop
msg)))

Related

emacs ipython autoclear buffer after executing

I'm fairly new to Emacs and finally figured out how to set up python environment. I'm using elpy and iPython as python shell interpreter. My question is as follows:
After I press C-c C-c, the buffer on the right shows the executed result. The red box is the code being executed and the blue box is the executed result returned from python. My questions are:
Is there a way only showing the result?
And every time after the script being executed, how to set the buffer on the right only showing the current result, ie, clear previous executed results? Thanks.
Is there a way only showing the result?
elpy already has a custom variable for this:
(setq elpy-shell-echo-input nil)
And every time after the script being executed, how to set the buffer
on the right only showing the current result, ie, clear previous
executed results? Thanks.
elpy doesn't have this functionality. But you can create a wrapper function and bind it to the same keys:
(define-key elpy-mode-map (kbd "C-c C-c")
(lambda (p)
(interactive "P")
(ignore-errors
(with-current-buffer (process-buffer (python-shell-get-process-or-error))
(let ((comint-buffer-maximum-size 0))
(comint-clear-buffer))))
(elpy-shell-send-region-or-buffer p)))

Is there a function like eval-print-last-sexp for Comint-Mode?

Is there an equivalent function to eval-print-last-sexp for comint derived modes in emacs?
Specifically, I'm using python-mode (with elpy) and am looking for a way to send the contents of a region to the Python process and then print the results on the next line of the python script I'm working in.
Printing the results to the Messages buffer would be acceptable as well, however the behavior of eval-print-last-sexp would be preferred.
I'm running Emacs 25.1.1 and elpy 1.13.0.
It would depend on the comint-derived mode, since you would need to redirect the process output. Different modes have different methods of interacting with inferior processes. Python mode already has a function to do this, python-shell-send-string-no-output (other modes have similar functions, but you would need to search for them).
I'm not sure how exactly you want to define a sexp for python, but here is an example of sending the current line, with output like eval-print-last-sexp.
(defun python-eval-print-last-sexp ()
"Print result of evaluating current line into current buffer."
(interactive)
(let ((res (python-shell-send-string-no-output
;; modify to get a different sexp
(buffer-substring (line-beginning-position)
(line-end-position))))
(standard-output (current-buffer)))
(when res
(terpri)
(princ res)
(terpri))))
elpy-shell-send-statement from the Emacs Python Development Environment.

evaluating buffer in emacs python-mode on remote host

I'm using emacs23 with tramp to modify python scripts on a remote host.
I found that when I start the python shell within emacs it starts up
python on the remote host.
My problem is that when I then try to call python-send-buffer via C-c C-c it comes up with the error
Traceback (most recent call last):
File "", line 1, in ?
ImportError: No module named emacs
Traceback (most recent call last):
File "", line 1, in ?
NameError: name 'emacs' is not defined
Now, I must admit that I don't really know what's going on here. Is there a way for me to configure emacs so that I can evaluate the buffer on the remote host?
Many thanks.
Edit: I've followed eichin's advice and re-implemented python-send-region. See my answer below.
I'm currently trying to to get my unregistered question merged with this account, after which I'll be able to accept eichin's answer and edit my post to include my solution.
I followed eichin's suggestion and copied the emacs2.py emacs3.py and emacs.py files to the remote host and added their directory to PYTHONPATH in the tramp-remote-process-environment variable.
I then reimplemented the python-send-buffer function in my .emacs
(require 'python)
(defun python-send-region (start end)
"Send the region to the inferior Python process."
(interactive "r")
(let* ((loc_name)
(f (if (file-remote-p default-directory)
(let* ((con (tramp-dissect-file-name default-directory)))
(setq loc_name (tramp-make-tramp-temp-file con))
(concat "/"
(tramp-file-name-method con) ":"
(tramp-file-name-user con) "#"
(tramp-file-name-host con) ":"
loc_name
))
(setq loc_name (make-temp-file "py"))))
(command (format "emacs.eexecfile(%S)" loc_name))
(orig-start (copy-marker start)))
(save-excursion
(let ((curbuf (current-buffer))
(tempbuf (get-buffer-create "*python_temp*")))
(set-buffer tempbuf)
(delete-region (point-min) (point-max))
(insert-buffer-substring curbuf start end)
(python-mode)
(when (save-excursion
(goto-char (point-min))
(/= 0 (current-indentation)))
(python-shift-left (point-min) (point-max)))
(write-region nil nil f nil 'nomsg))
(python-send-command command)
(with-current-buffer (process-buffer (python-proc))
;; Tell compile.el to redirect error locations in file `f' to
;; positions past marker `orig-start'. It has to be done *after*
;; `python-send-command''s call to `compilation-forget-errors'.
(compilation-fake-loc orig-start f)))
))
I essentially copy the region into a new buffer, adjust the indentation and then write it into a temporary file, created with tramp-make-tramp-temp-file or make-temp-file, depending on whether the visited file is remote or local.
I had some problems with tramp-handle-write-region, which didn't seem to accept a string as a first argument, which is why I did all the formatting in a separate buffer first.
Let me know if there are still any problems with the code, but this is my first attempt at elisp coding, so please be gentle.
Short answer: not without writing some missing elisp code.
Long version: In python.el, run-python adds data-directory (which on my Ubuntu 10.10 box is /usr/share/emacs/23.1/etc/ ) to $PYTHONPATH, specifically so that it can find emacs.py (as supplied by the local emacs distribution.) Then it does a (python-send-string "import emacs") and expects it to work...
It looks like the defadvice wrappers that tramp uses don't actually pass PYTHONPATH, so this doesn't work even if you have the matching emacs version on the remote system.
If you M-x customize-variable RET tramp-remote-process-environment RET
then hit one of the INS buttons and add PYTHONPATH=/usr/share/emacs/23.1/etc then hit STATE and set it to "current session" (just to test it, or "save for future sessions" if it works for you) it almost works - the complaint goes away, in any case, because the remote python can now find the remote emacs.py. If you now go back to the original question, doing python-send-buffer, you just run into a different error: No such file or directory: '/tmp/py24574XdA' because python-mode just stuffs the content into a temporary file and tells the python subprocess to load that.
You'd have to change python-send-region (the other functions call it) and particularly the way it uses make-temp-file to be tramp-aware - there's even a tramp-make-tramp-temp-file you could probably build upon. (Be sure to post it if you do...)

Emacs: pass arguments to inferior Python shell during buffer evaluation

recently I started using Emacs as a Python IDE, and it not quite intuitive... The problem I am struggling with right now is how to pass command line arguments to the inferior python shell when the buffer is evaluated with C-c C-c. Thanks for help.
This doesn't appear to be easily possible; the inferior process managed by the python.el module is designed to persist across many invocations of python-send-buffer (and friends). One solution I've found is to write your own function that sets sys.argv programmatically from within the inferior process:
(defun python-send-buffer-with-my-args (args)
(interactive "sPython arguments: ")
(let ((source-buffer (current-buffer)))
(with-temp-buffer
(insert "import sys; sys.argv = '''" args "'''.split()\n")
(insert-buffer-substring source-buffer)
(python-send-buffer))))
Execute this function in your *scratch* buffer and/or save it in your .emacs file, then, if you want, bind it to a convenient key sequence. C-c C-a doesn't seem to be used by python-mode, so perhaps:
(global-set-key "\C-c\C-a" 'python-send-buffer-with-my-args)
The command will prompt you for arguments to use, then copy your source buffer into a temporary buffer, prepending it with a code snippet that sets sys.argv to the list of arguments you supplied, and finally will call python-send-buffer.
The above code will just naively split the string you type on whitespace, so if you need to supply arguments that have whitespace in them, you'll need a more sophisticated algorithm.

Emacs defadvice on python-mode function

In python-mode, there is a function called py-execute-region which sends a highlighted region of code to the Python buffer for evaluation. After evaluation, the cursor is in the Python buffer, but I would prefer that it remain in the script buffer so I can continue producing more code. I wrote a simple advising function:
(defadvice py-execute-region
(after py-execute-region-other-window activate)
""" After execution, return cursor to script buffer """
(other-window 1)
)
But this does not do anything at all. I've tried other variants like using 'around' rather than 'after'; setting a variable to the script buffer name and then pop-to-buffer to that buffer and stuff like that. No success! I wonder if the mechanics of this is obvious to someone... Thanks!
In this case the solution appears to be
(custom-set-variables
'(py-shell-switch-buffers-on-execute nil))
Use around-advice to wrap the function in a call to
save-window-excursion, which will restore the previous window
configuration after the command completes.
(defadvice py-execute-region
(around preserve-window-configuration activate)
"After execution, return cursor to script buffer"
(save-window-excursion ad-do-it))
Keep in mind, though, that if the Python buffer wasn't already shown,
it will still be hidden after the command completes. To remedy that,
you can add another piece of advice to call switch-to-buffer-other-window at the
end:
(defadvice py-execute-region
(after show-pybuf-other-window activate)
"After execution, show the python buffer in another window."
(switch-to-buffer-other-window "[PYTHON BUFFER NAME]"))
Also, make sure you don't use """triple quotes""" in elisp. I don't
think they work.
What you have there works fine for me. And it should auto-activate, so a separate activation should be unnecessary. However, you do need to de-active and re-activate advice for changes to take effect:
1) define and activate advice
2) it doesn't do what you want, so change the advice
3) deactivate it: (ad-deactivate 'py-execute-region)
4) reactivate it: (ad-activate 'py-execute-region)
Step 4 should pick up the changes you made in step 2. Alternately, you can change the code in step 2 and then just re-evaluate the code in step 4 (assuming the activate flag is set).
I haven't actually tried this out, but I did do something similar for find-file, and over there I needed to call interactive before calling other-window. I actually have no real idea of Emacs Lisp, but this may work.
(defadvice py-execute-region
(after py-execute-region-other-window activate)
(interactive)
(other-window 1)
)

Categories