rust-cpython: make `NonNormalEntires` iterable to fix `fsmonitor` (issue6276)
authorRaphaël Gomès <rgomes@octobus.net>
Mon, 24 Feb 2020 17:57:57 +0100
changeset 44416 8ac5726d695d
parent 44413 4cabeea6d214
child 44417 7c0b8652fd8c
rust-cpython: make `NonNormalEntires` iterable to fix `fsmonitor` (issue6276) This fixes a bug when using `fsmonitor` that tries to iterate on the non normal set, by adding a shared iterator interface. Differential Revision: https://phab.mercurial-scm.org/D8143
rust/hg-core/src/dirstate/dirstate_map.rs
rust/hg-cpython/src/dirstate/dirstate_map.rs
rust/hg-cpython/src/dirstate/non_normal_entries.rs
--- a/rust/hg-core/src/dirstate/dirstate_map.rs	Sat Sep 07 14:50:39 2019 +0200
+++ b/rust/hg-core/src/dirstate/dirstate_map.rs	Mon Feb 24 17:57:57 2020 +0100
@@ -237,6 +237,25 @@
         )
     }
 
+    /// Useful to get immutable references to those sets in contexts where
+    /// you only have an immutable reference to the `DirstateMap`, like when
+    /// sharing references with Python.
+    ///
+    /// TODO, get rid of this along with the other "setter/getter" stuff when
+    /// a nice typestate plan is defined.
+    ///
+    /// # Panics
+    ///
+    /// Will panic if either set is `None`.
+    pub fn get_non_normal_other_parent_entries_panic(
+        &self,
+    ) -> (&HashSet<HgPathBuf>, &HashSet<HgPathBuf>) {
+        (
+            self.non_normal_set.as_ref().unwrap(),
+            self.other_parent_set.as_ref().unwrap(),
+        )
+    }
+
     pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
         if !force
             && self.non_normal_set.is_some()
--- a/rust/hg-cpython/src/dirstate/dirstate_map.rs	Sat Sep 07 14:50:39 2019 +0200
+++ b/rust/hg-cpython/src/dirstate/dirstate_map.rs	Mon Feb 24 17:57:57 2020 +0100
@@ -20,7 +20,9 @@
 
 use crate::{
     dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator},
-    dirstate::non_normal_entries::NonNormalEntries,
+    dirstate::non_normal_entries::{
+        NonNormalEntries, NonNormalEntriesIterator,
+    },
     dirstate::{dirs_multiset::Dirs, make_dirstate_tuple},
 };
 use hg::{
@@ -244,6 +246,22 @@
         Ok(ret)
     }
 
+    def non_normal_entries_iter(&self) -> PyResult<NonNormalEntriesIterator> {
+        // Make sure the sets are defined before we no longer have a mutable
+        // reference to the dmap.
+        self.inner(py)
+            .borrow_mut()
+            .set_non_normal_other_parent_entries(false);
+
+        let leaked_ref = self.inner(py).leak_immutable();
+
+        NonNormalEntriesIterator::from_inner(py, unsafe {
+            leaked_ref.map(py, |o| {
+                o.get_non_normal_other_parent_entries_panic().0.iter()
+            })
+        })
+    }
+
     def hastrackeddir(&self, d: PyObject) -> PyResult<PyBool> {
         let d = d.extract::<PyBytes>(py)?;
         Ok(self.inner(py).borrow_mut()
--- a/rust/hg-cpython/src/dirstate/non_normal_entries.rs	Sat Sep 07 14:50:39 2019 +0200
+++ b/rust/hg-cpython/src/dirstate/non_normal_entries.rs	Mon Feb 24 17:57:57 2020 +0100
@@ -6,11 +6,15 @@
 // GNU General Public License version 2 or any later version.
 
 use cpython::{
-    exc::NotImplementedError, CompareOp, ObjectProtocol, PyErr, PyList,
-    PyObject, PyResult, PyString, Python, PythonObject, ToPyObject,
+    exc::NotImplementedError, CompareOp, ObjectProtocol, PyBytes, PyClone,
+    PyErr, PyList, PyObject, PyResult, PyString, Python, PythonObject,
+    ToPyObject, UnsafePyLeaked,
 };
 
 use crate::dirstate::DirstateMap;
+use hg::utils::hg_path::HgPathBuf;
+use std::cell::RefCell;
+use std::collections::hash_set;
 
 py_class!(pub class NonNormalEntries |py| {
     data dmap: DirstateMap;
@@ -34,6 +38,10 @@
     def __repr__(&self) -> PyResult<PyString> {
         self.dmap(py).non_normal_entries_display(py)
     }
+
+    def __iter__(&self) -> PyResult<NonNormalEntriesIterator> {
+        self.dmap(py).non_normal_entries_iter(py)
+    }
 });
 
 impl NonNormalEntries {
@@ -49,4 +57,20 @@
         }
         Ok(true)
     }
+
+    fn translate_key(
+        py: Python,
+        key: &HgPathBuf,
+    ) -> PyResult<Option<PyBytes>> {
+        Ok(Some(PyBytes::new(py, key.as_ref())))
+    }
 }
+
+type NonNormalEntriesIter<'a> = hash_set::Iter<'a, HgPathBuf>;
+
+py_shared_iterator!(
+    NonNormalEntriesIterator,
+    UnsafePyLeaked<NonNormalEntriesIter<'static>>,
+    NonNormalEntries::translate_key,
+    Option<PyBytes>
+);