Coverage for tests / test_configOverrides.py: 21%

159 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-30 08:49 +0000

1# This file is part of pipe_base. 

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/>. 

27 

28"""Simple unit test for configOverrides.""" 

29 

30import tempfile 

31import unittest 

32 

33import lsst.pex.config as pexConfig 

34import lsst.utils.tests 

35from lsst.pipe.base.configOverrides import ConfigOverrides 

36from lsst.pipe.base.pipelineIR import ParametersIR 

37 

38# This is used in testSettingVar unit test 

39TEST_CHOICE_VALUE = 1 

40 

41 

42class ConfigTest(pexConfig.Config): 

43 """Configuration used for testing.""" 

44 

45 fStr = pexConfig.Field(dtype=str, default="default", doc="test") 

46 fBool = pexConfig.Field(dtype=bool, default=False, doc="test") 

47 fInt = pexConfig.Field(dtype=int, default=-1, doc="test") 

48 fFloat = pexConfig.Field(dtype=float, default=-1.0, doc="test") 

49 

50 fListStr = pexConfig.ListField(dtype=str, default=[], doc="test") 

51 fListBool = pexConfig.ListField(dtype=bool, default=[], doc="test") 

52 fListInt = pexConfig.ListField(dtype=int, default=[], doc="test") 

53 

54 fChoiceStr = pexConfig.ChoiceField(dtype=str, allowed=dict(A="a", B="b", C="c"), doc="test") 

55 fChoiceInt = pexConfig.ChoiceField(dtype=int, allowed={1: "a", 2: "b", 3: "c"}, doc="test") 

56 

57 fDictStrInt = pexConfig.DictField(keytype=str, itemtype=int, doc="test") 

58 

59 

60class ConfigOverridesTestCase(unittest.TestCase): 

61 """A test case for Task.""" 

62 

63 def checkSingleFieldOverride(self, field, value, result=None): 

64 """Apply overrides of a single field. 

65 

66 Parameters 

67 ---------- 

68 field : `str` 

69 Field name. 

70 value : `typing.Any` 

71 Field value to set, can be a string or anything else. 

72 result : `typing.Any`, optional 

73 Expected value of the field. 

74 """ 

75 config = ConfigTest() 

76 overrides = ConfigOverrides() 

77 overrides.addValueOverride(field, value) 

78 overrides.applyTo(config) 

79 self.assertEqual(getattr(config, field), result) 

80 

81 def testSimpleValueStr(self): 

82 """Test for applying value override to a string field.""" 

83 field = "fStr" 

84 

85 # values of supported type 

86 self.checkSingleFieldOverride(field, "string", "string") 

87 

88 # invalid input 

89 with self.assertRaises(pexConfig.FieldValidationError): 

90 self.checkSingleFieldOverride(field, 1) 

91 

92 def testSimpleValueBool(self): 

93 """Test for applying value override to a boolean field.""" 

94 field = "fBool" 

95 

96 # values of supported type 

97 self.checkSingleFieldOverride(field, True, True) 

98 self.checkSingleFieldOverride(field, False, False) 

99 

100 # supported string conversions 

101 self.checkSingleFieldOverride(field, "True", True) 

102 self.checkSingleFieldOverride(field, "False", False) 

103 

104 # parseable but invalid input 

105 with self.assertRaises(pexConfig.FieldValidationError): 

106 self.checkSingleFieldOverride(field, 1) 

107 with self.assertRaises(pexConfig.FieldValidationError): 

108 self.checkSingleFieldOverride(field, []) 

109 with self.assertRaises(pexConfig.FieldValidationError): 

110 self.checkSingleFieldOverride(field, "1") 

111 

112 def testSimpleValueInt(self): 

113 """Test for applying value override to a int field.""" 

114 field = "fInt" 

115 

116 # values of supported type 

117 self.checkSingleFieldOverride(field, 0, 0) 

118 self.checkSingleFieldOverride(field, 100, 100) 

119 

120 # supported string conversions 

121 self.checkSingleFieldOverride(field, "0", 0) 

122 self.checkSingleFieldOverride(field, "100", 100) 

123 self.checkSingleFieldOverride(field, "-100", -100) 

124 self.checkSingleFieldOverride(field, "0x100", 0x100) 

125 

126 # parseable but invalid input 

127 with self.assertRaises(pexConfig.FieldValidationError): 

128 self.checkSingleFieldOverride(field, 1.0) 

129 with self.assertRaises(pexConfig.FieldValidationError): 

130 self.checkSingleFieldOverride(field, "1.0") 

131 with self.assertRaises(pexConfig.FieldValidationError): 

132 self.checkSingleFieldOverride(field, "[]") 

133 

134 def testSimpleValueFloat(self): 

135 """Test for applying value override to a float field.""" 

136 field = "fFloat" 

137 

138 # values of supported type 

139 self.checkSingleFieldOverride(field, 0.0, 0.0) 

140 self.checkSingleFieldOverride(field, 100.0, 100.0) 

141 

142 # supported string conversions 

143 self.checkSingleFieldOverride(field, "0.", 0.0) 

144 self.checkSingleFieldOverride(field, "100.0", 100.0) 

145 self.checkSingleFieldOverride(field, "-1.2e10", -1.2e10) 

146 

147 # parseable but invalid input 

148 with self.assertRaises(pexConfig.FieldValidationError): 

149 self.checkSingleFieldOverride(field, []) 

150 with self.assertRaises(pexConfig.FieldValidationError): 

151 self.checkSingleFieldOverride(field, "(1, 1)") 

152 

153 def testListValueStr(self): 

154 """Test for applying value override to a list field.""" 

155 field = "fListStr" 

156 

157 # values of supported type 

158 self.checkSingleFieldOverride(field, ["a", "b"], ["a", "b"]) 

159 self.checkSingleFieldOverride(field, ("a", "b"), ["a", "b"]) 

160 

161 # supported string conversions 

162 self.checkSingleFieldOverride(field, '["a", "b"]', ["a", "b"]) 

163 self.checkSingleFieldOverride(field, '("a", "b")', ["a", "b"]) 

164 

165 # parseable but invalid input 

166 with self.assertRaises(pexConfig.FieldValidationError): 

167 self.checkSingleFieldOverride(field, "['a', []]") 

168 

169 def testListValueBool(self): 

170 """Test for applying value override to a list field.""" 

171 field = "fListBool" 

172 

173 # values of supported type 

174 self.checkSingleFieldOverride(field, [True, False], [True, False]) 

175 self.checkSingleFieldOverride(field, (True, False), [True, False]) 

176 

177 # supported string conversions 

178 self.checkSingleFieldOverride(field, "[True, False]", [True, False]) 

179 self.checkSingleFieldOverride(field, "(True, False)", [True, False]) 

180 self.checkSingleFieldOverride(field, "['True', 'False']", [True, False]) 

181 

182 with self.assertRaises(pexConfig.FieldValidationError): 

183 self.checkSingleFieldOverride(field, "[1, 2]") 

184 with self.assertRaises(pexConfig.FieldValidationError): 

185 self.checkSingleFieldOverride(field, [0, 1]) 

186 with self.assertRaises(pexConfig.FieldValidationError): 

187 self.checkSingleFieldOverride(field, "5") 

188 

189 def testListValueInt(self): 

190 """Test for applying value override to a list field.""" 

191 field = "fListInt" 

192 

193 # values of supported type 

194 self.checkSingleFieldOverride(field, [1, 2], [1, 2]) 

195 self.checkSingleFieldOverride(field, (1, 2), [1, 2]) 

196 

197 # supported string conversions 

198 self.checkSingleFieldOverride(field, "[1, 2]", [1, 2]) 

199 self.checkSingleFieldOverride(field, "(1, 2)", [1, 2]) 

200 self.checkSingleFieldOverride(field, "['1', '2']", [1, 2]) 

201 

202 # parseable but invalid input 

203 with self.assertRaises(pexConfig.FieldValidationError): 

204 self.checkSingleFieldOverride(field, "[1.0, []]") 

205 with self.assertRaises(pexConfig.FieldValidationError): 

206 self.checkSingleFieldOverride(field, [[], []]) 

207 with self.assertRaises(pexConfig.FieldValidationError): 

208 self.checkSingleFieldOverride(field, "5") 

209 

210 def testChoiceValueStr(self): 

211 """Test for applying value override to a choice field.""" 

212 field = "fChoiceStr" 

213 

214 # values of supported type 

215 self.checkSingleFieldOverride(field, "A", "A") 

216 self.checkSingleFieldOverride(field, "B", "B") 

217 

218 # non-allowed value 

219 with self.assertRaises(pexConfig.FieldValidationError): 

220 self.checkSingleFieldOverride(field, "X") 

221 

222 def testChoiceValueInt(self): 

223 """Test for applying value override to a choice field.""" 

224 field = "fChoiceInt" 

225 

226 # values of supported type 

227 self.checkSingleFieldOverride(field, 1, 1) 

228 self.checkSingleFieldOverride(field, 3, 3) 

229 

230 # supported string conversions 

231 self.checkSingleFieldOverride(field, "1", 1) 

232 

233 # parseable but invalid input 

234 with self.assertRaises(pexConfig.FieldValidationError): 

235 self.checkSingleFieldOverride(field, "0") 

236 with self.assertRaises(pexConfig.FieldValidationError): 

237 self.checkSingleFieldOverride(field, "[1]") 

238 with self.assertRaises(pexConfig.FieldValidationError): 

239 self.checkSingleFieldOverride(field, [0, 1]) 

240 

241 def testSettingVar(self): 

242 """Test setting a field with a string that represents a variable.""" 

243 field = "fChoiceInt" 

244 

245 # verify loading variable 

246 self.checkSingleFieldOverride(field, "TEST_CHOICE_VALUE", 1) 

247 

248 # Verify That python importing a variable works 

249 config = ConfigTest() 

250 overrides = ConfigOverrides() 

251 overrides.addPythonOverride("from math import pi") 

252 overrides.addValueOverride("fFloat", "pi") 

253 overrides.applyTo(config) 

254 from math import pi 

255 

256 self.assertEqual(config.fFloat, pi) 

257 

258 def testDictValueInt(self): 

259 """Test for applying value override to a dict field.""" 

260 field = "fDictStrInt" 

261 

262 # values of supported type 

263 self.checkSingleFieldOverride(field, dict(a=1, b=2), dict(a=1, b=2)) 

264 

265 # supported string conversions 

266 self.checkSingleFieldOverride(field, "{'a': 1, 'b': 2}", dict(a=1, b=2)) 

267 

268 # parseable but invalid input 

269 with self.assertRaises(pexConfig.FieldValidationError): 

270 self.checkSingleFieldOverride(field, "{1: 2}") 

271 with self.assertRaises(pexConfig.FieldValidationError): 

272 self.checkSingleFieldOverride(field, "{'a': 'b'}") 

273 with self.assertRaises(pexConfig.FieldValidationError): 

274 self.checkSingleFieldOverride(field, {"a": "b"}) 

275 

276 def testConfigParameters(self): 

277 """Test that passing parameters works.""" 

278 config = ConfigTest() 

279 parameters = ParametersIR(mapping={"number": 6, "text": "hello world"}) 

280 overrides = ConfigOverrides() 

281 overrides.addParameters(parameters) 

282 overrides.addPythonOverride("config.fStr = parameters.text") 

283 with tempfile.NamedTemporaryFile(mode="w") as fileOverride: 

284 fileOverride.write("config.fInt = parameters.number") 

285 fileOverride.seek(0) 

286 overrides.addFileOverride(fileOverride.name) 

287 overrides.applyTo(config) 

288 self.assertEqual(config.fStr, parameters.mapping["text"]) 

289 self.assertEqual(config.fInt, parameters.mapping["number"]) 

290 

291 overrides = ConfigOverrides() 

292 overrides.addParameters(parameters) 

293 overrides.addPythonOverride("parameters.fail = 9") 

294 with self.assertRaises(ValueError): 

295 overrides.applyTo(config) 

296 

297 

298class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase): 

299 """Check for memory leaks.""" 

300 

301 

302def setup_module(module): 

303 """Configure pytest.""" 

304 lsst.utils.tests.init() 

305 

306 

307if __name__ == "__main__": 

308 lsst.utils.tests.init() 

309 unittest.main()