From e0d39f3428b026249f8097a60a6cb3fd82b47261 Mon Sep 17 00:00:00 2001 From: Matti Kortelainen Date: Fri, 18 Feb 2022 21:04:38 +0100 Subject: [PATCH] Wrap Process into more constraining class for ProcessAccelerator.apply() --- FWCore/ParameterSet/python/Config.py | 56 ++++++++++++++++--- .../CUDACore/python/ProcessAcceleratorCUDA.py | 3 +- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/FWCore/ParameterSet/python/Config.py b/FWCore/ParameterSet/python/Config.py index 12138cda850ae..0533e1bc96af9 100644 --- a/FWCore/ParameterSet/python/Config.py +++ b/FWCore/ParameterSet/python/Config.py @@ -1480,8 +1480,9 @@ def handleProcessAccelerators(self, parameterSet): parameterSet.addVString(False, "@selected_accelerators", selectedAccelerators) # Customize + wrapped = ProcessForProcessAccelerator(self) for acc in self.__dict__['_Process__accelerators'].values(): - acc.apply(self, selectedAccelerators) + acc.apply(wrapped, selectedAccelerators) def prefer(self, esmodule,*args,**kargs): """Prefer this ES source or producer. The argument can @@ -1883,10 +1884,14 @@ class ProcessAccelerator(_ConfigureComponent,_Unlabelable): specific customization can be applied to the Process on a worker node at the point where the python configuration is serialized for C++. - The customizations must touch only untracked parameters. Each - deriving class should have a specific unit test enabling all - combinations of accelerators and assert that the configuration - hash does not change.""" + The customization must not change the configuration hash. To + enforce this reuirement, the customization gets a + ProcessForProcessAccelerator wrapper that gives access to only + those parts of the configuration that can be changed. Nevertheless + it would be good to have specific unit test for each deriving + class to ensure that all combinations of the enabled accelerators + give the same configuration hash. + """ def __init__(self): pass def _place(self, name, proc): @@ -1925,6 +1930,28 @@ def apply(self, process, accelerators): """ pass +class ProcessForProcessAccelerator(object): + """This class is inteded to wrap the Process object to constrain the + available functionality for ProcessAccelerator.apply()""" + def __init__(self, process): + self.__process = process + def __getattr__(self, label): + value = getattr(self.__process, label) + if not isinstance(value, Service): + raise TypeError("ProcessAccelerator.apply() can get only Services. Tried to get {} with label {}".format(str(type(value)), label)) + return value + def __setattr__(self, label, value): + if label == "_ProcessForProcessAccelerator__process": + super().__setattr__(label, value) + else: + if not isinstance(value, Service): + raise TypeError("ProcessAccelerator.apply() can only set Services. Tried to set {} with label {}".format(str(type(value)), label)) + setattr(self.__process, label, value) + def add_(self, value): + if not isinstance(value, Service): + raise TypeError("ProcessAccelerator.apply() can only add Services. Tried to set {} with label {}".format(str(type(value)), label)) + self.__process.add_(value) + # Need to be a module-level function for the configuration with a # SwitchProducer to be pickleable. def _switchproducer_test2_case1(accelerators): @@ -2061,8 +2088,7 @@ def labels(self): def enabledLabels(self): return self._enabled def apply(self, process, accelerators): - process.acceleratorTestProducer = EDProducer("AcceleratorTestProducer") - process.acceleratorTestPath = Path(process.acceleratorTestProducer) + process.AcceleratorTestService = Service("AcceleratorTestService") specialImportRegistry.registerSpecialImportForType(ProcessAcceleratorTest, "from test import ProcessAcceleratorTest") class ProcessAcceleratorTest2(ProcessAccelerator): @@ -3944,6 +3970,20 @@ def testProcessFragment(self): p = Process('PROCESS') p.extend(f) self.assertTrue(hasattr(p,'fltr')) + def testProcessForProcessAccelerator(self): + proc = Process("TEST") + p = ProcessForProcessAccelerator(proc) + p.TestService = Service("TestService") + self.assertTrue(hasattr(proc, "TestService")) + self.assertEqual(proc.TestService.type_(), "TestService") + self.assertRaises(TypeError, setattr, p, "a", EDProducer("Foo")) + p.add_(Service("TestServiceTwo")) + self.assertTrue(hasattr(proc, "TestServiceTwo")) + self.assertEqual(proc.TestServiceTwo.type_(), "TestServiceTwo") + p.TestService.foo = untracked.uint32(42) + self.assertEqual(proc.TestService.foo.value(), 42) + proc.mod = EDProducer("Producer") + self.assertRaises(TypeError, getattr, p, "mod") def testProcessAccelerator(self): proc = Process("TEST") p = TestMakePSet() @@ -4012,7 +4052,7 @@ def testProcessAccelerator(self): self.assertEqual(["*"], p.values["options"][1].values["accelerators"][1]) self.assertFalse(p.values["options"][1].values["accelerators"][0]) self.assertTrue(["anothertest3", "cpu", "test1", "test2"], p.values["@selected_accelerators"][1]) - self.assertEqual((True, "AcceleratorTestProducer"), p.values["acceleratorTestProducer"][1].values["@module_type"]) + self.assertEqual("AcceleratorTestService", p.values["services"][1][0].values["@service_type"][1]) self.assertFalse(p.values["@available_accelerators"][0]) self.assertTrue(["anothertest3", "cpu", "test1", "test2"], p.values["@available_accelerators"][1]) diff --git a/HeterogeneousCore/CUDACore/python/ProcessAcceleratorCUDA.py b/HeterogeneousCore/CUDACore/python/ProcessAcceleratorCUDA.py index 3a1990bcc2824..19aca3fc6e4ce 100644 --- a/HeterogeneousCore/CUDACore/python/ProcessAcceleratorCUDA.py +++ b/HeterogeneousCore/CUDACore/python/ProcessAcceleratorCUDA.py @@ -16,7 +16,8 @@ def enabledLabels(self): return [] def apply(self, process, accelerators): if not hasattr(process, "CUDAService"): - process.load("HeterogeneousCore.CUDAServices.CUDAService_cfi") + from HeterogeneousCore.CUDAServices.CUDAService_cfi import CUDAService + process.add_(CUDAService) if self._label in accelerators: process.CUDAService.enabled = True