##// END OF EJS Templates
rust-index: handle `MixedIndex` in `pyindex_to_graph`...
marmoute -
r44463:c627f1b2 default
parent child Browse files
Show More
@@ -1,218 +1,221 b''
1 1 // revlog.rs
2 2 //
3 3 // Copyright 2019 Georges Racinet <georges.racinet@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 8 use crate::cindex;
9 9 use cpython::{
10 10 ObjectProtocol, PyClone, PyDict, PyModule, PyObject, PyResult, PyTuple, Python, PythonObject,
11 11 ToPyObject,
12 12 };
13 13 use hg::Revision;
14 14 use std::cell::RefCell;
15 15
16 16 /// Return a Struct implementing the Graph trait
17 17 pub(crate) fn pyindex_to_graph(py: Python, index: PyObject) -> PyResult<cindex::Index> {
18 cindex::Index::new(py, index)
18 match index.extract::<MixedIndex>(py) {
19 Ok(midx) => Ok(midx.clone_cindex(py)),
20 Err(_) => cindex::Index::new(py, index),
21 }
19 22 }
20 23
21 24 py_class!(pub class MixedIndex |py| {
22 25 data cindex: RefCell<cindex::Index>;
23 26
24 27 def __new__(_cls, cindex: PyObject) -> PyResult<MixedIndex> {
25 28 Self::create_instance(py, RefCell::new(
26 29 cindex::Index::new(py, cindex)?))
27 30 }
28 31
29 32
30 33 // Reforwarded C index API
31 34
32 35 // index_methods (tp_methods). Same ordering as in revlog.c
33 36
34 37 /// return the gca set of the given revs
35 38 def ancestors(&self, *args, **kw) -> PyResult<PyObject> {
36 39 self.call_cindex(py, "ancestors", args, kw)
37 40 }
38 41
39 42 /// return the heads of the common ancestors of the given revs
40 43 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> {
41 44 self.call_cindex(py, "commonancestorsheads", args, kw)
42 45 }
43 46
44 47 /// clear the index caches
45 48 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> {
46 49 self.call_cindex(py, "clearcaches", args, kw)
47 50 }
48 51
49 52 /// get an index entry
50 53 def get(&self, *args, **kw) -> PyResult<PyObject> {
51 54 self.call_cindex(py, "get", args, kw)
52 55 }
53 56
54 57 /// return `rev` associated with a node or None
55 58 def get_rev(&self, *args, **kw) -> PyResult<PyObject> {
56 59 self.call_cindex(py, "get_rev", args, kw)
57 60 }
58 61
59 62 /// return True if the node exist in the index
60 63 def has_node(&self, *args, **kw) -> PyResult<PyObject> {
61 64 self.call_cindex(py, "has_node", args, kw)
62 65 }
63 66
64 67 /// return `rev` associated with a node or raise RevlogError
65 68 def rev(&self, *args, **kw) -> PyResult<PyObject> {
66 69 self.call_cindex(py, "rev", args, kw)
67 70 }
68 71
69 72 /// compute phases
70 73 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
71 74 self.call_cindex(py, "computephasesmapsets", args, kw)
72 75 }
73 76
74 77 /// reachableroots
75 78 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> {
76 79 self.call_cindex(py, "reachableroots2", args, kw)
77 80 }
78 81
79 82 /// get head revisions
80 83 def headrevs(&self, *args, **kw) -> PyResult<PyObject> {
81 84 self.call_cindex(py, "headrevs", args, kw)
82 85 }
83 86
84 87 /// get filtered head revisions
85 88 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
86 89 self.call_cindex(py, "headrevsfiltered", args, kw)
87 90 }
88 91
89 92 /// True if the object is a snapshot
90 93 def issnapshot(&self, *args, **kw) -> PyResult<PyObject> {
91 94 self.call_cindex(py, "issnapshot", args, kw)
92 95 }
93 96
94 97 /// Gather snapshot data in a cache dict
95 98 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> {
96 99 self.call_cindex(py, "findsnapshots", args, kw)
97 100 }
98 101
99 102 /// determine revisions with deltas to reconstruct fulltext
100 103 def deltachain(&self, *args, **kw) -> PyResult<PyObject> {
101 104 self.call_cindex(py, "deltachain", args, kw)
102 105 }
103 106
104 107 /// slice planned chunk read to reach a density threshold
105 108 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> {
106 109 self.call_cindex(py, "slicechunktodensity", args, kw)
107 110 }
108 111
109 112 /// append an index entry
110 113 def append(&self, *args, **kw) -> PyResult<PyObject> {
111 114 self.call_cindex(py, "append", args, kw)
112 115 }
113 116
114 117 /// match a potentially ambiguous node ID
115 118 def partialmatch(&self, *args, **kw) -> PyResult<PyObject> {
116 119 self.call_cindex(py, "partialmatch", args, kw)
117 120 }
118 121
119 122 /// find length of shortest hex nodeid of a binary ID
120 123 def shortest(&self, *args, **kw) -> PyResult<PyObject> {
121 124 self.call_cindex(py, "shortest", args, kw)
122 125 }
123 126
124 127 /// stats for the index
125 128 def stats(&self, *args, **kw) -> PyResult<PyObject> {
126 129 self.call_cindex(py, "stats", args, kw)
127 130 }
128 131
129 132 // index_sequence_methods and index_mapping_methods.
130 133 //
131 134 // Since we call back through the high level Python API,
132 135 // there's no point making a distinction between index_get
133 136 // and index_getitem.
134 137
135 138 def __len__(&self) -> PyResult<usize> {
136 139 self.cindex(py).borrow().inner().len(py)
137 140 }
138 141
139 142 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
140 143 // this conversion seems needless, but that's actually because
141 144 // `index_getitem` does not handle conversion from PyLong,
142 145 // which expressions such as [e for e in index] internally use.
143 146 // Note that we don't seem to have a direct way to call
144 147 // PySequence_GetItem (does the job), which would be better for
145 148 // for performance
146 149 let key = match key.extract::<Revision>(py) {
147 150 Ok(rev) => rev.to_py_object(py).into_object(),
148 151 Err(_) => key,
149 152 };
150 153 self.cindex(py).borrow().inner().get_item(py, key)
151 154 }
152 155
153 156 def __setitem__(&self, key: PyObject, value: PyObject) -> PyResult<()> {
154 157 self.cindex(py).borrow().inner().set_item(py, key, value)
155 158 }
156 159
157 160 def __delitem__(&self, key: PyObject) -> PyResult<()> {
158 161 self.cindex(py).borrow().inner().del_item(py, key)
159 162 }
160 163
161 164 def __contains__(&self, item: PyObject) -> PyResult<bool> {
162 165 // ObjectProtocol does not seem to provide contains(), so
163 166 // this is an equivalent implementation of the index_contains()
164 167 // defined in revlog.c
165 168 let cindex = self.cindex(py).borrow();
166 169 match item.extract::<Revision>(py) {
167 170 Ok(rev) => {
168 171 Ok(rev >= -1 && rev < cindex.inner().len(py)? as Revision)
169 172 }
170 173 Err(_) => {
171 174 cindex.inner().call_method(
172 175 py,
173 176 "has_node",
174 177 PyTuple::new(py, &[item]),
175 178 None)?
176 179 .extract(py)
177 180 }
178 181 }
179 182 }
180 183
181 184
182 185 });
183 186
184 187 impl MixedIndex {
185 188 /// forward a method call to the underlying C index
186 189 fn call_cindex(
187 190 &self,
188 191 py: Python,
189 192 name: &str,
190 193 args: &PyTuple,
191 194 kwargs: Option<&PyDict>,
192 195 ) -> PyResult<PyObject> {
193 196 self.cindex(py)
194 197 .borrow()
195 198 .inner()
196 199 .call_method(py, name, args, kwargs)
197 200 }
198 201
199 202 pub fn clone_cindex(&self, py: Python) -> cindex::Index {
200 203 self.cindex(py).borrow().clone_ref(py)
201 204 }
202 205 }
203 206
204 207 /// Create the module, with __package__ given from parent
205 208 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
206 209 let dotted_name = &format!("{}.revlog", package);
207 210 let m = PyModule::new(py, dotted_name)?;
208 211 m.add(py, "__package__", package)?;
209 212 m.add(py, "__doc__", "RevLog - Rust implementations")?;
210 213
211 214 m.add_class::<MixedIndex>(py)?;
212 215
213 216 let sys = PyModule::import(py, "sys")?;
214 217 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
215 218 sys_modules.set_item(py, dotted_name, &m)?;
216 219
217 220 Ok(m)
218 221 }
@@ -1,34 +1,53 b''
1 1 from __future__ import absolute_import
2 2 import unittest
3 3
4 4 try:
5 5 from mercurial import rustext
6 6
7 7 rustext.__name__ # trigger immediate actual import
8 8 except ImportError:
9 9 rustext = None
10 10 else:
11 11 from mercurial.rustext import revlog
12 12
13 # this would fail already without appropriate ancestor.__package__
14 from mercurial.rustext.ancestor import LazyAncestors
15
13 16 from mercurial.testing import revlog as revlogtesting
14 17
15 18
16 19 @unittest.skipIf(
17 20 rustext is None, "rustext module revlog relies on is not available",
18 21 )
19 22 class RustRevlogIndexTest(revlogtesting.RevlogBasedTestBase):
20 23 def test_heads(self):
21 24 idx = self.parseindex()
22 25 rustidx = revlog.MixedIndex(idx)
23 26 self.assertEqual(rustidx.headrevs(), idx.headrevs())
24 27
25 28 def test_len(self):
26 29 idx = self.parseindex()
27 30 rustidx = revlog.MixedIndex(idx)
28 31 self.assertEqual(len(rustidx), len(idx))
29 32
33 def test_ancestors(self):
34 idx = self.parseindex()
35 rustidx = revlog.MixedIndex(idx)
36 lazy = LazyAncestors(rustidx, [3], 0, True)
37 # we have two more references to the index:
38 # - in its inner iterator for __contains__ and __bool__
39 # - in the LazyAncestors instance itself (to spawn new iterators)
40 self.assertTrue(2 in lazy)
41 self.assertTrue(bool(lazy))
42 self.assertEqual(list(lazy), [3, 2, 1, 0])
43 # a second time to validate that we spawn new iterators
44 self.assertEqual(list(lazy), [3, 2, 1, 0])
45
46 # let's check bool for an empty one
47 self.assertFalse(LazyAncestors(idx, [0], 0, False))
48
30 49
31 50 if __name__ == '__main__':
32 51 import silenttestrunner
33 52
34 53 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now