view hgdemandimport/demandimportpy3.py @ 35792:1d118f9f4f57

bundle2: always advertise client support for stream parts Previously, enabling of stream clone over bundle2 was a server-side only change. And clients would only advertise bundle2 support for stream clones if an experimental config option was set. This situation wasn't forward compatible because in the future (when the feature is enabled on servers by default), an old client would send a request to the server but it wouldn't send its own bundle2 capability support for stream parts. Servers would have to infer that clients not sending this capability were old Mercurial clients that only sent the capability if the feature was explicitly enabled. Implicit and inferred behavior makes implementing servers hard. It's much better to be explicit about client features. We should either make the config option for bundle2 stream clones disable the feature client-side as well (so a server doesn't see a request from a client not advertising stream support). Or we should always advertise stream support if a client is willing to accept stream parts. Since I anticipating stabilizing stream clone support in bundle2 quickly, I think it's safe to always advertise client support in the bundle2 capabilities. So this commit changes things to do that. Because capabilities now vary between client and server, we had to create variations of the variable substitutions for those strings. Differential Revision: https://phab.mercurial-scm.org/D1931
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 22 Jan 2018 12:23:47 -0800
parents fcb1ecf2bef7
children 670eb4fa1b86
line wrap: on
line source

# demandimportpy3 - global demand-loading of modules for Mercurial
#
# Copyright 2017 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.

"""Lazy loading for Python 3.6 and above.

This uses the new importlib finder/loader functionality available in Python 3.5
and up. The code reuses most of the mechanics implemented inside importlib.util,
but with a few additions:

* Allow excluding certain modules from lazy imports.
* Expose an interface that's substantially the same as demandimport for
  Python 2.

This also has some limitations compared to the Python 2 implementation:

* Much of the logic is per-package, not per-module, so any packages loaded
  before demandimport is enabled will not be lazily imported in the future. In
  practice, we only expect builtins to be loaded before demandimport is
  enabled.
"""

# This line is unnecessary, but it satisfies test-check-py3-compat.t.
from __future__ import absolute_import

import contextlib
import importlib.abc
import importlib.machinery
import importlib.util
import sys

_deactivated = False

class _lazyloaderex(importlib.util.LazyLoader):
    """This is a LazyLoader except it also follows the _deactivated global and
    the ignore list.
    """
    def exec_module(self, module):
        """Make the module load lazily."""
        if _deactivated or module.__name__ in ignore:
            self.loader.exec_module(module)
        else:
            super().exec_module(module)

# This is 3.6+ because with Python 3.5 it isn't possible to lazily load
# extensions. See the discussion in https://bugs.python.org/issue26186 for more.
_extensions_loader = _lazyloaderex.factory(
    importlib.machinery.ExtensionFileLoader)
_bytecode_loader = _lazyloaderex.factory(
    importlib.machinery.SourcelessFileLoader)
_source_loader = _lazyloaderex.factory(importlib.machinery.SourceFileLoader)

def _makefinder(path):
    return importlib.machinery.FileFinder(
        path,
        # This is the order in which loaders are passed in in core Python.
        (_extensions_loader, importlib.machinery.EXTENSION_SUFFIXES),
        (_source_loader, importlib.machinery.SOURCE_SUFFIXES),
        (_bytecode_loader, importlib.machinery.BYTECODE_SUFFIXES),
    )

ignore = []

def init(ignorelist):
    global ignore
    ignore = ignorelist

def isenabled():
    return _makefinder in sys.path_hooks and not _deactivated

def disable():
    try:
        while True:
            sys.path_hooks.remove(_makefinder)
    except ValueError:
        pass

def enable():
    sys.path_hooks.insert(0, _makefinder)

@contextlib.contextmanager
def deactivated():
    # This implementation is a bit different from Python 2's. Python 3
    # maintains a per-package finder cache in sys.path_importer_cache (see
    # PEP 302). This means that we can't just call disable + enable.
    # If we do that, in situations like:
    #
    #   demandimport.enable()
    #   ...
    #   from foo.bar import mod1
    #   with demandimport.deactivated():
    #       from foo.bar import mod2
    #
    # mod2 will be imported lazily. (The converse also holds -- whatever finder
    # first gets cached will be used.)
    #
    # Instead, have a global flag the LazyLoader can use.
    global _deactivated
    demandenabled = isenabled()
    if demandenabled:
        _deactivated = True
    try:
        yield
    finally:
        if demandenabled:
            _deactivated = False