##// END OF EJS Templates
copies: introduce the hg-cpython wrapper for `combine_changeset_copies`...
marmoute -
r46557:50c5ee3b default
parent child Browse files
Show More
@@ -0,0 +1,295 b''
1 use cpython::ObjectProtocol;
2 use cpython::PyBool;
3 use cpython::PyBytes;
4 use cpython::PyDict;
5 use cpython::PyList;
6 use cpython::PyModule;
7 use cpython::PyObject;
8 use cpython::PyResult;
9 use cpython::PyTuple;
10 use cpython::Python;
11
12 use hg::copy_tracing::combine_changeset_copies;
13 use hg::copy_tracing::ChangedFiles;
14 use hg::copy_tracing::RevInfo;
15 use hg::utils::hg_path::HgPathBuf;
16 use hg::Revision;
17
18 /// Combines copies information contained into revision `revs` to build a copy
19 /// map.
20 ///
21 /// See mercurial/copies.py for details
22 pub fn combine_changeset_copies_wrapper(
23 py: Python,
24 revs: PyList,
25 children: PyDict,
26 target_rev: Revision,
27 rev_info: PyObject,
28 is_ancestor: PyObject,
29 ) -> PyResult<PyDict> {
30 let revs: PyResult<_> =
31 revs.iter(py).map(|r| Ok(r.extract(py)?)).collect();
32
33 // Wrap the `is_ancestor` python callback as a Rust closure
34 //
35 // No errors are expected from the Python side, and they will should only
36 // happens in case of programing error or severe data corruption. Such
37 // errors will raise panic and the rust-cpython harness will turn them into
38 // Python exception.
39 let is_ancestor_wrap = |anc: Revision, desc: Revision| -> bool {
40 is_ancestor
41 .call(py, (anc, desc), None)
42 .expect(
43 "rust-copy-tracing: python call to `is_ancestor` \
44 failed",
45 )
46 .cast_into::<PyBool>(py)
47 .expect(
48 "rust-copy-tracing: python call to `is_ancestor` \
49 returned unexpected non-Bool value",
50 )
51 .is_true()
52 };
53
54 // Wrap the `rev_info_maker` python callback as a Rust closure
55 //
56 // No errors are expected from the Python side, and they will should only
57 // happens in case of programing error or severe data corruption. Such
58 // errors will raise panic and the rust-cpython harness will turn them into
59 // Python exception.
60 let rev_info_maker = |rev: Revision| -> RevInfo {
61 let res: PyTuple = rev_info
62 .call(py, (rev,), None)
63 .expect("rust-copy-tracing: python call to `rev_info` failed")
64 .cast_into(py)
65 .expect(
66 "rust-copy_tracing: python call to `rev_info` returned \
67 unexpected non-Tuple value",
68 );
69 let p1 = res.get_item(py, 0).extract(py).expect(
70 "rust-copy-tracing: \
71 rev_info return is invalid, first item is a not a revision",
72 );
73 let p2 = res.get_item(py, 1).extract(py).expect(
74 "rust-copy-tracing: \
75 rev_info return is invalid, second item is a not a revision",
76 );
77
78 let changes = res.get_item(py, 2);
79
80 let files;
81 if !changes
82 .hasattr(py, "copied_from_p1")
83 .expect("rust-copy-tracing: python call to `hasattr` failed")
84 {
85 files = ChangedFiles::new_empty();
86 } else {
87 let p1_copies: PyDict = changes
88 .getattr(py, "copied_from_p1")
89 .expect(
90 "rust-copy-tracing: retrieval of python attribute \
91 `copied_from_p1` failed",
92 )
93 .cast_into(py)
94 .expect(
95 "rust-copy-tracing: failed to convert `copied_from_p1` \
96 to PyDict",
97 );
98 let p1_copies: PyResult<_> = p1_copies
99 .items(py)
100 .iter()
101 .map(|(key, value)| {
102 let key = key.extract::<PyBytes>(py).expect(
103 "rust-copy-tracing: conversion of copy destination to\
104 PyBytes failed",
105 );
106 let key = key.data(py);
107 let value = value.extract::<PyBytes>(py).expect(
108 "rust-copy-tracing: conversion of copy source to \
109 PyBytes failed",
110 );
111 let value = value.data(py);
112 Ok((
113 HgPathBuf::from_bytes(key),
114 HgPathBuf::from_bytes(value),
115 ))
116 })
117 .collect();
118
119 let p2_copies: PyDict = changes
120 .getattr(py, "copied_from_p2")
121 .expect(
122 "rust-copy-tracing: retrieval of python attribute \
123 `copied_from_p2` failed",
124 )
125 .cast_into(py)
126 .expect(
127 "rust-copy-tracing: failed to convert `copied_from_p2` \
128 to PyDict",
129 );
130 let p2_copies: PyResult<_> = p2_copies
131 .items(py)
132 .iter()
133 .map(|(key, value)| {
134 let key = key.extract::<PyBytes>(py).expect(
135 "rust-copy-tracing: conversion of copy destination to \
136 PyBytes failed");
137 let key = key.data(py);
138 let value = value.extract::<PyBytes>(py).expect(
139 "rust-copy-tracing: conversion of copy source to \
140 PyBytes failed",
141 );
142 let value = value.data(py);
143 Ok((
144 HgPathBuf::from_bytes(key),
145 HgPathBuf::from_bytes(value),
146 ))
147 })
148 .collect();
149
150 let removed: PyObject = changes.getattr(py, "removed").expect(
151 "rust-copy-tracing: retrieval of python attribute \
152 `removed` failed",
153 );
154 let removed: PyResult<_> = removed
155 .iter(py)
156 .expect(
157 "rust-copy-tracing: getting a python iterator over the \
158 `removed` set failed",
159 )
160 .map(|filename| {
161 let filename = filename
162 .expect(
163 "rust-copy-tracing: python iteration over the \
164 `removed` set failed",
165 )
166 .extract::<PyBytes>(py)
167 .expect(
168 "rust-copy-tracing: \
169 conversion of `removed` item to PyBytes failed",
170 );
171 let filename = filename.data(py);
172 Ok(HgPathBuf::from_bytes(filename))
173 })
174 .collect();
175
176 let merged: PyObject = changes.getattr(py, "merged").expect(
177 "rust-copy-tracing: retrieval of python attribute \
178 `merged` failed",
179 );
180 let merged: PyResult<_> = merged
181 .iter(py)
182 .expect(
183 "rust-copy-tracing: getting a python iterator over the \
184 `merged` set failed",
185 )
186 .map(|filename| {
187 let filename = filename
188 .expect(
189 "rust-copy-tracing: python iteration over the \
190 `merged` set failed",
191 )
192 .extract::<PyBytes>(py)
193 .expect(
194 "rust-copy-tracing: \
195 conversion of `merged` item to PyBytes failed",
196 );
197 let filename = filename.data(py);
198 Ok(HgPathBuf::from_bytes(filename))
199 })
200 .collect();
201
202 let salvaged: PyObject = changes.getattr(py, "salvaged").expect(
203 "rust-copy-tracing: retrieval of python attribute \
204 `salvaged` failed",
205 );
206 let salvaged: PyResult<_> = salvaged
207 .iter(py)
208 .expect(
209 "rust-copy-tracing: getting a python iterator over the \
210 `salvaged` set failed",
211 )
212 .map(|filename| {
213 let filename = filename
214 .expect(
215 "rust-copy-tracing: python iteration over the \
216 `salvaged` set failed",
217 )
218 .extract::<PyBytes>(py)
219 .expect(
220 "rust-copy-tracing: \
221 conversion of `salvaged` item to PyBytes failed",
222 );
223 let filename = filename.data(py);
224 Ok(HgPathBuf::from_bytes(filename))
225 })
226 .collect();
227 files = ChangedFiles::new(
228 removed.unwrap(),
229 merged.unwrap(),
230 salvaged.unwrap(),
231 p1_copies.unwrap(),
232 p2_copies.unwrap(),
233 );
234 }
235
236 (p1, p2, files)
237 };
238 let children: PyResult<_> = children
239 .items(py)
240 .iter()
241 .map(|(k, v)| {
242 let v: &PyList = v.cast_as(py)?;
243 let v: PyResult<_> =
244 v.iter(py).map(|child| Ok(child.extract(py)?)).collect();
245 Ok((k.extract(py)?, v?))
246 })
247 .collect();
248
249 let res = combine_changeset_copies(
250 revs?,
251 children?,
252 target_rev,
253 &rev_info_maker,
254 &is_ancestor_wrap,
255 );
256 let out = PyDict::new(py);
257 for (dest, source) in res.into_iter() {
258 out.set_item(
259 py,
260 PyBytes::new(py, &dest.into_vec()),
261 PyBytes::new(py, &source.into_vec()),
262 )?;
263 }
264 Ok(out)
265 }
266
267 /// Create the module, with `__package__` given from parent
268 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
269 let dotted_name = &format!("{}.copy_tracing", package);
270 let m = PyModule::new(py, dotted_name)?;
271
272 m.add(py, "__package__", package)?;
273 m.add(py, "__doc__", "Copy tracing - Rust implementation")?;
274
275 m.add(
276 py,
277 "combine_changeset_copies",
278 py_fn!(
279 py,
280 combine_changeset_copies_wrapper(
281 revs: PyList,
282 children: PyDict,
283 target_rev: Revision,
284 rev_info: PyObject,
285 is_ancestor: PyObject
286 )
287 ),
288 )?;
289
290 let sys = PyModule::import(py, "sys")?;
291 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
292 sys_modules.set_item(py, dotted_name, &m)?;
293
294 Ok(m)
295 }
@@ -1,69 +1,75 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 pub mod copy_tracing;
32 33 pub mod dagops;
33 34 pub mod debug;
34 35 pub mod dirstate;
35 36 pub mod discovery;
36 37 pub mod exceptions;
37 38 pub mod parsers;
38 39 pub mod revlog;
39 40 pub mod utils;
40 41
41 42 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
42 43 m.add(
43 44 py,
44 45 "__doc__",
45 46 "Mercurial core concepts - Rust implementation",
46 47 )?;
47 48
48 49 let dotted_name: String = m.get(py, "__name__")?.extract(py)?;
49 50 m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
50 51 m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?;
51 52 m.add(py, "debug", debug::init_module(py, &dotted_name)?)?;
53 m.add(
54 py,
55 "copy_tracing",
56 copy_tracing::init_module(py, &dotted_name)?,
57 )?;
52 58 m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?;
53 59 m.add(py, "dirstate", dirstate::init_module(py, &dotted_name)?)?;
54 60 m.add(py, "revlog", revlog::init_module(py, &dotted_name)?)?;
55 61 m.add(
56 62 py,
57 63 "parsers",
58 64 parsers::init_parsers_module(py, &dotted_name)?,
59 65 )?;
60 66 m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
61 67 Ok(())
62 68 });
63 69
64 70 #[cfg(not(any(feature = "python27-bin", feature = "python3-bin")))]
65 71 #[test]
66 72 #[ignore]
67 73 fn libpython_must_be_linked_to_run_tests() {
68 74 // stub function to tell that some tests wouldn't run
69 75 }
General Comments 0
You need to be logged in to leave comments. Login now