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__) |