Save and run at the same time in Vim - python

I do a lot of Python quick simulation stuff and I'm constantly saving (:w) and then running (:!!). Is there a way to combine these actions?
Maybe a "save and run" command.

Okay, the simplest form of what you're looking for is the pipe command. It allows you to run multiple cmdline commands on the same line. In your case, the two commands are write `w` and execute current file `! %:p`. If you have a specific command you run for you current file, the second command becomes, e.g. `!python %:p`. So, the simplest answer to you question becomes:
:w | ! %:p
^ ^ ^
| | |--Execute current file
| |--Chain two commands
|--Save current file
One last thing to note is that not all commands can be chained. According to the Vim docs, certain commands accept a pipe as an argument, and thus break the chain...

Option 1:
Write a function similar to this and place it in your startup settings:
function myex()
execute ':w'
execute ':!!'
endfunction
You could even map a key combination to it -- look at the documentation.
Option 2 (better):
Look at the documentation for remapping keystrokes - you may be able to accomplish it through a simple key remap. The following works, but has "filename.py" hardcoded. Perhaps you can dig in and figure out how to replace that with the current file?
:map <F2> <Esc>:w<CR>:!filename.py<CR>
After mapping that, you can just press F2 in command mode.
imap, vmap, etc... are mappings in different modes. The above only applies to command mode. The following should work in insert mode also:
:imap <F2> <Esc>:w<CR>:!filename.py<CR>a
Section 40.1 of the Vim manual is very helpful.

Use the autowrite option:
:set autowrite
Write the contents of the file, if it has been modified, on each :next, :rewind, :last, :first, :previous, :stop, :suspend, :tag, :!, :make, CTRL-] and CTRL-^ command [...]

Here you go:
:nmap <F1> :w<cr>:!%<cr>
Save and run (you have to be in n mode though - just add esc and a for i mode).

Command combination seems to work through the | character, so perhaps something like aliasing :w|!your-command-here to a distinct key combination.

Another possibility:
au BufWriteCmd *.py write | !!
Though this will run every time you save, which might not be what you want.

In Vim, you could simply redirect any range of your current buffer to an external command (be it Bash, the Python interpreter, or you own Python script).
# Redirect whole buffer to 'python'
:%w !python
Suppose your current buffer contains two lines as below,
import numpy as np
print np.arange(12).reshape(3, 4)
then :%w !python will run it, be it saved or not. And print something like below on your terminal,
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
Of course, you could make something persistent, for example, some keymaps.
nnoremap <F8> :.w !python<CR>
vnoremap <F8> :w !python<CR>
The first one, run the current line. The second one, run the visual selection, via the Python interpreter.
#!! Be careful, in Vim ':w!python' and ':.w !python' are very different. The
first write (create or overwrite) a file named 'python' with contents of
current buffer, and the second redirects the selected cmdline range (here dot .,
which mean current line) to external command (here 'python').
For cmdline range, see
:h cmdline-ranges
Not the below one, which concerning normal command, not cmdline one.
:h command-range
This was inspired by Execute current line in Bash from Vim.

This is what I put in my .vimrc file and works like a charm:
nnoremap <leader>r :w<CR>:!!<CR>
Of course you need to run your shell command once before this, so it knows what command to execute.
Example:
:!node ./test.js

I got the following from the Vim tips wiki:
command! -complete=file -nargs=+ shell call s:runshellcommand(<q-args>)
function! s:runshellcommand(cmdline)
botright vnew
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile nowrap
call setline(1,a:cmdline)
call setline(2,substitute(a:cmdline,'.','=','g'))
execute 'silent $read !'.escape(a:cmdline,'%#')
setlocal nomodifiable
1
endfunction
But I changed new to vnew on the third line, and then for Python I have the following:
map <F9> :w:Shell python %<cr><c-w>
Hitting F9 saves, runs, and dumps the output into a new vertically split scratch buffer, for easy yanking, saving, etc. It also hits c-w so I only have to press h/c to close it and move back to my code.

Try making it inside the Bash.
In case of a C file called t.c, this is very convenient:
vi t.c && cc t.c -o t && ./t
The and symbols (&&) ensure that one error message breaks the command chain.
For Python this might be even easier:
vi t.py && python t.py

This will work in insert mode too:
" F5 => Save & Run python3 "
nnoremap <F5> :w <CR> :!sh -c 'python3 %' <CR>
inoremap <F5> <Esc> :w <CR> :!sh -c 'python3 %' <CR>
I use it all the time to test stuff that is just too long to retype in the interpreter.

Consider switching to IDLE. F5 does everything.
Consider switching to Komodo IDE. You can define a command so that F5 does everything.

Related

Executing string of python code within bash script

I've come across a situation where it would be convenient to use python within a bash script I'm writing. I call some executables within my script, then want to do a bit of light data processing with python, then carry on. It doesn't seem worth it to me to write a dedicated script for the processing.
So what I want to do is something like the following:
# do some stuff in bash script
# write some data into datafile.d
python_fragment= << EOF
f = open("datafile.d")
// do some stuff with opened file
print(result)
EOF
result=$(execute_python_fragment $python_fragment) # <- what I want to do
# do some stuff with result
Basically all I want to do is execute a string containing python code. I could of course just make another file containing the python code and execute that, but I'd prefer not to do so. I could do something like echo $python_fragment > temp_code_file, then execute temp_code_file, but that seems inelegant. I just want to execute the string directly, if that's possible.
What I want to do seems simple enough, but haven't figured it out or found the solution online.
Thanks!
You can run a python command direct from the command line with -c option
python -c 'from foo import hello; print (hello())'
Then with bash you could do something like
result=$(python -c '$python_fragment')
You only have to redirect that here-string/document to python
python <<< "print('Hello')"
or
python <<EOF
print('Hello')
EOF
and encapsulate that in a function
execute_python_fragment() {
python <<< "$1"
}
and now you can do your
result=$(execute_python_fragment "${python_fragment}")
You should also add some kind of error control, input sanitizing... it's up to you the level of security you need in this function.
If the string contains the exact python code, then this simple eval() function works.
Here's a really basic example:
>>> eval("print(2)")
2
Hope that helps.
maybe something like
result=$(echo $python_fragment | python3)
only problem is the heredoc assignment in the question doesn't work either. But https://stackoverflow.com/a/1167849 suggests a way to do it if that is what you want to do:
python_fragment=$(cat <<EOF
print('test message')
EOF
) ;
result=$(echo $python_fragment | python3)
echo result was $result

Run .py file until specified line number

In a linux terminal typing
python script.py
Will run run script.py and exit the python console, but what if I just want to run a part of the script and leave the console open? For example, run script.py until line 15 and leave the console open for further scripting. How would I do this?
Let's say it's possible, then with the console still open and script.py run until line 15, can I then from inside the console call line fragments from other py files?
...something like
python script.py 15 #(opens script and runs lines 1-15 and leaves console open)
Then having the console open, I would like to run lines 25-42 from anotherscript.py
>15 lines of python code run from script.py
> run('anotherscript.py', lines = 25-42)
> print "I'm so happy the console is still open so I can script some more")
I'm so happy the console is still open so I can script some more
>
Your best bet might be pdb, the Python debugger. You can start you script under pdb, set a breakpoint on line 15, and then run your script.
python -m pdb script.py
b 15 # <-- Set breakpoint on line 15
c # "continue" -> run your program
# will break on line 15
You can then inspect your variables and call functions. Since Python 3.2, you can also use the interact command inside pdb to get a regular Python shell at the current execution point!
If that fits your bill and you also like IPython, you can check out IPdb, which is a bit nicer than normal pdb, and drops you into an IPython shell with interact.
if you want to run script.py from line a to line b, simply use this bash snippet:
cat script.py|head -{a+b}|tail -{b-a}|python -i
replace {a+b} and {b-a} with their values
You could use the python -i option to leave the console open at the end of the script.
It lets your script run until it exits, and you can then examine variables, call any function and any Python code, including importing and using other modules.
Of course your script needs to exit first, either at the end or, if your goal is to debug that part of the script, you could add a sys.exit() or os._exit() call where you want it to stop (such as your line 15).
For instance:
import os
print "Script starting"
a=1
def f(x):
return x
print "Exiting on line 8"
os._exit(0) # to avoid the standard SystemExit exception
print "Code continuing"
Usage example:
python -i test_exit.py
Scrit starting
Exiting on line 8
>>> print a
1
>>> f(4)
4
>>>
You cannot do that directly but you can do something similar from inside Python console (or IDLE) with exec :
just open you favorite Python console
load wanted lines into a string and exec them :
script = 'script.py'
txt = ''
with open(script) as sc:
for i, line in enumerate(sc):
if i >= begline and i<= endline:
txt = txt + line
exec(txt)
You can even write your own partial script runner based on that code ...
EDIT
I must admit that above answer alone really deserved to be downvoted. It is technically correct and probably the one that most closely meet what you asked for. But I should have warned you that it is bad pratice. Relying on line numbers to load pieces of source files is error prone and you should avoid it unless you really know what you are doing and why you do it that way. Python debugger at least allows you to control what are the lines you are about to execute.
If you really have to use this solution be sure to always print and double check the lines that you are about to execute. IMHO it is always both simpler and safer to copy and past lines in an IDE like IDLE that is packaged into any standard Python installation.

calling several matlab commands from python using os.system

I just want to call two matlab commands from Python: the fist command just adds the folder and subfolders fo the desired path ( addpath(genpath('c:/file1/file2')) ), while the second command calls the function I want to use ( myfunction.m ).
I always used os.system("command") when I needed to call a function and os.system("command1 | command2") when I wanted to call two functions for example, so I tried:
os.system("matlab -r addpath(genpath('c:/file1/file2')) | matlab -r myfunction")
But obviously this opens two matlab windows, the first one for the fist command and the second one for the second command. What I want is to call both commands in the same window, one after the other. However, if I try simply:
os.system("matlab -r addpath(genpath('c:/file1/file2')) -r myfunction")
it doesn't work...It performs the first command ( addpath ) but it does not call the function....
What I am doing wrong?? Any idea about how to do that?? I read lot of posts but they weren't very helpful, even if this is a very simple case.
Thanks in advance guys!! I really appreciate your help
The idea is to execute the following:
matlab.exe -r "addpath('c:\some\folder'); someFunction(); quit;"
Now you have to plug that inside a os.system call, and properly escape quotation marks...
For example, you can try:
>>> os.system("matlab.exe -nosplash -nodesktop -wait -r \"addpath('c:\\some\\folder'); someFunction(); quit();\"")
(note that backslashes and double-quotations are escaped, plus I added the -wait option so that the command doesn't return until MATLAB finishes execution).

OS X & Python 3: Behavior while executing bash command in new terminal?

I've seen similar questions (e.g. Running a command in a new Mac OS X Terminal window ) but I need to confirm this command and its expected behavior in a mac (which I don't have). If anyone can run the following in Python 3 Mac:
import subprocess, os
def runcom(bashCommand):
sp = subprocess.Popen(['osascript'], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
sp.communicate('''tell application "Terminal"\nactivate\ndo script with command "{0} $EXIT"\nend tell'''.format(bashCommand))
runcom('''echo \\"This is a test\\n\\nThis should come two lines later; press any key\\";read throwaway''')
runcom('''echo \\"This is a test\\"\n\necho \\"This should come one line later; press any key\\";read throwaway''')
runcom('''echo \\"This is testing whether I can have you enter your sudo pw on separate terminal\\";sudo ls;\necho \\"You should see your current directory; press any key\\";read throwaway''')
Firstly, and most basically, is the "spawn new terminal and execute" command correct? (For reference, this version of the runcom function came from this answer below, and is much cleaner than my original.)
As for the actual tests: the first one tests that internal double escaped \\n characters really work. The second tests that we can put (unescaped) newlines into the "script" and still have it work just like semicolon. Finally, the last one tests whether you can call a sudo process in a separate terminal (my ultimate goal).
In all cases, the new terminal should disappear as soon as you "press any key". Please also confirm this.
If one of these doesn't work, a correction/diagnosis would be most appreciated. Also appreciated: is there a more pythonic way of spawning a terminal on Mac then executing a (sudo, extended) bash commands on it?
Thanks!
[...] its expected behavior [...]
This is hard to answer, since those commands do what I expect them to do, which might not be what you expect them to do.
As for the actual tests: the first one tests that internal double escaped \n characters really work.
The \\n with the doubled backslash does indeed work correctly in that it causes echo to emit a newline character. However, no double quotes are emitted by echo.
The second tests that we can put (unescaped) newlines into the "script" and still have it work just like semicolon.
That works also.
Finally, the last one tests whether you can call a sudo process in a separate terminal (my ultimate goal).
There is no reason why this should not work also, and indeed it does.
In all cases, the new terminal should disappear as soon as you "press any key". Please also confirm this.
That will not work because of several reasons:
read in bash will by default read a whole line, not just one character
after the script you supply is executed, there is no reason for the shell within the terminal to exit
even if the shell would exit, the user can configure Terminal.app not to close a window after the shell exits (this is even the default setting)
Other problems:
the script you supply to osascript will appear in the terminal window before it is executed. in the examples above, the user will see every "This is a test [...]" twice.
I cannot figure out what $EXIT is supposed to do
The ls command will show the user "the current directory" only in the sense that the current working directory in a new terminal window will always be the user's home directory
throwaway will not be available after the script bashCommand exits
Finally, this script will not work at all under Python 3, because it crashes with a TypeError: communicate() takes a byte string as argument, not a string.
Also appreciated: is there a more pythonic way of spawning a terminal on Mac [...]
You should look into PyObjC! It's not necessarily more pythonic, but at least you would eliminate some layers of indirection.
I don't have Python 3, but I edited your runcom function a little and it should work:
def runcom(bashCommand):
sp = subprocess.Popen(['osascript'], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
sp.communicate('''tell application "Terminal"\nactivate\ndo script with command "{0} $EXIT"\nend tell'''.format(bashCommand))

How can I get a file to autorun before I run any command in ipython?

I have a python file that holds a bunch of functions that I'm continually modifying and then testing in ipython. My current workflow is to run "%run myfile.py" before each command. However, ideally, I'd like that just to happen automatically. Is that possible?
If you really want to use rlwrap for this, write a filter! Just define an input_handler that adds %run myfile.py to the input, and an echo_handler to echo your original input so that you won't see this happening (man RlwrapFilter tells you all you ever wanted to know about filter writing, and then some).
But isn't it more elegant to solve this within ipython itself, using IPython.hooks.pre_runcode_hook?
import os
import IPython
ip = IPython.ipapi.get()
def runMyFile(self):
ip.magic('%run myFile.py')
raise IPython.ipapi.TryNext()
ip.set_hook('pre_runcode_hook', runMyFile)
I can't find any elegant way. This is the ugly way. Run:
rlwrap awk '{print "%run myfile.py"} {print} {fflush()}' |ipython
This reads from STDIN, but prints the command you wanted before each command. fflush is there to disable buffering and pass things to ipython immediately. rlwrap is there to keep the readline bindings; you can remove it if you don't have it, but this will be less convenient (no arrow keys, etc.).
Mind that you will have to type your commands before the ipython prompt appears. There might be other more annoying things which break, I haven't tested thoroughly.

Categories

Resources