That is only used by the Python dirstatemap, which not used when Rust
is enabled.
This allows removing the private extract_dirstate function which
creates DirstateEntry values. This in turn will make easier upcoming
changes to DirstateEntry.
( )
| Alphare |
| hg-reviewers |
That is only used by the Python dirstatemap, which not used when Rust
is enabled.
This allows removing the private extract_dirstate function which
creates DirstateEntry values. This in turn will make easier upcoming
changes to DirstateEntry.
| Automatic diff as part of commit; lint not applicable. |
| Automatic diff as part of commit; unit tests not applicable. |
| Path | Packages | |||
|---|---|---|---|---|
| M | rust/hg-cpython/src/dirstate.rs (34 lines) | |||
| M | rust/hg-cpython/src/dirstate/dirs_multiset.rs (24 lines) | |||
| M | tests/test-dirs.py (4 lines) |
| Status | Author | Revision | |
|---|---|---|---|
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin | ||
| Closed | SimonSapin |
| mod status; | mod status; | ||||
| use crate::{ | use crate::{ | ||||
| dirstate::{ | dirstate::{ | ||||
| dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper, | dirs_multiset::Dirs, dirstate_map::DirstateMap, status::status_wrapper, | ||||
| }, | }, | ||||
| exceptions, | exceptions, | ||||
| }; | }; | ||||
| use cpython::{ | use cpython::{ | ||||
| exc, PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult, | PyBytes, PyDict, PyErr, PyList, PyModule, PyObject, PyResult, Python, | ||||
| PySequence, Python, | |||||
| }; | }; | ||||
| use hg::dirstate_tree::on_disk::V2_FORMAT_MARKER; | use hg::dirstate_tree::on_disk::V2_FORMAT_MARKER; | ||||
| use hg::{utils::hg_path::HgPathBuf, DirstateEntry, EntryState, StateMap}; | use hg::DirstateEntry; | ||||
| use libc::{c_char, c_int}; | use libc::{c_char, c_int}; | ||||
| use std::convert::TryFrom; | |||||
| // C code uses a custom `dirstate_tuple` type, checks in multiple instances | // C code uses a custom `dirstate_tuple` type, checks in multiple instances | ||||
| // for this type, and raises a Python `Exception` if the check does not pass. | // for this type, and raises a Python `Exception` if the check does not pass. | ||||
| // Because this type differs only in name from the regular Python tuple, it | // Because this type differs only in name from the regular Python tuple, it | ||||
| // would be a good idea in the near future to remove it entirely to allow | // would be a good idea in the near future to remove it entirely to allow | ||||
| // for a pure Python tuple of the same effective structure to be used, | // for a pure Python tuple of the same effective structure to be used, | ||||
| // rendering this type and the capsule below useless. | // rendering this type and the capsule below useless. | ||||
| py_capsule_fn!( | py_capsule_fn!( | ||||
| let make = make_dirstate_item_capi::retrieve(py)?; | let make = make_dirstate_item_capi::retrieve(py)?; | ||||
| let maybe_obj = unsafe { | let maybe_obj = unsafe { | ||||
| let ptr = make(state as c_char, mode, size, mtime); | let ptr = make(state as c_char, mode, size, mtime); | ||||
| PyObject::from_owned_ptr_opt(py, ptr) | PyObject::from_owned_ptr_opt(py, ptr) | ||||
| }; | }; | ||||
| maybe_obj.ok_or_else(|| PyErr::fetch(py)) | maybe_obj.ok_or_else(|| PyErr::fetch(py)) | ||||
| } | } | ||||
| pub fn extract_dirstate(py: Python, dmap: &PyDict) -> Result<StateMap, PyErr> { | |||||
| dmap.items(py) | |||||
| .iter() | |||||
| .map(|(filename, stats)| { | |||||
| let stats = stats.extract::<PySequence>(py)?; | |||||
| let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?; | |||||
| let state = | |||||
| EntryState::try_from(state.data(py)[0]).map_err(|e| { | |||||
| PyErr::new::<exc::ValueError, _>(py, e.to_string()) | |||||
| })?; | |||||
| let mode = stats.get_item(py, 1)?.extract(py)?; | |||||
| let size = stats.get_item(py, 2)?.extract(py)?; | |||||
| let mtime = stats.get_item(py, 3)?.extract(py)?; | |||||
| let filename = filename.extract::<PyBytes>(py)?; | |||||
| let filename = filename.data(py); | |||||
| Ok(( | |||||
| HgPathBuf::from(filename.to_owned()), | |||||
| DirstateEntry { | |||||
| state, | |||||
| mode, | |||||
| size, | |||||
| mtime, | |||||
| }, | |||||
| )) | |||||
| }) | |||||
| .collect() | |||||
| } | |||||
| /// Create the module, with `__package__` given from parent | /// Create the module, with `__package__` given from parent | ||||
| pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { | pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { | ||||
| let dotted_name = &format!("{}.dirstate", package); | let dotted_name = &format!("{}.dirstate", package); | ||||
| let m = PyModule::new(py, dotted_name)?; | let m = PyModule::new(py, dotted_name)?; | ||||
| env_logger::init(); | env_logger::init(); | ||||
| m.add(py, "__package__", package)?; | m.add(py, "__package__", package)?; | ||||
| // dirs_multiset.rs | // dirs_multiset.rs | ||||
| // | // | ||||
| // Copyright 2019 Raphaël Gomès <rgomes@octobus.net> | // Copyright 2019 Raphaël Gomès <rgomes@octobus.net> | ||||
| // | // | ||||
| // This software may be used and distributed according to the terms of the | // This software may be used and distributed according to the terms of the | ||||
| // GNU General Public License version 2 or any later version. | // GNU General Public License version 2 or any later version. | ||||
| //! Bindings for the `hg::dirstate::dirs_multiset` file provided by the | //! Bindings for the `hg::dirstate::dirs_multiset` file provided by the | ||||
| //! `hg-core` package. | //! `hg-core` package. | ||||
| use std::cell::RefCell; | use std::cell::RefCell; | ||||
| use cpython::{ | use cpython::{ | ||||
| exc, ObjectProtocol, PyBool, PyBytes, PyClone, PyDict, PyErr, PyObject, | exc, ObjectProtocol, PyBytes, PyClone, PyDict, PyErr, PyObject, PyResult, | ||||
| PyResult, Python, UnsafePyLeaked, | Python, UnsafePyLeaked, | ||||
| }; | }; | ||||
| use crate::dirstate::extract_dirstate; | |||||
| use hg::{ | use hg::{ | ||||
| utils::hg_path::{HgPath, HgPathBuf}, | utils::hg_path::{HgPath, HgPathBuf}, | ||||
| DirsMultiset, DirsMultisetIter, DirstateError, DirstateMapError, | DirsMultiset, DirsMultisetIter, DirstateMapError, | ||||
| }; | }; | ||||
| py_class!(pub class Dirs |py| { | py_class!(pub class Dirs |py| { | ||||
| @shared data inner: DirsMultiset; | @shared data inner: DirsMultiset; | ||||
| // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes | // `map` is either a `dict` or a flat iterator (usually a `set`, sometimes | ||||
| // a `list`) | // a `list`) | ||||
| def __new__( | def __new__( | ||||
| _cls, | _cls, | ||||
| map: PyObject, | map: PyObject, | ||||
| only_tracked: Option<PyObject> = None | |||||
| ) -> PyResult<Self> { | ) -> PyResult<Self> { | ||||
| let only_tracked_b = if let Some(only_tracked) = only_tracked { | let inner = if map.cast_as::<PyDict>(py).is_ok() { | ||||
| only_tracked.extract::<PyBool>(py)?.is_true() | let err = "pathutil.dirs() with a dict should only be used by the Python dirstatemap \ | ||||
| } else { | and should not be used when Rust is enabled"; | ||||
| false | return Err(PyErr::new::<exc::TypeError, _>(py, err.to_string())) | ||||
| }; | |||||
| let inner = if let Ok(map) = map.cast_as::<PyDict>(py) { | |||||
| let dirstate = extract_dirstate(py, &map)?; | |||||
| let dirstate = dirstate.iter().map(|(k, v)| Ok((k, *v))); | |||||
| DirsMultiset::from_dirstate(dirstate, only_tracked_b) | |||||
| .map_err(|e: DirstateError| { | |||||
| PyErr::new::<exc::ValueError, _>(py, e.to_string()) | |||||
| })? | |||||
| } else { | } else { | ||||
| let map: Result<Vec<HgPathBuf>, PyErr> = map | let map: Result<Vec<HgPathBuf>, PyErr> = map | ||||
| .iter(py)? | .iter(py)? | ||||
| .map(|o| { | .map(|o| { | ||||
| Ok(HgPathBuf::from_bytes( | Ok(HgPathBuf::from_bytes( | ||||
| o?.extract::<PyBytes>(py)?.data(py), | o?.extract::<PyBytes>(py)?.data(py), | ||||
| )) | )) | ||||
| }) | }) | ||||
| from __future__ import absolute_import | from __future__ import absolute_import | ||||
| import unittest | import unittest | ||||
| import silenttestrunner | import silenttestrunner | ||||
| from mercurial import pathutil | from mercurial import pathutil | ||||
| class dirstests(unittest.TestCase): | class dirstests(unittest.TestCase): | ||||
| def testdirs(self): | def testdirs(self): | ||||
| for case, want in [ | for case, want in [ | ||||
| (b'a/a/a', [b'a', b'a/a', b'']), | (b'a/a/a', [b'a', b'a/a', b'']), | ||||
| (b'alpha/beta/gamma', [b'', b'alpha', b'alpha/beta']), | (b'alpha/beta/gamma', [b'', b'alpha', b'alpha/beta']), | ||||
| ]: | ]: | ||||
| d = pathutil.dirs({}) | d = pathutil.dirs([]) | ||||
| d.addpath(case) | d.addpath(case) | ||||
| self.assertEqual(sorted(d), sorted(want)) | self.assertEqual(sorted(d), sorted(want)) | ||||
| def testinvalid(self): | def testinvalid(self): | ||||
| with self.assertRaises(ValueError): | with self.assertRaises(ValueError): | ||||
| d = pathutil.dirs({}) | d = pathutil.dirs([]) | ||||
| d.addpath(b'a//b') | d.addpath(b'a//b') | ||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||
| silenttestrunner.main(__name__) | silenttestrunner.main(__name__) | ||||