##// 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 // 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 copy_tracing;
32 pub mod dagops;
33 pub mod dagops;
33 pub mod debug;
34 pub mod debug;
34 pub mod dirstate;
35 pub mod dirstate;
35 pub mod discovery;
36 pub mod discovery;
36 pub mod exceptions;
37 pub mod exceptions;
37 pub mod parsers;
38 pub mod parsers;
38 pub mod revlog;
39 pub mod revlog;
39 pub mod utils;
40 pub mod utils;
40
41
41 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
42 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
42 m.add(
43 m.add(
43 py,
44 py,
44 "__doc__",
45 "__doc__",
45 "Mercurial core concepts - Rust implementation",
46 "Mercurial core concepts - Rust implementation",
46 )?;
47 )?;
47
48
48 let dotted_name: String = m.get(py, "__name__")?.extract(py)?;
49 let dotted_name: String = m.get(py, "__name__")?.extract(py)?;
49 m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
50 m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
50 m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?;
51 m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?;
51 m.add(py, "debug", debug::init_module(py, &dotted_name)?)?;
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 m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?;
58 m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?;
53 m.add(py, "dirstate", dirstate::init_module(py, &dotted_name)?)?;
59 m.add(py, "dirstate", dirstate::init_module(py, &dotted_name)?)?;
54 m.add(py, "revlog", revlog::init_module(py, &dotted_name)?)?;
60 m.add(py, "revlog", revlog::init_module(py, &dotted_name)?)?;
55 m.add(
61 m.add(
56 py,
62 py,
57 "parsers",
63 "parsers",
58 parsers::init_parsers_module(py, &dotted_name)?,
64 parsers::init_parsers_module(py, &dotted_name)?,
59 )?;
65 )?;
60 m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
66 m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
61 Ok(())
67 Ok(())
62 });
68 });
63
69
64 #[cfg(not(any(feature = "python27-bin", feature = "python3-bin")))]
70 #[cfg(not(any(feature = "python27-bin", feature = "python3-bin")))]
65 #[test]
71 #[test]
66 #[ignore]
72 #[ignore]
67 fn libpython_must_be_linked_to_run_tests() {
73 fn libpython_must_be_linked_to_run_tests() {
68 // stub function to tell that some tests wouldn't run
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