Merge pull request #1693 from zackw/static-honor-ignore-files

Honor 'IGNORE_FILES' in StaticGenerator._copy_paths (#1692).
This commit is contained in:
Justin Mayer 2015-06-10 11:54:29 -07:00
commit 1da84349c4
3 changed files with 70 additions and 37 deletions

View file

@ -108,9 +108,10 @@ Setting name (followed by default value, if any)
process or ignore. For example, to avoid processing .html files, process or ignore. For example, to avoid processing .html files,
set: ``READERS = {'html': None}``. To add a custom reader for the set: ``READERS = {'html': None}``. To add a custom reader for the
``foo`` extension, set: ``READERS = {'foo': FooReader}`` ``foo`` extension, set: ``READERS = {'foo': FooReader}``
``IGNORE_FILES = ['.#*']`` A list of file globbing patterns to match against the ``IGNORE_FILES = ['.#*']`` A list of glob patterns. Files and directories matching any
source files to be ignored by the processor. For example, of these patterns will be ignored by the processor. For example,
the default ``['.#*']`` will ignore emacs lock files. the default ``['.#*']`` will ignore emacs lock files, and
``['__pycache__']`` would ignore Python 3's bytecode caches.
``MD_EXTENSIONS =`` ``['codehilite(css_class=highlight)','extra']`` A list of the extensions that the Markdown processor ``MD_EXTENSIONS =`` ``['codehilite(css_class=highlight)','extra']`` A list of the extensions that the Markdown processor
will use. Refer to the Python Markdown documentation's will use. Refer to the Python Markdown documentation's
`Extensions section <http://pythonhosted.org/Markdown/extensions/>`_ `Extensions section <http://pythonhosted.org/Markdown/extensions/>`_

View file

@ -132,15 +132,23 @@ class Generator(object):
exclusions_by_dirpath.setdefault(parent_path, set()).add(subdir) exclusions_by_dirpath.setdefault(parent_path, set()).add(subdir)
files = [] files = []
ignores = self.settings['IGNORE_FILES']
for path in paths: for path in paths:
# careful: os.path.join() will add a slash when path == ''. # careful: os.path.join() will add a slash when path == ''.
root = os.path.join(self.path, path) if path else self.path root = os.path.join(self.path, path) if path else self.path
if os.path.isdir(root): if os.path.isdir(root):
for dirpath, dirs, temp_files in os.walk(root, followlinks=True): for dirpath, dirs, temp_files in os.walk(root, followlinks=True):
for e in exclusions_by_dirpath.get(dirpath, ()): drop = []
if e in dirs: excl = exclusions_by_dirpath.get(dirpath, ())
dirs.remove(e) for d in dirs:
if (d in excl or
any(fnmatch.fnmatch(d, ignore)
for ignore in ignores)):
drop.append(d)
for d in drop:
dirs.remove(d)
reldir = os.path.relpath(dirpath, self.path) reldir = os.path.relpath(dirpath, self.path)
for f in temp_files: for f in temp_files:
fp = os.path.join(reldir, f) fp = os.path.join(reldir, f)
@ -668,10 +676,12 @@ class StaticGenerator(Generator):
for path in paths: for path in paths:
if final_path: if final_path:
copy(os.path.join(source, path), copy(os.path.join(source, path),
os.path.join(output_path, destination, final_path)) os.path.join(output_path, destination, final_path),
self.settings['IGNORE_FILES'])
else: else:
copy(os.path.join(source, path), copy(os.path.join(source, path),
os.path.join(output_path, destination, path)) os.path.join(output_path, destination, path),
self.settings['IGNORE_FILES'])
def generate_context(self): def generate_context(self):
self.staticfiles = [] self.staticfiles = []

View file

@ -272,51 +272,73 @@ def slugify(value, substitutions=()):
return value.decode('ascii') return value.decode('ascii')
def copy(source, destination): def copy(source, destination, ignores=None):
"""Recursively copy source into destination. """Recursively copy source into destination.
If source is a file, destination has to be a file as well. If source is a file, destination has to be a file as well.
The function is able to copy either files or directories. The function is able to copy either files or directories.
:param source: the source file or directory :param source: the source file or directory
:param destination: the destination file or directory :param destination: the destination file or directory
:param ignores: either None, or a list of glob patterns;
files matching those patterns will _not_ be copied.
""" """
def walk_error(err):
logger.warning("While copying %s: %s: %s",
source_, err.filename, err.strerror)
source_ = os.path.abspath(os.path.expanduser(source)) source_ = os.path.abspath(os.path.expanduser(source))
destination_ = os.path.abspath(os.path.expanduser(destination)) destination_ = os.path.abspath(os.path.expanduser(destination))
if not os.path.exists(destination_) and not os.path.isfile(source_): if ignores is None:
os.makedirs(destination_) ignores = []
def recurse(source, destination): if any(fnmatch.fnmatch(os.path.basename(source), ignore)
for entry in os.listdir(source): for ignore in ignores):
entry_path = os.path.join(source, entry) logger.info('Not copying %s due to ignores', source_)
if os.path.isdir(entry_path): return
entry_dest = os.path.join(destination, entry)
if os.path.exists(entry_dest):
if not os.path.isdir(entry_dest):
raise IOError('Failed to copy {0} a directory.'
.format(entry_dest))
recurse(entry_path, entry_dest)
else:
shutil.copytree(entry_path, entry_dest)
else:
shutil.copy2(entry_path, destination)
if os.path.isfile(source_):
if os.path.isdir(source_): dst_dir = os.path.dirname(destination_)
recurse(source_, destination_) if not os.path.exists(dst_dir):
logger.info('Creating directory %s', dst_dir)
elif os.path.isfile(source_): os.makedirs(dst_dir)
dest_dir = os.path.dirname(destination_)
if not os.path.exists(dest_dir):
os.makedirs(dest_dir)
shutil.copy2(source_, destination_)
logger.info('Copying %s to %s', source_, destination_) logger.info('Copying %s to %s', source_, destination_)
else: shutil.copy2(source_, destination_)
logger.warning('Skipped copy %s to %s', source_, destination_)
elif os.path.isdir(source_):
if not os.path.exists(destination_):
logger.info('Creating directory %s', destination_)
os.makedirs(destination_)
if not os.path.isdir(destination_):
logger.warning('Cannot copy %s (a directory) to %s (a file)',
source_, destination_)
return
for src_dir, subdirs, others in os.walk(source_):
dst_dir = os.path.join(destination_,
os.path.relpath(src_dir, source_))
subdirs[:] = (s for s in subdirs if not any(fnmatch.fnmatch(s, i)
for i in ignores))
others[:] = (o for o in others if not any(fnmatch.fnmatch(o, i)
for i in ignores))
if not os.path.isdir(dst_dir):
logger.info('Creating directory %s', dst_dir)
# Parent directories are known to exist, so 'mkdir' suffices.
os.mkdir(dst_dir)
for o in others:
src_path = os.path.join(src_dir, o)
dst_path = os.path.join(dst_dir, o)
if os.path.isfile(src_path):
logger.info('Copying %s to %s', src_path, dst_path)
shutil.copy2(src_path, dst_path)
else:
logger.warning('Skipped copy %s (not a file or directory) to %s',
src_path, dst_path)
def clean_output_dir(path, retention): def clean_output_dir(path, retention):
"""Remove all files from output directory except those in retention list""" """Remove all files from output directory except those in retention list"""