开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜