mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Use microsecond-resolution timestamps for outdated file detection (#11435)
Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
This commit is contained in:
parent
d6f10904a3
commit
ecc8613fc4
@ -496,11 +496,7 @@ class Builder:
|
||||
doctree = publisher.document
|
||||
|
||||
# store time of reading, for outdated files detection
|
||||
# (Some filesystems have coarse timestamp resolution;
|
||||
# therefore time.time() can be older than filesystem's timestamp.
|
||||
# For example, FAT32 has 2sec timestamp resolution.)
|
||||
self.env.all_docs[docname] = max(time.time(),
|
||||
path.getmtime(self.env.doc2path(docname)))
|
||||
self.env.all_docs[docname] = time.time_ns() // 1_000
|
||||
|
||||
# cleanup
|
||||
self.env.temp_data.clear()
|
||||
|
@ -7,7 +7,7 @@ import os
|
||||
import pickle
|
||||
from collections import defaultdict
|
||||
from copy import copy
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from os import path
|
||||
from typing import TYPE_CHECKING, Any, Callable, Generator, Iterator
|
||||
|
||||
@ -55,7 +55,7 @@ default_settings: dict[str, Any] = {
|
||||
|
||||
# This is increased every time an environment attribute is added
|
||||
# or changed to properly invalidate pickle files.
|
||||
ENV_VERSION = 57
|
||||
ENV_VERSION = 58
|
||||
|
||||
# config status
|
||||
CONFIG_OK = 1
|
||||
@ -166,9 +166,9 @@ class BuildEnvironment:
|
||||
# All "docnames" here are /-separated and relative and exclude
|
||||
# the source suffix.
|
||||
|
||||
# docname -> mtime at the time of reading
|
||||
# docname -> time of reading (in integer microseconds)
|
||||
# contains all read docnames
|
||||
self.all_docs: dict[str, float] = {}
|
||||
self.all_docs: dict[str, int] = {}
|
||||
# docname -> set of dependent file
|
||||
# names, relative to documentation root
|
||||
self.dependencies: dict[str, set[str]] = defaultdict(set)
|
||||
@ -481,12 +481,14 @@ class BuildEnvironment:
|
||||
continue
|
||||
# check the mtime of the document
|
||||
mtime = self.all_docs[docname]
|
||||
newmtime = path.getmtime(self.doc2path(docname))
|
||||
newmtime = _last_modified_time(self.doc2path(docname))
|
||||
if newmtime > mtime:
|
||||
# convert integer microseconds to floating-point seconds,
|
||||
# and then to timezone-aware datetime objects.
|
||||
mtime_dt = datetime.fromtimestamp(mtime / 1_000_000, tz=timezone.utc)
|
||||
newmtime_dt = datetime.fromtimestamp(mtime / 1_000_000, tz=timezone.utc)
|
||||
logger.debug('[build target] outdated %r: %s -> %s',
|
||||
docname,
|
||||
datetime.utcfromtimestamp(mtime),
|
||||
datetime.utcfromtimestamp(newmtime))
|
||||
docname, mtime_dt, newmtime_dt)
|
||||
changed.add(docname)
|
||||
continue
|
||||
# finally, check the mtime of dependencies
|
||||
@ -497,7 +499,7 @@ class BuildEnvironment:
|
||||
if not path.isfile(deppath):
|
||||
changed.add(docname)
|
||||
break
|
||||
depmtime = path.getmtime(deppath)
|
||||
depmtime = _last_modified_time(deppath)
|
||||
if depmtime > mtime:
|
||||
changed.add(docname)
|
||||
break
|
||||
@ -728,3 +730,18 @@ class BuildEnvironment:
|
||||
for domain in self.domains.values():
|
||||
domain.check_consistency()
|
||||
self.events.emit('env-check-consistency', self)
|
||||
|
||||
|
||||
def _last_modified_time(filename: str | os.PathLike[str]) -> int:
|
||||
"""Return the last modified time of ``filename``.
|
||||
|
||||
The time is returned as integer microseconds.
|
||||
The lowest common denominator of modern file-systems seems to be
|
||||
microsecond-level precision.
|
||||
|
||||
We prefer to err on the side of re-rendering a file,
|
||||
so we round up to the nearest microsecond.
|
||||
"""
|
||||
|
||||
# upside-down floor division to get the ceiling
|
||||
return -(os.stat(filename).st_mtime_ns // -1_000)
|
||||
|
Loading…
Reference in New Issue
Block a user