view tests/lib-stat.py @ 372:79192d0078dc

Provide a "pats" (patterns) argument to _do_restore_timestamps() to be used for FILE parameters given on the command line. "pats" are used for filtering too. It could be implemented by giving an intersectmater to the given matcher parameter.
author Franz Glasner <fzglas.hg@dom66.de>
date Tue, 05 Mar 2019 16:23:40 +0100
parents 7ec353866f70
children 379050873141
line wrap: on
line source

"""Cross-platform file modification check.

This is because the stat(1) utility has incompatible cross-platform usage.

Usage: lib-stat.py [ OPTIONS ] FILE ...

Options:

  -c TS, --check=TS    Assert that the given timestamp TS matches the given
                       FILE's modtime
  -d TS, --differs=TS  Assert that the given timestamp TS differs from the
                       given FILE's modtime

If no option is given print the modification time for each FILE to stdout.

"""

from __future__ import absolute_import, print_function


import datetime
import getopt
import os
import sys


class FloatTimesInStat(object):
    """Context manager to ensure that stat returns float values.

    For 3.7 <= Mercurial < 4.6: Mercurial calls :func:`os.stat_float_times`
    for `stat` and friends to return :class:`int` values.

    Temporarily fix this.

    """

    __slots__ = ("_do_reset",)

    def __init__(self):
        self._do_reset = False

    def __enter__(self):
        if not os.stat_float_times():
            os.stat_float_times(True)
            self._do_reset = True
        return self

    def __exit__(self, *args):
        if self._do_reset:
            os.stat_float_times(False)


def to_isoformat(t):
    """Return the POSIX timestamp `t` formatted in full ISO format.

    `t` is expected to be in the UTC timezone.

    """
    dt = datetime.datetime.utcfromtimestamp(t)
    if dt.utcoffset() is None:
        return dt.isoformat() + "Z"
    else:
        return dt.isoformat()


def main():
    opt_check = None
    opt_differs = None

    opts, args = getopt.getopt(sys.argv[1:],
                               "c:d:",
                               ["check=",
                                "differs="])
    for opt, val in opts:
        if opt in ("-c", "--check"):
            opt_check = val
        elif opt in ("-d", "--differs"):
            opt_differs = val
        else:
            assert False

    if opt_check:
        with FloatTimesInStat():
            for f in args:
                st = os.lstat(f)
                tss = to_isoformat(st.st_mtime)
                if tss != opt_check:
                    print(
                        "mtime does not match for file `{filename}': expected {expected}, current: {current}".format(
                            filename=f,
                            expected=opt_check,
                            current=tss),
                        file=sys.stderr)
                    return 1
    elif opt_differs:
        with FloatTimesInStat():
            for f in args:
                st = os.lstat(f)
                tss = to_isoformat(st.st_mtime)
                if tss == opt_differs:
                    print(
                        "mtime does not differ for file `{filename}': {timestamp}".format(
                            filename=f,
                            timestamp=tss),
                        file=sys.stderr)
                    return 1
    else:
        with FloatTimesInStat():
            for f in args:
                st = os.lstat(f)
                print(to_isoformat(st.st_mtime))
    return 0


if __name__ == "__main__":
    sys.exit(main())