LDMX Software
Public Member Functions | Static Public Attributes | Private Attributes | List of all members
framework::ConfigurePython Class Reference

Utility class which reads/executes a python script and creates a Process object based on the input. More...

#include <ConfigurePython.h>

Public Member Functions

 ConfigurePython (const std::string &pythonScript, char *args[], int nargs)
 Class constructor.
 
 ~ConfigurePython ()=default
 Class destructor.
 
ProcessHandle makeProcess ()
 Create a process object based on the python file information.
 
const framework::config::Parameters get () const
 Get a handle to the configuration.
 

Static Public Attributes

static std::string root_module = "ldmxcfg"
 the root configuration module name
 
static std::string root_class = "Process"
 the root configuration class name
 
static std::string root_object = "lastProcess"
 the root configuration object name
 

Private Attributes

framework::config::Parameters configuration_
 The entire configuration for this process.
 

Detailed Description

Utility class which reads/executes a python script and creates a Process object based on the input.

Note
The configuration language is documented in the overall Framework package documentation.

Definition at line 19 of file ConfigurePython.h.

Constructor & Destructor Documentation

◆ ConfigurePython()

framework::ConfigurePython::ConfigurePython ( const std::string &  pythonScript,
char *  args[],
int  nargs 
)

Class constructor.

This method contains all the parsing and execution of the python script.

Exceptions
Exceptionif any necessary components of the python configuration are missing. e.g. The Process class or the different members of the lastProcess object.

The basic premise of this constructor is to execute the python configuration script by loading it into python as a module. Then, after the script has been executed, all of the parameters for the Process and EventProcessors are gathered from python. The fact that the script has been executed means that the user can get up to a whole lot of shenanigans that can help them make their work more efficient.

Parameters
pythonScriptFilename location of the python script.
argsCommandline arguments to be passed to the python script.
nargsNumber of commandline arguments.

Definition at line 273 of file ConfigurePython.cxx.

274 {
275 // assumes that nargs >= 0
276 // this is true always because we error out if no python script has been
277 // found
278
279 // load a handle to the config file into memory (and check that it exists)
280 FILE* config_file{fopen(pythonScript.c_str(), "r")};
281 if (config_file == NULL) {
282 EXCEPTION_RAISE("ConfigDNE",
283 "Passed config script '" + pythonScript +
284 "' is not accessible.\n"
285 " Did you make a typo in the path to the script?\n"
286 " Are you referencing a directory that is not "
287 "mounted to the container?");
288 }
289
290 std::string cmd = pythonScript;
291 if (pythonScript.rfind("/") != std::string::npos) {
292 cmd = pythonScript.substr(pythonScript.rfind("/") + 1);
293 }
294 cmd = cmd.substr(0, cmd.find(".py"));
295
296 // python needs the argument list as if you are on the command line
297 // targs = [ script , arg0 , arg1 , ... ] ==> len(targs) = nargs+1
298 // PySys_SetArgvEx uses wchar_t instead of char in python3
299 wchar_t** targs = new wchar_t*[nargs + 1];
300 targs[0] = Py_DecodeLocale(pythonScript.c_str(), NULL);
301 for (int i = 0; i < nargs; i++) targs[i + 1] = Py_DecodeLocale(args[i], NULL);
302
303 // name our program after the script that is being run
304 Py_SetProgramName(targs[0]);
305
306 // start up python interpreter
307 Py_Initialize();
308
309 // The third argument to PySys_SetArgvEx tells python to import
310 // the args and add the directory of the first argument to
311 // the PYTHONPATH
312 // This way, the command to import the module just needs to be
313 // the name of the python script
314 PySys_SetArgvEx(nargs + 1, targs, 1);
315
316 // run the file as a python script
317 if (PyRun_AnyFile(config_file, pythonScript.c_str()) != 0) {
318 PyErr_Print();
319 EXCEPTION_RAISE("ConfigureError", "Problem running python script.");
320 }
321
322 // script has been run so we can
323 // free up arguments to python script
324 fclose(config_file);
325 for (int i = 0; i < nargs + 1; i++) PyMem_RawFree(targs[i]);
326 delete[] targs;
327
328 // when a script runs in python, the script itself becomes the module
329 // named __main__, we retrieve a handle to that module by "importing"
330 // it (since it is already within the python interpreter, we are just
331 // getting the handle and not actually running anything here
332 PyObject* script = PyImport_ImportModule("__main__");
333 if (script == NULL) {
334 PyErr_Print();
335 EXCEPTION_RAISE("ConfigureError", "Problem loading python script");
336 }
337 PyObject* pCMod = PyObject_GetAttrString(script, root_module.c_str());
338 Py_DECREF(script); // don't need the script anymore
339 if (pCMod == 0) {
340 PyErr_Print();
341 EXCEPTION_RAISE("ConfigureError",
342 "Did not import root configuration module " + root_module);
343 }
344
345 PyObject* pProcessClass = PyObject_GetAttrString(pCMod, root_class.c_str());
346 Py_DECREF(pCMod); // don't need the config module anymore
347 if (pProcessClass == 0) {
348 PyErr_Print();
349 EXCEPTION_RAISE("ConfigureError",
350 "Did not import root configuration class " + root_class);
351 }
352
353 PyObject* pProcess =
354 PyObject_GetAttrString(pProcessClass, root_object.c_str());
355 Py_DECREF(pProcessClass); // don't need the Process class anymore
356 if (pProcess == 0) {
357 // wasn't able to get lastProcess class member
358 PyErr_Print();
359 EXCEPTION_RAISE(
360 "ConfigureError",
361 "Process object not defined. This object is required to run.");
362 } else if (pProcess == Py_None) {
363 // lastProcess was left undefined
364 EXCEPTION_RAISE("ConfigureError",
365 "Did not create a configuration class instance");
366 }
367
368 // okay, now we have fully imported the script and gotten the handle
369 // to the last Process object defined in the script.
370 // We can now look at pProcess and get all of our parameters out of it.
371
373
374 // all done with python nonsense
375 // delete one parent python object
376 // MEMORY still not sure if this is enough, but not super worried about it
377 // because this only happens once per run
378 Py_DECREF(pProcess);
379 // close up python interpreter
380 if (Py_FinalizeEx() < 0) {
381 PyErr_Print();
382 EXCEPTION_RAISE("PyError",
383 "I wasn't able to close up the python interpreter!");
384 }
385}
framework::config::Parameters configuration_
The entire configuration for this process.
static std::string root_object
the root configuration object name
static std::string root_module
the root configuration module name
static std::string root_class
the root configuration class name
void setParameters(std::map< std::string, std::any > parameters)
Set the mapping of parameter names to value.
Definition Parameters.h:41
static std::map< std::string, std::any > getMembers(PyObject *object)
Extract members from a python object.

References configuration_, framework::getMembers(), root_class, root_module, root_object, and framework::config::Parameters::setParameters().

◆ ~ConfigurePython()

framework::ConfigurePython::~ConfigurePython ( )
default

Class destructor.

Does nothing at the moment.

Member Function Documentation

◆ get()

const framework::config::Parameters framework::ConfigurePython::get ( ) const
inline

Get a handle to the configuration.

Definition at line 71 of file ConfigurePython.h.

71{ return configuration_; }

References configuration_.

◆ makeProcess()

ProcessHandle framework::ConfigurePython::makeProcess ( )

Create a process object based on the python file information.

No python parsing actually happens in this function. All of the parsing is done in the constructor, this just copies that information from configuration_ into the Process object.

Returns
ProcessHandle handle to process that is configured and ready to go

Definition at line 387 of file ConfigurePython.cxx.

387 {
388 // no python nonsense happens in here,
389 // this just takes the parameters determined earlier
390 // and puts them into the Process + EventProcessor framework
391
392 return std::make_unique<Process>(configuration_);
393}

References configuration_.

Member Data Documentation

◆ configuration_

framework::config::Parameters framework::ConfigurePython::configuration_
private

The entire configuration for this process.

Contains all parameters that were passed in the python in a recursive manner. For example, this would contain a key called 'sequence' that has a value of a vector of Parmaeters, each one corresponding to a processor.

Definition at line 82 of file ConfigurePython.h.

Referenced by ConfigurePython(), get(), and makeProcess().

◆ root_class

std::string framework::ConfigurePython::root_class = "Process"
static

the root configuration class name

Definition at line 24 of file ConfigurePython.h.

Referenced by ConfigurePython().

◆ root_module

std::string framework::ConfigurePython::root_module = "ldmxcfg"
static

the root configuration module name

Definition at line 22 of file ConfigurePython.h.

Referenced by ConfigurePython().

◆ root_object

std::string framework::ConfigurePython::root_object = "lastProcess"
static

the root configuration object name

Definition at line 26 of file ConfigurePython.h.

Referenced by ConfigurePython().


The documentation for this class was generated from the following files: