The program should backup files in a source folder to a zip file in destination, and keep track of how many versions there are (which explains the shelve). In order to copy these files, apparently I have to change the cwd
to the source path (folder), because otherwise the program will not detect them. While it may not seem to cause that much of an issue, my OCD triggers when I see the shelve files in the new .zip
folder. I thought about adding lines to delete the shelve files, but I was wondering if there is some other way.
(This program is a project in a certain book I'm reading, and while the project demanded a more basic version of this, I like to take it a step forward.)
import os, shelve, zipfile
def backupToZip(folder, destination, copies=1):
#Folder
fName= os.path.basename(folder)
#Shelfing for copy number
namesNums = shelve.open('names')
if fName not in namesNums:
num= 1
namesNums[fName]= num
else:
num= namesNums[fName]
#Producing zips
os.chdir(folder)
for i in range(int(copies)):
zipPath= os.path.join(destination, fName+'_'+str(num)+'.zip')
theZip= zipfile.ZipFile(zipPath, 'w')
for foldername, subfolders, filenames in os.walk(folder):
for filename in filenames:
theZip.write(filename)
num+=1
namesNums[fName]= num
namesNums.close()
-
1\$\begingroup\$ (Welcome to CR!) The title of your question does not comply with State what your code does in your title, not your main concerns about it. While any aspect of the code posted is fair game, it may be useful to pose explicit questions in the question body. \$\endgroup\$greybeard– greybeard2018年01月01日 08:06:40 +00:00Commented Jan 1, 2018 at 8:06
-
\$\begingroup\$ @greybeard Thank you for the welcome! I will keep that in mind in my future posts/questions (will edit this one as well later on). \$\endgroup\$hman_codes– hman_codes2018年01月02日 06:45:35 +00:00Commented Jan 2, 2018 at 6:45
1 Answer 1
shelve
supports all methods thatdict
ionarys do.Shelf objects support all methods supported by dictionaries. This eases the transition from dictionary based scripts to those requiring persistent storage.
And so you can use
dict.setdefault
, rather than yourif-else
.Rather than using
num += 1
, you can userange(num, num + copies)
. Sincenum
is defined in an outer scope, you can use it after thefor
loop too.Rather than using string concatination, you can use f-strings in Python 3.6+ or format strings:
# yours fName+'_'+str(num)+'.zip' # f-string f'{fName}_{num}.zip' # string format '{}_{}.zip'.format(fName, num) '{fName}_{num}.zip'.format(fName=fName, num=num)
It's simpler to use
with
when handling content managers. And so you should use them onshelve.open
andzipfile.ZipFile
.Not only does it make you not have to learn how to close all types of context managers. The context will be closed in the case of an error too.
Don't use
CamelCase
orcamelCase
, instead usesnake_case
.- Use
_
as a 'throw away' variable. Some people, however, like to instead use__
. - Put a space either side of your equal signs.
a = b
, nota= b
.
This can become:
import os, shelve, zipfile
def backup_to_zip(folder, destination, copies=1):
f_name= os.path.basename(folder)
with shelve.open('names') as names:
num = names.setdafault(f_name, 1)
os.chdir(folder)
for num in range(num, num + copies):
zip_path = os.path.join(destination, f'{f_name}_{num}.zip')
with zipfile.ZipFile(zip_path, 'w') as the_zip:
for _, _, filenames in os.walk(folder):
for filename in filenames:
the_zip.write(filename)
names[f_name] = num
You could also use pathlib
rather than os.path
. Which may allow you to use:
def backup_to_zip(folder, destination, copies=1):
folder = Path(folder)
with shelve.open('names') as names:
num = names.setdefault(folder.name, 1)
path = folder / Path(destination)
for num in range(num, num + copies):
with zipfile.ZipFile(path / f'{folder.name}_{num}.zip', 'w') as f:
for file in folder.iterdir():
if file.is_file():
f.write(file.name)
names[folder.name] = num
-
\$\begingroup\$ okay so I got 4 things to point out: 1) So using
with - as
closes the stated file when the program ends? 2) Doesn'tsetdefault
method addskey: value
to the dictionary ONLY if thekey
doesn't exists? 3) B-b-but I like using camelCase... 4) I fixed the problem in my own code by changing thecwd
back to the original one after the final loop ends \$\endgroup\$hman_codes– hman_codes2018年01月02日 10:18:09 +00:00Commented Jan 2, 2018 at 10:18 -
\$\begingroup\$ @Skullz 1) it closes when you exit the
with
scope. Wether that be by normal execution, or via an error. 2) Yes, that's what you're doing. 3) Python programmers only use them for Classes... \$\endgroup\$2018年01月02日 10:22:54 +00:00Commented Jan 2, 2018 at 10:22 -
\$\begingroup\$ hmm.. when I ran the first code you mentioned I got
num = names.setdafault(f_name, 1) AttributeError: 'DbfilenameShelf' object has no attribute 'setdafault'
. Doesn't that mean that shelves aren't really dictionaries in that regard? \$\endgroup\$hman_codes– hman_codes2018年01月02日 10:52:49 +00:00Commented Jan 2, 2018 at 10:52 -
\$\begingroup\$ @Skullz that's due to a late night typo.
setdafault
->setdefault
\$\endgroup\$2018年01月02日 10:55:35 +00:00Commented Jan 2, 2018 at 10:55 -
\$\begingroup\$ oh my. I didn't notice it, sorry about that. It partially works now, but it still doesn't solve my main issue: the shelve files
.bak
and.dat
are being copied into the destination.zip
. Plus, an error is being producedwith _io.open(self._datfile, 'rb+') as f: FileNotFoundError: [Errno 2] No such file or directory: 'names.dat'
. I'd like to know how to fix this, but I'll update my post later with my final code either way. Thank you for your time. \$\endgroup\$hman_codes– hman_codes2018年01月02日 11:38:36 +00:00Commented Jan 2, 2018 at 11:38