Coverage for tests/test_deprecated.py: 100%
85 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-09 08:50 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-09 08:50 +0000
1# This file is part of utils.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://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 program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
22import copy
23import unittest
24import warnings
26import lsst.utils
27import lsst.utils.tests
28from lsst.utils.deprecated import DeprecatedDict
31class DeprecatedDictTestCase(lsst.utils.tests.TestCase):
32 """Test `~lsst.utils.deprecated.DeprecatedDict`."""
34 def makeDict(self, **kwargs):
35 # A DeprecatedDict with one deprecated key ("old") and one live key
36 # ("new"). Construction with a non-empty `deprecations` never warns.
37 return DeprecatedDict(
38 {"old": 1, "new": 2},
39 deprecations={"old": "Use `.new` instead."},
40 version="v30.0",
41 **kwargs,
42 )
44 def testIsADict(self):
45 d = self.makeDict()
46 self.assertIsInstance(d, dict)
47 self.assertEqual(d["new"], 2) # Live key never warns.
49 def testNoDeprecatedKeysWarns(self):
50 with self.assertWarns(UserWarning):
51 DeprecatedDict({"a": 1}, deprecations={})
53 def testGetItemWarnsOnce(self):
54 d = self.makeDict()
55 with self.assertWarns(FutureWarning) as cm:
56 self.assertEqual(d["old"], 1)
57 message = str(cm.warning)
58 self.assertIn("v30.0", message)
59 self.assertIn("Use `.new` instead.", message)
60 # A second access of the same key is silent.
61 with warnings.catch_warnings():
62 warnings.simplefilter("error")
63 self.assertEqual(d["old"], 1)
65 def testTargetedAccessorsWarn(self):
66 cases = {
67 "getitem": lambda d: d["old"],
68 "get": lambda d: d.get("old"),
69 "pop": lambda d: d.pop("old"),
70 "setdefault": lambda d: d.setdefault("old", 9),
71 "setitem": lambda d: d.__setitem__("old", 9),
72 "delitem": lambda d: d.__delitem__("old"),
73 }
74 for name, op in cases.items():
75 with self.subTest(accessor=name):
76 d = self.makeDict()
77 with self.assertWarns(FutureWarning):
78 op(d)
80 def testCustomCategory(self):
81 d = self.makeDict(category=DeprecationWarning)
82 with self.assertWarns(DeprecationWarning):
83 _ = d["old"]
85 def testPerKeyReasonAndCategory(self):
86 d = DeprecatedDict(
87 {"a": 1, "b": 2},
88 deprecations={"a": "Use `.a` instead.", "b": "Use `.b` instead."},
89 deprecated_categories={"b": DeprecationWarning},
90 )
91 # "a" uses the default category and its own reason.
92 with self.assertWarns(FutureWarning) as cm:
93 _ = d["a"]
94 self.assertIn("Use `.a` instead.", str(cm.warning))
95 # "b" overrides the category and carries its own reason.
96 with self.assertWarns(DeprecationWarning) as cm:
97 _ = d["b"]
98 self.assertIn("Use `.b` instead.", str(cm.warning))
100 def testLiveKeyNeverWarns(self):
101 d = self.makeDict()
102 with warnings.catch_warnings():
103 warnings.simplefilter("error")
104 _ = d["new"]
105 _ = d.get("new")
106 d["new"] = 3
108 def testBulkOperationsAreSilent(self):
109 d = self.makeDict()
110 with warnings.catch_warnings():
111 warnings.simplefilter("error")
112 self.assertEqual(dict(d), {"old": 1, "new": 2})
113 self.assertEqual({**d}, {"old": 1, "new": 2})
114 self.assertEqual(sorted(d.keys()), ["new", "old"])
115 self.assertEqual(sorted(d.items()), [("new", 2), ("old", 1)])
116 self.assertIn("old", d)
117 other = {}
118 other.update(d)
119 self.assertEqual(other, {"old": 1, "new": 2})
121 def testDeepcopyIsSilentAndIndependent(self):
122 d = self.makeDict()
123 with warnings.catch_warnings():
124 warnings.simplefilter("ignore")
125 _ = d["old"] # Latch the original.
126 with warnings.catch_warnings():
127 warnings.simplefilter("error")
128 clone = copy.deepcopy(d)
129 # The clone inherits the "already warned" latch, so it too is
130 # silent for the key already warned on the original.
131 _ = clone["old"]
132 self.assertIsInstance(clone, DeprecatedDict)
133 self.assertEqual(dict(clone), {"old": 1, "new": 2})
134 self.assertEqual(dict(clone), dict(d))
137class DeprecatedTestCase(lsst.utils.tests.TestCase):
138 """Test depreaction."""
140 def test_deprecate_pybind11(self):
141 def old(x):
142 """Docstring."""
143 return x + 1
145 # Use an unusual category
146 old = lsst.utils.deprecate_pybind11(
147 old, reason="For testing.", version="unknown", category=PendingDeprecationWarning
148 )
149 with self.assertWarnsRegex(
150 PendingDeprecationWarning,
151 r"Call to deprecated function \(or staticmethod\) old\. \(For testing\.\) "
152 "-- Deprecated since version unknown.$",
153 ):
154 # Check that the function still works
155 self.assertEqual(old(3), 4)
156 self.assertIn("Docstring", old.__doc__)
157 self.assertIn("For testing.", old.__doc__)
160if __name__ == "__main__":
161 unittest.main()