diff --git a/CHANGELOG.md b/CHANGELOG.md index ee974a7..a2fbd66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 4.3.1 - 2024-11-18 +### Changed +- `__str__` changed to `__repr__` +- Free-threading is supported now +- Dependencies updated + ## 4.3.0 - 2024-11-08 ### Added - Add `always_copy` parameter to `cached` and `cachedmethod` decorators diff --git a/Cargo.lock b/Cargo.lock index f31c67d..ddf373a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,7 +22,7 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cachebox" -version = "4.3.0" +version = "4.3.1" dependencies = [ "cfg-if", "fastrand", @@ -134,9 +134,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.22.6" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884" +checksum = "7ebb0c0cc0de9678e53be9ccf8a2ab53045e6e3a8be03393ceccc5e7396ccb40" dependencies = [ "cfg-if", "indoc", @@ -152,9 +152,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.22.6" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38" +checksum = "80e3ce69c4ec34476534b490e412b871ba03a82e35604c3dfb95fcb6bfb60c09" dependencies = [ "once_cell", "target-lexicon", @@ -162,9 +162,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.22.6" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636" +checksum = "3b09f311c76b36dfd6dd6f7fa6f9f18e7e46a1c937110d283e80b12ba2468a75" dependencies = [ "libc", "pyo3-build-config", @@ -172,9 +172,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.22.6" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453" +checksum = "fd4f74086536d1e1deaff99ec0387481fb3325c82e4e48be0e75ab3d3fcb487a" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -184,9 +184,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.22.6" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe" +checksum = "9e77dfeb76b32bbf069144a5ea0a36176ab59c8db9ce28732d0f06f096bbfbc8" dependencies = [ "heck", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index a9342ec..9903f08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cachebox" -version = "4.3.0" +version = "4.3.1" edition = "2021" description = "The fastest memoizing and caching Python library written in Rust" readme = "README.md" @@ -23,14 +23,14 @@ strip = "symbols" [dependencies] hashbrown = { version = "^0.14", default-features = false, features=["inline-more", "raw"]} fastrand = "^2.1" -pyo3 = { version = "0.22.6", default-features = false, features=["macros", "extension-module"] } +pyo3 = { version = "0.23.1", default-features = false, features=["macros", "extension-module"] } cfg-if = "1.0" parking_lot_core = { version = "^0.9", default-features = false } lock_api = { version = "^0.4", default-features = false } fxhash = {version = "^0.2"} [build-dependencies] -pyo3-build-config = { version = "0.22.6", features = ["resolve-config"] } +pyo3-build-config = { version = "0.23.1", features = ["resolve-config"] } [lints.clippy] dbg_macro = "warn" diff --git a/cachebox/utils.py b/cachebox/utils.py index 0f93fc9..6898ba1 100644 --- a/cachebox/utils.py +++ b/cachebox/utils.py @@ -62,7 +62,7 @@ def __delitem__(self, key: KT) -> VT: raise TypeError("This cache is frozen.") - def __str__(self) -> str: + def __repr__(self) -> str: return f"" def __iter__(self) -> typing.Iterator[KT]: diff --git a/src/bridge/cache.rs b/src/bridge/cache.rs index c72fbaa..ff21486 100644 --- a/src/bridge/cache.rs +++ b/src/bridge/cache.rs @@ -11,7 +11,7 @@ use crate::util::_KeepForIter; /// - it supports useful and new methods for managing memory, while `dict` does not. /// - it does not support `popitem`, while `dict` does. /// - You can limit the size of [`Cache`], but you cannot for `dict`. -#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl)] +#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl, frozen)] pub struct Cache { // Why [`Box`]? We using [`Box`] here so that there's no need for `&mut self` // in this struct; so RuntimeError never occurred for using this class in multiple threads. @@ -127,8 +127,8 @@ impl Cache { } } - /// Returns str(self) - pub fn __str__(&self) -> String { + /// Returns repr(self) + pub fn __repr__(&self) -> String { let lock = self.raw.lock(); format!( diff --git a/src/bridge/fifocache.rs b/src/bridge/fifocache.rs index c12ffe8..955ce18 100644 --- a/src/bridge/fifocache.rs +++ b/src/bridge/fifocache.rs @@ -5,7 +5,7 @@ use crate::{hashedkey::HashedKey, util::_KeepForIter}; /// FIFO Cache implementation - First-In First-Out Policy (thread-safe). /// /// In simple terms, the FIFO cache will remove the element that has been in the cache the longest. -#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl)] +#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl, frozen)] pub struct FIFOCache { // Why [`Box`]? We using [`Box`] here so that there's no need for `&mut self` // in this struct; so RuntimeError never occurred for using this class in multiple threads. @@ -119,8 +119,8 @@ impl FIFOCache { } } - /// Returns str(self) - pub fn __str__(&self) -> String { + /// Returns repr(self) + pub fn __repr__(&self) -> String { let lock = self.raw.lock(); format!( diff --git a/src/bridge/lfucache.rs b/src/bridge/lfucache.rs index c8161f9..daf4be6 100644 --- a/src/bridge/lfucache.rs +++ b/src/bridge/lfucache.rs @@ -5,7 +5,7 @@ use crate::{hashedkey::HashedKey, util::_KeepForIter}; /// LFU Cache implementation - Least frequantly used policy (thread-safe). /// /// In simple terms, the LFU cache will remove the element in the cache that has been accessed the least, regardless of time -#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl)] +#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl, frozen)] pub struct LFUCache { // Why [`Box`]? We using [`Box`] here so that there's no need for `&mut self` // in this struct; so RuntimeError never occurred for using this class in multiple threads. @@ -126,8 +126,8 @@ impl LFUCache { } } - /// Returns str(self) - pub fn __str__(&self) -> String { + /// Returns repr(self) + pub fn __repr__(&self) -> String { let lock = self.raw.lock(); format!( diff --git a/src/bridge/lrucache.rs b/src/bridge/lrucache.rs index 010afda..f8c8be2 100644 --- a/src/bridge/lrucache.rs +++ b/src/bridge/lrucache.rs @@ -5,7 +5,7 @@ use crate::{hashedkey::HashedKey, util::_KeepForIter}; /// LRU Cache implementation - Least recently used policy (thread-safe). /// /// In simple terms, the LRU cache will remove the element in the cache that has not been accessed in the longest time. -#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl)] +#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl, frozen)] pub struct LRUCache { // Why [`Box`]? We using [`Box`] here so that there's no need for `&mut self` // in this struct; so RuntimeError never occurred for using this class in multiple threads. @@ -120,8 +120,8 @@ impl LRUCache { } } - /// Returns str(self) - pub fn __str__(&self) -> String { + /// Returns repr(self) + pub fn __repr__(&self) -> String { let lock = self.raw.lock(); format!( diff --git a/src/bridge/rrcache.rs b/src/bridge/rrcache.rs index 685de37..8f25d97 100644 --- a/src/bridge/rrcache.rs +++ b/src/bridge/rrcache.rs @@ -42,7 +42,7 @@ macro_rules! insert_rr { /// RRCache implementation - Random Replacement policy (thread-safe). /// /// In simple terms, the RR cache will choice randomly element to remove it to make space when necessary. -#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl)] +#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl, frozen)] pub struct RRCache { // Why [`Box`]? We using [`Box`] here so that there's no need for `&mut self` // in this struct; so RuntimeError never occurred for using this class in multiple threads. @@ -156,8 +156,8 @@ impl RRCache { } } - /// Returns str(self) - pub fn __str__(&self) -> String { + /// Returns repr(self) + pub fn __repr__(&self) -> String { let lock = self.raw.lock(); format!( @@ -409,7 +409,7 @@ impl RRCache { Ok(()) } else { - for pair in iterable.bind(py).iter()? { + for pair in iterable.bind(py).try_iter()? { let (key, value) = pair?.extract::<(pyo3::PyObject, pyo3::PyObject)>()?; let hk = HashedKey::from_pyobject(py, key)?; diff --git a/src/bridge/ttlcache.rs b/src/bridge/ttlcache.rs index dbced07..fe30473 100644 --- a/src/bridge/ttlcache.rs +++ b/src/bridge/ttlcache.rs @@ -5,7 +5,7 @@ use crate::{hashedkey::HashedKey, internal::TTLElement, util::_KeepForIter}; /// TTL Cache implementation - Time-To-Live Policy (thread-safe). /// /// In simple terms, the TTL cache will automatically remove the element in the cache that has expired:: -#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl)] +#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl, frozen)] pub struct TTLCache { // Why [`Box`]? We using [`Box`] here so that there's no need for `&mut self` // in this struct; so RuntimeError never occurred for using this class in multiple threads. @@ -138,8 +138,8 @@ impl TTLCache { } } - /// Returns str(self) - pub fn __str__(&self) -> String { + /// Returns repr(self) + pub fn __repr__(&self) -> String { let mut lock = self.raw.lock(); lock.expire(); diff --git a/src/bridge/vttlcache.rs b/src/bridge/vttlcache.rs index db671b6..65cac83 100644 --- a/src/bridge/vttlcache.rs +++ b/src/bridge/vttlcache.rs @@ -5,7 +5,7 @@ use crate::{hashedkey::HashedKey, internal::VTTLElement, util::_KeepForIter}; /// VTTL Cache Implementation - Time-To-Live Per-Key Policy (thread-safe). /// /// In simple terms, the TTL cache will automatically remove the element in the cache that has expired when need. -#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl)] +#[pyo3::pyclass(module="cachebox._cachebox", extends=crate::bridge::baseimpl::BaseCacheImpl, frozen)] pub struct VTTLCache { // Why [`Box`]? We using [`Box`] here so that there's no need for `&mut self` // in this struct; so RuntimeError never occurred for using this class in multiple threads. @@ -125,8 +125,8 @@ impl VTTLCache { } } - /// Returns str(self) - pub fn __str__(&self) -> String { + /// Returns repr(self) + pub fn __repr__(&self) -> String { let lock = self.raw.lock(); format!( diff --git a/src/internal/fifo.rs b/src/internal/fifo.rs index 8a1ee6e..e509439 100644 --- a/src/internal/fifo.rs +++ b/src/internal/fifo.rs @@ -203,7 +203,7 @@ impl FIFOPolicy { Ok(()) } else { - for pair in iterable.bind(py).iter()? { + for pair in iterable.bind(py).try_iter()? { let (key, value) = pair?.extract::<(pyo3::PyObject, pyo3::PyObject)>()?; let hk = HashedKey::from_pyobject(py, key)?; diff --git a/src/internal/lfu.rs b/src/internal/lfu.rs index 30b287a..de674ae 100644 --- a/src/internal/lfu.rs +++ b/src/internal/lfu.rs @@ -169,7 +169,7 @@ impl LFUPolicy { Ok(()) } else { - for pair in iterable.bind(py).iter()? { + for pair in iterable.bind(py).try_iter()? { let (key, value) = pair?.extract::<(pyo3::PyObject, pyo3::PyObject)>()?; let hk = HashedKey::from_pyobject(py, key)?; @@ -267,7 +267,7 @@ impl LFUPolicy { let mut new = Self::new(maxsize, capacity)?; - for pair in iterable.bind(py).iter()? { + for pair in iterable.bind(py).try_iter()? { let (key, value, fr) = pair?.extract::<(pyo3::PyObject, pyo3::PyObject, usize)>()?; let hk = HashedKey::from_pyobject(py, key)?; diff --git a/src/internal/lru.rs b/src/internal/lru.rs index 2921923..4465128 100644 --- a/src/internal/lru.rs +++ b/src/internal/lru.rs @@ -162,7 +162,7 @@ impl LRUPolicy { Ok(()) } else { - for pair in iterable.bind(py).iter()? { + for pair in iterable.bind(py).try_iter()? { let (key, value) = pair?.extract::<(pyo3::PyObject, pyo3::PyObject)>()?; let hk = HashedKey::from_pyobject(py, key)?; diff --git a/src/internal/nopolicy.rs b/src/internal/nopolicy.rs index 9abbf6b..106a737 100644 --- a/src/internal/nopolicy.rs +++ b/src/internal/nopolicy.rs @@ -97,7 +97,7 @@ impl NoPolicy { Ok(()) } else { - for pair in iterable.bind(py).iter()? { + for pair in iterable.bind(py).try_iter()? { let (key, value) = pair?.extract::<(pyo3::PyObject, pyo3::PyObject)>()?; let hk = HashedKey::from_pyobject(py, key)?; diff --git a/src/internal/ttl.rs b/src/internal/ttl.rs index e6c9ae6..7bf4446 100644 --- a/src/internal/ttl.rs +++ b/src/internal/ttl.rs @@ -233,7 +233,7 @@ impl TTLPolicy { Ok(()) } else { - for pair in iterable.bind(py).iter()? { + for pair in iterable.bind(py).try_iter()? { let (key, value) = pair?.extract::<(pyo3::PyObject, pyo3::PyObject)>()?; let hk = HashedKey::from_pyobject(py, key)?; @@ -344,7 +344,7 @@ impl TTLPolicy { let mut new = Self::new(maxsize, capacity, ttl)?; - for pair in iterable.bind(py).iter()? { + for pair in iterable.bind(py).try_iter()? { let (key, value, timestamp) = pair?.extract::<(pyo3::PyObject, pyo3::PyObject, f64)>()?; diff --git a/src/internal/vttl.rs b/src/internal/vttl.rs index f5efd47..05b6d07 100644 --- a/src/internal/vttl.rs +++ b/src/internal/vttl.rs @@ -261,7 +261,7 @@ impl VTTLPolicy { Ok(()) } else { - for pair in iterable.bind(py).iter()? { + for pair in iterable.bind(py).try_iter()? { let (key, value) = pair?.extract::<(pyo3::PyObject, pyo3::PyObject)>()?; let hk = HashedKey::from_pyobject(py, key)?; @@ -362,7 +362,7 @@ impl VTTLPolicy { let mut new = Self::new(maxsize, capacity)?; - for pair in iterable.bind(py).iter()? { + for pair in iterable.bind(py).try_iter()? { let (key, value, timestamp) = pair?.extract::<(pyo3::PyObject, pyo3::PyObject, f64)>()?; diff --git a/src/lib.rs b/src/lib.rs index 929527d..91dfa37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,6 +41,8 @@ pub fn version_info() -> (u8, u8, u8, bool) { #[pymodule] #[pyo3(name = "_cachebox")] fn _cachebox(m: &Bound<'_, PyModule>) -> PyResult<()> { + m.gil_used(false)?; + m.add("__version__", CACHEBOX_VERSION)?; m.add("version_info", version_info())?; m.add("__author__", "awolverp")?; diff --git a/src/util.rs b/src/util.rs index 65314d5..42550b4 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,5 +1,5 @@ #[allow(unused_imports)] -use pyo3::IntoPy; +use pyo3::IntoPyObject; macro_rules! err { ($type:ty, $val:expr) => { @@ -140,7 +140,7 @@ unsafe fn _get_capacity( ) -> pyo3::PyResult<*mut pyo3::ffi::PyObject> { cfg_if::cfg_if! { if #[cfg(all(Py_3_9, not(any(Py_LIMITED_API, PyPy, GraalPy))))] { - let m_name: pyo3::Py = "capacity".into_py(py); + let m_name: pyo3::Bound<'_, pyo3::types::PyString> = "capacity".into_pyobject(py)?; Ok(pyo3::ffi::PyObject_CallMethodNoArgs(ptr, m_name.as_ptr())) } else { let capacity_fn = diff --git a/tests/mixin.py b/tests/mixin.py index 30226c0..aacc207 100644 --- a/tests/mixin.py +++ b/tests/mixin.py @@ -151,9 +151,10 @@ def test___setitem__(self): with pytest.raises(KeyError): cache[2] - def test___str__(self): + def test___repr__(self): cache = self.CACHE(2, **self.KWARGS, capacity=2) - assert str(cache) != repr(cache) + assert str(cache) == repr(cache) + assert repr(cache).startswith(self.CACHE.__name__) def test_insert(self): cache = self.CACHE(5, **self.KWARGS, capacity=5)