| """Timestamp comparison of files and groups of files.""" | |
| import functools | |
| import os.path | |
| from ._functools import splat | |
| from .errors import DistutilsFileError | |
| from .py39compat import zip_strict | |
| def _newer(source, target): | |
| return not os.path.exists(target) or ( | |
| os.path.getmtime(source) > os.path.getmtime(target) | |
| ) | |
| def newer(source, target): | |
| """ | |
| Is source modified more recently than target. | |
| Returns True if 'source' is modified more recently than | |
| 'target' or if 'target' does not exist. | |
| Raises DistutilsFileError if 'source' does not exist. | |
| """ | |
| if not os.path.exists(source): | |
| raise DistutilsFileError("file '%s' does not exist" % os.path.abspath(source)) | |
| return _newer(source, target) | |
| def newer_pairwise(sources, targets, newer=newer): | |
| """ | |
| Filter filenames where sources are newer than targets. | |
| Walk two filename iterables in parallel, testing if each source is newer | |
| than its corresponding target. Returns a pair of lists (sources, | |
| targets) where source is newer than target, according to the semantics | |
| of 'newer()'. | |
| """ | |
| newer_pairs = filter(splat(newer), zip_strict(sources, targets)) | |
| return tuple(map(list, zip(*newer_pairs))) or ([], []) | |
| def newer_group(sources, target, missing='error'): | |
| """ | |
| Is target out-of-date with respect to any file in sources. | |
| Return True if 'target' is out-of-date with respect to any file | |
| listed in 'sources'. In other words, if 'target' exists and is newer | |
| than every file in 'sources', return False; otherwise return True. | |
| ``missing`` controls how to handle a missing source file: | |
| - error (default): allow the ``stat()`` call to fail. | |
| - ignore: silently disregard any missing source files. | |
| - newer: treat missing source files as "target out of date". This | |
| mode is handy in "dry-run" mode: it will pretend to carry out | |
| commands that wouldn't work because inputs are missing, but | |
| that doesn't matter because dry-run won't run the commands. | |
| """ | |
| def missing_as_newer(source): | |
| return missing == 'newer' and not os.path.exists(source) | |
| ignored = os.path.exists if missing == 'ignore' else None | |
| return any( | |
| missing_as_newer(source) or _newer(source, target) | |
| for source in filter(ignored, sources) | |
| ) | |
| newer_pairwise_group = functools.partial(newer_pairwise, newer=newer_group) | |