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. :-)