Skip to content

Commit

Permalink
Alternative python module configuration syntax
Browse files Browse the repository at this point in the history
Generates a new python file from the C++ configuration description
which describes the default. This can be imported to allow a
more simplified module configuration syntax.
  • Loading branch information
Dr15Jones committed Feb 29, 2024
1 parent d9a62e0 commit acefa33
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 12 deletions.
5 changes: 3 additions & 2 deletions FWCore/Integration/test/check_empty_event_cfg.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import FWCore.ParameterSet.Config as cms
from FWCore.Modules.modules import EventContentAnalyzer, EmptySource

process = cms.Process("EMPTY")

process.test = cms.EDAnalyzer("EventContentAnalyzer", listPathStatus = cms.untracked.bool(True))
process.test = EventContentAnalyzer(listPathStatus = True)

process.e = cms.EndPath(process.test)

#process.out = cms.OutputModule("AsciiOutputModule")
#process.e2 = cms.EndPath(process.out)

process.source = cms.Source("EmptySource")
process.source = EmptySource()

process.maxEvents.input = 1
24 changes: 14 additions & 10 deletions FWCore/Integration/test/delayedreader_throw_cfg.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import FWCore.ParameterSet.Config as cms
from FWCore.Framework.modules import AddIntsProducer, IntProductFilter
from FWCore.Modules.modules import AsciiOutputModule
from FWCore.Integration.modules import DelayedReaderThrowingSource

process = cms.Process("TEST")

process.source = cms.Source("DelayedReaderThrowingSource", labels = cms.untracked.vstring("test", "test2", "test3"))
process.source = DelayedReaderThrowingSource( labels = ["test", "test2", "test3"])

process.getter = cms.EDProducer("AddIntsProducer", labels = cms.VInputTag(cms.InputTag("test","","INPUTTEST")))
process.onPath = cms.EDProducer("AddIntsProducer", labels = cms.VInputTag(cms.InputTag("test2", "", "INPUTTEST"), cms.InputTag("getter", "other")))
process.f1 = cms.EDFilter("IntProductFilter", label = cms.InputTag("onPath"), shouldProduce = cms.bool(True))
process.f2 = cms.EDFilter("IntProductFilter", label = cms.InputTag("onPath"), shouldProduce = cms.bool(True))
process.inFront = cms.EDFilter("IntProductFilter", label = cms.InputTag("test3"))
process.getter = AddIntsProducer(labels = [("test","","INPUTTEST")])
process.onPath = AddIntsProducer(labels = [("test2", "", "INPUTTEST"), ("getter", "other")])
process.f1 = IntProductFilter( label = "onPath", shouldProduce = True)
process.f2 = IntProductFilter( label = "onPath", shouldProduce = True)
process.inFront = IntProductFilter( label = "test3")

process.p1 = cms.Path(process.inFront+process.onPath+process.f1+process.f2)
process.p3 = cms.Path(process.onPath+process.f1, cms.Task(process.getter))

process.p2 = cms.Path(process.onPath+process.f2)


#process.dump = cms.EDAnalyzer("EventContentAnalyzer")
#from FWCore.Modules.modules import EventContentAnalyzer import *
#process.dump = EventContentAnalyzer()
#process.p = cms.Path(process.dump)

process.out = cms.OutputModule("AsciiOutputModule")
process.out = AsciiOutputModule()
process.e = cms.EndPath(process.out, cms.Task(process.getter))

process.maxEvents.input = 1

#process.add_(cms.Service("Tracer"))
#from FWCore.Services.modules import Tracer
#process.add_(Tracer())
19 changes: 19 additions & 0 deletions FWCore/ParameterSet/bin/edmWriteConfigs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
#include <utility>
#include "FWCore/Utilities/interface/Signal.h"
#include <sstream>
#include <fstream>
#include <filesystem>

static char const* const kHelpOpt = "help";
static char const* const kHelpCommandOpt = "help,h";
Expand Down Expand Up @@ -133,6 +135,20 @@ namespace {
pluginNames.push_back(nameAndType.first);
}
}

void writeModulesFile() {
if (std::filesystem::exists(std::filesystem::current_path() / "modules.py"))
return;
std::array<char, L_tmpnam> buffer;
std::tmpnam(buffer.data());
std::ofstream file{buffer.data()};

file << "from FWCore.ParameterSet.ModulesProxy import _setupProxies\n"
"locals().update(_setupProxies(__file__))\n";
file.close();
std::filesystem::copy(buffer.data(), "modules.py");
std::filesystem::remove(buffer.data());
}
} // namespace

int main(int argc, char** argv) try {
Expand Down Expand Up @@ -287,6 +303,9 @@ int main(int argc, char** argv) try {

std::set<std::string> usedCfiFileNames;
edm::for_all(pluginNames, std::bind(&writeCfisForPlugin, _1, factory, std::ref(usedCfiFileNames)));
if (not pluginNames.empty()) {
writeModulesFile();
}
});
} catch (cms::Exception& iException) {
if (!library.empty()) {
Expand Down
2 changes: 2 additions & 0 deletions FWCore/ParameterSet/interface/ConfigurationDescriptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ namespace edm {
std::string const& pluginName,
std::set<std::string>& usedCfiFileNames);

void writeClassFile(ParameterSetDescription const&) const;

void printForLabel(std::pair<std::string, ParameterSetDescription> const& labelAndDesc,
std::ostream& os,
std::string const& moduleLabel,
Expand Down
24 changes: 24 additions & 0 deletions FWCore/ParameterSet/python/ModulesProxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import os
from os.path import sep, join
import importlib

class _ModuleProxy (object):
def __init__(self, package, name):
self._package = package
self._name = name
self._caller = None
def __call__(self,**kwargs):
if not self._caller:
self._caller = getattr(importlib.import_module(self._package+'.'+self._name),self._name)
return self._caller(**kwargs)


def _setupProxies(fullName):
_cmssw_package_name='.'.join(fullName.split(sep)[-3:-1])
basename = fullName.split(sep)[-1]
pathname = fullName[:-1*len(basename)]
proxies = dict()
for filename in ( x for x in os.listdir(pathname) if (len(x) > 3 and x[-3:] == '.py' and x != basename and ((len(x) < 6) or (x[-6:] != 'cfi.py')))):
name = filename[:-3]
proxies[name] = _ModuleProxy(_cmssw_package_name, name)
return proxies
48 changes: 48 additions & 0 deletions FWCore/ParameterSet/src/ConfigurationDescriptions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,54 @@ namespace edm {
std::cref(baseType_),
std::cref(pluginName_),
std::ref(usedCfiFileNames)));
if (defaultDescDefined_) {
writeClassFile(defaultDesc_);
} else if (descriptions_.size() == 1) {
writeClassFile(descriptions_.begin()->second);
}
}

void ConfigurationDescriptions::writeClassFile(ParameterSetDescription const& iDesc) const {
std::string pluginName = pluginName_;
auto found = pluginName.find("::");
while (found != std::string::npos) {
pluginName.replace(found, 2, "_");
found = pluginName.find("::");
}
//Symbols that can appear in our plugin names but can't in python function names
const std::string toReplace("@<>,");
found = pluginName.find_first_of(toReplace);
while (found != std::string::npos) {
pluginName.replace(found, 1, "_");
found = pluginName.find_first_of(toReplace, found);
}

std::string fileName = pluginName + ".py";
std::ofstream outFile(fileName.c_str());
if (outFile.fail()) {
edm::Exception ex(edm::errors::LogicError, "Creating class file failed.\n");
ex << "Opening a file '" << fileName << "' failed.\n";
ex << "Error code from errno " << errno << ": " << std::strerror(errno) << "\n";

ex.addContext("Executing function ConfigurationDescriptions::writeDefault");
throw ex;
}
outFile << "import FWCore.ParameterSet.Config as cms\n\n";
outFile << "def " << pluginName
<< "(**kwargs):\n"
" mod = cms."
<< baseType_ << "('" << pluginName_ << "'";

bool startWithComma = true;
int indentation = 4;
iDesc.writeCfi(outFile, startWithComma, indentation);

outFile << ")\n"
" for k,v in kwargs.items():\n"
" setattr(mod, k, v)\n"
" return mod\n";

outFile.close();
}

void ConfigurationDescriptions::writeCfiForLabel(std::pair<std::string, ParameterSetDescription> const& labelAndDesc,
Expand Down

0 comments on commit acefa33

Please sign in to comment.