From b3d70a7a93ec2e44372bc49c1a7cf398797c727a Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Thu, 26 May 2016 20:03:01 +0200 Subject: [PATCH] Use os.fstat wherever possible --- vdirsyncer/storage/filesystem.py | 7 +++--- vdirsyncer/utils/__init__.py | 37 ++++++++++++-------------------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/vdirsyncer/storage/filesystem.py b/vdirsyncer/storage/filesystem.py index 573f3e2..356c1e6 100644 --- a/vdirsyncer/storage/filesystem.py +++ b/vdirsyncer/storage/filesystem.py @@ -9,8 +9,7 @@ from atomicwrites import atomic_write from .base import Item, Storage, normalize_meta_value from .. import exceptions -from ..utils import checkdir, expand_path, generate_href, get_etag_from_file, \ - get_etag_from_fileobject +from ..utils import checkdir, expand_path, generate_href, get_etag_from_file from ..utils.compat import text_type, to_native logger = logging.getLogger(__name__) @@ -137,7 +136,7 @@ class FilesystemStorage(Storage): try: with atomic_write(fpath, mode='wb', overwrite=False) as f: f.write(item.raw.encode(self.encoding)) - return fpath, get_etag_from_fileobject(f) + return fpath, get_etag_from_file(f) except OSError as e: if e.errno == errno.EEXIST: raise exceptions.AlreadyExistingError(existing_href=href) @@ -157,7 +156,7 @@ class FilesystemStorage(Storage): with atomic_write(fpath, mode='wb', overwrite=True) as f: f.write(item.raw.encode(self.encoding)) - etag = get_etag_from_fileobject(f) + etag = get_etag_from_file(f) if self.post_hook: self._run_post_hook(fpath) diff --git a/vdirsyncer/utils/__init__.py b/vdirsyncer/utils/__init__.py index 0a480b0..707d606 100644 --- a/vdirsyncer/utils/__init__.py +++ b/vdirsyncer/utils/__init__.py @@ -53,35 +53,26 @@ def uniq(s): yield x -def get_etag_from_file(fpath): - '''Get mtime-based etag from a filepath.''' - stat = os.stat(fpath) +def get_etag_from_file(f): + '''Get mtime-based etag from a filepath or file-like object. + + This function will flush/sync the file as much as necessary to obtain a + correct mtime. + ''' + if hasattr(f, 'read'): + f.flush() # Only this is necessary on Linux + if sys.platform == 'win32': + os.fsync(f.fileno()) # Apparently necessary on Windows + stat = os.fstat(f.fileno()) + else: + stat = os.stat(f) + mtime = getattr(stat, 'st_mtime_ns', None) if mtime is None: mtime = stat.st_mtime return '{:.9f}'.format(mtime) -def get_etag_from_fileobject(f): - ''' - Get mtime-based etag from a local file's fileobject. - - This function will flush/sync the file as much as necessary to obtain a - correct mtime. - - In filesystem-based storages, this is used instead of - :py:func:`get_etag_from_file` to determine the correct etag *before* - writing the temporary file to the target location. - - This works because, as far as I've tested, moving and linking a file - doesn't change its mtime. - ''' - f.flush() # Only this is necessary on Linux - if sys.platform == 'win32': - os.fsync(f.fileno()) # Apparently necessary on Windows - return get_etag_from_file(f.name) - - def get_storage_init_specs(cls, stop_at=object): if cls is stop_at: return ()