# HG changeset patch # User Jason R. Coombs # Date 2023-03-22 00:47:30 # Node ID 82e5a9b1ef1ea7f8b2bc351df75fa3a67f5cf9a4 # Parent e8f1e0e295bb0b7a92848da1fb16867c82f98bf9 extras: re-use Projection from jaraco.collections diff --git a/contrib/import-checker.py b/contrib/import-checker.py --- a/contrib/import-checker.py +++ b/contrib/import-checker.py @@ -44,6 +44,7 @@ allowsymbolimports = ( # third-party imports should be directly imported 'mercurial.thirdparty', 'mercurial.thirdparty.attr', + 'mercurial.thirdparty.jaraco.collections', 'mercurial.thirdparty.zope', 'mercurial.thirdparty.zope.interface', 'typing', diff --git a/hgext/rebase.py b/hgext/rebase.py --- a/hgext/rebase.py +++ b/hgext/rebase.py @@ -24,6 +24,7 @@ from mercurial.node import ( wdirrev, ) from mercurial.pycompat import open +from mercurial.thirdparty.jaraco.collections import Projection from mercurial import ( bookmarks, cmdutil, @@ -52,6 +53,7 @@ from mercurial import ( util, ) + # The following constants are used throughout the rebase module. The ordering of # their values must be maintained. @@ -93,19 +95,8 @@ def retained_extras(): yield b'intermediate-source' -def _project(orig, names): - """Project a subset of names from orig.""" - names_saved = tuple(names) - values = (orig.get(name, None) for name in names_saved) - return { - name: value - for name, value in zip(names_saved, values) - if value is not None - } - - def _save_extras(ctx, extra): - extra.update(_project(ctx.extra(), retained_extras())) + extra.update(Projection(retained_extras(), ctx.extra())) def _savebranch(ctx, extra): diff --git a/mercurial/thirdparty/jaraco/__init__.py b/mercurial/thirdparty/jaraco/__init__.py new file mode 100644 diff --git a/mercurial/thirdparty/jaraco/collections.py b/mercurial/thirdparty/jaraco/collections.py new file mode 100644 --- /dev/null +++ b/mercurial/thirdparty/jaraco/collections.py @@ -0,0 +1,56 @@ +# adapted from jaraco.collections 3.9 + +import collections + + +class Projection(collections.abc.Mapping): + """ + Project a set of keys over a mapping + + >>> sample = {'a': 1, 'b': 2, 'c': 3} + >>> prj = Projection(['a', 'c', 'd'], sample) + >>> prj == {'a': 1, 'c': 3} + True + + Keys should only appear if they were specified and exist in the space. + + >>> sorted(list(prj.keys())) + ['a', 'c'] + + Attempting to access a key not in the projection + results in a KeyError. + + >>> prj['b'] + Traceback (most recent call last): + ... + KeyError: 'b' + + Use the projection to update another dict. + + >>> target = {'a': 2, 'b': 2} + >>> target.update(prj) + >>> target == {'a': 1, 'b': 2, 'c': 3} + True + + Also note that Projection keeps a reference to the original dict, so + if you modify the original dict, that could modify the Projection. + + >>> del sample['a'] + >>> dict(prj) + {'c': 3} + """ + + def __init__(self, keys, space): + self._keys = tuple(keys) + self._space = space + + def __getitem__(self, key): + if key not in self._keys: + raise KeyError(key) + return self._space[key] + + def __iter__(self): + return iter(set(self._keys).intersection(self._space)) + + def __len__(self): + return len(tuple(iter(self))) diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -1302,6 +1302,7 @@ packages = [ 'mercurial.templates', 'mercurial.thirdparty', 'mercurial.thirdparty.attr', + 'mercurial.thirdparty.jaraco', 'mercurial.thirdparty.zope', 'mercurial.thirdparty.zope.interface', 'mercurial.upgrade_utils',