# HG changeset patch # User Jason R. Coombs # Date 1679446050 14400 # Node ID 82e5a9b1ef1ea7f8b2bc351df75fa3a67f5cf9a4 # Parent e8f1e0e295bb0b7a92848da1fb16867c82f98bf9 extras: re-use Projection from jaraco.collections diff -r e8f1e0e295bb -r 82e5a9b1ef1e contrib/import-checker.py --- a/contrib/import-checker.py Tue Mar 21 17:21:45 2023 -0400 +++ b/contrib/import-checker.py Tue Mar 21 20:47:30 2023 -0400 @@ -44,6 +44,7 @@ # 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 -r e8f1e0e295bb -r 82e5a9b1ef1e hgext/rebase.py --- a/hgext/rebase.py Tue Mar 21 17:21:45 2023 -0400 +++ b/hgext/rebase.py Tue Mar 21 20:47:30 2023 -0400 @@ -24,6 +24,7 @@ wdirrev, ) from mercurial.pycompat import open +from mercurial.thirdparty.jaraco.collections import Projection from mercurial import ( bookmarks, cmdutil, @@ -52,6 +53,7 @@ util, ) + # The following constants are used throughout the rebase module. The ordering of # their values must be maintained. @@ -93,19 +95,8 @@ 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 -r e8f1e0e295bb -r 82e5a9b1ef1e mercurial/thirdparty/jaraco/__init__.py diff -r e8f1e0e295bb -r 82e5a9b1ef1e mercurial/thirdparty/jaraco/collections.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/thirdparty/jaraco/collections.py Tue Mar 21 20:47:30 2023 -0400 @@ -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 -r e8f1e0e295bb -r 82e5a9b1ef1e setup.py --- a/setup.py Tue Mar 21 17:21:45 2023 -0400 +++ b/setup.py Tue Mar 21 20:47:30 2023 -0400 @@ -1302,6 +1302,7 @@ 'mercurial.templates', 'mercurial.thirdparty', 'mercurial.thirdparty.attr', + 'mercurial.thirdparty.jaraco', 'mercurial.thirdparty.zope', 'mercurial.thirdparty.zope.interface', 'mercurial.upgrade_utils',