##// END OF EJS Templates
hg-cpython: use ancestor iterator impls from vcsgraph...
pacien -
r49350:35ebe6f8 default
parent child Browse files
Show More
@@ -1,227 +1,231 b''
1 1 // ancestors.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 //! Bindings for the `hg::ancestors` module provided by the
9 9 //! `hg-core` crate. From Python, this will be seen as `rustext.ancestor`
10 10 //! and can be used as replacement for the the pure `ancestor` Python module.
11 11 //!
12 12 //! # Classes visible from Python:
13 13 //! - [`LazyAncestors`] is the Rust implementation of
14 14 //! `mercurial.ancestor.lazyancestors`. The only difference is that it is
15 15 //! instantiated with a C `parsers.index` instance instead of a parents
16 16 //! function.
17 17 //!
18 18 //! - [`MissingAncestors`] is the Rust implementation of
19 19 //! `mercurial.ancestor.incrementalmissingancestors`.
20 20 //!
21 21 //! API differences:
22 22 //! + it is instantiated with a C `parsers.index`
23 23 //! instance instead of a parents function.
24 24 //! + `MissingAncestors.bases` is a method returning a tuple instead of
25 25 //! a set-valued attribute. We could return a Python set easily if our
26 26 //! [PySet PR](https://github.com/dgrunwald/rust-cpython/pull/165)
27 27 //! is accepted.
28 28 //!
29 29 //! - [`AncestorsIterator`] is the Rust counterpart of the
30 30 //! `ancestor._lazyancestorsiter` Python generator. From Python, instances of
31 31 //! this should be mainly obtained by calling `iter()` on a [`LazyAncestors`]
32 32 //! instance.
33 33 //!
34 34 //! [`LazyAncestors`]: struct.LazyAncestors.html
35 35 //! [`MissingAncestors`]: struct.MissingAncestors.html
36 36 //! [`AncestorsIterator`]: struct.AncestorsIterator.html
37 37 use crate::revlog::pyindex_to_graph;
38 38 use crate::{
39 39 cindex::Index, conversion::rev_pyiter_collect, exceptions::GraphError,
40 40 };
41 41 use cpython::{
42 42 ObjectProtocol, PyClone, PyDict, PyList, PyModule, PyObject, PyResult,
43 43 Python, PythonObject, ToPyObject,
44 44 };
45 use hg::MissingAncestors as CoreMissing;
45 46 use hg::Revision;
46 use hg::{
47 AncestorsIterator as CoreIterator, LazyAncestors as CoreLazy,
48 MissingAncestors as CoreMissing,
49 };
50 47 use std::cell::RefCell;
51 48 use std::collections::HashSet;
49 use vcsgraph::lazy_ancestors::{
50 AncestorsIterator as VCGAncestorsIterator,
51 LazyAncestors as VCGLazyAncestors,
52 };
52 53
53 54 py_class!(pub class AncestorsIterator |py| {
54 data inner: RefCell<Box<CoreIterator<Index>>>;
55 data inner: RefCell<Box<VCGAncestorsIterator<Index>>>;
55 56
56 57 def __next__(&self) -> PyResult<Option<Revision>> {
57 58 match self.inner(py).borrow_mut().next() {
58 Some(Err(e)) => Err(GraphError::pynew(py, e)),
59 Some(Err(e)) => Err(GraphError::pynew_from_vcsgraph(py, e)),
59 60 None => Ok(None),
60 61 Some(Ok(r)) => Ok(Some(r)),
61 62 }
62 63 }
63 64
64 65 def __contains__(&self, rev: Revision) -> PyResult<bool> {
65 66 self.inner(py).borrow_mut().contains(rev)
66 .map_err(|e| GraphError::pynew(py, e))
67 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))
67 68 }
68 69
69 70 def __iter__(&self) -> PyResult<Self> {
70 71 Ok(self.clone_ref(py))
71 72 }
72 73
73 74 def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision,
74 75 inclusive: bool) -> PyResult<AncestorsIterator> {
75 76 let initvec: Vec<Revision> = rev_pyiter_collect(py, &initrevs)?;
76 let ait = CoreIterator::new(
77 let ait = VCGAncestorsIterator::new(
77 78 pyindex_to_graph(py, index)?,
78 79 initvec,
79 80 stoprev,
80 81 inclusive,
81 82 )
82 .map_err(|e| GraphError::pynew(py, e))?;
83 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))?;
83 84 AncestorsIterator::from_inner(py, ait)
84 85 }
85 86
86 87 });
87 88
88 89 impl AncestorsIterator {
89 pub fn from_inner(py: Python, ait: CoreIterator<Index>) -> PyResult<Self> {
90 pub fn from_inner(
91 py: Python,
92 ait: VCGAncestorsIterator<Index>,
93 ) -> PyResult<Self> {
90 94 Self::create_instance(py, RefCell::new(Box::new(ait)))
91 95 }
92 96 }
93 97
94 98 py_class!(pub class LazyAncestors |py| {
95 data inner: RefCell<Box<CoreLazy<Index>>>;
99 data inner: RefCell<Box<VCGLazyAncestors<Index>>>;
96 100
97 101 def __contains__(&self, rev: Revision) -> PyResult<bool> {
98 102 self.inner(py)
99 103 .borrow_mut()
100 104 .contains(rev)
101 .map_err(|e| GraphError::pynew(py, e))
105 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))
102 106 }
103 107
104 108 def __iter__(&self) -> PyResult<AncestorsIterator> {
105 109 AncestorsIterator::from_inner(py, self.inner(py).borrow().iter())
106 110 }
107 111
108 112 def __bool__(&self) -> PyResult<bool> {
109 113 Ok(!self.inner(py).borrow().is_empty())
110 114 }
111 115
112 116 def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision,
113 117 inclusive: bool) -> PyResult<Self> {
114 118 let initvec: Vec<Revision> = rev_pyiter_collect(py, &initrevs)?;
115 119
116 120 let lazy =
117 CoreLazy::new(pyindex_to_graph(py, index)?,
121 VCGLazyAncestors::new(pyindex_to_graph(py, index)?,
118 122 initvec, stoprev, inclusive)
119 .map_err(|e| GraphError::pynew(py, e))?;
123 .map_err(|e| GraphError::pynew_from_vcsgraph(py, e))?;
120 124
121 125 Self::create_instance(py, RefCell::new(Box::new(lazy)))
122 126 }
123 127
124 128 });
125 129
126 130 py_class!(pub class MissingAncestors |py| {
127 131 data inner: RefCell<Box<CoreMissing<Index>>>;
128 132
129 133 def __new__(
130 134 _cls,
131 135 index: PyObject,
132 136 bases: PyObject
133 137 )
134 138 -> PyResult<MissingAncestors> {
135 139 let bases_vec: Vec<Revision> = rev_pyiter_collect(py, &bases)?;
136 140 let inner = CoreMissing::new(pyindex_to_graph(py, index)?, bases_vec);
137 141 MissingAncestors::create_instance(py, RefCell::new(Box::new(inner)))
138 142 }
139 143
140 144 def hasbases(&self) -> PyResult<bool> {
141 145 Ok(self.inner(py).borrow().has_bases())
142 146 }
143 147
144 148 def addbases(&self, bases: PyObject) -> PyResult<PyObject> {
145 149 let mut inner = self.inner(py).borrow_mut();
146 150 let bases_vec: Vec<Revision> = rev_pyiter_collect(py, &bases)?;
147 151 inner.add_bases(bases_vec);
148 152 // cpython doc has examples with PyResult<()> but this gives me
149 153 // the trait `cpython::ToPyObject` is not implemented for `()`
150 154 // so let's return an explicit None
151 155 Ok(py.None())
152 156 }
153 157
154 158 def bases(&self) -> PyResult<HashSet<Revision>> {
155 159 Ok(self.inner(py).borrow().get_bases().clone())
156 160 }
157 161
158 162 def basesheads(&self) -> PyResult<HashSet<Revision>> {
159 163 let inner = self.inner(py).borrow();
160 164 inner.bases_heads().map_err(|e| GraphError::pynew(py, e))
161 165 }
162 166
163 167 def removeancestorsfrom(&self, revs: PyObject) -> PyResult<PyObject> {
164 168 let mut inner = self.inner(py).borrow_mut();
165 169 // this is very lame: we convert to a Rust set, update it in place
166 170 // and then convert back to Python, only to have Python remove the
167 171 // excess (thankfully, Python is happy with a list or even an iterator)
168 172 // Leads to improve this:
169 173 // - have the CoreMissing instead do something emit revisions to
170 174 // discard
171 175 // - define a trait for sets of revisions in the core and implement
172 176 // it for a Python set rewrapped with the GIL marker
173 177 let mut revs_pyset: HashSet<Revision> = rev_pyiter_collect(py, &revs)?;
174 178 inner.remove_ancestors_from(&mut revs_pyset)
175 179 .map_err(|e| GraphError::pynew(py, e))?;
176 180
177 181 // convert as Python list
178 182 let mut remaining_pyint_vec: Vec<PyObject> = Vec::with_capacity(
179 183 revs_pyset.len());
180 184 for rev in revs_pyset {
181 185 remaining_pyint_vec.push(rev.to_py_object(py).into_object());
182 186 }
183 187 let remaining_pylist = PyList::new(py, remaining_pyint_vec.as_slice());
184 188 revs.call_method(py, "intersection_update", (remaining_pylist, ), None)
185 189 }
186 190
187 191 def missingancestors(&self, revs: PyObject) -> PyResult<PyList> {
188 192 let mut inner = self.inner(py).borrow_mut();
189 193 let revs_vec: Vec<Revision> = rev_pyiter_collect(py, &revs)?;
190 194 let missing_vec = match inner.missing_ancestors(revs_vec) {
191 195 Ok(missing) => missing,
192 196 Err(e) => {
193 197 return Err(GraphError::pynew(py, e));
194 198 }
195 199 };
196 200 // convert as Python list
197 201 let mut missing_pyint_vec: Vec<PyObject> = Vec::with_capacity(
198 202 missing_vec.len());
199 203 for rev in missing_vec {
200 204 missing_pyint_vec.push(rev.to_py_object(py).into_object());
201 205 }
202 206 Ok(PyList::new(py, missing_pyint_vec.as_slice()))
203 207 }
204 208 });
205 209
206 210 /// Create the module, with __package__ given from parent
207 211 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
208 212 let dotted_name = &format!("{}.ancestor", package);
209 213 let m = PyModule::new(py, dotted_name)?;
210 214 m.add(py, "__package__", package)?;
211 215 m.add(
212 216 py,
213 217 "__doc__",
214 218 "Generic DAG ancestor algorithms - Rust implementation",
215 219 )?;
216 220 m.add_class::<AncestorsIterator>(py)?;
217 221 m.add_class::<LazyAncestors>(py)?;
218 222 m.add_class::<MissingAncestors>(py)?;
219 223
220 224 let sys = PyModule::import(py, "sys")?;
221 225 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
222 226 sys_modules.set_item(py, dotted_name, &m)?;
223 227 // Example C code (see pyexpat.c and import.c) will "give away the
224 228 // reference", but we won't because it will be consumed once the
225 229 // Rust PyObject is dropped.
226 230 Ok(m)
227 231 }
@@ -1,44 +1,70 b''
1 1 // ancestors.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 //! Bindings for Rust errors
9 9 //!
10 10 //! [`GraphError`] exposes `hg::GraphError` as a subclass of `ValueError`
11 11 //! but some variants of `hg::GraphError` can be converted directly to other
12 12 //! existing Python exceptions if appropriate.
13 13 //!
14 14 //! [`GraphError`]: struct.GraphError.html
15 15 use cpython::{
16 16 exc::{RuntimeError, ValueError},
17 17 py_exception, PyErr, Python,
18 18 };
19 19 use hg;
20 20
21 21 py_exception!(rustext, GraphError, ValueError);
22 22
23 23 impl GraphError {
24 24 pub fn pynew(py: Python, inner: hg::GraphError) -> PyErr {
25 25 match inner {
26 26 hg::GraphError::ParentOutOfRange(r) => {
27 27 GraphError::new(py, ("ParentOutOfRange", r))
28 28 }
29 29 hg::GraphError::WorkingDirectoryUnsupported => {
30 30 match py
31 31 .import("mercurial.error")
32 32 .and_then(|m| m.get(py, "WdirUnsupported"))
33 33 {
34 34 Err(e) => e,
35 35 Ok(cls) => PyErr::from_instance(py, cls),
36 36 }
37 37 }
38 38 }
39 39 }
40
41 pub fn pynew_from_vcsgraph(
42 py: Python,
43 inner: vcsgraph::graph::GraphReadError,
44 ) -> PyErr {
45 match inner {
46 vcsgraph::graph::GraphReadError::InconsistentGraphData => {
47 GraphError::new(py, "InconsistentGraphData")
48 }
49 vcsgraph::graph::GraphReadError::InvalidKey => {
50 GraphError::new(py, "ParentOutOfRange")
51 }
52 vcsgraph::graph::GraphReadError::KeyedInvalidKey(r) => {
53 GraphError::new(py, ("ParentOutOfRange", r))
54 }
55 vcsgraph::graph::GraphReadError::WorkingDirectoryUnsupported => {
56 match py
57 .import("mercurial.error")
58 .and_then(|m| m.get(py, "WdirUnsupported"))
59 {
60 Err(e) => e,
61 Ok(cls) => PyErr::from_instance(py, cls),
62 }
63 }
64 }
65 }
40 66 }
41 67
42 68 py_exception!(rustext, HgPathPyError, RuntimeError);
43 69 py_exception!(rustext, FallbackError, RuntimeError);
44 70 py_exception!(shared_ref, AlreadyBorrowed, RuntimeError);
General Comments 0
You need to be logged in to leave comments. Login now