| """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) |
|
|