New submission from Jakub Stasiak <jakub+python....@stasiak.at>: I've been wondering if it's worth it to have something like Rust's std::sync::Mutex[1] which is used like this:
let data = Mutex::new(0); { let mut unlocked = data.lock().unwrap(); *unlocked += 1; } // unlocked is no longer available here, we need to use data.lock() again Our (Python) [R]Lock is typically used like this: data_to_protect = whatever() lock = Lock() # ... with lock: data_to_protect.do_something() The inconvenience of this is obvious to me and it's more error prone if one forgets the lock when accessing data_to_protect. I wrote a quick prototype to get something like Mutex in Python: import threading from contextlib import contextmanager from typing import Any, cast, Dict, Generator, Generic, Optional, TypeVar T = TypeVar('T') class LockedData(Generic[T]): def __init__(self, data: T, lock: Any = None) -> None: self._data = data if lock is None: lock = threading.Lock() self._lock = lock @contextmanager def unlocked(self, timeout: float = -1.0) -> Generator[T, None, None]: acquired = None unlocked = None try: acquired = self._lock.acquire(timeout=timeout) if acquired is False: raise LockTimeout() unlocked = UnlockResult(self._data) yield unlocked finally: if acquired is True: if unlocked is not None: unlocked._unlocked = False self._data = unlocked._data unlocked._data = None self._lock.release() class UnlockResult(Generic[T]): _data: Optional[T] def __init__(self, data: T) -> None: self._data = data self._unlocked = True @property def data(self) -> T: assert self._unlocked return cast(T, self._data) @data.setter def data(self, data: T) -> None: assert self._unlocked self._data = data class LockTimeout(Exception): pass if __name__ == '__main__': locked_dict: LockedData[Dict[str, bool]] = LockedData({}) # Mutating the dictionary with locked_dict.unlocked() as result: result.data['hello'] = True with locked_dict.unlocked() as result: print(result.data) # Replacing the dictionary with locked_dict.unlocked() as result: result.data = {'a': True, 'b': False} with locked_dict.unlocked() as result: print(result.data) # Trying to access data after context closes print(result._data) print(result.data) Now this is obviously quite far from what Rust offers, as there's nothing to prevent a person from doing something like this: with locked_dict.unlocked() as result: data = result.data print('Oh no, look: %r' % (data,)) but it seems to me it's still an improvement. [1] https://doc.rust-lang.org/std/sync/struct.Mutex.html ---------- components: Library (Lib) messages: 382650 nosy: benjamin.peterson, jstasiak, pitrou, vstinner priority: normal severity: normal status: open title: Something like Rust's std::sync::Mutex – combining a mutex primitive and a piece of data it's protecting type: enhancement versions: Python 3.10 _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue42590> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com