##// END OF EJS Templates
rust-parsers: move parser bindings to their own file and Python module...
Raphaël Gomès -
r42992:760a7851 default
parent child Browse files
Show More
@@ -27,14 +27,14 b' from . import ('
27 util,
27 util,
28 )
28 )
29
29
30 parsers = policy.importmod(r'parsers')
30 orig_parsers = policy.importmod(r'parsers')
31 dirstatemod = policy.importrust(r'dirstate', default=parsers)
31 parsers = policy.importrust(r'parsers', default=orig_parsers)
32
32
33 propertycache = util.propertycache
33 propertycache = util.propertycache
34 filecache = scmutil.filecache
34 filecache = scmutil.filecache
35 _rangemask = 0x7fffffff
35 _rangemask = 0x7fffffff
36
36
37 dirstatetuple = parsers.dirstatetuple
37 dirstatetuple = orig_parsers.dirstatetuple
38
38
39 class repocache(filecache):
39 class repocache(filecache):
40 """filecache for files in .hg/"""
40 """filecache for files in .hg/"""
@@ -1475,7 +1475,7 b' class dirstatemap(object):'
1475 # parsing the dirstate.
1475 # parsing the dirstate.
1476 #
1476 #
1477 # (we cannot decorate the function directly since it is in a C module)
1477 # (we cannot decorate the function directly since it is in a C module)
1478 parse_dirstate = util.nogc(dirstatemod.parse_dirstate)
1478 parse_dirstate = util.nogc(parsers.parse_dirstate)
1479 p = parse_dirstate(self._map, self.copymap, st)
1479 p = parse_dirstate(self._map, self.copymap, st)
1480 if not self._dirtyparents:
1480 if not self._dirtyparents:
1481 self.setparents(*p)
1481 self.setparents(*p)
@@ -1486,8 +1486,8 b' class dirstatemap(object):'
1486 self.get = self._map.get
1486 self.get = self._map.get
1487
1487
1488 def write(self, st, now):
1488 def write(self, st, now):
1489 st.write(dirstatemod.pack_dirstate(self._map, self.copymap,
1489 st.write(parsers.pack_dirstate(self._map, self.copymap,
1490 self.parents(), now))
1490 self.parents(), now))
1491 st.close()
1491 st.close()
1492 self._dirtyparents = False
1492 self._dirtyparents = False
1493 self.nonnormalset, self.otherparentset = self.nonnormalentries()
1493 self.nonnormalset, self.otherparentset = self.nonnormalentries()
@@ -12,19 +12,14 b''
12 mod dirs_multiset;
12 mod dirs_multiset;
13 use crate::dirstate::dirs_multiset::Dirs;
13 use crate::dirstate::dirs_multiset::Dirs;
14 use cpython::{
14 use cpython::{
15 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyObject, PyResult,
15 PyBytes, PyDict, PyErr, PyModule, PyObject, PyResult, PySequence, Python,
16 PySequence, PyTuple, Python, PythonObject, ToPyObject,
17 };
16 };
18 use hg::{
17 use hg::{DirstateEntry, DirstateVec};
19 pack_dirstate, parse_dirstate, CopyVecEntry, DirstateEntry,
20 DirstatePackError, DirstateParents, DirstateParseError, DirstateVec,
21 };
22 use libc::{c_char, c_int};
18 use libc::{c_char, c_int};
23 #[cfg(feature = "python27")]
19 #[cfg(feature = "python27")]
24 use python27_sys::PyCapsule_Import;
20 use python27_sys::PyCapsule_Import;
25 #[cfg(feature = "python3")]
21 #[cfg(feature = "python3")]
26 use python3_sys::PyCapsule_Import;
22 use python3_sys::PyCapsule_Import;
27 use std::collections::HashMap;
28 use std::ffi::CStr;
23 use std::ffi::CStr;
29 use std::mem::transmute;
24 use std::mem::transmute;
30
25
@@ -44,7 +39,9 b' type MakeDirstateTupleFn = extern "C" fn'
44 /// This is largely a copy/paste from cindex.rs, pending the merge of a
39 /// This is largely a copy/paste from cindex.rs, pending the merge of a
45 /// `py_capsule_fn!` macro in the rust-cpython project:
40 /// `py_capsule_fn!` macro in the rust-cpython project:
46 /// https://github.com/dgrunwald/rust-cpython/pull/169
41 /// https://github.com/dgrunwald/rust-cpython/pull/169
47 fn decapsule_make_dirstate_tuple(py: Python) -> PyResult<MakeDirstateTupleFn> {
42 pub fn decapsule_make_dirstate_tuple(
43 py: Python,
44 ) -> PyResult<MakeDirstateTupleFn> {
48 unsafe {
45 unsafe {
49 let caps_name = CStr::from_bytes_with_nul_unchecked(
46 let caps_name = CStr::from_bytes_with_nul_unchecked(
50 b"mercurial.cext.parsers.make_dirstate_tuple_CAPI\0",
47 b"mercurial.cext.parsers.make_dirstate_tuple_CAPI\0",
@@ -57,52 +54,7 b' fn decapsule_make_dirstate_tuple(py: Pyt'
57 }
54 }
58 }
55 }
59
56
60 fn parse_dirstate_wrapper(
57 pub fn extract_dirstate_vec(
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 as c_char,
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 extract_dirstate_vec(
106 py: Python,
58 py: Python,
107 dmap: &PyDict,
59 dmap: &PyDict,
108 ) -> Result<DirstateVec, PyErr> {
60 ) -> Result<DirstateVec, PyErr> {
@@ -130,76 +82,6 b' fn extract_dirstate_vec('
130 .collect()
82 .collect()
131 }
83 }
132
84
133 fn pack_dirstate_wrapper(
134 py: Python,
135 dmap: PyDict,
136 copymap: PyDict,
137 pl: PyTuple,
138 now: PyInt,
139 ) -> PyResult<PyBytes> {
140 let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?;
141 let p1: &[u8] = p1.data(py);
142 let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?;
143 let p2: &[u8] = p2.data(py);
144
145 let dirstate_vec = extract_dirstate_vec(py, &dmap)?;
146
147 let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap
148 .items(py)
149 .iter()
150 .map(|(key, value)| {
151 Ok((
152 key.extract::<PyBytes>(py)?.data(py).to_owned(),
153 value.extract::<PyBytes>(py)?.data(py).to_owned(),
154 ))
155 })
156 .collect();
157
158 match pack_dirstate(
159 &dirstate_vec,
160 &copies?,
161 DirstateParents { p1, p2 },
162 now.as_object().extract::<i32>(py)?,
163 ) {
164 Ok((packed, new_dirstate_vec)) => {
165 for (
166 filename,
167 DirstateEntry {
168 state,
169 mode,
170 size,
171 mtime,
172 },
173 ) in new_dirstate_vec
174 {
175 dmap.set_item(
176 py,
177 PyBytes::new(py, &filename[..]),
178 decapsule_make_dirstate_tuple(py)?(
179 state as c_char,
180 mode,
181 size,
182 mtime,
183 ),
184 )?;
185 }
186 Ok(PyBytes::new(py, &packed))
187 }
188 Err(error) => Err(PyErr::new::<exc::ValueError, _>(
189 py,
190 match error {
191 DirstatePackError::CorruptedParent => {
192 "expected a 20-byte hash".to_string()
193 }
194 DirstatePackError::CorruptedEntry(e) => e,
195 DirstatePackError::BadSize(expected, actual) => {
196 format!("bad dirstate size: {} != {}", actual, expected)
197 }
198 },
199 )),
200 }
201 }
202
203 /// Create the module, with `__package__` given from parent
85 /// Create the module, with `__package__` given from parent
204 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
86 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
205 let dotted_name = &format!("{}.dirstate", package);
87 let dotted_name = &format!("{}.dirstate", package);
@@ -207,27 +89,6 b' pub fn init_module(py: Python, package: '
207
89
208 m.add(py, "__package__", package)?;
90 m.add(py, "__package__", package)?;
209 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
91 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
210 m.add(
211 py,
212 "parse_dirstate",
213 py_fn!(
214 py,
215 parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes)
216 ),
217 )?;
218 m.add(
219 py,
220 "pack_dirstate",
221 py_fn!(
222 py,
223 pack_dirstate_wrapper(
224 dmap: PyDict,
225 copymap: PyDict,
226 pl: PyTuple,
227 now: PyInt
228 )
229 ),
230 )?;
231
92
232 m.add_class::<Dirs>(py)?;
93 m.add_class::<Dirs>(py)?;
233
94
@@ -29,6 +29,7 b' mod cindex;'
29 mod conversion;
29 mod conversion;
30 pub mod dagops;
30 pub mod dagops;
31 pub mod dirstate;
31 pub mod dirstate;
32 pub mod parsers;
32 pub mod discovery;
33 pub mod discovery;
33 pub mod exceptions;
34 pub mod exceptions;
34 pub mod filepatterns;
35 pub mod filepatterns;
@@ -50,6 +51,11 b' py_module_initializer!(rustext, initrust'
50 "filepatterns",
51 "filepatterns",
51 filepatterns::init_module(py, &dotted_name)?,
52 filepatterns::init_module(py, &dotted_name)?,
52 )?;
53 )?;
54 m.add(
55 py,
56 "parsers",
57 parsers::init_parsers_module(py, &dotted_name)?,
58 )?;
53 m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
59 m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?;
54 m.add(
60 m.add(
55 py,
61 py,
@@ -1,61 +1,28 b''
1 // dirstate.rs
1 // parsers.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@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::dirstate` module provided by the
8 //! Bindings for the `hg::dirstate::parsers` module provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10 //!
10 //!
11 //! From Python, this will be seen as `mercurial.rustext.dirstate`
11 //! From Python, this will be seen as `mercurial.rustext.parsers`
12 mod dirs_multiset;
12 //!
13 use crate::dirstate::dirs_multiset::Dirs;
14 use cpython::{
13 use cpython::{
15 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyObject, PyResult,
14 exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyResult, PyTuple, Python,
16 PySequence, PyTuple, Python, PythonObject, ToPyObject,
15 PythonObject, ToPyObject,
17 };
16 };
18 use hg::{
17 use hg::{
19 pack_dirstate, parse_dirstate, CopyVecEntry, DirstateEntry,
18 pack_dirstate, parse_dirstate, CopyVecEntry, DirstateEntry,
20 DirstatePackError, DirstateParents, DirstateParseError, DirstateVec,
19 DirstatePackError, DirstateParents, DirstateParseError,
21 };
20 };
22 use libc::{c_char, c_int};
23 #[cfg(feature = "python27")]
24 use python27_sys::PyCapsule_Import;
25 #[cfg(feature = "python3")]
26 use python3_sys::PyCapsule_Import;
27 use std::collections::HashMap;
21 use std::collections::HashMap;
28 use std::ffi::CStr;
29 use std::mem::transmute;
30
22
31 /// C code uses a custom `dirstate_tuple` type, checks in multiple instances
23 use libc::c_char;
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
24
44 /// This is largely a copy/paste from cindex.rs, pending the merge of a
25 use crate::dirstate::{decapsule_make_dirstate_tuple, extract_dirstate_vec};
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
26
60 fn parse_dirstate_wrapper(
27 fn parse_dirstate_wrapper(
61 py: Python,
28 py: Python,
@@ -102,34 +69,6 b' fn parse_dirstate_wrapper('
102 }
69 }
103 }
70 }
104
71
105 fn extract_dirstate_vec(
106 py: Python,
107 dmap: &PyDict,
108 ) -> Result<DirstateVec, PyErr> {
109 dmap.items(py)
110 .iter()
111 .map(|(filename, stats)| {
112 let stats = stats.extract::<PySequence>(py)?;
113 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
114 let state = state.data(py)[0] as i8;
115 let mode = stats.get_item(py, 1)?.extract(py)?;
116 let size = stats.get_item(py, 2)?.extract(py)?;
117 let mtime = stats.get_item(py, 3)?.extract(py)?;
118 let filename = filename.extract::<PyBytes>(py)?;
119 let filename = filename.data(py);
120 Ok((
121 filename.to_owned(),
122 DirstateEntry {
123 state,
124 mode,
125 size,
126 mtime,
127 },
128 ))
129 })
130 .collect()
131 }
132
133 fn pack_dirstate_wrapper(
72 fn pack_dirstate_wrapper(
134 py: Python,
73 py: Python,
135 dmap: PyDict,
74 dmap: PyDict,
@@ -201,12 +140,13 b' fn pack_dirstate_wrapper('
201 }
140 }
202
141
203 /// Create the module, with `__package__` given from parent
142 /// Create the module, with `__package__` given from parent
204 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
143 pub fn init_parsers_module(py: Python, package: &str) -> PyResult<PyModule> {
205 let dotted_name = &format!("{}.dirstate", package);
144 let dotted_name = &format!("{}.parsers", package);
206 let m = PyModule::new(py, dotted_name)?;
145 let m = PyModule::new(py, dotted_name)?;
207
146
208 m.add(py, "__package__", package)?;
147 m.add(py, "__package__", package)?;
209 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
148 m.add(py, "__doc__", "Parsers - Rust implementation")?;
149
210 m.add(
150 m.add(
211 py,
151 py,
212 "parse_dirstate",
152 "parse_dirstate",
@@ -229,8 +169,6 b' pub fn init_module(py: Python, package: '
229 ),
169 ),
230 )?;
170 )?;
231
171
232 m.add_class::<Dirs>(py)?;
233
234 let sys = PyModule::import(py, "sys")?;
172 let sys = PyModule::import(py, "sys")?;
235 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
173 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
236 sys_modules.set_item(py, dotted_name, &m)?;
174 sys_modules.set_item(py, dotted_name, &m)?;
@@ -30,6 +30,7 b" configitem(b'fakedirstatewritetime', b'f"
30 )
30 )
31
31
32 parsers = policy.importmod(r'parsers')
32 parsers = policy.importmod(r'parsers')
33 rustmod = policy.importrust(r'parsers')
33
34
34 def pack_dirstate(fakenow, orig, dmap, copymap, pl, now):
35 def pack_dirstate(fakenow, orig, dmap, copymap, pl, now):
35 # execute what original parsers.pack_dirstate should do actually
36 # execute what original parsers.pack_dirstate should do actually
@@ -57,16 +58,21 b' def fakewrite(ui, func):'
57 # 'fakenow' value and 'touch -t YYYYmmddHHMM' argument easy
58 # 'fakenow' value and 'touch -t YYYYmmddHHMM' argument easy
58 fakenow = dateutil.parsedate(fakenow, [b'%Y%m%d%H%M'])[0]
59 fakenow = dateutil.parsedate(fakenow, [b'%Y%m%d%H%M'])[0]
59
60
60 if rustext is not None:
61 if rustmod is not None:
61 orig_module = rustext.dirstate
62 # The Rust implementation does not use public parse/pack dirstate
62 orig_pack_dirstate = rustext.dirstate.pack_dirstate
63 # to prevent conversion round-trips
63 else:
64 orig_dirstatemap_write = dirstate.dirstatemap.write
64 orig_module = parsers
65 wrapper = lambda self, st, now: orig_dirstatemap_write(self,
65 orig_pack_dirstate = parsers.pack_dirstate
66 st,
67 fakenow)
68 dirstate.dirstatemap.write = wrapper
66
69
67 orig_dirstate_getfsnow = dirstate._getfsnow
70 orig_dirstate_getfsnow = dirstate._getfsnow
68 wrapper = lambda *args: pack_dirstate(fakenow, orig_pack_dirstate, *args)
71 wrapper = lambda *args: pack_dirstate(fakenow, orig_pack_dirstate, *args)
69
72
73 orig_module = parsers
74 orig_pack_dirstate = parsers.pack_dirstate
75
70 orig_module.pack_dirstate = wrapper
76 orig_module.pack_dirstate = wrapper
71 dirstate._getfsnow = lambda *args: fakenow
77 dirstate._getfsnow = lambda *args: fakenow
72 try:
78 try:
@@ -74,6 +80,8 b' def fakewrite(ui, func):'
74 finally:
80 finally:
75 orig_module.pack_dirstate = orig_pack_dirstate
81 orig_module.pack_dirstate = orig_pack_dirstate
76 dirstate._getfsnow = orig_dirstate_getfsnow
82 dirstate._getfsnow = orig_dirstate_getfsnow
83 if rustmod is not None:
84 dirstate.dirstatemap.write = orig_dirstatemap_write
77
85
78 def _poststatusfixup(orig, workingctx, status, fixup):
86 def _poststatusfixup(orig, workingctx, status, fixup):
79 ui = workingctx.repo().ui
87 ui = workingctx.repo().ui
General Comments 0
You need to be logged in to leave comments. Login now