##// END OF EJS Templates
rust-index: add a struct wrapping the C index...
Georges Racinet -
r44413:b69d5f3a default
parent child Browse files
Show More
@@ -0,0 +1,34 b''
1 from __future__ import absolute_import
2 import unittest
3
4 try:
5 from mercurial import rustext
6
7 rustext.__name__ # trigger immediate actual import
8 except ImportError:
9 rustext = None
10 else:
11 from mercurial.rustext import revlog
12
13 from mercurial.testing import revlog as revlogtesting
14
15
16 @unittest.skipIf(
17 rustext is None, "rustext module revlog relies on is not available",
18 )
19 class RustRevlogIndexTest(revlogtesting.RevlogBasedTestBase):
20 def test_heads(self):
21 idx = self.parseindex()
22 rustidx = revlog.MixedIndex(idx)
23 self.assertEqual(rustidx.headrevs(), idx.headrevs())
24
25 def test_len(self):
26 idx = self.parseindex()
27 rustidx = revlog.MixedIndex(idx)
28 self.assertEqual(len(rustidx), len(idx))
29
30
31 if __name__ == '__main__':
32 import silenttestrunner
33
34 silenttestrunner.main(__name__)
@@ -1,82 +1,83 b''
1 1 // lib.rs
2 2 //
3 3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
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 //! Python bindings of `hg-core` objects using the `cpython` crate.
9 9 //! Once compiled, the resulting single shared library object can be placed in
10 10 //! the `mercurial` package directly as `rustext.so` or `rustext.dll`.
11 11 //! It holds several modules, so that from the point of view of Python,
12 12 //! it behaves as the `cext` package.
13 13 //!
14 14 //! Example:
15 15 //!
16 16 //! ```text
17 17 //! >>> from mercurial.rustext import ancestor
18 18 //! >>> ancestor.__doc__
19 19 //! 'Generic DAG ancestor algorithms - Rust implementation'
20 20 //! ```
21 21
22 22 /// This crate uses nested private macros, `extern crate` is still needed in
23 23 /// 2018 edition.
24 24 #[macro_use]
25 25 extern crate cpython;
26 26
27 27 pub mod ancestors;
28 28 mod cindex;
29 29 mod conversion;
30 30 #[macro_use]
31 31 pub mod ref_sharing;
32 32 pub mod dagops;
33 33 pub mod dirstate;
34 34 pub mod discovery;
35 35 pub mod exceptions;
36 36 pub mod filepatterns;
37 37 pub mod parsers;
38 38 pub mod revlog;
39 39 pub mod utils;
40 40
41 41 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
42 42 m.add(
43 43 py,
44 44 "__doc__",
45 45 "Mercurial core concepts - Rust implementation",
46 46 )?;
47 47
48 48 let dotted_name: String = m.get(py, "__name__")?.extract(py)?;
49 49 m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
50 50 m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?;
51 51 m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?;
52 52 m.add(py, "dirstate", dirstate::init_module(py, &dotted_name)?)?;
53 m.add(py, "revlog", revlog::init_module(py, &dotted_name)?)?;
53 54 m.add(
54 55 py,
55 56 "filepatterns",
56 57 filepatterns::init_module(py, &dotted_name)?,
57 58 )?;
58 59 m.add(
59 60 py,
60 61 "parsers",
61 62 parsers::init_parsers_module(py, &dotted_name)?,
62 63 )?;
63 64 m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
64 65 m.add(
65 66 py,
66 67 "PatternFileError",
67 68 py.get_type::<exceptions::PatternFileError>(),
68 69 )?;
69 70 m.add(
70 71 py,
71 72 "PatternError",
72 73 py.get_type::<exceptions::PatternError>(),
73 74 )?;
74 75 Ok(())
75 76 });
76 77
77 78 #[cfg(not(any(feature = "python27-bin", feature = "python3-bin")))]
78 79 #[test]
79 80 #[ignore]
80 81 fn libpython_must_be_linked_to_run_tests() {
81 82 // stub function to tell that some tests wouldn't run
82 83 }
@@ -1,17 +1,217 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 use cpython::{PyObject, PyResult, Python};
9 use cpython::{
10 ObjectProtocol, PyDict, PyModule, PyObject, PyResult, PyTuple, Python,
11 PythonObject, ToPyObject,
12 };
13 use hg::Revision;
14 use std::cell::RefCell;
10 15
11 16 /// Return a Struct implementing the Graph trait
12 17 pub(crate) fn pyindex_to_graph(
13 18 py: Python,
14 19 index: PyObject,
15 20 ) -> PyResult<cindex::Index> {
16 21 cindex::Index::new(py, index)
17 22 }
23
24 py_class!(pub class MixedIndex |py| {
25 data cindex: RefCell<cindex::Index>;
26
27 def __new__(_cls, cindex: PyObject) -> PyResult<MixedIndex> {
28 Self::create_instance(py, RefCell::new(
29 cindex::Index::new(py, cindex)?))
30 }
31
32
33 // Reforwarded C index API
34
35 // index_methods (tp_methods). Same ordering as in revlog.c
36
37 /// return the gca set of the given revs
38 def ancestors(&self, *args, **kw) -> PyResult<PyObject> {
39 self.call_cindex(py, "ancestors", args, kw)
40 }
41
42 /// return the heads of the common ancestors of the given revs
43 def commonancestorsheads(&self, *args, **kw) -> PyResult<PyObject> {
44 self.call_cindex(py, "commonancestorsheads", args, kw)
45 }
46
47 /// clear the index caches
48 def clearcaches(&self, *args, **kw) -> PyResult<PyObject> {
49 self.call_cindex(py, "clearcaches", args, kw)
50 }
51
52 /// get an index entry
53 def get(&self, *args, **kw) -> PyResult<PyObject> {
54 self.call_cindex(py, "get", args, kw)
55 }
56
57 /// return `rev` associated with a node or None
58 def get_rev(&self, *args, **kw) -> PyResult<PyObject> {
59 self.call_cindex(py, "get_rev", args, kw)
60 }
61
62 /// return True if the node exist in the index
63 def has_node(&self, *args, **kw) -> PyResult<PyObject> {
64 self.call_cindex(py, "has_node", args, kw)
65 }
66
67 /// return `rev` associated with a node or raise RevlogError
68 def rev(&self, *args, **kw) -> PyResult<PyObject> {
69 self.call_cindex(py, "rev", args, kw)
70 }
71
72 /// compute phases
73 def computephasesmapsets(&self, *args, **kw) -> PyResult<PyObject> {
74 self.call_cindex(py, "computephasesmapsets", args, kw)
75 }
76
77 /// reachableroots
78 def reachableroots2(&self, *args, **kw) -> PyResult<PyObject> {
79 self.call_cindex(py, "reachableroots2", args, kw)
80 }
81
82 /// get head revisions
83 def headrevs(&self, *args, **kw) -> PyResult<PyObject> {
84 self.call_cindex(py, "headrevs", args, kw)
85 }
86
87 /// get filtered head revisions
88 def headrevsfiltered(&self, *args, **kw) -> PyResult<PyObject> {
89 self.call_cindex(py, "headrevsfiltered", args, kw)
90 }
91
92 /// True if the object is a snapshot
93 def issnapshot(&self, *args, **kw) -> PyResult<PyObject> {
94 self.call_cindex(py, "issnapshot", args, kw)
95 }
96
97 /// Gather snapshot data in a cache dict
98 def findsnapshots(&self, *args, **kw) -> PyResult<PyObject> {
99 self.call_cindex(py, "findsnapshots", args, kw)
100 }
101
102 /// determine revisions with deltas to reconstruct fulltext
103 def deltachain(&self, *args, **kw) -> PyResult<PyObject> {
104 self.call_cindex(py, "deltachain", args, kw)
105 }
106
107 /// slice planned chunk read to reach a density threshold
108 def slicechunktodensity(&self, *args, **kw) -> PyResult<PyObject> {
109 self.call_cindex(py, "slicechunktodensity", args, kw)
110 }
111
112 /// append an index entry
113 def append(&self, *args, **kw) -> PyResult<PyObject> {
114 self.call_cindex(py, "append", args, kw)
115 }
116
117 /// match a potentially ambiguous node ID
118 def partialmatch(&self, *args, **kw) -> PyResult<PyObject> {
119 self.call_cindex(py, "partialmatch", args, kw)
120 }
121
122 /// find length of shortest hex nodeid of a binary ID
123 def shortest(&self, *args, **kw) -> PyResult<PyObject> {
124 self.call_cindex(py, "shortest", args, kw)
125 }
126
127 /// stats for the index
128 def stats(&self, *args, **kw) -> PyResult<PyObject> {
129 self.call_cindex(py, "stats", args, kw)
130 }
131
132 // index_sequence_methods and index_mapping_methods.
133 //
134 // Since we call back through the high level Python API,
135 // there's no point making a distinction between index_get
136 // and index_getitem.
137
138 def __len__(&self) -> PyResult<usize> {
139 self.cindex(py).borrow().inner().len(py)
140 }
141
142 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
143 // this conversion seems needless, but that's actually because
144 // `index_getitem` does not handle conversion from PyLong,
145 // which expressions such as [e for e in index] internally use.
146 // Note that we don't seem to have a direct way to call
147 // PySequence_GetItem (does the job), which would be better for
148 // for performance
149 let key = match key.extract::<Revision>(py) {
150 Ok(rev) => rev.to_py_object(py).into_object(),
151 Err(_) => key,
152 };
153 self.cindex(py).borrow().inner().get_item(py, key)
154 }
155
156 def __setitem__(&self, key: PyObject, value: PyObject) -> PyResult<()> {
157 self.cindex(py).borrow().inner().set_item(py, key, value)
158 }
159
160 def __delitem__(&self, key: PyObject) -> PyResult<()> {
161 self.cindex(py).borrow().inner().del_item(py, key)
162 }
163
164 def __contains__(&self, item: PyObject) -> PyResult<bool> {
165 // ObjectProtocol does not seem to provide contains(), so
166 // this is an equivalent implementation of the index_contains()
167 // defined in revlog.c
168 let cindex = self.cindex(py).borrow();
169 match item.extract::<Revision>(py) {
170 Ok(rev) => {
171 Ok(rev >= -1 && rev < cindex.inner().len(py)? as Revision)
172 }
173 Err(_) => {
174 cindex.inner().call_method(
175 py,
176 "has_node",
177 PyTuple::new(py, &[item]),
178 None)?
179 .extract(py)
180 }
181 }
182 }
183
184
185 });
186
187 impl MixedIndex {
188 /// forward a method call to the underlying C index
189 fn call_cindex(
190 &self,
191 py: Python,
192 name: &str,
193 args: &PyTuple,
194 kwargs: Option<&PyDict>,
195 ) -> PyResult<PyObject> {
196 self.cindex(py)
197 .borrow()
198 .inner()
199 .call_method(py, name, args, kwargs)
200 }
201 }
202
203 /// Create the module, with __package__ given from parent
204 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
205 let dotted_name = &format!("{}.revlog", package);
206 let m = PyModule::new(py, dotted_name)?;
207 m.add(py, "__package__", package)?;
208 m.add(py, "__doc__", "RevLog - Rust implementations")?;
209
210 m.add_class::<MixedIndex>(py)?;
211
212 let sys = PyModule::import(py, "sys")?;
213 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
214 sys_modules.set_item(py, dotted_name, &m)?;
215
216 Ok(m)
217 }
General Comments 0
You need to be logged in to leave comments. Login now