##// END OF EJS Templates
rust: Remove support for passing a dict to the Rust pathutil.dirs()...
Simon Sapin -
r48833:627cd8f3 default
parent child Browse files
Show More
@@ -1,152 +1,122 b''
1 // dirstate.rs
1 // dirstate.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` 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.dirstate`
12 mod copymap;
12 mod copymap;
13 mod dirs_multiset;
13 mod dirs_multiset;
14 mod dirstate_map;
14 mod dirstate_map;
15 mod non_normal_entries;
15 mod non_normal_entries;
16 mod status;
16 mod status;
17 use crate::{
17 use crate::{
18 dirstate::{
18 dirstate::{
19 dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper,
19 dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper,
20 },
20 },
21 exceptions,
21 exceptions,
22 };
22 };
23 use cpython::{
23 use cpython::{
24 exc, PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult,
24 PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult, Python,
25 PySequence, Python,
26 };
25 };
27 use hg::dirstate_tree::on_disk::V2_FORMAT_MARKER;
26 use hg::dirstate_tree::on_disk::V2_FORMAT_MARKER;
28 use hg::{utils::hg_path::HgPathBuf, DirstateEntry, EntryState, StateMap};
27 use hg::DirstateEntry;
29 use libc::{c_char, c_int};
28 use libc::{c_char, c_int};
30 use std::convert::TryFrom;
31
29
32 // C code uses a custom `dirstate_tuple` type, checks in multiple instances
30 // C code uses a custom `dirstate_tuple` type, checks in multiple instances
33 // for this type, and raises a Python `Exception` if the check does not pass.
31 // for this type, and raises a Python `Exception` if the check does not pass.
34 // Because this type differs only in name from the regular Python tuple, it
32 // Because this type differs only in name from the regular Python tuple, it
35 // would be a good idea in the near future to remove it entirely to allow
33 // would be a good idea in the near future to remove it entirely to allow
36 // for a pure Python tuple of the same effective structure to be used,
34 // for a pure Python tuple of the same effective structure to be used,
37 // rendering this type and the capsule below useless.
35 // rendering this type and the capsule below useless.
38 py_capsule_fn!(
36 py_capsule_fn!(
39 from mercurial.cext.parsers import make_dirstate_item_CAPI
37 from mercurial.cext.parsers import make_dirstate_item_CAPI
40 as make_dirstate_item_capi
38 as make_dirstate_item_capi
41 signature (
39 signature (
42 state: c_char,
40 state: c_char,
43 mode: c_int,
41 mode: c_int,
44 size: c_int,
42 size: c_int,
45 mtime: c_int,
43 mtime: c_int,
46 ) -> *mut RawPyObject
44 ) -> *mut RawPyObject
47 );
45 );
48
46
49 pub fn make_dirstate_item(
47 pub fn make_dirstate_item(
50 py: Python,
48 py: Python,
51 entry: &DirstateEntry,
49 entry: &DirstateEntry,
52 ) -> PyResult<PyObject> {
50 ) -> PyResult<PyObject> {
53 let &DirstateEntry {
51 let &DirstateEntry {
54 state,
52 state,
55 mode,
53 mode,
56 size,
54 size,
57 mtime,
55 mtime,
58 } = entry;
56 } = entry;
59 // Explicitly go through u8 first, then cast to platform-specific `c_char`
57 // Explicitly go through u8 first, then cast to platform-specific `c_char`
60 // because Into<u8> has a specific implementation while `as c_char` would
58 // because Into<u8> has a specific implementation while `as c_char` would
61 // just do a naive enum cast.
59 // just do a naive enum cast.
62 let state_code: u8 = state.into();
60 let state_code: u8 = state.into();
63 make_dirstate_item_raw(py, state_code, mode, size, mtime)
61 make_dirstate_item_raw(py, state_code, mode, size, mtime)
64 }
62 }
65
63
66 pub fn make_dirstate_item_raw(
64 pub fn make_dirstate_item_raw(
67 py: Python,
65 py: Python,
68 state: u8,
66 state: u8,
69 mode: i32,
67 mode: i32,
70 size: i32,
68 size: i32,
71 mtime: i32,
69 mtime: i32,
72 ) -> PyResult<PyObject> {
70 ) -> PyResult<PyObject> {
73 let make = make_dirstate_item_capi::retrieve(py)?;
71 let make = make_dirstate_item_capi::retrieve(py)?;
74 let maybe_obj = unsafe {
72 let maybe_obj = unsafe {
75 let ptr = make(state as c_char, mode, size, mtime);
73 let ptr = make(state as c_char, mode, size, mtime);
76 PyObject::from_owned_ptr_opt(py, ptr)
74 PyObject::from_owned_ptr_opt(py, ptr)
77 };
75 };
78 maybe_obj.ok_or_else(|| PyErr::fetch(py))
76 maybe_obj.ok_or_else(|| PyErr::fetch(py))
79 }
77 }
80
78
81 pub fn extract_dirstate(py: Python, dmap: &PyDict) -> Result<StateMap, PyErr> {
82 dmap.items(py)
83 .iter()
84 .map(|(filename, stats)| {
85 let stats = stats.extract::<PySequence>(py)?;
86 let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?;
87 let state =
88 EntryState::try_from(state.data(py)[0]).map_err(|e| {
89 PyErr::new::<exc::ValueError, _>(py, e.to_string())
90 })?;
91 let mode = stats.get_item(py, 1)?.extract(py)?;
92 let size = stats.get_item(py, 2)?.extract(py)?;
93 let mtime = stats.get_item(py, 3)?.extract(py)?;
94 let filename = filename.extract::<PyBytes>(py)?;
95 let filename = filename.data(py);
96 Ok((
97 HgPathBuf::from(filename.to_owned()),
98 DirstateEntry {
99 state,
100 mode,
101 size,
102 mtime,
103 },
104 ))
105 })
106 .collect()
107 }
108
109 /// Create the module, with `__package__` given from parent
79 /// Create the module, with `__package__` given from parent
110 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
80 pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> {
111 let dotted_name = &format!("{}.dirstate", package);
81 let dotted_name = &format!("{}.dirstate", package);
112 let m = PyModule::new(py, dotted_name)?;
82 let m = PyModule::new(py, dotted_name)?;
113
83
114 env_logger::init();
84 env_logger::init();
115
85
116 m.add(py, "__package__", package)?;
86 m.add(py, "__package__", package)?;
117 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
87 m.add(py, "__doc__", "Dirstate - Rust implementation")?;
118
88
119 m.add(
89 m.add(
120 py,
90 py,
121 "FallbackError",
91 "FallbackError",
122 py.get_type::<exceptions::FallbackError>(),
92 py.get_type::<exceptions::FallbackError>(),
123 )?;
93 )?;
124 m.add_class::<Dirs>(py)?;
94 m.add_class::<Dirs>(py)?;
125 m.add_class::<DirstateMap>(py)?;
95 m.add_class::<DirstateMap>(py)?;
126 m.add(py, "V2_FORMAT_MARKER", PyBytes::new(py, V2_FORMAT_MARKER))?;
96 m.add(py, "V2_FORMAT_MARKER", PyBytes::new(py, V2_FORMAT_MARKER))?;
127 m.add(
97 m.add(
128 py,
98 py,
129 "status",
99 "status",
130 py_fn!(
100 py_fn!(
131 py,
101 py,
132 status_wrapper(
102 status_wrapper(
133 dmap: DirstateMap,
103 dmap: DirstateMap,
134 root_dir: PyObject,
104 root_dir: PyObject,
135 matcher: PyObject,
105 matcher: PyObject,
136 ignorefiles: PyList,
106 ignorefiles: PyList,
137 check_exec: bool,
107 check_exec: bool,
138 last_normal_time: i64,
108 last_normal_time: i64,
139 list_clean: bool,
109 list_clean: bool,
140 list_ignored: bool,
110 list_ignored: bool,
141 list_unknown: bool,
111 list_unknown: bool,
142 collect_traversed_dirs: bool
112 collect_traversed_dirs: bool
143 )
113 )
144 ),
114 ),
145 )?;
115 )?;
146
116
147 let sys = PyModule::import(py, "sys")?;
117 let sys = PyModule::import(py, "sys")?;
148 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
118 let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?;
149 sys_modules.set_item(py, dotted_name, &m)?;
119 sys_modules.set_item(py, dotted_name, &m)?;
150
120
151 Ok(m)
121 Ok(m)
152 }
122 }
@@ -1,134 +1,124 b''
1 // dirs_multiset.rs
1 // dirs_multiset.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::dirs_multiset` file provided by the
8 //! Bindings for the `hg::dirstate::dirs_multiset` file provided by the
9 //! `hg-core` package.
9 //! `hg-core` package.
10
10
11 use std::cell::RefCell;
11 use std::cell::RefCell;
12
12
13 use cpython::{
13 use cpython::{
14 exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyObject,
14 exc, ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyObject, PyResult,
15 PyResult, Python, UnsafePyLeaked,
15 Python, UnsafePyLeaked,
16 };
16 };
17
17
18 use crate::dirstate::extract_dirstate;
19 use hg::{
18 use hg::{
20 utils::hg_path::{HgPath, HgPathBuf},
19 utils::hg_path::{HgPath, HgPathBuf},
21 DirsMultiset, DirsMultisetIter, DirstateError, DirstateMapError,
20 DirsMultiset, DirsMultisetIter, DirstateMapError,
22 };
21 };
23
22
24 py_class!(pub class Dirs |py| {
23 py_class!(pub class Dirs |py| {
25 @shared data inner: DirsMultiset;
24 @shared data inner: DirsMultiset;
26
25
27 // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes
26 // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes
28 // a `list`)
27 // a `list`)
29 def __new__(
28 def __new__(
30 _cls,
29 _cls,
31 map: PyObject,
30 map: PyObject,
32 only_tracked: Option<PyObject> = None
33 ) -> PyResult<Self> {
31 ) -> PyResult<Self> {
34 let only_tracked_b = if let Some(only_tracked) = only_tracked {
32 let inner = if map.cast_as::<PyDict>(py).is_ok() {
35 only_tracked.extract::<PyBool>(py)?.is_true()
33 let err = "pathutil.dirs() with a dict should only be used by the Python dirstatemap \
36 } else {
34 and should not be used when Rust is enabled";
37 false
35 return Err(PyErr::new::<exc::TypeError, _>(py, err.to_string()))
38 };
39 let inner = if let Ok(map) = map.cast_as::<PyDict>(py) {
40 let dirstate = extract_dirstate(py, &map)?;
41 let dirstate = dirstate.iter().map(|(k, v)| Ok((k, *v)));
42 DirsMultiset::from_dirstate(dirstate, only_tracked_b)
43 .map_err(|e: DirstateError| {
44 PyErr::new::<exc::ValueError, _>(py, e.to_string())
45 })?
46 } else {
36 } else {
47 let map: Result<Vec<HgPathBuf>, PyErr> = map
37 let map: Result<Vec<HgPathBuf>, PyErr> = map
48 .iter(py)?
38 .iter(py)?
49 .map(|o| {
39 .map(|o| {
50 Ok(HgPathBuf::from_bytes(
40 Ok(HgPathBuf::from_bytes(
51 o?.extract::<PyBytes>(py)?.data(py),
41 o?.extract::<PyBytes>(py)?.data(py),
52 ))
42 ))
53 })
43 })
54 .collect();
44 .collect();
55 DirsMultiset::from_manifest(&map?)
45 DirsMultiset::from_manifest(&map?)
56 .map_err(|e| {
46 .map_err(|e| {
57 PyErr::new::<exc::ValueError, _>(py, e.to_string())
47 PyErr::new::<exc::ValueError, _>(py, e.to_string())
58 })?
48 })?
59 };
49 };
60
50
61 Self::create_instance(py, inner)
51 Self::create_instance(py, inner)
62 }
52 }
63
53
64 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
54 def addpath(&self, path: PyObject) -> PyResult<PyObject> {
65 self.inner(py).borrow_mut().add_path(
55 self.inner(py).borrow_mut().add_path(
66 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
56 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
67 ).and(Ok(py.None())).or_else(|e| {
57 ).and(Ok(py.None())).or_else(|e| {
68 match e {
58 match e {
69 DirstateMapError::EmptyPath => {
59 DirstateMapError::EmptyPath => {
70 Ok(py.None())
60 Ok(py.None())
71 },
61 },
72 e => {
62 e => {
73 Err(PyErr::new::<exc::ValueError, _>(
63 Err(PyErr::new::<exc::ValueError, _>(
74 py,
64 py,
75 e.to_string(),
65 e.to_string(),
76 ))
66 ))
77 }
67 }
78 }
68 }
79 })
69 })
80 }
70 }
81
71
82 def delpath(&self, path: PyObject) -> PyResult<PyObject> {
72 def delpath(&self, path: PyObject) -> PyResult<PyObject> {
83 self.inner(py).borrow_mut().delete_path(
73 self.inner(py).borrow_mut().delete_path(
84 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
74 HgPath::new(path.extract::<PyBytes>(py)?.data(py)),
85 )
75 )
86 .and(Ok(py.None()))
76 .and(Ok(py.None()))
87 .or_else(|e| {
77 .or_else(|e| {
88 match e {
78 match e {
89 DirstateMapError::EmptyPath => {
79 DirstateMapError::EmptyPath => {
90 Ok(py.None())
80 Ok(py.None())
91 },
81 },
92 e => {
82 e => {
93 Err(PyErr::new::<exc::ValueError, _>(
83 Err(PyErr::new::<exc::ValueError, _>(
94 py,
84 py,
95 e.to_string(),
85 e.to_string(),
96 ))
86 ))
97 }
87 }
98 }
88 }
99 })
89 })
100 }
90 }
101 def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> {
91 def __iter__(&self) -> PyResult<DirsMultisetKeysIterator> {
102 let leaked_ref = self.inner(py).leak_immutable();
92 let leaked_ref = self.inner(py).leak_immutable();
103 DirsMultisetKeysIterator::from_inner(
93 DirsMultisetKeysIterator::from_inner(
104 py,
94 py,
105 unsafe { leaked_ref.map(py, |o| o.iter()) },
95 unsafe { leaked_ref.map(py, |o| o.iter()) },
106 )
96 )
107 }
97 }
108
98
109 def __contains__(&self, item: PyObject) -> PyResult<bool> {
99 def __contains__(&self, item: PyObject) -> PyResult<bool> {
110 Ok(self.inner(py).borrow().contains(HgPath::new(
100 Ok(self.inner(py).borrow().contains(HgPath::new(
111 item.extract::<PyBytes>(py)?.data(py).as_ref(),
101 item.extract::<PyBytes>(py)?.data(py).as_ref(),
112 )))
102 )))
113 }
103 }
114 });
104 });
115
105
116 impl Dirs {
106 impl Dirs {
117 pub fn from_inner(py: Python, d: DirsMultiset) -> PyResult<Self> {
107 pub fn from_inner(py: Python, d: DirsMultiset) -> PyResult<Self> {
118 Self::create_instance(py, d)
108 Self::create_instance(py, d)
119 }
109 }
120
110
121 fn translate_key(
111 fn translate_key(
122 py: Python,
112 py: Python,
123 res: &HgPathBuf,
113 res: &HgPathBuf,
124 ) -> PyResult<Option<PyBytes>> {
114 ) -> PyResult<Option<PyBytes>> {
125 Ok(Some(PyBytes::new(py, res.as_bytes())))
115 Ok(Some(PyBytes::new(py, res.as_bytes())))
126 }
116 }
127 }
117 }
128
118
129 py_shared_iterator!(
119 py_shared_iterator!(
130 DirsMultisetKeysIterator,
120 DirsMultisetKeysIterator,
131 UnsafePyLeaked<DirsMultisetIter<'static>>,
121 UnsafePyLeaked<DirsMultisetIter<'static>>,
132 Dirs::translate_key,
122 Dirs::translate_key,
133 Option<PyBytes>
123 Option<PyBytes>
134 );
124 );
@@ -1,27 +1,27 b''
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import unittest
3 import unittest
4
4
5 import silenttestrunner
5 import silenttestrunner
6
6
7 from mercurial import pathutil
7 from mercurial import pathutil
8
8
9
9
10 class dirstests(unittest.TestCase):
10 class dirstests(unittest.TestCase):
11 def testdirs(self):
11 def testdirs(self):
12 for case, want in [
12 for case, want in [
13 (b'a/a/a', [b'a', b'a/a', b'']),
13 (b'a/a/a', [b'a', b'a/a', b'']),
14 (b'alpha/beta/gamma', [b'', b'alpha', b'alpha/beta']),
14 (b'alpha/beta/gamma', [b'', b'alpha', b'alpha/beta']),
15 ]:
15 ]:
16 d = pathutil.dirs({})
16 d = pathutil.dirs([])
17 d.addpath(case)
17 d.addpath(case)
18 self.assertEqual(sorted(d), sorted(want))
18 self.assertEqual(sorted(d), sorted(want))
19
19
20 def testinvalid(self):
20 def testinvalid(self):
21 with self.assertRaises(ValueError):
21 with self.assertRaises(ValueError):
22 d = pathutil.dirs({})
22 d = pathutil.dirs([])
23 d.addpath(b'a//b')
23 d.addpath(b'a//b')
24
24
25
25
26 if __name__ == '__main__':
26 if __name__ == '__main__':
27 silenttestrunner.main(__name__)
27 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now