From fa203a72dd87ed84af40b28fce4a5f396d1e662e Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Tue, 27 Jun 2023 16:27:53 -0400 Subject: [PATCH] texture: Add a Texture Cache The Texture Cache will be used to map filenames to Gdk.Textures loaded into memory. The application can then re-use textures instead of making expensive filesystem calls and loading the same images multiple times. Implements: #51 ("Texture Cache") Signed-off-by: Anna Schumaker --- emmental/texture.py | 26 ++++++++++++++++++++++++++ tests/test_texture.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 emmental/texture.py create mode 100644 tests/test_texture.py diff --git a/emmental/texture.py b/emmental/texture.py new file mode 100644 index 0000000..444b25d --- /dev/null +++ b/emmental/texture.py @@ -0,0 +1,26 @@ +# Copyright 2023 (c) Anna Schumaker. +"""A cache to hold Gdk.Textures used by cover art.""" +import pathlib +from gi.repository import Gdk + + +class _TextureCache(dict): + """A custom dictionary for storing texture files.""" + + def __missing__(self, path: pathlib.Path | None) -> Gdk.Texture: + """Load a cache item from disk or add a new item entirely.""" + texture = Gdk.Texture.new_from_filename(str(path)) + self.__setitem__(path, texture) + return texture + + def __getitem__(self, path: pathlib.Path | None) -> Gdk.Texture | None: + """Get a Gdk.Texture cache item from the cache.""" + if path is not None and path.is_file(): + return super().__getitem__(path) + + def drop(self, path: pathlib.Path | None) -> None: + """Drop a single cache item from the cache.""" + self.pop(path, None) + + +CACHE = _TextureCache() diff --git a/tests/test_texture.py b/tests/test_texture.py new file mode 100644 index 0000000..022c42c --- /dev/null +++ b/tests/test_texture.py @@ -0,0 +1,41 @@ +# Copyright 2023 (c) Anna Schumaker. +"""Tests our Gdk.Texture cache.""" +import emmental.texture +import pathlib +import tests.util +import unittest +from gi.repository import Gdk + + +class TestTextureCache(unittest.TestCase): + """Test our custom cache dictionary.""" + + def setUp(self): + """Set up common variables.""" + self.cache = emmental.texture._TextureCache() + + def test_init(self): + """Test that the cache dict is initialized properly.""" + self.assertIsInstance(emmental.texture.CACHE, + emmental.texture._TextureCache) + self.assertDictEqual(emmental.texture.CACHE, {}) + + self.assertIsInstance(self.cache, dict) + self.assertDictEqual(self.cache, {}) + + def test_drop(self): + """Test dropping items from the cache.""" + self.cache[tests.util.COVER_JPG] + self.cache.drop(tests.util.COVER_JPG) + self.assertDictEqual(self.cache, {}) + + def test_getitem(self): + """Test getting and creating items in the cache dict.""" + self.assertIsNone(self.cache[None]) + self.assertIsNone(self.cache[pathlib.Path("/no/such/path")]) + self.assertDictEqual(self.cache, {}) + + texture = self.cache[tests.util.COVER_JPG] + self.assertIsInstance(texture, Gdk.Texture) + self.assertDictEqual(self.cache, {tests.util.COVER_JPG: texture}) + self.assertEqual(self.cache[tests.util.COVER_JPG], texture)