Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vim cannot save to temporary files created by python

Goal

I am trying to create and edit a temporary file in vim (exactly the same behavior as a commit script in git/hg/svn).

Current code

I found a method to do so in this answer: call up an EDITOR (vim) from a python script

import sys, tempfile, os
from subprocess import call
EDITOR = os.environ.get('EDITOR','vim')
initial_message = "write message here:"
with tempfile.NamedTemporaryFile(suffix=".tmp") as tmp:
  tmp.write(initial_message)
  tmp.flush()
  call([EDITOR, tmp.name])
  tmp.seek(0)
  print tmp.read()

The Issue

When I run the above code, the tempfile does not read the changes made in vim. Here is the output after I have added several other lines in vim:

fgimenez@dn0a22805f> ./note.py
Please edit the file:

fgimenez@dn0a22805f>

Now for the interesting (weird) part. If I change my editor to nano or emacs, the script works just fine! So far, this only seems to break when I use vim or textedit.

As another experiment, I tried calling a couple editors in a row to see what happens. The modified code is:

with tempfile.NamedTemporaryFile(suffix=".tmp") as tmp:
  tmp.write(initial_message)
  tmp.flush()
  # CALLING TWO EDITORS HERE, VIM THEN NANO
  call(['vim', tmp.name])
  raw_input("pausing between editors, just press enter")
  call(['nano', tmp.name])
  tmp.seek(0)
  print tmp.read()

I.e. I edit with vim then nano. What happens is that nano DOES register the changes made by vim, but python doesn't register anything (same result as before):

fgimenez@dn0a22805f> ./note.py
Please edit the file:

fgimenez@dn0a22805f>

BUT, if I edit with nano first, then vim, python still registers the nano edits but not the vim ones!

with tempfile.NamedTemporaryFile(suffix=".tmp") as tmp:
  tmp.write(initial_message)
  tmp.flush()
  # CALLING TWO EDITORS HERE, NANO THEN VIM
  call(['nano', tmp.name])
  raw_input("pausing between editors, just press enter")
  call(['vim', tmp.name])
  tmp.seek(0)
  print tmp.read()

Ouput from running the program and adding a\nb\nc in nano and d\ne\nf in vim:

fgimenez@dn0a22805f> ./note.py
Please edit the file:
a
b
c

fgimenez@dn0a22805f>

It seems as if using vim or textedit eliminates the ability to append to the file. I'm completely confused here, and I just want to edit my notes in vim...

Edit 1: Clarifications

  • I am on osx Mavericks

  • I call vim from the shell (not MacVim) and end the session with ZZ (also tried :w :q)

like image 363
Francisco Avatar asked Oct 02 '22 02:10

Francisco


2 Answers

I'm no Python expert, but it looks like you're keeping the handle to the temp file open while Vim is editing the file, and then attempt to read in the edited contents from the handle. By default, Vim creates a copy of the original file, writes the new contents to another file, and then renames it to the original (see :help 'backupcopy' for the details; other editors like nano apparently don't do it this way). This means that the Python handle still points to the original file (even though it may have already been deleted from the file system, depending on the Vim settings), and you get the original content.

You either need to reconfigure Vim (see :help 'writebackup'), or (better) change the Python implementation to re-open the same temp file name after Vim has exited, in order to get a handle to the new written file contents.

like image 59
Ingo Karkat Avatar answered Oct 03 '22 15:10

Ingo Karkat


I had the same problem on OS X after my code worked fine on Linux. As Ingo suggests, you can get the latest contents by re-opening the file. To do this, you probably want to create a temporary file with delete=False and then explicitly delete the file when you're done:

import sys, tempfile, os
from subprocess import call
EDITOR = os.environ.get('EDITOR','vim')
initial_message = "write message here:"
with tempfile.NamedTemporaryFile(suffix=".tmp", delete=False) as tmp:
  tmp.write(initial_message)
  tmp.flush()
  call([EDITOR, tmp.name])
  tmp.close()
  with open(tmp.name) as f:
    print f.read()
  os.unlink(tmp.name)
like image 45
Aaron Feldman Avatar answered Oct 03 '22 16:10

Aaron Feldman