Coverage for tests / test_separable_pipeline_executor.py: 12%
613 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-19 01:13 -0800
« prev ^ index » next coverage.py v7.12.0, created at 2025-12-19 01:13 -0800
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# (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 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 <https://www.gnu.org/licenses/>.
29import os
30import tempfile
31import unittest
33import lsst.daf.butler
34import lsst.daf.butler.tests as butlerTests
35import lsst.pex.config
36import lsst.utils.tests
37from lsst.daf.butler.registry import RegistryDefaults
38from lsst.pipe.base import Instrument, Pipeline, PipelineGraph, QuantumGraph, TaskMetadata
39from lsst.pipe.base.all_dimensions_quantum_graph_builder import AllDimensionsQuantumGraphBuilder
40from lsst.pipe.base.automatic_connection_constants import PACKAGES_INIT_OUTPUT_NAME
41from lsst.pipe.base.quantum_graph_builder import OutputExistsError
42from lsst.pipe.base.separable_pipeline_executor import SeparablePipelineExecutor
43from lsst.resources import ResourcePath
45TESTDIR = os.path.abspath(os.path.dirname(__file__))
48class SeparablePipelineExecutorTests(lsst.utils.tests.TestCase):
49 """Test the SeparablePipelineExecutor API with a trivial task."""
51 pipeline_file = os.path.join(TESTDIR, "pipelines", "pipeline_separable.yaml")
53 def setUp(self):
54 repodir = tempfile.TemporaryDirectory()
55 # TemporaryDirectory warns on leaks; addCleanup also keeps it from
56 # getting garbage-collected.
57 self.addCleanup(tempfile.TemporaryDirectory.cleanup, repodir)
59 # standalone parameter forces the returned config to also include
60 # the information from the search paths.
61 config = lsst.daf.butler.Butler.makeRepo(
62 repodir.name, standalone=True, searchPaths=[os.path.join(TESTDIR, "config")]
63 )
64 butler = lsst.daf.butler.Butler.from_config(config, writeable=True)
65 self.enterContext(butler)
66 output = "fake"
67 output_run = f"{output}/{Instrument.makeCollectionTimestamp()}"
68 butler.registry.registerCollection(output_run, lsst.daf.butler.CollectionType.RUN)
69 butler.registry.registerCollection(output, lsst.daf.butler.CollectionType.CHAINED)
70 butler.registry.setCollectionChain(output, [output_run])
71 butler.registry.defaults = RegistryDefaults(collections=[output], run=output_run)
72 self.butler = butler
74 butlerTests.addDatasetType(self.butler, "input", set(), "StructuredDataDict")
75 butlerTests.addDatasetType(self.butler, "intermediate", set(), "StructuredDataDict")
76 butlerTests.addDatasetType(self.butler, "a_log", set(), "ButlerLogRecords")
77 butlerTests.addDatasetType(self.butler, "a_metadata", set(), "TaskMetadata")
78 butlerTests.addDatasetType(self.butler, "a_config", set(), "Config")
80 def build_empty_quantum_graph(self) -> None:
81 pipeline_graph = PipelineGraph(universe=self.butler.dimensions)
82 pipeline_graph.resolve(self.butler.registry)
83 builder = AllDimensionsQuantumGraphBuilder(pipeline_graph, self.butler)
84 return builder.finish(attach_datastore_records=False).assemble()
86 def test_pre_execute_qgraph_old(self):
87 # Too hard to make a quantum graph from scratch.
88 executor = SeparablePipelineExecutor(self.butler)
89 pipeline = Pipeline.fromFile(self.pipeline_file)
90 self.butler.put({"zero": 0}, "input")
91 graph = executor.make_quantum_graph(pipeline)
93 butlerTests.addDatasetType(self.butler, "output", set(), "StructuredDataDict")
94 butlerTests.addDatasetType(self.butler, "b_config", set(), "Config")
95 butlerTests.addDatasetType(self.butler, "b_log", set(), "ButlerLogRecords")
96 butlerTests.addDatasetType(self.butler, "b_metadata", set(), "TaskMetadata")
97 butlerTests.addDatasetType(self.butler, PACKAGES_INIT_OUTPUT_NAME, set(), "Packages")
99 executor.pre_execute_qgraph(
100 graph,
101 register_dataset_types=False,
102 save_init_outputs=False,
103 save_versions=False,
104 )
105 self.assertFalse(self.butler.exists("a_config", {}, collections=[self.butler.run]))
106 self.assertFalse(self.butler.exists(PACKAGES_INIT_OUTPUT_NAME, {}))
108 def test_pre_execute_qgraph(self):
109 # Too hard to make a quantum graph from scratch.
110 executor = SeparablePipelineExecutor(self.butler)
111 pipeline = Pipeline.fromFile(self.pipeline_file)
112 self.butler.put({"zero": 0}, "input")
113 graph = executor.build_quantum_graph(pipeline)
115 butlerTests.addDatasetType(self.butler, "output", set(), "StructuredDataDict")
116 butlerTests.addDatasetType(self.butler, "b_config", set(), "Config")
117 butlerTests.addDatasetType(self.butler, "b_log", set(), "ButlerLogRecords")
118 butlerTests.addDatasetType(self.butler, "b_metadata", set(), "TaskMetadata")
119 butlerTests.addDatasetType(self.butler, PACKAGES_INIT_OUTPUT_NAME, set(), "Packages")
121 executor.pre_execute_qgraph(
122 graph,
123 register_dataset_types=False,
124 save_init_outputs=False,
125 save_versions=False,
126 )
127 self.assertFalse(self.butler.exists("a_config", {}, collections=[self.butler.run]))
128 self.assertFalse(self.butler.exists(PACKAGES_INIT_OUTPUT_NAME, {}))
130 def test_pre_execute_qgraph_unconnected_old(self):
131 # Unconnected graph; see
132 # test_make_quantum_graph_nowhere_skippartial_clobber.
133 executor = SeparablePipelineExecutor(
134 self.butler,
135 skip_existing_in=[self.butler.run],
136 clobber_output=True,
137 )
138 pipeline = Pipeline.fromFile(self.pipeline_file)
139 self.butler.put({"zero": 0}, "input")
140 self.butler.put({"zero": 0}, "intermediate")
141 graph = executor.make_quantum_graph(pipeline)
143 butlerTests.addDatasetType(self.butler, "output", set(), "StructuredDataDict")
144 butlerTests.addDatasetType(self.butler, "b_config", set(), "Config")
145 butlerTests.addDatasetType(self.butler, "b_log", set(), "ButlerLogRecords")
146 butlerTests.addDatasetType(self.butler, "b_metadata", set(), "TaskMetadata")
147 butlerTests.addDatasetType(self.butler, PACKAGES_INIT_OUTPUT_NAME, set(), "Packages")
149 executor.pre_execute_qgraph(
150 graph,
151 register_dataset_types=False,
152 save_init_outputs=False,
153 save_versions=False,
154 )
155 self.assertFalse(self.butler.exists("a_config", {}, collections=[self.butler.run]))
156 self.assertFalse(self.butler.exists(PACKAGES_INIT_OUTPUT_NAME, {}))
158 def test_pre_execute_qgraph_unconnected(self):
159 # Unconnected graph; see
160 # test_make_quantum_graph_nowhere_skippartial_clobber.
161 executor = SeparablePipelineExecutor(
162 self.butler,
163 skip_existing_in=[self.butler.run],
164 clobber_output=True,
165 )
166 pipeline = Pipeline.fromFile(self.pipeline_file)
167 self.butler.put({"zero": 0}, "input")
168 self.butler.put({"zero": 0}, "intermediate")
169 graph = executor.build_quantum_graph(pipeline)
171 butlerTests.addDatasetType(self.butler, "output", set(), "StructuredDataDict")
172 butlerTests.addDatasetType(self.butler, "b_config", set(), "Config")
173 butlerTests.addDatasetType(self.butler, "b_log", set(), "ButlerLogRecords")
174 butlerTests.addDatasetType(self.butler, "b_metadata", set(), "TaskMetadata")
175 butlerTests.addDatasetType(self.butler, PACKAGES_INIT_OUTPUT_NAME, set(), "Packages")
177 executor.pre_execute_qgraph(
178 graph,
179 register_dataset_types=False,
180 save_init_outputs=False,
181 save_versions=False,
182 )
183 self.assertFalse(self.butler.exists("a_config", {}, collections=[self.butler.run]))
184 self.assertFalse(self.butler.exists(PACKAGES_INIT_OUTPUT_NAME, {}))
186 def test_pre_execute_qgraph_empty_old(self):
187 executor = SeparablePipelineExecutor(self.butler)
188 graph = QuantumGraph({}, universe=self.butler.dimensions)
190 butlerTests.addDatasetType(self.butler, "output", set(), "StructuredDataDict")
191 butlerTests.addDatasetType(self.butler, "b_config", set(), "Config")
192 butlerTests.addDatasetType(self.butler, "b_log", set(), "ButlerLogRecords")
193 butlerTests.addDatasetType(self.butler, "b_metadata", set(), "TaskMetadata")
194 butlerTests.addDatasetType(self.butler, PACKAGES_INIT_OUTPUT_NAME, set(), "Packages")
196 executor.pre_execute_qgraph(
197 graph,
198 register_dataset_types=False,
199 save_init_outputs=False,
200 save_versions=False,
201 )
202 self.assertFalse(self.butler.exists("a_config", {}, collections=[self.butler.run]))
203 self.assertFalse(self.butler.exists(PACKAGES_INIT_OUTPUT_NAME, {}))
205 def test_pre_execute_qgraph_empty(self):
206 executor = SeparablePipelineExecutor(self.butler)
207 graph = self.build_empty_quantum_graph()
209 butlerTests.addDatasetType(self.butler, "output", set(), "StructuredDataDict")
210 butlerTests.addDatasetType(self.butler, "b_config", set(), "Config")
211 butlerTests.addDatasetType(self.butler, "b_log", set(), "ButlerLogRecords")
212 butlerTests.addDatasetType(self.butler, "b_metadata", set(), "TaskMetadata")
213 butlerTests.addDatasetType(self.butler, PACKAGES_INIT_OUTPUT_NAME, set(), "Packages")
215 executor.pre_execute_qgraph(
216 graph,
217 register_dataset_types=False,
218 save_init_outputs=False,
219 save_versions=False,
220 )
221 self.assertFalse(self.butler.exists("a_config", {}, collections=[self.butler.run]))
222 self.assertFalse(self.butler.exists(PACKAGES_INIT_OUTPUT_NAME, {}))
224 def test_pre_execute_qgraph_register_old(self):
225 executor = SeparablePipelineExecutor(self.butler)
226 pipeline = Pipeline.fromFile(self.pipeline_file)
227 self.butler.put({"zero": 0}, "input")
228 graph = executor.make_quantum_graph(pipeline)
230 executor.pre_execute_qgraph(
231 graph,
232 register_dataset_types=True,
233 save_init_outputs=False,
234 save_versions=False,
235 )
236 self.assertEqual({d.name for d in self.butler.registry.queryDatasetTypes("output")}, {"output"})
237 self.assertEqual(
238 {d.name for d in self.butler.registry.queryDatasetTypes("b_*")},
239 {"b_config", "b_log", "b_metadata"},
240 )
241 self.assertFalse(self.butler.exists("a_config", {}, collections=[self.butler.run]))
242 self.assertFalse(self.butler.exists(PACKAGES_INIT_OUTPUT_NAME, {}))
244 def test_pre_execute_qgraph_register(self):
245 executor = SeparablePipelineExecutor(self.butler)
246 pipeline = Pipeline.fromFile(self.pipeline_file)
247 self.butler.put({"zero": 0}, "input")
248 graph = executor.build_quantum_graph(pipeline)
250 executor.pre_execute_qgraph(
251 graph,
252 register_dataset_types=True,
253 save_init_outputs=False,
254 save_versions=False,
255 )
256 self.assertEqual({d.name for d in self.butler.registry.queryDatasetTypes("output")}, {"output"})
257 self.assertEqual(
258 {d.name for d in self.butler.registry.queryDatasetTypes("b_*")},
259 {"b_config", "b_log", "b_metadata"},
260 )
261 self.assertFalse(self.butler.exists("a_config", {}, collections=[self.butler.run]))
262 self.assertFalse(self.butler.exists(PACKAGES_INIT_OUTPUT_NAME, {}))
264 def test_pre_execute_qgraph_init_outputs_old(self):
265 # Too hard to make a quantum graph from scratch.
266 executor = SeparablePipelineExecutor(self.butler)
267 pipeline = Pipeline.fromFile(self.pipeline_file)
268 self.butler.put({"zero": 0}, "input")
269 graph = executor.make_quantum_graph(pipeline)
271 butlerTests.addDatasetType(self.butler, "output", set(), "StructuredDataDict")
272 butlerTests.addDatasetType(self.butler, "b_config", set(), "Config")
273 butlerTests.addDatasetType(self.butler, "b_log", set(), "ButlerLogRecords")
274 butlerTests.addDatasetType(self.butler, "b_metadata", set(), "TaskMetadata")
275 butlerTests.addDatasetType(self.butler, PACKAGES_INIT_OUTPUT_NAME, set(), "Packages")
277 executor.pre_execute_qgraph(
278 graph,
279 register_dataset_types=False,
280 save_init_outputs=True,
281 save_versions=False,
282 )
283 self.assertTrue(self.butler.exists("a_config", {}, collections=[self.butler.run]))
284 self.assertFalse(self.butler.exists(PACKAGES_INIT_OUTPUT_NAME, {}))
286 def test_pre_execute_qgraph_init_outputs(self):
287 # Too hard to make a quantum graph from scratch.
288 executor = SeparablePipelineExecutor(self.butler)
289 pipeline = Pipeline.fromFile(self.pipeline_file)
290 self.butler.put({"zero": 0}, "input")
291 graph = executor.build_quantum_graph(pipeline)
293 butlerTests.addDatasetType(self.butler, "output", set(), "StructuredDataDict")
294 butlerTests.addDatasetType(self.butler, "b_config", set(), "Config")
295 butlerTests.addDatasetType(self.butler, "b_log", set(), "ButlerLogRecords")
296 butlerTests.addDatasetType(self.butler, "b_metadata", set(), "TaskMetadata")
297 butlerTests.addDatasetType(self.butler, PACKAGES_INIT_OUTPUT_NAME, set(), "Packages")
299 executor.pre_execute_qgraph(
300 graph,
301 register_dataset_types=False,
302 save_init_outputs=True,
303 save_versions=False,
304 )
305 self.assertTrue(self.butler.exists("a_config", {}, collections=[self.butler.run]))
306 self.assertFalse(self.butler.exists(PACKAGES_INIT_OUTPUT_NAME, {}))
308 def test_pre_execute_qgraph_versions_old(self):
309 executor = SeparablePipelineExecutor(self.butler)
310 pipeline = Pipeline.fromFile(self.pipeline_file)
311 self.butler.put({"zero": 0}, "input")
312 graph = executor.make_quantum_graph(pipeline)
314 butlerTests.addDatasetType(self.butler, "output", set(), "StructuredDataDict")
315 butlerTests.addDatasetType(self.butler, "b_config", set(), "Config")
316 butlerTests.addDatasetType(self.butler, "b_log", set(), "ButlerLogRecords")
317 butlerTests.addDatasetType(self.butler, "b_metadata", set(), "TaskMetadata")
318 butlerTests.addDatasetType(self.butler, PACKAGES_INIT_OUTPUT_NAME, set(), "Packages")
320 executor.pre_execute_qgraph(
321 graph,
322 register_dataset_types=False,
323 save_init_outputs=True,
324 save_versions=True,
325 )
326 self.assertTrue(self.butler.exists("a_config", {}, collections=[self.butler.run]))
327 self.assertTrue(self.butler.exists(PACKAGES_INIT_OUTPUT_NAME, {}))
329 def test_pre_execute_qgraph_versions(self):
330 executor = SeparablePipelineExecutor(self.butler)
331 pipeline = Pipeline.fromFile(self.pipeline_file)
332 self.butler.put({"zero": 0}, "input")
333 graph = executor.build_quantum_graph(pipeline)
335 butlerTests.addDatasetType(self.butler, "output", set(), "StructuredDataDict")
336 butlerTests.addDatasetType(self.butler, "b_config", set(), "Config")
337 butlerTests.addDatasetType(self.butler, "b_log", set(), "ButlerLogRecords")
338 butlerTests.addDatasetType(self.butler, "b_metadata", set(), "TaskMetadata")
339 butlerTests.addDatasetType(self.butler, PACKAGES_INIT_OUTPUT_NAME, set(), "Packages")
341 executor.pre_execute_qgraph(
342 graph,
343 register_dataset_types=False,
344 save_init_outputs=True,
345 save_versions=True,
346 )
347 self.assertTrue(self.butler.exists("a_config", {}, collections=[self.butler.run]))
348 self.assertTrue(self.butler.exists(PACKAGES_INIT_OUTPUT_NAME, {}))
350 def test_init_badinput(self):
351 with lsst.daf.butler.Butler.from_config(butler=self.butler, collections=[], run="foo") as butler:
352 with self.assertRaises(ValueError):
353 SeparablePipelineExecutor(butler)
355 def test_init_badoutput(self):
356 with lsst.daf.butler.Butler.from_config(butler=self.butler, collections=["foo"]) as butler:
357 with self.assertRaises(ValueError):
358 SeparablePipelineExecutor(butler)
360 def test_make_pipeline_full(self):
361 executor = SeparablePipelineExecutor(self.butler)
362 for uri in [
363 self.pipeline_file,
364 ResourcePath(self.pipeline_file),
365 ResourcePath(self.pipeline_file).geturl(),
366 ]:
367 pipeline_graph = executor.make_pipeline(uri).to_graph()
368 self.assertEqual(set(pipeline_graph.tasks), {"a", "b"})
370 def test_make_pipeline_subset(self):
371 executor = SeparablePipelineExecutor(self.butler)
372 path = self.pipeline_file + "#a"
373 for uri in [
374 path,
375 ResourcePath(path),
376 ResourcePath(path).geturl(),
377 ]:
378 pipeline_graph = executor.make_pipeline(uri).to_graph()
379 self.assertEqual(set(pipeline_graph.tasks), {"a"})
381 def test_build_quantum_graph_nowhere_noskip_noclobber(self):
382 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=False)
383 pipeline = Pipeline.fromFile(self.pipeline_file)
385 self.butler.put({"zero": 0}, "input")
387 graph = executor.make_quantum_graph(pipeline)
388 self.assertTrue(graph.isConnected)
389 self.assertEqual(len(graph), 2)
390 self.assertEqual({q.taskDef.label for q in graph.inputQuanta}, {"a"})
391 self.assertEqual({q.taskDef.label for q in graph.outputQuanta}, {"b"})
393 def test_make_quantum_graph_nowhere_noskip_noclobber(self):
394 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=False)
395 pipeline = Pipeline.fromFile(self.pipeline_file)
397 self.butler.put({"zero": 0}, "input")
399 graph = executor.build_quantum_graph(pipeline)
400 self.assertEqual(len(graph), 2)
401 self.assertEqual(graph.quanta_by_task.keys(), {"a", "b"})
403 def test_make_quantum_graph_nowhere_noskip_noclobber_conflict(self):
404 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=False)
405 pipeline = Pipeline.fromFile(self.pipeline_file)
407 self.butler.put({"zero": 0}, "input")
408 self.butler.put({"zero": 0}, "intermediate")
409 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
410 self.butler.put(TaskMetadata(), "a_metadata")
412 with self.assertRaises(OutputExistsError):
413 executor.build_quantum_graph(pipeline)
415 # TODO: need more complex task and Butler to test
416 # make_quantum_graph(where=...)
418 def test_build_quantum_graph_nowhere_skipnone_noclobber(self):
419 executor = SeparablePipelineExecutor(
420 self.butler,
421 skip_existing_in=[self.butler.run],
422 clobber_output=False,
423 )
424 pipeline = Pipeline.fromFile(self.pipeline_file)
426 self.butler.put({"zero": 0}, "input")
428 graph = executor.make_quantum_graph(pipeline)
429 self.assertTrue(graph.isConnected)
430 self.assertEqual(len(graph), 2)
431 self.assertEqual({q.taskDef.label for q in graph.inputQuanta}, {"a"})
432 self.assertEqual({q.taskDef.label for q in graph.outputQuanta}, {"b"})
434 def test_make_quantum_graph_nowhere_skipnone_noclobber(self):
435 executor = SeparablePipelineExecutor(
436 self.butler,
437 skip_existing_in=[self.butler.run],
438 clobber_output=False,
439 )
440 pipeline = Pipeline.fromFile(self.pipeline_file)
441 self.butler.put({"zero": 0}, "input")
442 graph = executor.build_quantum_graph(pipeline)
443 self.assertEqual(len(graph), 2)
444 self.assertEqual(graph.quanta_by_task.keys(), {"a", "b"})
446 def test_build_quantum_graph_nowhere_skiptotal_noclobber(self):
447 executor = SeparablePipelineExecutor(
448 self.butler,
449 skip_existing_in=[self.butler.run],
450 clobber_output=False,
451 )
452 pipeline = Pipeline.fromFile(self.pipeline_file)
454 self.butler.put({"zero": 0}, "input")
455 self.butler.put({"zero": 0}, "intermediate")
456 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
457 self.butler.put(TaskMetadata(), "a_metadata")
458 self.butler.put(lsst.pex.config.Config(), "a_config")
460 graph = executor.make_quantum_graph(pipeline)
461 self.assertTrue(graph.isConnected)
462 self.assertEqual(len(graph), 1)
463 self.assertEqual({q.taskDef.label for q in graph.inputQuanta}, {"b"})
464 self.assertEqual({q.taskDef.label for q in graph.outputQuanta}, {"b"})
466 def test_make_quantum_graph_nowhere_skiptotal_noclobber(self):
467 executor = SeparablePipelineExecutor(
468 self.butler,
469 skip_existing_in=[self.butler.run],
470 clobber_output=False,
471 )
472 pipeline = Pipeline.fromFile(self.pipeline_file)
474 self.butler.put({"zero": 0}, "input")
475 self.butler.put({"zero": 0}, "intermediate")
476 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
477 self.butler.put(TaskMetadata(), "a_metadata")
478 self.butler.put(lsst.pex.config.Config(), "a_config")
480 graph = executor.build_quantum_graph(pipeline)
481 self.assertEqual(len(graph), 1)
482 self.assertEqual(graph.header.n_task_quanta["a"], 0)
483 self.assertEqual(graph.header.n_task_quanta["b"], 1)
485 def test_make_quantum_graph_nowhere_skippartial_noclobber(self):
486 executor = SeparablePipelineExecutor(
487 self.butler,
488 skip_existing_in=[self.butler.run],
489 clobber_output=False,
490 )
491 pipeline = Pipeline.fromFile(self.pipeline_file)
493 self.butler.put({"zero": 0}, "input")
494 self.butler.put({"zero": 0}, "intermediate")
496 with self.assertRaises(OutputExistsError):
497 executor.build_quantum_graph(pipeline)
499 def test_build_quantum_graph_nowhere_noskip_clobber(self):
500 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=True)
501 pipeline = Pipeline.fromFile(self.pipeline_file)
503 self.butler.put({"zero": 0}, "input")
505 graph = executor.make_quantum_graph(pipeline)
506 self.assertTrue(graph.isConnected)
507 self.assertEqual(len(graph), 2)
508 self.assertEqual({q.taskDef.label for q in graph.inputQuanta}, {"a"})
509 self.assertEqual({q.taskDef.label for q in graph.outputQuanta}, {"b"})
511 def test_make_quantum_graph_nowhere_noskip_clobber(self):
512 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=True)
513 pipeline = Pipeline.fromFile(self.pipeline_file)
514 self.butler.put({"zero": 0}, "input")
515 graph = executor.build_quantum_graph(pipeline)
516 self.assertEqual(len(graph), 2)
517 self.assertEqual(graph.quanta_by_task.keys(), {"a", "b"})
519 def test_build_quantum_graph_nowhere_noskip_clobber_conflict(self):
520 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=True)
521 pipeline = Pipeline.fromFile(self.pipeline_file)
523 self.butler.put({"zero": 0}, "input")
524 self.butler.put({"zero": 0}, "intermediate")
525 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
526 self.butler.put(TaskMetadata(), "a_metadata")
528 graph = executor.make_quantum_graph(pipeline)
529 self.assertTrue(graph.isConnected)
530 self.assertEqual(len(graph), 2)
531 self.assertEqual({q.taskDef.label for q in graph.inputQuanta}, {"a"})
532 self.assertEqual({q.taskDef.label for q in graph.outputQuanta}, {"b"})
534 def test_make_quantum_graph_nowhere_noskip_clobber_conflict(self):
535 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=True)
536 pipeline = Pipeline.fromFile(self.pipeline_file)
537 self.butler.put({"zero": 0}, "input")
538 self.butler.put({"zero": 0}, "intermediate")
539 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
540 self.butler.put(TaskMetadata(), "a_metadata")
541 graph = executor.build_quantum_graph(pipeline)
542 self.assertEqual(len(graph), 2)
543 self.assertEqual(graph.quanta_by_task.keys(), {"a", "b"})
545 def test_build_quantum_graph_nowhere_skipnone_clobber(self):
546 executor = SeparablePipelineExecutor(
547 self.butler,
548 skip_existing_in=[self.butler.run],
549 clobber_output=True,
550 )
551 pipeline = Pipeline.fromFile(self.pipeline_file)
553 self.butler.put({"zero": 0}, "input")
555 graph = executor.make_quantum_graph(pipeline)
556 self.assertTrue(graph.isConnected)
557 self.assertEqual(len(graph), 2)
558 self.assertEqual({q.taskDef.label for q in graph.inputQuanta}, {"a"})
559 self.assertEqual({q.taskDef.label for q in graph.outputQuanta}, {"b"})
561 def test_make_quantum_graph_nowhere_skipnone_clobber(self):
562 executor = SeparablePipelineExecutor(
563 self.butler,
564 skip_existing_in=[self.butler.run],
565 clobber_output=True,
566 )
567 pipeline = Pipeline.fromFile(self.pipeline_file)
568 self.butler.put({"zero": 0}, "input")
569 graph = executor.build_quantum_graph(pipeline)
570 self.assertEqual(len(graph), 2)
571 self.assertEqual(graph.quanta_by_task.keys(), {"a", "b"})
573 def test_make_quantum_graph_nowhere_skiptotal_clobber(self):
574 executor = SeparablePipelineExecutor(
575 self.butler,
576 skip_existing_in=[self.butler.run],
577 clobber_output=True,
578 )
579 pipeline = Pipeline.fromFile(self.pipeline_file)
581 self.butler.put({"zero": 0}, "input")
582 self.butler.put({"zero": 0}, "intermediate")
583 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
584 self.butler.put(TaskMetadata(), "a_metadata")
585 self.butler.put(lsst.pex.config.Config(), "a_config")
587 graph = executor.make_quantum_graph(pipeline)
588 self.assertTrue(graph.isConnected)
589 self.assertEqual(len(graph), 1)
590 self.assertEqual({q.taskDef.label for q in graph.inputQuanta}, {"b"})
591 self.assertEqual({q.taskDef.label for q in graph.outputQuanta}, {"b"})
593 def test_build_quantum_graph_nowhere_skiptotal_clobber(self):
594 executor = SeparablePipelineExecutor(
595 self.butler,
596 skip_existing_in=[self.butler.run],
597 clobber_output=True,
598 )
599 pipeline = Pipeline.fromFile(self.pipeline_file)
601 self.butler.put({"zero": 0}, "input")
602 self.butler.put({"zero": 0}, "intermediate")
603 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
604 self.butler.put(TaskMetadata(), "a_metadata")
605 self.butler.put(lsst.pex.config.Config(), "a_config")
607 graph = executor.make_quantum_graph(pipeline)
608 self.assertTrue(graph.isConnected)
609 self.assertEqual(len(graph), 1)
610 self.assertEqual({q.taskDef.label for q in graph.inputQuanta}, {"b"})
611 self.assertEqual({q.taskDef.label for q in graph.outputQuanta}, {"b"})
613 def test_make_quantum_graph_nowhere_skippartial_clobber(self):
614 executor = SeparablePipelineExecutor(
615 self.butler,
616 skip_existing_in=[self.butler.run],
617 clobber_output=True,
618 )
619 pipeline = Pipeline.fromFile(self.pipeline_file)
620 self.butler.put({"zero": 0}, "input")
621 self.butler.put({"zero": 0}, "intermediate")
622 graph = executor.build_quantum_graph(pipeline)
623 self.assertEqual(len(graph), 2)
624 self.assertEqual(graph.quanta_by_task.keys(), {"a", "b"})
626 def test_make_quantum_graph_noinput(self):
627 executor = SeparablePipelineExecutor(self.butler)
628 pipeline = Pipeline.fromFile(self.pipeline_file)
630 graph = executor.make_quantum_graph(pipeline)
631 self.assertEqual(len(graph), 0)
633 def test_build_quantum_graph_noinput(self):
634 executor = SeparablePipelineExecutor(self.butler)
635 pipeline = Pipeline.fromFile(self.pipeline_file)
637 graph = executor.build_quantum_graph(pipeline)
638 self.assertEqual(len(graph), 0)
640 def test_make_quantum_graph_alloutput_skip(self):
641 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=[self.butler.run])
642 pipeline = Pipeline.fromFile(self.pipeline_file)
644 butlerTests.addDatasetType(self.butler, "output", set(), "StructuredDataDict")
645 butlerTests.addDatasetType(self.butler, "b_log", set(), "ButlerLogRecords")
646 butlerTests.addDatasetType(self.butler, "b_metadata", set(), "TaskMetadata")
647 butlerTests.addDatasetType(self.butler, "b_config", set(), "Config")
649 self.butler.put({"zero": 0}, "input")
650 self.butler.put({"zero": 0}, "intermediate")
651 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
652 self.butler.put(TaskMetadata(), "a_metadata")
653 self.butler.put({"zero": 0}, "output")
654 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "b_log")
655 self.butler.put(TaskMetadata(), "b_metadata")
656 self.butler.put(lsst.pex.config.Config(), "a_config")
657 self.butler.put(lsst.pex.config.Config(), "b_config")
659 graph = executor.make_quantum_graph(pipeline)
660 self.assertEqual(len(graph), 0)
662 def test_build_quantum_graph_alloutput_skip(self):
663 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=[self.butler.run])
664 pipeline = Pipeline.fromFile(self.pipeline_file)
666 butlerTests.addDatasetType(self.butler, "output", set(), "StructuredDataDict")
667 butlerTests.addDatasetType(self.butler, "b_log", set(), "ButlerLogRecords")
668 butlerTests.addDatasetType(self.butler, "b_metadata", set(), "TaskMetadata")
669 butlerTests.addDatasetType(self.butler, "b_config", set(), "Config")
671 self.butler.put({"zero": 0}, "input")
672 self.butler.put({"zero": 0}, "intermediate")
673 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
674 self.butler.put(TaskMetadata(), "a_metadata")
675 self.butler.put({"zero": 0}, "output")
676 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "b_log")
677 self.butler.put(TaskMetadata(), "b_metadata")
678 self.butler.put(lsst.pex.config.Config(), "a_config")
679 self.butler.put(lsst.pex.config.Config(), "b_config")
681 graph = executor.build_quantum_graph(pipeline)
682 self.assertEqual(len(graph), 0)
684 def test_run_pipeline_noskip_noclobber_fullgraph(self):
685 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=False)
686 pipeline = Pipeline.fromFile(self.pipeline_file)
687 self.butler.put({"zero": 0}, "input")
688 graph = executor.make_quantum_graph(pipeline)
689 executor.pre_execute_qgraph(
690 graph,
691 register_dataset_types=True,
692 save_init_outputs=True,
693 save_versions=False,
694 )
696 executor.run_pipeline(graph)
697 self.butler.registry.refresh()
698 self.assertEqual(self.butler.get("intermediate"), {"zero": 0, "one": 1})
699 self.assertEqual(self.butler.get("output"), {"zero": 0, "one": 1, "two": 2})
701 def test_run_pipeline_noskip_noclobber_fullgraph_old(self):
702 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=False)
703 pipeline = Pipeline.fromFile(self.pipeline_file)
704 self.butler.put({"zero": 0}, "input")
705 graph = executor.build_quantum_graph(pipeline)
706 executor.pre_execute_qgraph(
707 graph,
708 register_dataset_types=True,
709 save_init_outputs=True,
710 save_versions=False,
711 )
713 executor.run_pipeline(graph)
714 self.butler.registry.refresh()
715 self.assertEqual(self.butler.get("intermediate"), {"zero": 0, "one": 1})
716 self.assertEqual(self.butler.get("output"), {"zero": 0, "one": 1, "two": 2})
718 def test_run_pipeline_noskip_noclobber_emptygraph_old(self):
719 old_repo_size = self.butler.registry.queryDatasets(...).count()
721 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=False)
722 graph = QuantumGraph({}, universe=self.butler.dimensions)
723 executor.pre_execute_qgraph(
724 graph,
725 register_dataset_types=True,
726 save_init_outputs=True,
727 save_versions=False,
728 )
730 executor.run_pipeline(graph)
731 self.butler.registry.refresh()
732 # Empty graph execution should do nothing.
733 self.assertEqual(self.butler.registry.queryDatasets(...).count(), old_repo_size)
735 def test_run_pipeline_noskip_noclobber_emptygraph(self):
736 old_repo_size = self.butler.registry.queryDatasets(...).count()
738 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=False)
739 graph = self.build_empty_quantum_graph()
740 executor.pre_execute_qgraph(
741 graph,
742 register_dataset_types=True,
743 save_init_outputs=True,
744 save_versions=False,
745 )
747 executor.run_pipeline(graph)
748 self.butler.registry.refresh()
749 # Empty graph execution should do nothing.
750 self.assertEqual(self.butler.registry.queryDatasets(...).count(), old_repo_size)
752 def test_run_pipeline_skipnone_noclobber_old(self):
753 executor = SeparablePipelineExecutor(
754 self.butler,
755 skip_existing_in=[self.butler.run],
756 clobber_output=False,
757 )
758 pipeline = Pipeline.fromFile(self.pipeline_file)
759 self.butler.put({"zero": 0}, "input")
760 graph = executor.make_quantum_graph(pipeline)
761 executor.pre_execute_qgraph(
762 graph,
763 register_dataset_types=True,
764 save_init_outputs=True,
765 save_versions=False,
766 )
768 executor.run_pipeline(graph)
769 self.butler.registry.refresh()
770 self.assertEqual(self.butler.get("intermediate"), {"zero": 0, "one": 1})
771 self.assertEqual(self.butler.get("output"), {"zero": 0, "one": 1, "two": 2})
773 def test_run_pipeline_skipnone_noclobber(self):
774 executor = SeparablePipelineExecutor(
775 self.butler,
776 skip_existing_in=[self.butler.run],
777 clobber_output=False,
778 )
779 pipeline = Pipeline.fromFile(self.pipeline_file)
780 self.butler.put({"zero": 0}, "input")
781 graph = executor.build_quantum_graph(pipeline)
782 executor.pre_execute_qgraph(
783 graph,
784 register_dataset_types=True,
785 save_init_outputs=True,
786 save_versions=False,
787 )
789 executor.run_pipeline(graph)
790 self.butler.registry.refresh()
791 self.assertEqual(self.butler.get("intermediate"), {"zero": 0, "one": 1})
792 self.assertEqual(self.butler.get("output"), {"zero": 0, "one": 1, "two": 2})
794 def test_run_pipeline_skiptotal_noclobber_old(self):
795 executor = SeparablePipelineExecutor(
796 self.butler,
797 skip_existing_in=[self.butler.run],
798 clobber_output=False,
799 )
800 pipeline = Pipeline.fromFile(self.pipeline_file)
801 self.butler.put({"zero": 0}, "input")
802 self.butler.put({"zero": 0}, "intermediate")
803 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
804 self.butler.put(TaskMetadata(), "a_metadata")
805 self.butler.put(lsst.pex.config.Config(), "a_config")
806 graph = executor.make_quantum_graph(pipeline)
807 executor.pre_execute_qgraph(
808 graph,
809 register_dataset_types=True,
810 save_init_outputs=True,
811 save_versions=False,
812 )
814 executor.run_pipeline(graph)
815 self.butler.registry.refresh()
816 self.assertEqual(self.butler.get("output"), {"zero": 0, "two": 2})
818 def test_run_pipeline_skiptotal_noclobber(self):
819 executor = SeparablePipelineExecutor(
820 self.butler,
821 skip_existing_in=[self.butler.run],
822 clobber_output=False,
823 )
824 pipeline = Pipeline.fromFile(self.pipeline_file)
825 self.butler.put({"zero": 0}, "input")
826 self.butler.put({"zero": 0}, "intermediate")
827 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
828 self.butler.put(TaskMetadata(), "a_metadata")
829 self.butler.put(lsst.pex.config.Config(), "a_config")
830 graph = executor.build_quantum_graph(pipeline)
831 executor.pre_execute_qgraph(
832 graph,
833 register_dataset_types=True,
834 save_init_outputs=True,
835 save_versions=False,
836 )
838 executor.run_pipeline(graph)
839 self.butler.registry.refresh()
840 self.assertEqual(self.butler.get("output"), {"zero": 0, "two": 2})
842 def test_run_pipeline_noskip_clobber_connected_old(self):
843 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=True)
844 pipeline = Pipeline.fromFile(self.pipeline_file)
845 self.butler.put({"zero": 0}, "input")
846 graph = executor.make_quantum_graph(pipeline)
847 executor.pre_execute_qgraph(
848 graph,
849 register_dataset_types=True,
850 save_init_outputs=True,
851 save_versions=False,
852 )
854 executor.run_pipeline(graph)
855 self.butler.registry.refresh()
856 self.assertEqual(self.butler.get("intermediate"), {"zero": 0, "one": 1})
857 self.assertEqual(self.butler.get("output"), {"zero": 0, "one": 1, "two": 2})
859 def test_run_pipeline_noskip_clobber_connected(self):
860 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=True)
861 pipeline = Pipeline.fromFile(self.pipeline_file)
862 self.butler.put({"zero": 0}, "input")
863 graph = executor.build_quantum_graph(pipeline)
864 executor.pre_execute_qgraph(
865 graph,
866 register_dataset_types=True,
867 save_init_outputs=True,
868 save_versions=False,
869 )
871 executor.run_pipeline(graph)
872 self.butler.registry.refresh()
873 self.assertEqual(self.butler.get("intermediate"), {"zero": 0, "one": 1})
874 self.assertEqual(self.butler.get("output"), {"zero": 0, "one": 1, "two": 2})
876 def test_run_pipeline_noskip_clobber_unconnected_old(self):
877 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=True)
878 pipeline = Pipeline.fromFile(self.pipeline_file)
879 self.butler.put({"zero": 0}, "input")
880 self.butler.put({"zero": 0}, "intermediate")
881 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
882 self.butler.put(TaskMetadata(), "a_metadata")
883 graph = executor.make_quantum_graph(pipeline)
884 executor.pre_execute_qgraph(
885 graph,
886 register_dataset_types=True,
887 save_init_outputs=True,
888 save_versions=False,
889 )
891 executor.run_pipeline(graph)
892 self.butler.registry.refresh()
893 self.assertEqual(self.butler.get("intermediate"), {"zero": 0, "one": 1})
894 # The value of output is undefined; it depends on which task ran first.
895 self.assertTrue(self.butler.exists("output", {}))
897 def test_run_pipeline_noskip_clobber_unconnected(self):
898 executor = SeparablePipelineExecutor(self.butler, skip_existing_in=None, clobber_output=True)
899 pipeline = Pipeline.fromFile(self.pipeline_file)
900 self.butler.put({"zero": 0}, "input")
901 self.butler.put({"zero": 0}, "intermediate")
902 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
903 self.butler.put(TaskMetadata(), "a_metadata")
904 graph = executor.build_quantum_graph(pipeline)
905 executor.pre_execute_qgraph(
906 graph,
907 register_dataset_types=True,
908 save_init_outputs=True,
909 save_versions=False,
910 )
912 executor.run_pipeline(graph)
913 self.butler.registry.refresh()
914 self.assertEqual(self.butler.get("intermediate"), {"zero": 0, "one": 1})
915 # The value of output is undefined; it depends on which task ran first.
916 self.assertTrue(self.butler.exists("output", {}))
918 def test_run_pipeline_skipnone_clobber_old(self):
919 executor = SeparablePipelineExecutor(
920 self.butler,
921 skip_existing_in=[self.butler.run],
922 clobber_output=True,
923 )
924 pipeline = Pipeline.fromFile(self.pipeline_file)
925 self.butler.put({"zero": 0}, "input")
926 graph = executor.make_quantum_graph(pipeline)
927 executor.pre_execute_qgraph(
928 graph,
929 register_dataset_types=True,
930 save_init_outputs=True,
931 save_versions=False,
932 )
934 executor.run_pipeline(graph)
935 self.butler.registry.refresh()
936 self.assertEqual(self.butler.get("intermediate"), {"zero": 0, "one": 1})
937 self.assertEqual(self.butler.get("output"), {"zero": 0, "one": 1, "two": 2})
939 def test_run_pipeline_skipnone_clobber(self):
940 executor = SeparablePipelineExecutor(
941 self.butler,
942 skip_existing_in=[self.butler.run],
943 clobber_output=True,
944 )
945 pipeline = Pipeline.fromFile(self.pipeline_file)
946 self.butler.put({"zero": 0}, "input")
947 graph = executor.build_quantum_graph(pipeline)
948 executor.pre_execute_qgraph(
949 graph,
950 register_dataset_types=True,
951 save_init_outputs=True,
952 save_versions=False,
953 )
955 executor.run_pipeline(graph)
956 self.butler.registry.refresh()
957 self.assertEqual(self.butler.get("intermediate"), {"zero": 0, "one": 1})
958 self.assertEqual(self.butler.get("output"), {"zero": 0, "one": 1, "two": 2})
960 def test_run_pipeline_skiptotal_clobber_connected_old(self):
961 executor = SeparablePipelineExecutor(
962 self.butler,
963 skip_existing_in=[self.butler.run],
964 clobber_output=True,
965 )
966 pipeline = Pipeline.fromFile(self.pipeline_file)
967 self.butler.put({"zero": 0}, "input")
968 self.butler.put({"zero": 0}, "intermediate")
969 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
970 self.butler.put(TaskMetadata(), "a_metadata")
971 self.butler.put(lsst.pex.config.Config(), "a_config")
972 graph = executor.make_quantum_graph(pipeline)
973 executor.pre_execute_qgraph(
974 graph,
975 register_dataset_types=True,
976 save_init_outputs=True,
977 save_versions=False,
978 )
980 executor.run_pipeline(graph)
981 self.butler.registry.refresh()
982 self.assertEqual(self.butler.get("output"), {"zero": 0, "two": 2})
984 def test_run_pipeline_skiptotal_clobber_connected(self):
985 executor = SeparablePipelineExecutor(
986 self.butler,
987 skip_existing_in=[self.butler.run],
988 clobber_output=True,
989 )
990 pipeline = Pipeline.fromFile(self.pipeline_file)
991 self.butler.put({"zero": 0}, "input")
992 self.butler.put({"zero": 0}, "intermediate")
993 self.butler.put(lsst.daf.butler.ButlerLogRecords.from_records([]), "a_log")
994 self.butler.put(TaskMetadata(), "a_metadata")
995 self.butler.put(lsst.pex.config.Config(), "a_config")
996 graph = executor.build_quantum_graph(pipeline)
997 executor.pre_execute_qgraph(
998 graph,
999 register_dataset_types=True,
1000 save_init_outputs=True,
1001 save_versions=False,
1002 )
1004 executor.run_pipeline(graph)
1005 self.butler.registry.refresh()
1006 self.assertEqual(self.butler.get("output"), {"zero": 0, "two": 2})
1008 def test_run_pipeline_skippartial_clobber_unconnected_old(self):
1009 executor = SeparablePipelineExecutor(
1010 self.butler,
1011 skip_existing_in=[self.butler.run],
1012 clobber_output=True,
1013 )
1014 pipeline = Pipeline.fromFile(self.pipeline_file)
1015 self.butler.put({"zero": 0}, "input")
1016 self.butler.put({"zero": 0}, "intermediate")
1017 graph = executor.make_quantum_graph(pipeline)
1018 executor.pre_execute_qgraph(
1019 graph,
1020 register_dataset_types=True,
1021 save_init_outputs=True,
1022 save_versions=False,
1023 )
1025 executor.run_pipeline(graph)
1026 self.butler.registry.refresh()
1027 self.assertEqual(self.butler.get("intermediate"), {"zero": 0, "one": 1})
1028 # The value of output is undefined; it depends on which task ran first.
1029 self.assertTrue(self.butler.exists("output", {}))
1031 def test_run_pipeline_skippartial_clobber_unconnected(self):
1032 executor = SeparablePipelineExecutor(
1033 self.butler,
1034 skip_existing_in=[self.butler.run],
1035 clobber_output=True,
1036 )
1037 pipeline = Pipeline.fromFile(self.pipeline_file)
1038 self.butler.put({"zero": 0}, "input")
1039 self.butler.put({"zero": 0}, "intermediate")
1040 graph = executor.build_quantum_graph(pipeline)
1041 executor.pre_execute_qgraph(
1042 graph,
1043 register_dataset_types=True,
1044 save_init_outputs=True,
1045 save_versions=False,
1046 )
1047 executor.run_pipeline(graph)
1048 self.butler.registry.refresh()
1049 self.assertEqual(self.butler.get("intermediate"), {"zero": 0, "one": 1})
1050 # The value of output is undefined; it depends on which task ran first.
1051 self.assertTrue(self.butler.exists("output", {}))
1054class MemoryTester(lsst.utils.tests.MemoryTestCase):
1055 """Generic test for file leaks."""
1058def setup_module(module):
1059 """Set up the module for pytest.
1061 Parameters
1062 ----------
1063 module : `~types.ModuleType`
1064 Module to set up.
1065 """
1066 lsst.utils.tests.init()
1069if __name__ == "__main__":
1070 lsst.utils.tests.init()
1071 unittest.main()