Copying from a directory without using os.chdir() 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()
shelve supports all methods that dictionarys 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 your if-else.
Rather than using num += 1, you can use range(num, num + copies). Since num is defined in an outer scope, you can use it after the for 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 on shelve.open and zipfile.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 or camelCase, instead use snake_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, not a= 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