diff --git a/FWCore/Integration/test/unit_test_outputs/testProducerWithPsetDesc_cfi.py b/FWCore/Integration/test/unit_test_outputs/testProducerWithPsetDesc_cfi.py index 7fba48e18a9a1..be4ba045a19bb 100644 --- a/FWCore/Integration/test/unit_test_outputs/testProducerWithPsetDesc_cfi.py +++ b/FWCore/Integration/test/unit_test_outputs/testProducerWithPsetDesc_cfi.py @@ -187,10 +187,25 @@ ouDrinks = cms.untracked.uint32(5) ), test104 = cms.untracked.VPSet( - cms.PSet() + cms.PSet(), + template = cms.PSetTemplate( + Drinks = cms.uint32(5), + ndoDrinks = cms.optional.uint32, + ndouDrinks = cms.optional.untracked.uint32, + oDrinks = cms.uint32(5), + ouDrinks = cms.untracked.uint32(5), + uDrinks = cms.untracked.uint32(5) + ) ), test105 = cms.untracked.VPSet( - ), + template = cms.PSetTemplate( + Drinks = cms.uint32(5), + ndoDrinks = cms.optional.uint32, + ndouDrinks = cms.optional.untracked.uint32, + oDrinks = cms.uint32(5), + ouDrinks = cms.untracked.uint32(5), + uDrinks = cms.untracked.uint32(5) + ) ), test1 = cms.double(0.1), test2 = cms.double(0.2), testA = cms.string('fooA'), @@ -228,6 +243,35 @@ xvalue = cms.int32(17) ) ) + ), + template = cms.PSetTemplate( + Drinks = cms.uint32(5), + anotherVPSet = cms.VPSet( + cms.PSet(), + cms.PSet( + xvalue = cms.int32(100) + ), + template = cms.PSetTemplate( + xvalue = cms.int32(7) + ) + ), + ndoDrinks = cms.optional.uint32, + ndouDrinks = cms.optional.untracked.uint32, + oDrinks = cms.uint32(5), + ouDrinks = cms.untracked.uint32(5), + testDeeplyNested = cms.PSet( + bswitch = cms.bool(False), + bvalue1 = cms.double(101), + bvalue2 = cms.double(101), + iswitch = cms.int32(1), + ivalue1 = cms.double(101), + ivalue2 = cms.untracked.double(101), + sswitch = cms.string('1'), + svalue1 = cms.double(101), + svalue2 = cms.double(101), + testint = cms.int32(1000) + ), + uDrinks = cms.untracked.uint32(5) ) ), subpset = cms.PSet( @@ -369,6 +413,13 @@ cms.PSet( type = cms.string('edmtestAnotherMakerWithRecursivePlugin'), value = cms.int32(11) + ), + template = cms.PSetTemplate( + pluginRecursive = cms.PSet( + + ), + type = cms.string('edmtestAnotherMakerWithRecursivePlugin'), + value = cms.int32(5) ) ), mightGet = cms.optional.untracked.vstring diff --git a/FWCore/ParameterSet/interface/ParameterSetDescription.h b/FWCore/ParameterSet/interface/ParameterSetDescription.h index 17f422a39dc76..79bacc2bf71ae 100644 --- a/FWCore/ParameterSet/interface/ParameterSetDescription.h +++ b/FWCore/ParameterSet/interface/ParameterSetDescription.h @@ -296,6 +296,7 @@ namespace edm { const_iterator end() const { return entries_.end(); } + bool empty() const noexcept { return entries_.empty(); } // Better performance if space is reserved for the number of // top level parameters before any are added. void reserve(SetDescriptionEntries::size_type n) { entries_.reserve(n); } diff --git a/FWCore/ParameterSet/python/Config.py b/FWCore/ParameterSet/python/Config.py index c729bd14f56c9..e87e8ba4433a1 100644 --- a/FWCore/ParameterSet/python/Config.py +++ b/FWCore/ParameterSet/python/Config.py @@ -1059,9 +1059,9 @@ def dumpPython(self, options=PrintOptions()) -> str: specialImportRegistry._reset() header = "import FWCore.ParameterSet.Config as cms" result = "process = cms.Process(\""+self.__name+"\")\n\n" - if self.source_(): + if not self.source_() is None: result += "process.source = "+self.source_().dumpPython(options) - if self.looper_(): + if not self.looper_() is None: result += "process.looper = "+self.looper_().dumpPython() result+=self._dumpPythonList(self.psets, options) result+=self._dumpPythonList(self.vpsets, options) @@ -1103,10 +1103,10 @@ def splitPython(self, options:PrintOptions = PrintOptions()) -> dict: result = 'process = cms.Process("' + self.__name + '")\n\n' - if self.source_(): + if not self.source_() is None: parts['source'] = (None, 'source = ' + self.source_().dumpPython(options)) - if self.looper_(): + if not self.looper_() is None: parts['looper'] = (None, 'looper = ' + self.looper_().dumpPython()) parts.update(self._splitPythonList('psets', self.psets, options)) diff --git a/FWCore/ParameterSet/python/Mixins.py b/FWCore/ParameterSet/python/Mixins.py index 28707628d5d62..4221b70149087 100644 --- a/FWCore/ParameterSet/python/Mixins.py +++ b/FWCore/ParameterSet/python/Mixins.py @@ -268,6 +268,8 @@ def __setParameters(self,parameters): self.__addParameter(name, value) if v is not None: self.__validator=v + def hasNoParameters(self) -> bool: + return len(self.__parameterNames) == 0 def __setattr__(self,name:str,value): #since labels are not supposed to have underscores at the beginning # I will assume that if we have such then we are setting an internal variable @@ -697,7 +699,9 @@ def dumpPython(self, options:PrintOptions=PrintOptions()) -> str: if n>=256: #wrap in a tuple since they don't have a size constraint result+=" (" + wroteAtLeastOne = False for i, v in enumerate(self): + wroteAtLeastOne = True if i == 0: if n>nPerLine: result += '\n'+options.indentation() else: @@ -710,7 +714,7 @@ def dumpPython(self, options:PrintOptions=PrintOptions()) -> str: result +=' ) ' moreArgs = self._additionalInitArguments(options) if moreArgs: - if i > 0: + if wroteAtLeastOne: result += ', \n' + options.indentation() result += moreArgs if n>nPerLine: diff --git a/FWCore/ParameterSet/python/Types.py b/FWCore/ParameterSet/python/Types.py index 98e5e53832f95..87ae772120be4 100644 --- a/FWCore/ParameterSet/python/Types.py +++ b/FWCore/ParameterSet/python/Types.py @@ -197,6 +197,8 @@ def __init__(self, *args, **kargs): def __call__(self, value): self.__dict__ return self._pset.clone(**value) + def _isEmpty(self) -> bool: + return self._pset.hasNoParameters() def _isValid(self, value) -> bool: return isinstance(value,dict) or isinstance(value, PSet) def dumpPython(self, options:PrintOptions=PrintOptions()) -> str: @@ -1459,7 +1461,7 @@ def template(self): def copy(self): return copy.copy(self) def _additionalInitArguments(self, options): - if self._template: + if self._template and not self._template._isEmpty(): #NOTE: PSetTemplate.dumpPython does not include the 'cms.' part return 'template = cms.'+self._template.dumpPython(options) return None @@ -2420,6 +2422,10 @@ def testVPSetWithTemplate(self): ptest = VPSet(PSet(b=int32(3)), template = PSetTemplate(a=required.int32)) self.assertEqual(len(ptest), 1) self.assertEqual(ptest[0].b.value(), 3) + self.assertEqual(ptest.dumpPython(),"cms.VPSet(cms.PSet(\n b = cms.int32(3)\n), \ntemplate = cms.PSetTemplate(\n a = cms.required.int32\n))" + ) + ptest = VPSet(template=PSetTemplate()) + self.assertEqual(ptest.dumpPython(),"cms.VPSet()") #will inject `a=required.int32` into the PSet when starting from a dict() ptest = VPSet(dict(), template = PSetTemplate(a=required.int32)) self.assertEqual(len(ptest), 1) diff --git a/FWCore/ParameterSet/src/ParameterDescription.cc b/FWCore/ParameterSet/src/ParameterDescription.cc index 88eb3eb3bd6df..6e83f8173a33c 100644 --- a/FWCore/ParameterSet/src/ParameterDescription.cc +++ b/FWCore/ParameterSet/src/ParameterDescription.cc @@ -365,14 +365,23 @@ namespace edm { int indentation, CfiOptions& options) const { bool nextOneStartsWithAComma = false; - for_all(vPset_, - std::bind(&writeOneElementToCfi, - std::placeholders::_1, - std::ref(os), - indentation, - options, - std::ref(nextOneStartsWithAComma))); - os << "\n"; + for (auto const& p : vPset_) { + writeOneElementToCfi(p, os, indentation, options, nextOneStartsWithAComma); + } + if (cfi::shouldWriteUntyped(options) or psetDesc_->empty() or psetDesc_->anythingAllowed() or + psetDesc_->isUnknown()) { + os << "\n"; + } else { + if (not vPset_.empty()) { + os << ","; + } + os << "\n"; + printSpaces(os, indentation + 2); + CfiOptions fullOp = cfi::Typed{}; + os << "template = cms.PSetTemplate("; + psetDesc_->writeCfi(os, false, indentation + 4, fullOp); + os << ")\n"; + } printSpaces(os, indentation); } diff --git a/FWCore/ParameterSet/test/test_catch_ParameterSetDescription.cc b/FWCore/ParameterSet/test/test_catch_ParameterSetDescription.cc index 00b4819bd936e..280346699780c 100644 --- a/FWCore/ParameterSet/test/test_catch_ParameterSetDescription.cc +++ b/FWCore/ParameterSet/test/test_catch_ParameterSetDescription.cc @@ -1398,6 +1398,36 @@ vp = cms.VPSet( p = cms.PSet(), vp = cms.VPSet( ) + ), + template = cms.PSetTemplate( + i = cms.required.int32, + vi = cms.required.vint32, + ui = cms.required.uint32, + vui = cms.required.vuint32, + l = cms.required.int64, + vl = cms.required.vint64, + ul = cms.required.uint64, + vul = cms.required.vuint64, + b = cms.required.bool, + d = cms.required.double, + vd = cms.required.vdouble, + s = cms.required.string, + vs = cms.required.vstring, + t = cms.required.InputTag, + vt = cms.required.VInputTag, + et = cms.required.ESInputTag, + vet = cms.required.VESInputTag, + f = cms.required.FileInPath, + e = cms.required.EventID, + ve = cms.required.VEventID, + L = cms.required.LuminosityBlockID, + vL = cms.required.VLuminosityBlockID, + er = cms.required.EventRange, + ver = cms.required.VEventRange, + Lr = cms.required.LuminosityBlockRange, + vLr = cms.required.VLuminosityBlockRange, + p = cms.PSet(), + vp = cms.required.VPSet ) ) )-";