view hgext/fsmonitor/state.py @ 45755:8ed69bd42f10 stable

demandimport: don't raise AttributeError if `exec_module` is missing I assume this was meant to do the check gracefully. After shoveling a bunch of modules into the ignore list in order to get keyring to work out of the box on CentOS 8, I hit the following error accessing the password, which the change fixes. Now the SecretStorage backend works out of the box, without any edits to the ignore list. ** Unknown exception encountered with possibly-broken third-party extension mercurial_keyring ** which supports versions unknown of Mercurial. ** Please disable mercurial_keyring and try your action again. ** If that fixes the bug please report it to https://foss.heptapod.net/mercurial/mercurial_keyring/issues ** Python 3.6.8 (default, Apr 16 2020, 01:36:27) [GCC 8.3.1 20191121 (Red Hat 8.3.1-5)] ** Mercurial Distributed SCM (version 5.5.2) ** Extensions loaded: evolve, topic, rebase, absorb, mercurial_keyring Traceback (most recent call last): File "/home/mharbison/hg_py3.6.8_venv/lib64/python3.6/site-packages/mercurial_keyring.py", line 230, in _read_password_from_keyring password = keyring.get_password(KEYRING_SERVICE, pwdkey) File "/home/mharbison/hg_py3.6.8_venv/lib64/python3.6/site-packages/keyring/core.py", line 53, in get_password return _keyring_backend.get_password(service_name, username) File "/home/mharbison/hg_py3.6.8_venv/lib64/python3.6/site-packages/keyring/backends/chainer.py", line 51, in get_password password = keyring.get_password(service, username) File "/home/mharbison/hg_py3.6.8_venv/lib64/python3.6/site-packages/keyring/backends/SecretService.py", line 79, in get_password return item.get_secret().decode('utf-8') File "/home/mharbison/hg_py3.6.8_venv/lib64/python3.6/site-packages/secretstorage/item.py", line 105, in get_secret decryptor = Cipher(aes, modes.CBC(aes_iv), default_backend()).decryptor() File "/home/mharbison/hg_py3.6.8_venv/lib64/python3.6/site-packages/cryptography/hazmat/backends/__init__.py", line 15, in default_backend from cryptography.hazmat.backends.openssl.backend import backend File "<frozen importlib._bootstrap>", line 971, in _find_and_load File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 665, in _load_unlocked File "/home/mharbison/hg_py3.6.8_venv/lib64/python3.6/site-packages/hgdemandimport/demandimportpy3.py", line 53, in exec_module self.loader.exec_module(module) File "/home/mharbison/hg_py3.6.8_venv/lib64/python3.6/site-packages/cryptography/hazmat/backends/openssl/__init__.py", line 7, in <module> from cryptography.hazmat.backends.openssl.backend import backend File "<frozen importlib._bootstrap>", line 971, in _find_and_load File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 665, in _load_unlocked File "/home/mharbison/hg_py3.6.8_venv/lib64/python3.6/site-packages/hgdemandimport/demandimportpy3.py", line 53, in exec_module self.loader.exec_module(module) File "/home/mharbison/hg_py3.6.8_venv/lib64/python3.6/site-packages/cryptography/hazmat/backends/openssl/backend.py", line 14, in <module> from six.moves import range File "<frozen importlib._bootstrap>", line 971, in _find_and_load File "<frozen importlib._bootstrap>", line 951, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 894, in _find_spec File "/home/mharbison/hg_py3.6.8_venv/lib64/python3.6/site-packages/hgdemandimport/demandimportpy3.py", line 117, in find_spec and getattr(spec.loader, "exec_module") AttributeError: '_SixMetaPathImporter' object has no attribute 'exec_module' Differential Revision: https://phab.mercurial-scm.org/D9243
author Matt Harbison <matt_harbison@yahoo.com>
date Thu, 22 Oct 2020 18:38:41 -0400
parents 8a9e53b974ee
children 6000f5b25c9b
line wrap: on
line source

# state.py - fsmonitor persistent state
#
# Copyright 2013-2016 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

from __future__ import absolute_import

import errno
import os
import socket
import struct

from mercurial.i18n import _
from mercurial import (
    encoding,
    pathutil,
    util,
)

_version = 4
_versionformat = b">I"


class state(object):
    def __init__(self, repo):
        self._vfs = repo.vfs
        self._ui = repo.ui
        self._rootdir = pathutil.normasprefix(repo.root)
        self._lastclock = None
        self._identity = util.filestat(None)

        self.mode = self._ui.config(b'fsmonitor', b'mode')
        self.walk_on_invalidate = self._ui.configbool(
            b'fsmonitor', b'walk_on_invalidate'
        )
        self.timeout = float(self._ui.config(b'fsmonitor', b'timeout'))

    def get(self):
        try:
            file = self._vfs(b'fsmonitor.state', b'rb')
        except IOError as inst:
            self._identity = util.filestat(None)
            if inst.errno != errno.ENOENT:
                raise
            return None, None, None

        self._identity = util.filestat.fromfp(file)

        versionbytes = file.read(4)
        if len(versionbytes) < 4:
            self._ui.log(
                b'fsmonitor',
                b'fsmonitor: state file only has %d bytes, '
                b'nuking state\n' % len(versionbytes),
            )
            self.invalidate()
            return None, None, None
        try:
            diskversion = struct.unpack(_versionformat, versionbytes)[0]
            if diskversion != _version:
                # different version, nuke state and start over
                self._ui.log(
                    b'fsmonitor',
                    b'fsmonitor: version switch from %d to '
                    b'%d, nuking state\n' % (diskversion, _version),
                )
                self.invalidate()
                return None, None, None

            state = file.read().split(b'\0')
            # state = hostname\0clock\0ignorehash\0 + list of files, each
            # followed by a \0
            if len(state) < 3:
                self._ui.log(
                    b'fsmonitor',
                    b'fsmonitor: state file truncated (expected '
                    b'3 chunks, found %d), nuking state\n',
                    len(state),
                )
                self.invalidate()
                return None, None, None
            diskhostname = state[0]
            hostname = encoding.strtolocal(socket.gethostname())
            if diskhostname != hostname:
                # file got moved to a different host
                self._ui.log(
                    b'fsmonitor',
                    b'fsmonitor: stored hostname "%s" '
                    b'different from current "%s", nuking state\n'
                    % (diskhostname, hostname),
                )
                self.invalidate()
                return None, None, None

            clock = state[1]
            ignorehash = state[2]
            # discard the value after the last \0
            notefiles = state[3:-1]

        finally:
            file.close()

        return clock, ignorehash, notefiles

    def set(self, clock, ignorehash, notefiles):
        if clock is None:
            self.invalidate()
            return

        # Read the identity from the file on disk rather than from the open file
        # pointer below, because the latter is actually a brand new file.
        identity = util.filestat.frompath(self._vfs.join(b'fsmonitor.state'))
        if identity != self._identity:
            self._ui.debug(
                b'skip updating fsmonitor.state: identity mismatch\n'
            )
            return

        try:
            file = self._vfs(
                b'fsmonitor.state', b'wb', atomictemp=True, checkambig=True
            )
        except (IOError, OSError):
            self._ui.warn(_(b"warning: unable to write out fsmonitor state\n"))
            return

        with file:
            file.write(struct.pack(_versionformat, _version))
            file.write(encoding.strtolocal(socket.gethostname()) + b'\0')
            file.write(clock + b'\0')
            file.write(ignorehash + b'\0')
            if notefiles:
                file.write(b'\0'.join(notefiles))
                file.write(b'\0')

    def invalidate(self):
        try:
            os.unlink(os.path.join(self._rootdir, b'.hg', b'fsmonitor.state'))
        except OSError as inst:
            if inst.errno != errno.ENOENT:
                raise
        self._identity = util.filestat(None)

    def setlastclock(self, clock):
        self._lastclock = clock

    def getlastclock(self):
        return self._lastclock