It sounds like you want shutil.copytree utilizing the ignore parameter:
If ignore is given, it must be a callable that will receive as its arguments the directory being visited by
copytree()
, and a list of its contents, as returned byos.listdir()
. Sincecopytree()
is called recursively, the ignore callable will be called once for each directory that is copied. The callable must return a sequence of directory and file names relative to the current directory (i.e. a subset of the items in its second argument); these names will then be ignored in the copy process. 'ignore_patterns()' can be used to create such a callable that ignores names based on glob-style patterns.
So something like this should work:
def what_to_ignore(path,names):
if is_special(path):
# process names here returning any or all to ignore
shutil.copytree(indir,outdir,ignore=what_to_ignore)
Edit for expanded question and example
Here's an example. The simplified ignore function still creates an empty special directory, but it is easy to delete before doing the special zip copy. I also nested the special function so my_copy
could be used more than once without relying on using a global variable. Doing the zip is an exercise for the user:
import fnmatch
import shutil
import os
def my_copy(indir, outdir):
special = []
def is_special_dir(path, names):
"""directories are special if they have .bar files"""
if fnmatch.filter(names,'*.bar'):
special.append(path)
return names
return []
shutil.copytree(indir, outdir, ignore=is_special_dir)
print('Found special dirs:',special)
for src in special:
rel = os.path.relpath(src,indir)
dst = os.path.join(outdir,rel)
os.rmdir(dst)
print('Zip "{}" to "{}.zip"'.format(src,dst))
my_copy('dir1','dira')
my_copy('dir1','dirb')
Output
Found special dirs: ['dir1\\specialdir']
Zip "dir1\specialdir" to "dira\specialdir.zip"
Found special dirs: ['dir1\\specialdir']
Zip "dir1\specialdir" to "dirb\specialdir.zip"