Coverage for python/lsst/daf/butler/_utilities/thread_safe_cache.py: 56%
14 statements
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-26 02:47 -0700
« prev ^ index » next coverage.py v7.5.0, created at 2024-04-26 02:47 -0700
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
28import threading
29from typing import Generic, TypeVar
31TKey = TypeVar("TKey")
32TValue = TypeVar("TValue")
35class ThreadSafeCache(Generic[TKey, TValue]):
36 """A simple thread-safe cache. Ensures that once a value is stored for
37 a key, it does not change.
38 """
40 def __init__(self) -> None:
41 # This mutex may not actually be necessary for the current version of
42 # CPython, because both the 'get' and 'set' operation use underlying C
43 # functions that don't drop the GIL (at first glance anyway.) This is
44 # not documented anywhere that I can find, however, so better safe than
45 # sorry.
46 self._mutex = threading.Lock()
47 self._cache: dict[TKey, TValue] = dict()
49 def get(self, key: TKey) -> TValue | None:
50 """Return the value associated with the given key, or ``None`` if no
51 value has been assigned to that key.
53 Parameters
54 ----------
55 key : ``TKey``
56 Key used to look up the value.
57 """
58 with self._mutex:
59 return self._cache.get(key)
61 def set_or_get(self, key: TKey, value: TValue) -> TValue:
62 """Set a value for a key if the key does not already have a value.
64 Parameters
65 ----------
66 key : ``TKey``
67 Key used to look up the value.
68 value : ``TValue``
69 Value to store in the cache.
71 Returns
72 -------
73 value : ``TValue``
74 The existing value stored for the key if it was present, or
75 ``value`` if this was a new key.
76 """
77 with self._mutex:
78 return self._cache.setdefault(key, value)