Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources]

David Ascher, Alex Martelli, Anna Ravenscroft

نسخه متنی -صفحه : 394/ 73
نمايش فراداده

Recipe 2.29. Versioning Filenames

Credit: Robin Parmar, Martin Miller

Problem

You want to make a backup copy of a file, before you overwrite it, with the standard convention of appending a three-digit version number to the name of the old file.

Solution

We just need to code a function to perform the backup copy appropriately:

def VersionFile(file_spec, vtype='copy'):
import os, shutil
if os.path.isfile(file_spec):
# check the 'vtype' parameter
if vtype not in ('copy', 'rename'):
raise ValueError, 'Unknown vtype %r' % (vtype,)
# Determine root filename so the extension doesn't get longer
n, e = os.path.splitext(file_spec)
# Is e a three-digits integer preceded by a dot?
if len(e) == 4 and e[1:].isdigit( ):
num = 1 + int(e[1:])
root = n
else:
num = 0
root = file_spec
# Find next available file version
for i in xrange(num, 1000):
new_file = '%s.%03d' % (root, i)
if not os.path.exists(new_file):
if vtype == 'copy':
shutil.copy(file_spec, new_file)
else:
os.rename(file_spec, new_file)
return True
raise RuntimeError, "Can't %s %r, all names taken"%(vtype,file_spec)
return False
if _ _name_ _ == '_ _main_ _':
import os
# create a dummy file 'test.txt'
tfn = 'test.txt'
open(tfn, 'w').close( )
# version it 3 times
print VersionFile(tfn)
# emits: True
print VersionFile(tfn)
# emits: True
print VersionFile(tfn)
# emits: True
# remove all test.txt* files we just made
for x in ('', '.000', '.001', '.002'):
os.unlink(tfn + x)
# show what happens when the file does not exist
print VersionFile(tfn)
# emits: False
print VersionFile(tfn)
# emits: False

Discussion

The purpose of the VersionFile function is to ensure that an existing file is copied (or renamed, as indicated by the optional second parameter) before you open it for writing or updating and therefore modify it. It is polite to make such backups of files before you mangle them (one functionality some people still pine for from the good old VMS operating system, which performed it automatically!). The actual copy or renaming is performed by shutil.copy and os.rename, respectively, so the only issue is which name to use as the target.

A popular way to determine backups' names is versioning (i.e., appending to the filename a gradually incrementing number). This recipe determines the new name by first extracting the filename's root (just in case you call it with an already-versioned filename) and then successively appending to that root the further extensions .000, .001, and so on, until a name built in this manner does not correspond to any existing file. Then, and only then, is the name used as the target of a copy or renaming. Note that VersionFile is limited to 1,000 versions, so you should have an archive plan after that. The file must exist before it is first versionedyou cannot back up what does not yet exist. However, if the file doesn't exist, function VersionFile simply returns False (while it returns TRue if the file exists and has been successfully versioned), so you don't need to check before calling it!

See Also

Documentation for the os and shutil modules in the Library Reference and Python in a Nutshell.