--- 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',
--- 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):
--- /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)))
--- 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',