| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- """Timestamp comparison of files and groups of files."""
- from __future__ import annotations
- import functools
- import os.path
- from collections.abc import Callable, Iterable
- from typing import Literal, TypeVar
- from jaraco.functools import splat
- from .compat.py39 import zip_strict
- from .errors import DistutilsFileError
- _SourcesT = TypeVar(
- "_SourcesT", bound="str | bytes | os.PathLike[str] | os.PathLike[bytes]"
- )
- _TargetsT = TypeVar(
- "_TargetsT", bound="str | bytes | os.PathLike[str] | os.PathLike[bytes]"
- )
- def _newer(source, target):
- return not os.path.exists(target) or (
- os.path.getmtime(source) > os.path.getmtime(target)
- )
- def newer(
- source: str | bytes | os.PathLike[str] | os.PathLike[bytes],
- target: str | bytes | os.PathLike[str] | os.PathLike[bytes],
- ) -> bool:
- """
- 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(f"file {os.path.abspath(source)!r} does not exist")
- return _newer(source, target)
- def newer_pairwise(
- sources: Iterable[_SourcesT],
- targets: Iterable[_TargetsT],
- newer: Callable[[_SourcesT, _TargetsT], bool] = newer,
- ) -> tuple[list[_SourcesT], list[_TargetsT]]:
- """
- 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: Iterable[str | bytes | os.PathLike[str] | os.PathLike[bytes]],
- target: str | bytes | os.PathLike[str] | os.PathLike[bytes],
- missing: Literal["error", "ignore", "newer"] = "error",
- ) -> bool:
- """
- 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 not os.path.exists(target) or 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)
|