##// END OF EJS Templates
rust-index: add a function to convert PyObject index for hg-core...
marmoute -
r44398:f98f0e3d default
parent child Browse files
Show More
@@ -0,0 +1,17 b''
1 // revlog.rs
2 //
3 // Copyright 2019 Georges Racinet <georges.racinet@octobus.net>
4 //
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
7
8 use crate::cindex;
9 use cpython::{PyObject, PyResult, Python};
10
11 /// Return a Struct implementing the Graph trait
12 pub(crate) fn pyindex_to_graph(
13 py: Python,
14 index: PyObject,
15 ) -> PyResult<cindex::Index> {
16 cindex::Index::new(py, index)
17 }
@@ -1,220 +1,222 b''
1 // ancestors.rs
1 // ancestors.rs
2 //
2 //
3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::ancestors` module provided by the
8 //! Bindings for the `hg::ancestors` module provided by the
9 //! `hg-core` crate. From Python, this will be seen as `rustext.ancestor`
9 //! `hg-core` crate. From Python, this will be seen as `rustext.ancestor`
10 //! and can be used as replacement for the the pure `ancestor` Python module.
10 //! and can be used as replacement for the the pure `ancestor` Python module.
11 //!
11 //!
12 //! # Classes visible from Python:
12 //! # Classes visible from Python:
13 //! - [`LazyAncestors`] is the Rust implementation of
13 //! - [`LazyAncestors`] is the Rust implementation of
14 //! `mercurial.ancestor.lazyancestors`. The only difference is that it is
14 //! `mercurial.ancestor.lazyancestors`. The only difference is that it is
15 //! instantiated with a C `parsers.index` instance instead of a parents
15 //! instantiated with a C `parsers.index` instance instead of a parents
16 //! function.
16 //! function.
17 //!
17 //!
18 //! - [`MissingAncestors`] is the Rust implementation of
18 //! - [`MissingAncestors`] is the Rust implementation of
19 //! `mercurial.ancestor.incrementalmissingancestors`.
19 //! `mercurial.ancestor.incrementalmissingancestors`.
20 //!
20 //!
21 //! API differences:
21 //! API differences:
22 //! + it is instantiated with a C `parsers.index`
22 //! + it is instantiated with a C `parsers.index`
23 //! instance instead of a parents function.
23 //! instance instead of a parents function.
24 //! + `MissingAncestors.bases` is a method returning a tuple instead of
24 //! + `MissingAncestors.bases` is a method returning a tuple instead of
25 //! a set-valued attribute. We could return a Python set easily if our
25 //! a set-valued attribute. We could return a Python set easily if our
26 //! [PySet PR](https://github.com/dgrunwald/rust-cpython/pull/165)
26 //! [PySet PR](https://github.com/dgrunwald/rust-cpython/pull/165)
27 //! is accepted.
27 //! is accepted.
28 //!
28 //!
29 //! - [`AncestorsIterator`] is the Rust counterpart of the
29 //! - [`AncestorsIterator`] is the Rust counterpart of the
30 //! `ancestor._lazyancestorsiter` Python generator. From Python, instances of
30 //! `ancestor._lazyancestorsiter` Python generator. From Python, instances of
31 //! this should be mainly obtained by calling `iter()` on a [`LazyAncestors`]
31 //! this should be mainly obtained by calling `iter()` on a [`LazyAncestors`]
32 //! instance.
32 //! instance.
33 //!
33 //!
34 //! [`LazyAncestors`]: struct.LazyAncestors.html
34 //! [`LazyAncestors`]: struct.LazyAncestors.html
35 //! [`MissingAncestors`]: struct.MissingAncestors.html
35 //! [`MissingAncestors`]: struct.MissingAncestors.html
36 //! [`AncestorsIterator`]: struct.AncestorsIterator.html
36 //! [`AncestorsIterator`]: struct.AncestorsIterator.html
37 use crate::revlog::pyindex_to_graph;
37 use crate::{
38 use crate::{
38 cindex::Index, conversion::rev_pyiter_collect, exceptions::GraphError,
39 cindex::Index, conversion::rev_pyiter_collect, exceptions::GraphError,
39 };
40 };
40 use cpython::{
41 use cpython::{
41 ObjectProtocol, PyClone, PyDict, PyList, PyModule, PyObject, PyResult,
42 ObjectProtocol, PyClone, PyDict, PyList, PyModule, PyObject, PyResult,
42 Python, PythonObject, ToPyObject,
43 Python, PythonObject, ToPyObject,
43 };
44 };
44 use hg::Revision;
45 use hg::Revision;
45 use hg::{
46 use hg::{
46 AncestorsIterator as CoreIterator, LazyAncestors as CoreLazy,
47 AncestorsIterator as CoreIterator, LazyAncestors as CoreLazy,
47 MissingAncestors as CoreMissing,
48 MissingAncestors as CoreMissing,
48 };
49 };
49 use std::cell::RefCell;
50 use std::cell::RefCell;
50 use std::collections::HashSet;
51 use std::collections::HashSet;
51
52
52 py_class!(pub class AncestorsIterator |py| {
53 py_class!(pub class AncestorsIterator |py| {
53 data inner: RefCell<Box<CoreIterator<Index>>>;
54 data inner: RefCell<Box<CoreIterator<Index>>>;
54
55
55 def __next__(&self) -> PyResult<Option<Revision>> {
56 def __next__(&self) -> PyResult<Option<Revision>> {
56 match self.inner(py).borrow_mut().next() {
57 match self.inner(py).borrow_mut().next() {
57 Some(Err(e)) => Err(GraphError::pynew(py, e)),
58 Some(Err(e)) => Err(GraphError::pynew(py, e)),
58 None => Ok(None),
59 None => Ok(None),
59 Some(Ok(r)) => Ok(Some(r)),
60 Some(Ok(r)) => Ok(Some(r)),
60 }
61 }
61 }
62 }
62
63
63 def __contains__(&self, rev: Revision) -> PyResult<bool> {
64 def __contains__(&self, rev: Revision) -> PyResult<bool> {
64 self.inner(py).borrow_mut().contains(rev)
65 self.inner(py).borrow_mut().contains(rev)
65 .map_err(|e| GraphError::pynew(py, e))
66 .map_err(|e| GraphError::pynew(py, e))
66 }
67 }
67
68
68 def __iter__(&self) -> PyResult<Self> {
69 def __iter__(&self) -> PyResult<Self> {
69 Ok(self.clone_ref(py))
70 Ok(self.clone_ref(py))
70 }
71 }
71
72
72 def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision,
73 def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision,
73 inclusive: bool) -> PyResult<AncestorsIterator> {
74 inclusive: bool) -> PyResult<AncestorsIterator> {
74 let initvec: Vec<Revision> = rev_pyiter_collect(py, &initrevs)?;
75 let initvec: Vec<Revision> = rev_pyiter_collect(py, &initrevs)?;
75 let ait = CoreIterator::new(
76 let ait = CoreIterator::new(
76 Index::new(py, index)?,
77 pyindex_to_graph(py, index)?,
77 initvec,
78 initvec,
78 stoprev,
79 stoprev,
79 inclusive,
80 inclusive,
80 )
81 )
81 .map_err(|e| GraphError::pynew(py, e))?;
82 .map_err(|e| GraphError::pynew(py, e))?;
82 AncestorsIterator::from_inner(py, ait)
83 AncestorsIterator::from_inner(py, ait)
83 }
84 }
84
85
85 });
86 });
86
87
87 impl AncestorsIterator {
88 impl AncestorsIterator {
88 pub fn from_inner(py: Python, ait: CoreIterator<Index>) -> PyResult<Self> {
89 pub fn from_inner(py: Python, ait: CoreIterator<Index>) -> PyResult<Self> {
89 Self::create_instance(py, RefCell::new(Box::new(ait)))
90 Self::create_instance(py, RefCell::new(Box::new(ait)))
90 }
91 }
91 }
92 }
92
93
93 py_class!(pub class LazyAncestors |py| {
94 py_class!(pub class LazyAncestors |py| {
94 data inner: RefCell<Box<CoreLazy<Index>>>;
95 data inner: RefCell<Box<CoreLazy<Index>>>;
95
96
96 def __contains__(&self, rev: Revision) -> PyResult<bool> {
97 def __contains__(&self, rev: Revision) -> PyResult<bool> {
97 self.inner(py)
98 self.inner(py)
98 .borrow_mut()
99 .borrow_mut()
99 .contains(rev)
100 .contains(rev)
100 .map_err(|e| GraphError::pynew(py, e))
101 .map_err(|e| GraphError::pynew(py, e))
101 }
102 }
102
103
103 def __iter__(&self) -> PyResult<AncestorsIterator> {
104 def __iter__(&self) -> PyResult<AncestorsIterator> {
104 AncestorsIterator::from_inner(py, self.inner(py).borrow().iter())
105 AncestorsIterator::from_inner(py, self.inner(py).borrow().iter())
105 }
106 }
106
107
107 def __bool__(&self) -> PyResult<bool> {
108 def __bool__(&self) -> PyResult<bool> {
108 Ok(!self.inner(py).borrow().is_empty())
109 Ok(!self.inner(py).borrow().is_empty())
109 }
110 }
110
111
111 def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision,
112 def __new__(_cls, index: PyObject, initrevs: PyObject, stoprev: Revision,
112 inclusive: bool) -> PyResult<Self> {
113 inclusive: bool) -> PyResult<Self> {
113 let initvec: Vec<Revision> = rev_pyiter_collect(py, &initrevs)?;
114 let initvec: Vec<Revision> = rev_pyiter_collect(py, &initrevs)?;
114
115
115 let lazy =
116 let lazy =
116 CoreLazy::new(Index::new(py, index)?, initvec, stoprev, inclusive)
117 CoreLazy::new(pyindex_to_graph(py, index)?,
118 initvec, stoprev, inclusive)
117 .map_err(|e| GraphError::pynew(py, e))?;
119 .map_err(|e| GraphError::pynew(py, e))?;
118
120
119 Self::create_instance(py, RefCell::new(Box::new(lazy)))
121 Self::create_instance(py, RefCell::new(Box::new(lazy)))
120 }
122 }
121
123
122 });
124 });
123
125
124 py_class!(pub class MissingAncestors |py| {
126 py_class!(pub class MissingAncestors |py| {
125 data inner: RefCell<Box<CoreMissing<Index>>>;
127 data inner: RefCell<Box<CoreMissing<Index>>>;
126
128
127 def __new__(_cls, index: PyObject, bases: PyObject) -> PyResult<MissingAncestors> {
129 def __new__(_cls, index: PyObject, bases: PyObject) -> PyResult<MissingAncestors> {
128 let bases_vec: Vec<Revision> = rev_pyiter_collect(py, &bases)?;
130 let bases_vec: Vec<Revision> = rev_pyiter_collect(py, &bases)?;
129 let inner = CoreMissing::new(Index::new(py, index)?, bases_vec);
131 let inner = CoreMissing::new(pyindex_to_graph(py, index)?, bases_vec);
130 MissingAncestors::create_instance(py, RefCell::new(Box::new(inner)))
132 MissingAncestors::create_instance(py, RefCell::new(Box::new(inner)))
131 }
133 }
132
134
133 def hasbases(&self) -> PyResult<bool> {
135 def hasbases(&self) -> PyResult<bool> {
134 Ok(self.inner(py).borrow().has_bases())
136 Ok(self.inner(py).borrow().has_bases())
135 }
137 }
136
138
137 def addbases(&self, bases: PyObject) -> PyResult<PyObject> {
139 def addbases(&self, bases: PyObject) -> PyResult<PyObject> {
138 let mut inner = self.inner(py).borrow_mut();
140 let mut inner = self.inner(py).borrow_mut();
139 let bases_vec: Vec<Revision> = rev_pyiter_collect(py, &bases)?;
141 let bases_vec: Vec<Revision> = rev_pyiter_collect(py, &bases)?;
140 inner.add_bases(bases_vec);
142 inner.add_bases(bases_vec);
141 // cpython doc has examples with PyResult<()> but this gives me
143 // cpython doc has examples with PyResult<()> but this gives me
142 // the trait `cpython::ToPyObject` is not implemented for `()`
144 // the trait `cpython::ToPyObject` is not implemented for `()`
143 // so let's return an explicit None
145 // so let's return an explicit None
144 Ok(py.None())
146 Ok(py.None())
145 }
147 }
146
148
147 def bases(&self) -> PyResult<HashSet<Revision>> {
149 def bases(&self) -> PyResult<HashSet<Revision>> {
148 Ok(self.inner(py).borrow().get_bases().clone())
150 Ok(self.inner(py).borrow().get_bases().clone())
149 }
151 }
150
152
151 def basesheads(&self) -> PyResult<HashSet<Revision>> {
153 def basesheads(&self) -> PyResult<HashSet<Revision>> {
152 let inner = self.inner(py).borrow();
154 let inner = self.inner(py).borrow();
153 inner.bases_heads().map_err(|e| GraphError::pynew(py, e))
155 inner.bases_heads().map_err(|e| GraphError::pynew(py, e))
154 }
156 }
155
157
156 def removeancestorsfrom(&self, revs: PyObject) -> PyResult<PyObject> {
158 def removeancestorsfrom(&self, revs: PyObject) -> PyResult<PyObject> {
157 let mut inner = self.inner(py).borrow_mut();
159 let mut inner = self.inner(py).borrow_mut();
158 // this is very lame: we convert to a Rust set, update it in place
160 // this is very lame: we convert to a Rust set, update it in place
159 // and then convert back to Python, only to have Python remove the
161 // and then convert back to Python, only to have Python remove the
160 // excess (thankfully, Python is happy with a list or even an iterator)
162 // excess (thankfully, Python is happy with a list or even an iterator)
161 // Leads to improve this:
163 // Leads to improve this:
162 // - have the CoreMissing instead do something emit revisions to
164 // - have the CoreMissing instead do something emit revisions to
163 // discard
165 // discard
164 // - define a trait for sets of revisions in the core and implement
166 // - define a trait for sets of revisions in the core and implement
165 // it for a Python set rewrapped with the GIL marker
167 // it for a Python set rewrapped with the GIL marker
166 let mut revs_pyset: HashSet<Revision> = rev_pyiter_collect(py, &revs)?;
168 let mut revs_pyset: HashSet<Revision> = rev_pyiter_collect(py, &revs)?;
167 inner.remove_ancestors_from(&mut revs_pyset)
169 inner.remove_ancestors_from(&mut revs_pyset)
168 .map_err(|e| GraphError::pynew(py, e))?;
170 .map_err(|e| GraphError::pynew(py, e))?;
169
171
170 // convert as Python list
172 // convert as Python list
171 let mut remaining_pyint_vec: Vec<PyObject> = Vec::with_capacity(
173 let mut remaining_pyint_vec: Vec<PyObject> = Vec::with_capacity(
172 revs_pyset.len());
174 revs_pyset.len());
173 for rev in revs_pyset {
175 for rev in revs_pyset {
174 remaining_pyint_vec.push(rev.to_py_object(py).into_object());
176 remaining_pyint_vec.push(rev.to_py_object(py).into_object());
175 }
177 }
176 let remaining_pylist = PyList::new(py, remaining_pyint_vec.as_slice());
178 let remaining_pylist = PyList::new(py, remaining_pyint_vec.as_slice());
177 revs.call_method(py, "intersection_update", (remaining_pylist, ), None)
179 revs.call_method(py, "intersection_update", (remaining_pylist, ), None)
178 }
180 }
179
181
180 def missingancestors(&self, revs: PyObject) -> PyResult<PyList> {
182 def missingancestors(&self, revs: PyObject) -> PyResult<PyList> {
181 let mut inner = self.inner(py).borrow_mut();
183 let mut inner = self.inner(py).borrow_mut();
182 let revs_vec: Vec<Revision> = rev_pyiter_collect(py, &revs)?;
184 let revs_vec: Vec<Revision> = rev_pyiter_collect(py, &revs)?;
183 let missing_vec = match inner.missing_ancestors(revs_vec) {
185 let missing_vec = match inner.missing_ancestors(revs_vec) {
184 Ok(missing) => missing,
186 Ok(missing) => missing,
185 Err(e) => {
187 Err(e) => {
186 return Err(GraphError::pynew(py, e));
188 return Err(GraphError::pynew(py, e));
187 }
189 }
188 };
190 };
189 // convert as Python list
191 // convert as Python list
190 let mut missing_pyint_vec: Vec<PyObject> = Vec::with_capacity(
192 let mut missing_pyint_vec: Vec<PyObject> = Vec::with_capacity(
191 missing_vec.len());
193 missing_vec.len());
192 for rev in missing_vec {
194 for rev in missing_vec {
193 missing_pyint_vec.push(rev.to_py_object(py).into_object());
195 missing_pyint_vec.push(rev.to_py_object(py).into_object());
194 }
196 }
195 Ok(PyList::new(py, missing_pyint_vec.as_slice()))
197 Ok(PyList::new(py, missing_pyint_vec.as_slice()))
196 }
198 }
197 });
199 });
198
200
199 /// Create the module, with __package__ given from parent
201 /// Create the module, with __package__ given from parent
200 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
202 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
201 let dotted_name = &format!("{}.ancestor", package);
203 let dotted_name = &format!("{}.ancestor", package);
202 let m = PyModule::new(py, dotted_name)?;
204 let m = PyModule::new(py, dotted_name)?;
203 m.add(py, "__package__", package)?;
205 m.add(py, "__package__", package)?;
204 m.add(
206 m.add(
205 py,
207 py,
206 "__doc__",
208 "__doc__",
207 "Generic DAG ancestor algorithms - Rust implementation",
209 "Generic DAG ancestor algorithms - Rust implementation",
208 )?;
210 )?;
209 m.add_class::<AncestorsIterator>(py)?;
211 m.add_class::<AncestorsIterator>(py)?;
210 m.add_class::<LazyAncestors>(py)?;
212 m.add_class::<LazyAncestors>(py)?;
211 m.add_class::<MissingAncestors>(py)?;
213 m.add_class::<MissingAncestors>(py)?;
212
214
213 let sys = PyModule::import(py, "sys")?;
215 let sys = PyModule::import(py, "sys")?;
214 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
216 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
215 sys_modules.set_item(py, dotted_name, &m)?;
217 sys_modules.set_item(py, dotted_name, &m)?;
216 // Example C code (see pyexpat.c and import.c) will "give away the
218 // Example C code (see pyexpat.c and import.c) will "give away the
217 // reference", but we won't because it will be consumed once the
219 // reference", but we won't because it will be consumed once the
218 // Rust PyObject is dropped.
220 // Rust PyObject is dropped.
219 Ok(m)
221 Ok(m)
220 }
222 }
@@ -1,53 +1,53 b''
1 // dagops.rs
1 // dagops.rs
2 //
2 //
3 // Copyright 2019 Georges Racinet <georges.racinet@octobus.net>
3 // Copyright 2019 Georges Racinet <georges.racinet@octobus.net>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::dagops` module provided by the
8 //! Bindings for the `hg::dagops` module provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10 //!
10 //!
11 //! From Python, this will be seen as `mercurial.rustext.dagop`
11 //! From Python, this will be seen as `mercurial.rustext.dagop`
12 use crate::{
12 use crate::{conversion::rev_pyiter_collect, exceptions::GraphError};
13 cindex::Index, conversion::rev_pyiter_collect, exceptions::GraphError,
14 };
15 use cpython::{PyDict, PyModule, PyObject, PyResult, Python};
13 use cpython::{PyDict, PyModule, PyObject, PyResult, Python};
16 use hg::dagops;
14 use hg::dagops;
17 use hg::Revision;
15 use hg::Revision;
18 use std::collections::HashSet;
16 use std::collections::HashSet;
19
17
18 use crate::revlog::pyindex_to_graph;
19
20 /// Using the the `index`, return heads out of any Python iterable of Revisions
20 /// Using the the `index`, return heads out of any Python iterable of Revisions
21 ///
21 ///
22 /// This is the Rust counterpart for `mercurial.dagop.headrevs`
22 /// This is the Rust counterpart for `mercurial.dagop.headrevs`
23 pub fn headrevs(
23 pub fn headrevs(
24 py: Python,
24 py: Python,
25 index: PyObject,
25 index: PyObject,
26 revs: PyObject,
26 revs: PyObject,
27 ) -> PyResult<HashSet<Revision>> {
27 ) -> PyResult<HashSet<Revision>> {
28 let mut as_set: HashSet<Revision> = rev_pyiter_collect(py, &revs)?;
28 let mut as_set: HashSet<Revision> = rev_pyiter_collect(py, &revs)?;
29 dagops::retain_heads(&Index::new(py, index)?, &mut as_set)
29 dagops::retain_heads(&pyindex_to_graph(py, index)?, &mut as_set)
30 .map_err(|e| GraphError::pynew(py, e))?;
30 .map_err(|e| GraphError::pynew(py, e))?;
31 Ok(as_set)
31 Ok(as_set)
32 }
32 }
33
33
34 /// Create the module, with `__package__` given from parent
34 /// Create the module, with `__package__` given from parent
35 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
35 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
36 let dotted_name = &format!("{}.dagop", package);
36 let dotted_name = &format!("{}.dagop", package);
37 let m = PyModule::new(py, dotted_name)?;
37 let m = PyModule::new(py, dotted_name)?;
38 m.add(py, "__package__", package)?;
38 m.add(py, "__package__", package)?;
39 m.add(py, "__doc__", "DAG operations - Rust implementation")?;
39 m.add(py, "__doc__", "DAG operations - Rust implementation")?;
40 m.add(
40 m.add(
41 py,
41 py,
42 "headrevs",
42 "headrevs",
43 py_fn!(py, headrevs(index: PyObject, revs: PyObject)),
43 py_fn!(py, headrevs(index: PyObject, revs: PyObject)),
44 )?;
44 )?;
45
45
46 let sys = PyModule::import(py, "sys")?;
46 let sys = PyModule::import(py, "sys")?;
47 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
47 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
48 sys_modules.set_item(py, dotted_name, &m)?;
48 sys_modules.set_item(py, dotted_name, &m)?;
49 // Example C code (see pyexpat.c and import.c) will "give away the
49 // Example C code (see pyexpat.c and import.c) will "give away the
50 // reference", but we won't because it will be consumed once the
50 // reference", but we won't because it will be consumed once the
51 // Rust PyObject is dropped.
51 // Rust PyObject is dropped.
52 Ok(m)
52 Ok(m)
53 }
53 }
@@ -1,159 +1,161 b''
1 // discovery.rs
1 // discovery.rs
2 //
2 //
3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Bindings for the `hg::discovery` module provided by the
8 //! Bindings for the `hg::discovery` module provided by the
9 //! `hg-core` crate. From Python, this will be seen as `rustext.discovery`
9 //! `hg-core` crate. From Python, this will be seen as `rustext.discovery`
10 //!
10 //!
11 //! # Classes visible from Python:
11 //! # Classes visible from Python:
12 //! - [`PartialDiscover`] is the Rust implementation of
12 //! - [`PartialDiscover`] is the Rust implementation of
13 //! `mercurial.setdiscovery.partialdiscovery`.
13 //! `mercurial.setdiscovery.partialdiscovery`.
14
14
15 use crate::{
15 use crate::{
16 cindex::Index, conversion::rev_pyiter_collect, exceptions::GraphError,
16 cindex::Index, conversion::rev_pyiter_collect, exceptions::GraphError,
17 };
17 };
18 use cpython::{
18 use cpython::{
19 ObjectProtocol, PyDict, PyModule, PyObject, PyResult, PyTuple, Python,
19 ObjectProtocol, PyDict, PyModule, PyObject, PyResult, PyTuple, Python,
20 PythonObject, ToPyObject,
20 PythonObject, ToPyObject,
21 };
21 };
22 use hg::discovery::PartialDiscovery as CorePartialDiscovery;
22 use hg::discovery::PartialDiscovery as CorePartialDiscovery;
23 use hg::Revision;
23 use hg::Revision;
24 use std::collections::HashSet;
24 use std::collections::HashSet;
25
25
26 use std::cell::RefCell;
26 use std::cell::RefCell;
27
27
28 use crate::revlog::pyindex_to_graph;
29
28 py_class!(pub class PartialDiscovery |py| {
30 py_class!(pub class PartialDiscovery |py| {
29 data inner: RefCell<Box<CorePartialDiscovery<Index>>>;
31 data inner: RefCell<Box<CorePartialDiscovery<Index>>>;
30
32
31 // `_respectsize` is currently only here to replicate the Python API and
33 // `_respectsize` is currently only here to replicate the Python API and
32 // will be used in future patches inside methods that are yet to be
34 // will be used in future patches inside methods that are yet to be
33 // implemented.
35 // implemented.
34 def __new__(
36 def __new__(
35 _cls,
37 _cls,
36 repo: PyObject,
38 repo: PyObject,
37 targetheads: PyObject,
39 targetheads: PyObject,
38 respectsize: bool,
40 respectsize: bool,
39 randomize: bool = true
41 randomize: bool = true
40 ) -> PyResult<PartialDiscovery> {
42 ) -> PyResult<PartialDiscovery> {
41 let index = repo.getattr(py, "changelog")?.getattr(py, "index")?;
43 let index = repo.getattr(py, "changelog")?.getattr(py, "index")?;
42 Self::create_instance(
44 Self::create_instance(
43 py,
45 py,
44 RefCell::new(Box::new(CorePartialDiscovery::new(
46 RefCell::new(Box::new(CorePartialDiscovery::new(
45 Index::new(py, index)?,
47 pyindex_to_graph(py, index)?,
46 rev_pyiter_collect(py, &targetheads)?,
48 rev_pyiter_collect(py, &targetheads)?,
47 respectsize,
49 respectsize,
48 randomize,
50 randomize,
49 )))
51 )))
50 )
52 )
51 }
53 }
52
54
53 def addcommons(&self, commons: PyObject) -> PyResult<PyObject> {
55 def addcommons(&self, commons: PyObject) -> PyResult<PyObject> {
54 let mut inner = self.inner(py).borrow_mut();
56 let mut inner = self.inner(py).borrow_mut();
55 let commons_vec: Vec<Revision> = rev_pyiter_collect(py, &commons)?;
57 let commons_vec: Vec<Revision> = rev_pyiter_collect(py, &commons)?;
56 inner.add_common_revisions(commons_vec)
58 inner.add_common_revisions(commons_vec)
57 .map_err(|e| GraphError::pynew(py, e))?;
59 .map_err(|e| GraphError::pynew(py, e))?;
58 Ok(py.None())
60 Ok(py.None())
59 }
61 }
60
62
61 def addmissings(&self, missings: PyObject) -> PyResult<PyObject> {
63 def addmissings(&self, missings: PyObject) -> PyResult<PyObject> {
62 let mut inner = self.inner(py).borrow_mut();
64 let mut inner = self.inner(py).borrow_mut();
63 let missings_vec: Vec<Revision> = rev_pyiter_collect(py, &missings)?;
65 let missings_vec: Vec<Revision> = rev_pyiter_collect(py, &missings)?;
64 inner.add_missing_revisions(missings_vec)
66 inner.add_missing_revisions(missings_vec)
65 .map_err(|e| GraphError::pynew(py, e))?;
67 .map_err(|e| GraphError::pynew(py, e))?;
66 Ok(py.None())
68 Ok(py.None())
67 }
69 }
68
70
69 def addinfo(&self, sample: PyObject) -> PyResult<PyObject> {
71 def addinfo(&self, sample: PyObject) -> PyResult<PyObject> {
70 let mut missing: Vec<Revision> = Vec::new();
72 let mut missing: Vec<Revision> = Vec::new();
71 let mut common: Vec<Revision> = Vec::new();
73 let mut common: Vec<Revision> = Vec::new();
72 for info in sample.iter(py)? { // info is a pair (Revision, bool)
74 for info in sample.iter(py)? { // info is a pair (Revision, bool)
73 let mut revknown = info?.iter(py)?;
75 let mut revknown = info?.iter(py)?;
74 let rev: Revision = revknown.next().unwrap()?.extract(py)?;
76 let rev: Revision = revknown.next().unwrap()?.extract(py)?;
75 let known: bool = revknown.next().unwrap()?.extract(py)?;
77 let known: bool = revknown.next().unwrap()?.extract(py)?;
76 if known {
78 if known {
77 common.push(rev);
79 common.push(rev);
78 } else {
80 } else {
79 missing.push(rev);
81 missing.push(rev);
80 }
82 }
81 }
83 }
82 let mut inner = self.inner(py).borrow_mut();
84 let mut inner = self.inner(py).borrow_mut();
83 inner.add_common_revisions(common)
85 inner.add_common_revisions(common)
84 .map_err(|e| GraphError::pynew(py, e))?;
86 .map_err(|e| GraphError::pynew(py, e))?;
85 inner.add_missing_revisions(missing)
87 inner.add_missing_revisions(missing)
86 .map_err(|e| GraphError::pynew(py, e))?;
88 .map_err(|e| GraphError::pynew(py, e))?;
87 Ok(py.None())
89 Ok(py.None())
88 }
90 }
89
91
90 def hasinfo(&self) -> PyResult<bool> {
92 def hasinfo(&self) -> PyResult<bool> {
91 Ok(self.inner(py).borrow().has_info())
93 Ok(self.inner(py).borrow().has_info())
92 }
94 }
93
95
94 def iscomplete(&self) -> PyResult<bool> {
96 def iscomplete(&self) -> PyResult<bool> {
95 Ok(self.inner(py).borrow().is_complete())
97 Ok(self.inner(py).borrow().is_complete())
96 }
98 }
97
99
98 def stats(&self) -> PyResult<PyDict> {
100 def stats(&self) -> PyResult<PyDict> {
99 let stats = self.inner(py).borrow().stats();
101 let stats = self.inner(py).borrow().stats();
100 let as_dict: PyDict = PyDict::new(py);
102 let as_dict: PyDict = PyDict::new(py);
101 as_dict.set_item(py, "undecided",
103 as_dict.set_item(py, "undecided",
102 stats.undecided.map(
104 stats.undecided.map(
103 |l| l.to_py_object(py).into_object())
105 |l| l.to_py_object(py).into_object())
104 .unwrap_or_else(|| py.None()))?;
106 .unwrap_or_else(|| py.None()))?;
105 Ok(as_dict)
107 Ok(as_dict)
106 }
108 }
107
109
108 def commonheads(&self) -> PyResult<HashSet<Revision>> {
110 def commonheads(&self) -> PyResult<HashSet<Revision>> {
109 self.inner(py).borrow().common_heads()
111 self.inner(py).borrow().common_heads()
110 .map_err(|e| GraphError::pynew(py, e))
112 .map_err(|e| GraphError::pynew(py, e))
111 }
113 }
112
114
113 def takefullsample(&self, _headrevs: PyObject,
115 def takefullsample(&self, _headrevs: PyObject,
114 size: usize) -> PyResult<PyObject> {
116 size: usize) -> PyResult<PyObject> {
115 let mut inner = self.inner(py).borrow_mut();
117 let mut inner = self.inner(py).borrow_mut();
116 let sample = inner.take_full_sample(size)
118 let sample = inner.take_full_sample(size)
117 .map_err(|e| GraphError::pynew(py, e))?;
119 .map_err(|e| GraphError::pynew(py, e))?;
118 let as_vec: Vec<PyObject> = sample
120 let as_vec: Vec<PyObject> = sample
119 .iter()
121 .iter()
120 .map(|rev| rev.to_py_object(py).into_object())
122 .map(|rev| rev.to_py_object(py).into_object())
121 .collect();
123 .collect();
122 Ok(PyTuple::new(py, as_vec.as_slice()).into_object())
124 Ok(PyTuple::new(py, as_vec.as_slice()).into_object())
123 }
125 }
124
126
125 def takequicksample(&self, headrevs: PyObject,
127 def takequicksample(&self, headrevs: PyObject,
126 size: usize) -> PyResult<PyObject> {
128 size: usize) -> PyResult<PyObject> {
127 let mut inner = self.inner(py).borrow_mut();
129 let mut inner = self.inner(py).borrow_mut();
128 let revsvec: Vec<Revision> = rev_pyiter_collect(py, &headrevs)?;
130 let revsvec: Vec<Revision> = rev_pyiter_collect(py, &headrevs)?;
129 let sample = inner.take_quick_sample(revsvec, size)
131 let sample = inner.take_quick_sample(revsvec, size)
130 .map_err(|e| GraphError::pynew(py, e))?;
132 .map_err(|e| GraphError::pynew(py, e))?;
131 let as_vec: Vec<PyObject> = sample
133 let as_vec: Vec<PyObject> = sample
132 .iter()
134 .iter()
133 .map(|rev| rev.to_py_object(py).into_object())
135 .map(|rev| rev.to_py_object(py).into_object())
134 .collect();
136 .collect();
135 Ok(PyTuple::new(py, as_vec.as_slice()).into_object())
137 Ok(PyTuple::new(py, as_vec.as_slice()).into_object())
136 }
138 }
137
139
138 });
140 });
139
141
140 /// Create the module, with __package__ given from parent
142 /// Create the module, with __package__ given from parent
141 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
143 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
142 let dotted_name = &format!("{}.discovery", package);
144 let dotted_name = &format!("{}.discovery", package);
143 let m = PyModule::new(py, dotted_name)?;
145 let m = PyModule::new(py, dotted_name)?;
144 m.add(py, "__package__", package)?;
146 m.add(py, "__package__", package)?;
145 m.add(
147 m.add(
146 py,
148 py,
147 "__doc__",
149 "__doc__",
148 "Discovery of common node sets - Rust implementation",
150 "Discovery of common node sets - Rust implementation",
149 )?;
151 )?;
150 m.add_class::<PartialDiscovery>(py)?;
152 m.add_class::<PartialDiscovery>(py)?;
151
153
152 let sys = PyModule::import(py, "sys")?;
154 let sys = PyModule::import(py, "sys")?;
153 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
155 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
154 sys_modules.set_item(py, dotted_name, &m)?;
156 sys_modules.set_item(py, dotted_name, &m)?;
155 // Example C code (see pyexpat.c and import.c) will "give away the
157 // Example C code (see pyexpat.c and import.c) will "give away the
156 // reference", but we won't because it will be consumed once the
158 // reference", but we won't because it will be consumed once the
157 // Rust PyObject is dropped.
159 // Rust PyObject is dropped.
158 Ok(m)
160 Ok(m)
159 }
161 }
@@ -1,81 +1,82 b''
1 // lib.rs
1 // lib.rs
2 //
2 //
3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
3 // Copyright 2018 Georges Racinet <gracinet@anybox.fr>
4 //
4 //
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 //! Python bindings of `hg-core` objects using the `cpython` crate.
8 //! Python bindings of `hg-core` objects using the `cpython` crate.
9 //! Once compiled, the resulting single shared library object can be placed in
9 //! Once compiled, the resulting single shared library object can be placed in
10 //! the `mercurial` package directly as `rustext.so` or `rustext.dll`.
10 //! the `mercurial` package directly as `rustext.so` or `rustext.dll`.
11 //! It holds several modules, so that from the point of view of Python,
11 //! It holds several modules, so that from the point of view of Python,
12 //! it behaves as the `cext` package.
12 //! it behaves as the `cext` package.
13 //!
13 //!
14 //! Example:
14 //! Example:
15 //!
15 //!
16 //! ```text
16 //! ```text
17 //! >>> from mercurial.rustext import ancestor
17 //! >>> from mercurial.rustext import ancestor
18 //! >>> ancestor.__doc__
18 //! >>> ancestor.__doc__
19 //! 'Generic DAG ancestor algorithms - Rust implementation'
19 //! 'Generic DAG ancestor algorithms - Rust implementation'
20 //! ```
20 //! ```
21
21
22 /// This crate uses nested private macros, `extern crate` is still needed in
22 /// This crate uses nested private macros, `extern crate` is still needed in
23 /// 2018 edition.
23 /// 2018 edition.
24 #[macro_use]
24 #[macro_use]
25 extern crate cpython;
25 extern crate cpython;
26
26
27 pub mod ancestors;
27 pub mod ancestors;
28 mod cindex;
28 mod cindex;
29 mod conversion;
29 mod conversion;
30 #[macro_use]
30 #[macro_use]
31 pub mod ref_sharing;
31 pub mod ref_sharing;
32 pub mod dagops;
32 pub mod dagops;
33 pub mod dirstate;
33 pub mod dirstate;
34 pub mod discovery;
34 pub mod discovery;
35 pub mod exceptions;
35 pub mod exceptions;
36 pub mod filepatterns;
36 pub mod filepatterns;
37 pub mod parsers;
37 pub mod parsers;
38 pub mod revlog;
38 pub mod utils;
39 pub mod utils;
39
40
40 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
41 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
41 m.add(
42 m.add(
42 py,
43 py,
43 "__doc__",
44 "__doc__",
44 "Mercurial core concepts - Rust implementation",
45 "Mercurial core concepts - Rust implementation",
45 )?;
46 )?;
46
47
47 let dotted_name: String = m.get(py, "__name__")?.extract(py)?;
48 let dotted_name: String = m.get(py, "__name__")?.extract(py)?;
48 m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
49 m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
49 m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?;
50 m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?;
50 m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?;
51 m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?;
51 m.add(py, "dirstate", dirstate::init_module(py, &dotted_name)?)?;
52 m.add(py, "dirstate", dirstate::init_module(py, &dotted_name)?)?;
52 m.add(
53 m.add(
53 py,
54 py,
54 "filepatterns",
55 "filepatterns",
55 filepatterns::init_module(py, &dotted_name)?,
56 filepatterns::init_module(py, &dotted_name)?,
56 )?;
57 )?;
57 m.add(
58 m.add(
58 py,
59 py,
59 "parsers",
60 "parsers",
60 parsers::init_parsers_module(py, &dotted_name)?,
61 parsers::init_parsers_module(py, &dotted_name)?,
61 )?;
62 )?;
62 m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
63 m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
63 m.add(
64 m.add(
64 py,
65 py,
65 "PatternFileError",
66 "PatternFileError",
66 py.get_type::<exceptions::PatternFileError>(),
67 py.get_type::<exceptions::PatternFileError>(),
67 )?;
68 )?;
68 m.add(
69 m.add(
69 py,
70 py,
70 "PatternError",
71 "PatternError",
71 py.get_type::<exceptions::PatternError>(),
72 py.get_type::<exceptions::PatternError>(),
72 )?;
73 )?;
73 Ok(())
74 Ok(())
74 });
75 });
75
76
76 #[cfg(not(any(feature = "python27-bin", feature = "python3-bin")))]
77 #[cfg(not(any(feature = "python27-bin", feature = "python3-bin")))]
77 #[test]
78 #[test]
78 #[ignore]
79 #[ignore]
79 fn libpython_must_be_linked_to_run_tests() {
80 fn libpython_must_be_linked_to_run_tests() {
80 // stub function to tell that some tests wouldn't run
81 // stub function to tell that some tests wouldn't run
81 }
82 }
General Comments 0
You need to be logged in to leave comments. Login now