##// END OF EJS Templates
tests: drop references to the vendored copy of `zope`...
tests: drop references to the vendored copy of `zope` The `test-check-interfaces.py` test has mostly been a no-op since ef7d85089952. Somehow, checks are still done on mere imports, as these errors were seen when subclassing `Protocol` and adding the `self` argument to the repository interfaces. So just get rid of it. --- /builds/mercurial-ci/tests/test-check-interfaces.py.out +++ /builds/mercurial-ci/tests/test-check-interfaces.py.err @@ -0,0 +1,16 @@ +Traceback (most recent call last): + File "/builds/mercurial-ci/tests/test-check-interfaces.py", line 12, in <module> + from mercurial.interfaces import ( + File "/tmp/hgtests.hl7bqyl0/install/lib/python/mercurial/interfaces/repository.py", line 401, in <module> + @interfaceutil.implementer(ipeerbase) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/tmp/hgtests.hl7bqyl0/install/lib/python/mercurial/thirdparty/zope/interface/declarations.py", line 388, in __call__ + classImplements(ob, *self.interfaces) + File "/tmp/hgtests.hl7bqyl0/install/lib/python/mercurial/thirdparty/zope/interface/declarations.py", line 327, in classImplements + spec.declared += tuple(_normalizeargs(interfaces)) + ^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "/tmp/hgtests.hl7bqyl0/install/lib/python/mercurial/thirdparty/zope/interface/declarations.py", line 910, in _normalizeargs + _normalizeargs(v, output) + File "/tmp/hgtests.hl7bqyl0/install/lib/python/mercurial/thirdparty/zope/interface/declarations.py", line 909, in _normalizeargs + for v in sequence: +TypeError: '_ProtocolMeta' object is not iterable ERROR: test-check-interfaces.py output changed Additionally, as will be seen in the next commit, the fact that this code is imported at all has an influence on pytype checking, even when it shouldn't be getting used. Any replacement test will likely be a python file that instantiates things and tries to assign them to variables annotated with a Protocol, and is then checked with pytype. But in the meantime, the explicit subclassing of the Protocol classes will give us some coverage.

File last commit:

r52952:b332ae61 default
r53340:0cc50d9a default
Show More
timestamp.py
180 lines | 5.7 KiB | text/x-python | PythonLexer
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 # Copyright Mercurial Contributors
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Matt Harbison
typing: add `from __future__ import annotations` to most files...
r52756 from __future__ import annotations
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079
Simon Sapin
dirstate: ignore sub-second component when either is zero in mtime...
r49081 import functools
dirstate: move "get fs now" in the timestamp utility module...
r49202 import os
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 import stat
Raphaël Gomès
timestamp: add type information to the module...
r52949 import time
from typing import Optional, Tuple
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079
dirstate: drop comparison primitive on the timestamp class...
r49226 from .. import error
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079
rangemask = 0x7FFFFFFF
Simon Sapin
dirstate: ignore sub-second component when either is zero in mtime...
r49081 @functools.total_ordering
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 class timestamp(tuple):
"""
Simon Sapin
dirstate: ignore sub-second component when either is zero in mtime...
r49081 A Unix timestamp with optional nanoseconds precision,
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 modulo 2**31 seconds.
Simon Sapin
dirstate: Document Timestamp.second_ambiguous...
r49271 A 3-tuple containing:
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079
`truncated_seconds`: seconds since the Unix epoch,
truncated to its lower 31 bits
`subsecond_nanoseconds`: number of nanoseconds since `truncated_seconds`.
Simon Sapin
dirstate: ignore sub-second component when either is zero in mtime...
r49081 When this is zero, the sub-second precision is considered unknown.
Simon Sapin
dirstate: Document Timestamp.second_ambiguous...
r49271
`second_ambiguous`: whether this timestamp is still "reliable"
(see `reliable_mtime_of`) if we drop its sub-second component.
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 """
def __new__(cls, value):
dirstate-item: add a "second_ambiguous` flag in the mtime tuple...
r49227 truncated_seconds, subsec_nanos, second_ambiguous = value
value = (truncated_seconds & rangemask, subsec_nanos, second_ambiguous)
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 return super(timestamp, cls).__new__(cls, value)
Simon Sapin
dirstate: ignore sub-second component when either is zero in mtime...
r49081 def __eq__(self, other):
dirstate: drop comparison primitive on the timestamp class...
r49226 raise error.ProgrammingError(
'timestamp should never be compared directly'
Simon Sapin
dirstate: ignore sub-second component when either is zero in mtime...
r49081 )
def __gt__(self, other):
dirstate: drop comparison primitive on the timestamp class...
r49226 raise error.ProgrammingError(
'timestamp should never be compared directly'
)
Simon Sapin
dirstate: ignore sub-second component when either is zero in mtime...
r49081
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079
Raphaël Gomès
timestamp: add type information to the module...
r52949 def get_fs_now(vfs) -> Optional[timestamp]:
dirstate: move "get fs now" in the timestamp utility module...
r49202 """return a timestamp for "now" in the current vfs
This will raise an exception if no temporary files could be created.
"""
tmpfd, tmpname = vfs.mkstemp()
try:
return mtime_of(os.fstat(tmpfd))
finally:
os.close(tmpfd)
vfs.unlink(tmpname)
Raphaël Gomès
timestamp: add type information to the module...
r52949 def zero() -> timestamp:
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 """
Returns the `timestamp` at the Unix epoch.
"""
return tuple.__new__(timestamp, (0, 0))
Raphaël Gomès
timestamp: add type information to the module...
r52949 def mtime_of(stat_result: os.stat_result) -> timestamp:
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079 """
Takes an `os.stat_result`-like object and returns a `timestamp` object
for its modification time.
"""
Simon Sapin
dirstate-v2: actually use sub-second mtime precision...
r49082 try:
# TODO: add this attribute to `osutil.stat` objects,
# see `mercurial/cext/osutil.c`.
#
# This attribute is also not available on Python 2.
nanos = stat_result.st_mtime_ns
except AttributeError:
# https://docs.python.org/2/library/os.html#os.stat_float_times
# "For compatibility with older Python versions,
# accessing stat_result as a tuple always returns integers."
secs = stat_result[stat.ST_MTIME]
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079
Simon Sapin
dirstate-v2: actually use sub-second mtime precision...
r49082 subsec_nanos = 0
else:
billion = int(1e9)
secs = nanos // billion
subsec_nanos = nanos % billion
Simon Sapin
dirstate: store mtimes with nanosecond precision in memory...
r49079
dirstate-item: add a "second_ambiguous` flag in the mtime tuple...
r49227 return timestamp((secs, subsec_nanos, False))
status: move the boundary comparison logic within the timestamp module...
r49224
Raphaël Gomès
timestamp: add type information to the module...
r52949 def reliable_mtime_of(
stat_result: os.stat_result, present_mtime: timestamp
) -> Optional[timestamp]:
Raphaël Gomès
timestamp: make the reliable comparison more usable from outside...
r52950 """Wrapper for `make_mtime_reliable` for stat objects"""
file_mtime = mtime_of(stat_result)
return make_mtime_reliable(file_mtime, present_mtime)
def make_mtime_reliable(
file_timestamp: timestamp, present_mtime: timestamp
) -> Optional[timestamp]:
Simon Sapin
dirstate: Document Timestamp.second_ambiguous...
r49271 """Same as `mtime_of`, but return `None` or a `Timestamp` with
`second_ambiguous` set if the date might be ambiguous.
status: move the boundary comparison logic within the timestamp module...
r49224
A modification time is reliable if it is older than "present_time" (or
Simon Sapin
rhg: Update the dirstate on disk after status...
r49250 sufficiently in the future).
status: move the boundary comparison logic within the timestamp module...
r49224
Otherwise a concurrent modification might happens with the same mtime.
"""
Raphaël Gomès
timestamp: make the reliable comparison more usable from outside...
r52950 file_second = file_timestamp[0]
file_ns = file_timestamp[1]
status: move the boundary comparison logic within the timestamp module...
r49224 boundary_second = present_mtime[0]
status: keep second-ambiguous mtimes during fixup...
r49232 boundary_ns = present_mtime[1]
status: move the boundary comparison logic within the timestamp module...
r49224 # If the mtime of the ambiguous file is younger (or equal) to the starting
# point of the `status` walk, we cannot garantee that another, racy, write
# will not happen right after with the same mtime and we cannot cache the
# information.
#
status: keep second-ambiguous mtimes during fixup...
r49232 # However if the mtime is far away in the future, this is likely some
status: move the boundary comparison logic within the timestamp module...
r49224 # mismatch between the current clock and previous file system operation. So
# mtime more than one days in the future are considered fine.
status: keep second-ambiguous mtimes during fixup...
r49232 if boundary_second == file_second:
if file_ns and boundary_ns:
if file_ns < boundary_ns:
return timestamp((file_second, file_ns, True))
return None
elif boundary_second < file_second < (3600 * 24 + boundary_second):
status: move the boundary comparison logic within the timestamp module...
r49224 return None
else:
Raphaël Gomès
timestamp: make the reliable comparison more usable from outside...
r52950 return file_timestamp
Raphaël Gomès
merge: improve working-copy mtime race handling...
r52952
FS_TICK_WAIT_TIMEOUT = 0.1 # 100 milliseconds
def wait_until_fs_tick(vfs) -> Optional[Tuple[timestamp, bool]]:
"""Wait until the next update from the filesystem time by writing in a loop
a new temporary file inside the working directory and checking if its time
differs from the first one observed.
Returns `None` if we are unable to get the filesystem time,
`(timestamp, True)` if we've timed out waiting for the filesystem clock
to tick, and `(timestamp, False)` if we've waited successfully.
On Linux, your average tick is going to be a "jiffy", or 1/HZ.
HZ is your kernel's tick rate (if it has one configured) and the value
is the one returned by `grep 'CONFIG_HZ=' /boot/config-$(uname -r)`,
again assuming a normal setup.
In my case (Alphare) at the time of writing, I get `CONFIG_HZ=250`,
which equates to 4ms.
This might change with a series that could make it to Linux 6.12:
https://lore.kernel.org/all/20241002-mgtime-v10-8-d1c4717f5284@kernel.org
"""
start = time.monotonic()
try:
old_fs_time = get_fs_now(vfs)
new_fs_time = get_fs_now(vfs)
while (
new_fs_time[0] == old_fs_time[0]
and new_fs_time[1] == old_fs_time[1]
):
if time.monotonic() - start > FS_TICK_WAIT_TIMEOUT:
return (old_fs_time, True)
new_fs_time = get_fs_now(vfs)
except OSError:
return None
else:
return (new_fs_time, False)