##// END OF EJS Templates
rust-dirstate: add rust-cpython bindings to the new parse/pack functions...
Raphaël Gomès -
r42489:e240bec2 default
parent child Browse files
Show More
@@ -0,0 +1,227 b''
1 // dirstate.rs
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@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 //! Bindings for the `hg::dirstate` module provided by the
9 //! `hg-core` package.
10 //!
11 //! From Python, this will be seen as `mercurial.rustext.dirstate`
12
13 use cpython::{
14 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyObject, PyResult,
15 PySequence, PyTuple, Python, ToPyObject,
16 };
17 use hg::{
18 pack_dirstate, parse_dirstate, CopyVecEntry, DirstateEntry,
19 DirstatePackError, DirstateParents, DirstateParseError, DirstateVec,
20 };
21 use std::collections::HashMap;
22 use std::ffi::CStr;
23 #[cfg(feature = "python27")]
24 extern crate python27_sys as python_sys;
25 #[cfg(feature = "python3")]
26 extern crate python3_sys as python_sys;
27 use self::python_sys::PyCapsule_Import;
28 use libc::{c_char, c_int};
29 use std::mem::transmute;
30
31 /// C code uses a custom `dirstate_tuple` type, checks in multiple instances
32 /// for this type, and raises a Python `Exception` if the check does not pass.
33 /// Because this type differs only in name from the regular Python tuple, it
34 /// would be a good idea in the near future to remove it entirely to allow
35 /// for a pure Python tuple of the same effective structure to be used,
36 /// rendering this type and the capsule below useless.
37 type MakeDirstateTupleFn = extern "C" fn(
38 state: c_char,
39 mode: c_int,
40 size: c_int,
41 mtime: c_int,
42 ) -> PyObject;
43
44 /// This is largely a copy/paste from cindex.rs, pending the merge of a
45 /// `py_capsule_fn!` macro in the rust-cpython project:
46 /// https://github.com/dgrunwald/rust-cpython/pull/169
47 fn decapsule_make_dirstate_tuple(py: Python) -> PyResult<MakeDirstateTupleFn> {
48 unsafe {
49 let caps_name = CStr::from_bytes_with_nul_unchecked(
50 b"mercurial.cext.parsers.make_dirstate_tuple_CAPI\0",
51 );
52 let from_caps = PyCapsule_Import(caps_name.as_ptr(), 0);
53 if from_caps.is_null() {
54 return Err(PyErr::fetch(py));
55 }
56 Ok(transmute(from_caps))
57 }
58 }
59
60 fn parse_dirstate_wrapper(
61 py: Python,
62 dmap: PyDict,
63 copymap: PyDict,
64 st: PyBytes,
65 ) -> PyResult<PyTuple> {
66 match parse_dirstate(st.data(py)) {
67 Ok((parents, dirstate_vec, copies)) => {
68 for (filename, entry) in dirstate_vec {
69 dmap.set_item(
70 py,
71 PyBytes::new(py, &filename[..]),
72 decapsule_make_dirstate_tuple(py)?(
73 entry.state,
74 entry.mode,
75 entry.size,
76 entry.mtime,
77 ),
78 )?;
79 }
80 for CopyVecEntry { path, copy_path } in copies {
81 copymap.set_item(
82 py,
83 PyBytes::new(py, path),
84 PyBytes::new(py, copy_path),
85 )?;
86 }
87 Ok((PyBytes::new(py, parents.p1), PyBytes::new(py, parents.p2))
88 .to_py_object(py))
89 }
90 Err(e) => Err(PyErr::new::<exc::ValueError, _>(
91 py,
92 match e {
93 DirstateParseError::TooLittleData => {
94 "too little data for parents".to_string()
95 }
96 DirstateParseError::Overflow => {
97 "overflow in dirstate".to_string()
98 }
99 DirstateParseError::CorruptedEntry(e) => e,
100 },
101 )),
102 }
103 }
104
105 fn pack_dirstate_wrapper(
106 py: Python,
107 dmap: PyDict,
108 copymap: PyDict,
109 pl: PyTuple,
110 now: PyInt,
111 ) -> PyResult<PyBytes> {
112 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
113 let p1: &[u8] = p1.data(py);
114 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
115 let p2: &[u8] = p2.data(py);
116
117 let dirstate_vec: Result<DirstateVec, PyErr> = dmap
118 .items(py)
119 .iter()
120 .map(|(filename, stats)| {
121 let stats = stats.extract::<PySequence>(py)?;
122 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
123 let state = state.data(py)[0] as i8;
124 let mode = stats.get_item(py, 1)?.extract(py)?;
125 let size = stats.get_item(py, 2)?.extract(py)?;
126 let mtime = stats.get_item(py, 3)?.extract(py)?;
127 let filename = filename.extract::<PyBytes>(py)?;
128 let filename = filename.data(py);
129 Ok((
130 filename.to_owned(),
131 DirstateEntry {
132 state,
133 mode,
134 size,
135 mtime,
136 },
137 ))
138 })
139 .collect();
140
141 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
142 .items(py)
143 .iter()
144 .map(|(key, value)| {
145 Ok((
146 key.extract::<PyBytes>(py)?.data(py).to_owned(),
147 value.extract::<PyBytes>(py)?.data(py).to_owned(),
148 ))
149 })
150 .collect();
151
152 match pack_dirstate(
153 &dirstate_vec?,
154 &copies?,
155 DirstateParents { p1, p2 },
156 now.value(py) as i32,
157 ) {
158 Ok((packed, new_dirstate_vec)) => {
159 for (
160 filename,
161 DirstateEntry {
162 state,
163 mode,
164 size,
165 mtime,
166 },
167 ) in new_dirstate_vec
168 {
169 dmap.set_item(
170 py,
171 PyBytes::new(py, &filename[..]),
172 decapsule_make_dirstate_tuple(py)?(
173 state, mode, size, mtime,
174 ),
175 )?;
176 }
177 Ok(PyBytes::new(py, &packed))
178 }
179 Err(error) => Err(PyErr::new::<exc::ValueError, _>(
180 py,
181 match error {
182 DirstatePackError::CorruptedParent => {
183 "expected a 20-byte hash".to_string()
184 }
185 DirstatePackError::CorruptedEntry(e) => e,
186 DirstatePackError::BadSize(expected, actual) => {
187 format!("bad dirstate size: {} != {}", actual, expected)
188 }
189 },
190 )),
191 }
192 }
193
194 /// Create the module, with `__package__` given from parent
195 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
196 let dotted_name = &format!("{}.dirstate", package);
197 let m = PyModule::new(py, dotted_name)?;
198 m.add(py, "__package__", package)?;
199 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
200 m.add(
201 py,
202 "parse_dirstate",
203 py_fn!(
204 py,
205 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
206 ),
207 )?;
208 m.add(
209 py,
210 "pack_dirstate",
211 py_fn!(
212 py,
213 pack_dirstate_wrapper(
214 dmap: PyDict,
215 copymap: PyDict,
216 pl: PyTuple,
217 now: PyInt
218 )
219 ),
220 )?;
221
222 let sys = PyModule::import(py, "sys")?;
223 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
224 sys_modules.set_item(py, dotted_name, &m)?;
225
226 Ok(m)
227 }
@@ -671,6 +671,7 b' static const int version = 12;'
671
671
672 static void module_init(PyObject *mod)
672 static void module_init(PyObject *mod)
673 {
673 {
674 PyObject *capsule = NULL;
674 PyModule_AddIntConstant(mod, "version", version);
675 PyModule_AddIntConstant(mod, "version", version);
675
676
676 /* This module constant has two purposes. First, it lets us unit test
677 /* This module constant has two purposes. First, it lets us unit test
@@ -687,6 +688,12 b' static void module_init(PyObject *mod)'
687 manifest_module_init(mod);
688 manifest_module_init(mod);
688 revlog_module_init(mod);
689 revlog_module_init(mod);
689
690
691 capsule = PyCapsule_New(
692 make_dirstate_tuple,
693 "mercurial.cext.parsers.make_dirstate_tuple_CAPI", NULL);
694 if (capsule != NULL)
695 PyModule_AddObject(mod, "make_dirstate_tuple_CAPI", capsule);
696
690 if (PyType_Ready(&dirstateTupleType) < 0) {
697 if (PyType_Ready(&dirstateTupleType) < 0) {
691 return;
698 return;
692 }
699 }
@@ -3,7 +3,7 b' name = "aho-corasick"'
3 version = "0.6.9"
3 version = "0.6.9"
4 source = "registry+https://github.com/rust-lang/crates.io-index"
4 source = "registry+https://github.com/rust-lang/crates.io-index"
5 dependencies = [
5 dependencies = [
6 "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
6 "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
7 ]
7 ]
8
8
9 [[package]]
9 [[package]]
@@ -17,8 +17,8 b' version = "1.0.4"'
17 source = "registry+https://github.com/rust-lang/crates.io-index"
17 source = "registry+https://github.com/rust-lang/crates.io-index"
18
18
19 [[package]]
19 [[package]]
20 name = "cfg-if"
20 name = "byteorder"
21 version = "0.1.6"
21 version = "1.3.1"
22 source = "registry+https://github.com/rust-lang/crates.io-index"
22 source = "registry+https://github.com/rust-lang/crates.io-index"
23
23
24 [[package]]
24 [[package]]
@@ -49,6 +49,8 b' source = "registry+https://github.com/ru'
49 name = "hg-core"
49 name = "hg-core"
50 version = "0.1.0"
50 version = "0.1.0"
51 dependencies = [
51 dependencies = [
52 "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
53 "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
52 "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
54 "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
53 "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
55 "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
54 ]
56 ]
@@ -84,13 +86,8 b' source = "registry+https://github.com/ru'
84
86
85 [[package]]
87 [[package]]
86 name = "memchr"
88 name = "memchr"
87 version = "2.1.2"
89 version = "2.2.0"
88 source = "registry+https://github.com/rust-lang/crates.io-index"
90 source = "registry+https://github.com/rust-lang/crates.io-index"
89 dependencies = [
90 "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
91 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
92 "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
93 ]
94
91
95 [[package]]
92 [[package]]
96 name = "num-traits"
93 name = "num-traits"
@@ -225,7 +222,7 b' version = "1.1.0"'
225 source = "registry+https://github.com/rust-lang/crates.io-index"
222 source = "registry+https://github.com/rust-lang/crates.io-index"
226 dependencies = [
223 dependencies = [
227 "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
224 "aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
228 "memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
225 "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
229 "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
226 "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
230 "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
227 "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
231 "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
228 "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -279,11 +276,6 b' version = "1.0.2"'
279 source = "registry+https://github.com/rust-lang/crates.io-index"
276 source = "registry+https://github.com/rust-lang/crates.io-index"
280
277
281 [[package]]
278 [[package]]
282 name = "version_check"
283 version = "0.1.5"
284 source = "registry+https://github.com/rust-lang/crates.io-index"
285
286 [[package]]
287 name = "winapi"
279 name = "winapi"
288 version = "0.3.6"
280 version = "0.3.6"
289 source = "registry+https://github.com/rust-lang/crates.io-index"
281 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -306,13 +298,13 b' source = "registry+https://github.com/ru'
306 "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
298 "checksum aho-corasick 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9a933f4e58658d7b12defcf96dc5c720f20832deebe3e0a19efd3b6aaeeb9e"
307 "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
299 "checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
308 "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
300 "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
309 "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
301 "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
310 "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
302 "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
311 "checksum cpython 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b489034e723e7f5109fecd19b719e664f89ef925be785885252469e9822fa940"
303 "checksum cpython 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b489034e723e7f5109fecd19b719e664f89ef925be785885252469e9822fa940"
312 "checksum fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81f7f8eb465745ea9b02e2704612a9946a59fa40572086c6fd49d6ddcf30bf31"
304 "checksum fuchsia-cprng 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81f7f8eb465745ea9b02e2704612a9946a59fa40572086c6fd49d6ddcf30bf31"
313 "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
305 "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
314 "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
306 "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
315 "checksum memchr 2.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "db4c41318937f6e76648f42826b1d9ade5c09cafb5aef7e351240a70f39206e9"
307 "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
316 "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
308 "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
317 "checksum python27-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56114c37d4dca82526d74009df7782a28c871ac9d36b19d4cb9e67672258527e"
309 "checksum python27-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56114c37d4dca82526d74009df7782a28c871ac9d36b19d4cb9e67672258527e"
318 "checksum python3-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "61e4aac43f833fd637e429506cb2ac9d7df672c4b68f2eaaa163649b7fdc0444"
310 "checksum python3-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "61e4aac43f833fd637e429506cb2ac9d7df672c4b68f2eaaa163649b7fdc0444"
@@ -335,7 +327,6 b' source = "registry+https://github.com/ru'
335 "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
327 "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
336 "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
328 "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
337 "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
329 "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737"
338 "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
339 "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
330 "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
340 "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
331 "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
341 "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
332 "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
@@ -8,8 +8,13 b' extern crate memchr;'
8 mod ancestors;
8 mod ancestors;
9 pub mod dagops;
9 pub mod dagops;
10 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
10 pub use ancestors::{AncestorsIterator, LazyAncestors, MissingAncestors};
11 pub mod testing; // unconditionally built, for use from integration tests
11 mod dirstate;
12 pub mod discovery;
12 pub mod discovery;
13 pub mod testing; // unconditionally built, for use from integration tests
14 pub use dirstate::{
15 pack_dirstate, parse_dirstate, CopyVec, CopyVecEntry, DirstateEntry,
16 DirstateParents, DirstateVec,
17 };
13
18
14 /// Mercurial revision numbers
19 /// Mercurial revision numbers
15 ///
20 ///
@@ -17,7 +22,6 b' pub mod discovery;'
17 /// 4 bytes, and are liberally converted to ints, whence the i32
22 /// 4 bytes, and are liberally converted to ints, whence the i32
18 pub type Revision = i32;
23 pub type Revision = i32;
19
24
20
21 /// Marker expressing the absence of a parent
25 /// Marker expressing the absence of a parent
22 ///
26 ///
23 /// Independently of the actual representation, `NULL_REVISION` is guaranteed
27 /// Independently of the actual representation, `NULL_REVISION` is guaranteed
@@ -23,6 +23,7 b''
23 extern crate cpython;
23 extern crate cpython;
24 extern crate hg;
24 extern crate hg;
25 extern crate libc;
25 extern crate libc;
26 extern crate python27_sys;
26
27
27 pub mod ancestors;
28 pub mod ancestors;
28 mod cindex;
29 mod cindex;
@@ -30,6 +31,7 b' mod conversion;'
30 pub mod dagops;
31 pub mod dagops;
31 pub mod discovery;
32 pub mod discovery;
32 pub mod exceptions;
33 pub mod exceptions;
34 pub mod dirstate;
33
35
34 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
36 py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| {
35 m.add(
37 m.add(
@@ -42,6 +44,7 b' py_module_initializer!(rustext, initrust'
42 m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
44 m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?;
43 m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?;
45 m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?;
44 m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?;
46 m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?;
47 m.add(py, "dirstate", dirstate::init_module(py, &dotted_name)?)?;
45 m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
48 m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
46 Ok(())
49 Ok(())
47 });
50 });
General Comments 0
You need to be logged in to leave comments. Login now