##// END OF EJS Templates
emitrevision: consider ancestors revision to emit as available base...
emitrevision: consider ancestors revision to emit as available base This should make more delta base valid. This notably affects: * case where we skipped some parent with empty delta to directly delta against an ancestors * case where an intermediate snapshots is stored. This change means we could sent largish intermediate snapshots over the wire. However this is actually a sub goal here. Sending snapshots over the wire means the client have a high odd of simply storing the pre-computed delta instead of doing a lengthy process that will… end up doing the same intermediate snapshot. In addition the overall size of snapshot (or any level) is "only" some or the overall delta size. (0.17% for my mercurial clone, 20% for my clone of Mozilla try). So Sending them other the wire is unlikely to change large impact on the bandwidth used. If we decide that minimising the bandwidth is an explicit goal, we should introduce new logic to filter-out snapshot as delta. The current code has no notion explicite of snapshot so far, they just tended to fall into the wobbly filtering options. In some cases, this patch can yield large improvement to the bundling time: ### data-env-vars.name = mozilla-try-2019-02-18-zstd-sparse-revlog # benchmark.name = perf-bundle # benchmark.variants.revs = last-100000 before: 68.787066 seconds after: 47.552677 seconds (-30.87%) That translate to large improvement to the pull time : ### data-env-vars.name = mozilla-try-2019-02-18-zstd-sparse-revlog # benchmark.name = pull # benchmark.variants.issue6528 = disabled # benchmark.variants.revs = last-100000 before: 142.186625 seconds after: 75.897745 seconds (-46.62%) No significant negative impact have been observed.

File last commit:

r50669:48e38b17 stable
r50685:f463eb67 stable
Show More
demandimportpy3.py
174 lines | 5.1 KiB | text/x-python | PythonLexer
# 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.
import contextlib
import importlib.util
import sys
from . import tracing
_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."""
with tracing.log('demandimport %s', module):
if _deactivated or module.__name__ in ignores:
self.loader.exec_module(module)
else:
super().exec_module(module)
class LazyFinder:
"""A wrapper around a ``MetaPathFinder`` that makes loaders lazy.
``sys.meta_path`` finders have their ``find_spec()`` called to locate a
module. This returns a ``ModuleSpec`` if found or ``None``. The
``ModuleSpec`` has a ``loader`` attribute, which is called to actually
load a module.
Our class wraps an existing finder and overloads its ``find_spec()`` to
replace the ``loader`` with our lazy loader proxy.
We have to use __getattribute__ to proxy the instance because some meta
path finders don't support monkeypatching.
"""
__slots__ = ("_finder",)
def __init__(self, finder):
object.__setattr__(self, "_finder", finder)
def __repr__(self):
return "<LazyFinder for %r>" % object.__getattribute__(self, "_finder")
# __bool__ is canonical Python 3. But check-code insists on __nonzero__ being
# defined via `def`.
def __nonzero__(self):
return bool(object.__getattribute__(self, "_finder"))
__bool__ = __nonzero__
def __getattribute__(self, name):
if name in ("_finder", "find_spec"):
return object.__getattribute__(self, name)
return getattr(object.__getattribute__(self, "_finder"), name)
def __delattr__(self, name):
return delattr(object.__getattribute__(self, "_finder"), name)
def __setattr__(self, name, value):
return setattr(object.__getattribute__(self, "_finder"), name, value)
def find_spec(self, fullname, path, target=None):
finder = object.__getattribute__(self, "_finder")
try:
find_spec = finder.find_spec
except AttributeError:
loader = finder.find_module(fullname, path)
if loader is None:
spec = None
else:
spec = importlib.util.spec_from_loader(fullname, loader)
else:
spec = find_spec(fullname, path, target)
# Lazy loader requires exec_module().
if (
spec is not None
and spec.loader is not None
and getattr(spec.loader, "exec_module", None)
):
spec.loader = _lazyloaderex(spec.loader)
return spec
ignores = set()
def init(ignoreset):
global ignores
ignores = ignoreset
def isenabled():
return not _deactivated and any(
isinstance(finder, LazyFinder) for finder in sys.meta_path
)
def disable():
new_finders = []
for finder in sys.meta_path:
new_finders.append(
finder._finder if isinstance(finder, LazyFinder) else finder
)
sys.meta_path[:] = new_finders
def enable():
new_finders = []
for finder in sys.meta_path:
new_finders.append(
LazyFinder(finder) if not isinstance(finder, LazyFinder) else finder
)
sys.meta_path[:] = new_finders
@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