update file descriptor after os.rename()
What is the best 开发者_运维知识库way to handle Bad file descriptor
errors after calling os.rename()
in the following manner?
f = open('foo.txt', 'rw')
os.rename(f.name, f.name + ".bak")
In the file system there is no longer a foo.txt
but instead a foo.txt.bak
However..
f.name
gives foo.txt
instead of foo.txt.bak
But..
f.write("test")
gives Bad file descriptor
Is there a good way to update the file descriptor?
Should I still call f.close()
even tho the file has been renamed?
You can write a function that will rename an open file. Basically, you close, rename, and reopen the file, preserving attributes such as the file position and the mode. Some tweaking of the mode is necessary for the reopen -- if the file's mode is "w", reopening it with the same mode will lose everything in it, so we use "r+" mode in that case when reopening. (This isn't perfect, as it allows read access to the file, which it didn't have before, but it's the best we can do.) You will of course get a completely new file
object, which is the function's return value.
import os
def rename_open_file(fileobj, newname):
name = fileobj.name
mode = fileobj.mode.lower()
posn = fileobj.tell()
fileobj.close()
os.rename(name, newname)
newmode = mode
if "w" in mode: # can't reopen with "w" mode since
newmode = "r+" # it would empty the file; use "r+"
if "b" in mode:
newmode += "b"
fileobj = open(name, newmode)
fileobj.seek(posn)
return fileobj
f = rename_open_file(f, f.name + ".bak")
If you have more than one file
object referencing the open file, this isn't going to help, of course; all other references may break.
Caveat: the file's name
attribute isn't necessarily the full path, so if you opened the file using a relative path and have changed the directory since opening the file, this isn't going to work. You can write your own open()
that figures out the file's full pathname at open time (using os.path.abspath()
) if this is a problem.
Also, the buffer size given when opening the file isn't preserved since this is not recorded anywhere on the file object. Writing your own open()
can resolve this, too. The easiest way to do this is to subclass file
.
from os.path import abspath
class open(file):
def __init__(self, filename, mode="r", buffering=-1):
file.__init__(self, abspath(filename), mode, buffering)
self.buffering = buffering
Then you can add maintaining the buffering to your function:
import os
def rename_open_file(fileobj, newname):
name = fileobj.name
mode = fileobj.mode.lower()
posn = fileobj.tell()
buff = fileobj.buffering
fileobj.close()
os.rename(name, newname)
newmode = mode
if "w" in mode: # can't reopen with "w" mode since
newmode = "r+" # it would empty the file; use "r+"
if "b" in mode:
newmode += "b"
fileobj = open(name, newmode, buff)
fileobj.seek(posn)
return fileobj
You can also write a wrapper class for a file object, rather than subclassing file
, and have it pass through all file
's method calls to the wrapped object. Then rename()
can be a method of the wrapper, and do all of the above. Since calling code will keep a reference to the wrapper, it won't need to know that the underlying file
object is different. I will leave this as an exercise. :-)
os.rename() works on a pure file name without knowing about any open file objects referencing this file. Therefore you must not rely on the file object working after you did operations on the underlying file, so closing it is probably the right thing to do.
精彩评论