1919#include " sst/core/configGraph.h"
2020#include " sst/core/cputimer.h"
2121#include " sst/core/factory.h"
22+ #include " sst/core/from_string.h"
2223#include " sst/core/memuse.h"
2324#include " sst/core/model/element_python.h"
2425#include " sst/core/model/python/pymacros.h"
@@ -88,57 +89,57 @@ DISABLE_WARN_DEPRECATED_DECLARATION
8889#endif
8990#endif
9091static PyTypeObject ModuleLoaderType = {
91- SST_PY_OBJ_HEAD " ModuleLoader" , /* tp_name */
92- sizeof (ModuleLoaderPy_t), /* tp_basicsize */
93- 0 , /* tp_itemsize */
94- nullptr , /* tp_dealloc */
95- 0 , /* tp_vectorcall_offset */
96- nullptr , /* tp_getattr */
97- nullptr , /* tp_setattr */
98- nullptr , /* tp_as_sync */
99- nullptr , /* tp_repr */
100- nullptr , /* tp_as_number */
101- nullptr , /* tp_as_sequence */
102- nullptr , /* tp_as_mapping */
103- nullptr , /* tp_hash */
104- nullptr , /* tp_call */
105- nullptr , /* tp_str */
106- nullptr , /* tp_getattro */
107- nullptr , /* tp_setattro */
108- nullptr , /* tp_as_buffer */
109- Py_TPFLAGS_DEFAULT, /* tp_flags */
110- " SST Module Loader" , /* tp_doc */
111- nullptr , /* tp_traverse */
112- nullptr , /* tp_clear */
113- nullptr , /* tp_rich_compare */
114- 0 , /* tp_weaklistoffset */
115- nullptr , /* tp_iter */
116- nullptr , /* tp_iternext */
117- mlMethods, /* tp_methods */
118- nullptr , /* tp_members */
119- nullptr , /* tp_getset */
120- nullptr , /* tp_base */
121- nullptr , /* tp_dict */
122- nullptr , /* tp_descr_get */
123- nullptr , /* tp_descr_set */
124- 0 , /* tp_dictoffset */
125- nullptr , /* tp_init */
126- nullptr , /* tp_alloc */
127- nullptr , /* tp_new */
128- nullptr , /* tp_free */
129- nullptr , /* tp_is_gc */
130- nullptr , /* tp_bases */
131- nullptr , /* tp_mro */
132- nullptr , /* tp_cache */
133- nullptr , /* tp_subclasses */
134- nullptr , /* tp_weaklist */
135- nullptr , /* tp_del */
136- 0 , /* tp_version_tag */
137- nullptr , /* tp_finalize */
138- SST_TP_VECTORCALL /* Python3.8+ */
139- SST_TP_PRINT_DEP /* Python3.8 only */
140- SST_TP_WATCHED /* Python3.12+ */
141- SST_TP_VERSIONS_USED /* Python3.13+ */
92+ SST_PY_OBJ_HEAD " ModuleLoader" , /* tp_name */
93+ sizeof (ModuleLoaderPy_t), /* tp_basicsize */
94+ 0 , /* tp_itemsize */
95+ nullptr , /* tp_dealloc */
96+ 0 , /* tp_vectorcall_offset */
97+ nullptr , /* tp_getattr */
98+ nullptr , /* tp_setattr */
99+ nullptr , /* tp_as_sync */
100+ nullptr , /* tp_repr */
101+ nullptr , /* tp_as_number */
102+ nullptr , /* tp_as_sequence */
103+ nullptr , /* tp_as_mapping */
104+ nullptr , /* tp_hash */
105+ nullptr , /* tp_call */
106+ nullptr , /* tp_str */
107+ nullptr , /* tp_getattro */
108+ nullptr , /* tp_setattro */
109+ nullptr , /* tp_as_buffer */
110+ Py_TPFLAGS_DEFAULT, /* tp_flags */
111+ " SST Module Loader" , /* tp_doc */
112+ nullptr , /* tp_traverse */
113+ nullptr , /* tp_clear */
114+ nullptr , /* tp_rich_compare */
115+ 0 , /* tp_weaklistoffset */
116+ nullptr , /* tp_iter */
117+ nullptr , /* tp_iternext */
118+ mlMethods, /* tp_methods */
119+ nullptr , /* tp_members */
120+ nullptr , /* tp_getset */
121+ nullptr , /* tp_base */
122+ nullptr , /* tp_dict */
123+ nullptr , /* tp_descr_get */
124+ nullptr , /* tp_descr_set */
125+ 0 , /* tp_dictoffset */
126+ nullptr , /* tp_init */
127+ nullptr , /* tp_alloc */
128+ nullptr , /* tp_new */
129+ nullptr , /* tp_free */
130+ nullptr , /* tp_is_gc */
131+ nullptr , /* tp_bases */
132+ nullptr , /* tp_mro */
133+ nullptr , /* tp_cache */
134+ nullptr , /* tp_subclasses */
135+ nullptr , /* tp_weaklist */
136+ nullptr , /* tp_del */
137+ 0 , /* tp_version_tag */
138+ nullptr , /* tp_finalize */
139+ SST_TP_VECTORCALL /* Python3.8+ */
140+ SST_TP_PRINT_DEP /* Python3.8 only */
141+ SST_TP_WATCHED /* Python3.12+ */
142+ SST_TP_VERSIONS_USED /* Python3.13+ */
142143};
143144#if PY_MAJOR_VERSION == 3
144145#if PY_MINOR_VERSION == 8
@@ -1063,7 +1064,7 @@ PyInit_sst(void)
10631064
10641065void
10651066SSTPythonModelDefinition::initModel (
1066- const std::string& script_file, int verbosity, Config* UNUSED ( config) , int argc, char** argv)
1067+ const std::string& script_file, int verbosity, Config* config, int argc, char ** argv)
10671068{
10681069 output = new Output (" SSTPythonModel: " , verbosity, 0 , SST::Output::STDOUT);
10691070
@@ -1144,6 +1145,28 @@ SSTPythonModelDefinition::initModel(
11441145 // // directory to the path
11451146 PyRun_SimpleString (" sys.meta_path.append(sst.ModuleLoader())\n " );
11461147#endif
1148+
1149+
1150+ // Check to see if we need to import the coverage module (only works in Python >=3.9
1151+ #if PY_MINOR_VERSION >= 9
1152+ if ( config->enablePythonCoverage () ) { enablePythonCoverage = true ; }
1153+ else {
1154+ const char * envConfigCoverage = getenv (" SST_CONFIG_PYTHON_COVERAGE" );
1155+ if ( nullptr != envConfigCoverage ) {
1156+ std::string value (envConfigCoverage);
1157+ try {
1158+ enablePythonCoverage = SST::Core::from_string<bool >(value);
1159+ }
1160+ catch ( std::invalid_argument& e ) {
1161+ output->fatal (
1162+ CALL_INFO, 1 ,
1163+ " ERROR: Invalid format for SST_CONFIG_PYTHON_COVERAGE. Valid boolean pairs are true/false, t/f, "
1164+ " yes/no, y/n, on/off, or 1/0\n " );
1165+ enablePythonCoverage = false ;
1166+ }
1167+ }
1168+ }
1169+ #endif
11471170}
11481171
11491172SSTPythonModelDefinition::SSTPythonModelDefinition (
@@ -1236,8 +1259,29 @@ SSTPythonModelDefinition::createConfigGraph()
12361259{
12371260 output->verbose (CALL_INFO, 1 , 0 , " Creating config graph for SST using Python model...\n " );
12381261
1262+ #if PY_MINOR_VERSION >= 9
1263+ if ( enablePythonCoverage ) {
1264+ // Create coverage object with a name unlikely to be used in the user script
1265+ int startcoverageReturn = PyRun_SimpleString (" import coverage\n "
1266+ " zzzSSTPythonCoverageModule = coverage.Coverage()\n "
1267+ " zzzSSTPythonCoverageModule.start()\n " );
1268+ if ( nullptr != PyErr_Occurred () ) {
1269+ // Print the Python error and then let SST exit as a fatal-stop.
1270+ output->fatal (
1271+ CALL_INFO, 1 ,
1272+ " ERROR: Error occurred starting test coverage of the SST model script. Reported error:\n " );
1273+ PyErr_Print ();
1274+ }
1275+ if ( -1 == startcoverageReturn ) {
1276+ output->fatal (CALL_INFO, 1 , " Execution of starting test coverage failed\n %s" , loadErrors.c_str ());
1277+ }
1278+ }
1279+ #endif
1280+
1281+ // Open the input script
12391282 FILE* fp = fopen (scriptName.c_str (), " r" );
12401283 if ( !fp ) { output->fatal (CALL_INFO, 1 , " Unable to open python script %s\n " , scriptName.c_str ()); }
1284+ PyErr_Clear ();
12411285 int createReturn = PyRun_AnyFileEx (fp, scriptName.c_str (), 1 );
12421286
12431287 if ( nullptr != PyErr_Occurred () ) {
@@ -1256,6 +1300,24 @@ SSTPythonModelDefinition::createConfigGraph()
12561300 output->fatal (CALL_INFO, 1 , " Error occured handling the creation of the component graph in Python.\n " );
12571301 }
12581302
1303+ #if PY_MINOR_VERSION >= 9
1304+ // If coverage was enabled, stop the module and output the results
1305+ if ( enablePythonCoverage ) {
1306+ PyErr_Clear ();
1307+ int stopcoverageReturn = PyRun_SimpleString (" zzzSSTPythonCoverageModule.stop()\n "
1308+ " zzzSSTPythonCoverageModule.save()\n " );
1309+
1310+ if ( nullptr != PyErr_Occurred () ) {
1311+ // Print the Python error and then let SST exit as a fatal-stop.
1312+ output->fatal (
1313+ CALL_INFO, 1 , " ERROR: Error occurred stopping coverage of the SST model script. Reported error:\n " );
1314+ PyErr_Print ();
1315+ }
1316+ if ( -1 == stopcoverageReturn ) {
1317+ output->fatal (CALL_INFO, 1 , " Execution of stopping coverage failed\n %s" , loadErrors.c_str ());
1318+ }
1319+ }
1320+ #endif
12591321 return graph;
12601322}
12611323
0 commit comments