]> SALOME platform Git repositories - modules/gui.git/commitdiff
Salome HOME
Allow passing multiple scripts with arguments from command line
authoraguerre <aguerre>
Wed, 28 Aug 2013 14:18:22 +0000 (14:18 +0000)
committeraguerre <aguerre>
Wed, 28 Aug 2013 14:18:22 +0000 (14:18 +0000)
src/PyInterp/PyInterp_Interp.cxx
src/SalomeApp/SalomeApp_Application.cxx

index 8c01a492648df5e4183970b2d326a8d73022bd17..c2dcd254a5995972675410c15115ff6f3d8b3bfc 100644 (file)
@@ -34,6 +34,7 @@
 #include <vector>
 #include <map>
 #include <iostream>
+#include <algorithm>
 
 #define TOP_HISTORY_PY   "--- top of history ---"
 #define BEGIN_HISTORY_PY "--- begin of history ---"
@@ -50,7 +51,7 @@ std::map<long,PyThreadState*> currentThreadMap;
   \brief Constructor. Automatically acquires GIL.
   \param theThreadState python thread state
 */
-PyLockWrapper::PyLockWrapper(PyThreadState* theThreadState): 
+PyLockWrapper::PyLockWrapper(PyThreadState* theThreadState):
   myThreadState(theThreadState),
   mySaveThreadState(0)
 {
@@ -113,7 +114,7 @@ PyLockWrapper PyInterp_Interp::GetLockWrapper()
 }
 
 /*
-  The following functions are used to hook the Python 
+  The following functions are used to hook the Python
   interpreter output.
 */
 
@@ -237,11 +238,11 @@ PyInterpreterState* PyInterp_Interp::_interp       = NULL;
 
 /*!
   \brief Basic constructor.
-  
-  After construction the interpreter instance successor classes 
+
+  After construction the interpreter instance successor classes
   must call virtual method initalize().
 */
-PyInterp_Interp::PyInterp_Interp(): 
+PyInterp_Interp::PyInterp_Interp():
   _tstate(0), _vout(0), _verr(0), _g(0)
 {
 }
@@ -255,7 +256,7 @@ PyInterp_Interp::~PyInterp_Interp()
 
 /*!
   \brief Initialize embedded interpreter.
-  
+
   This method shoud be called after construction of the interpreter.
   The method initialize() calls virtuals methods
   - initPython()  to initialize global Python interpreter
@@ -267,7 +268,7 @@ PyInterp_Interp::~PyInterp_Interp()
 */
 void PyInterp_Interp::initialize()
 {
-  _history.clear();       // start a new list of user's commands 
+  _history.clear();       // start a new list of user's commands
   _ith = _history.begin();
 
   initPython();
@@ -288,7 +289,7 @@ void PyInterp_Interp::initialize()
   }
 
   // Create python objects to capture stdout and stderr
-  _vout=(PyObject*)newPyStdOut( false ); // stdout 
+  _vout=(PyObject*)newPyStdOut( false ); // stdout
   _verr=(PyObject*)newPyStdOut( true );  // stderr
 
   // All the initRun outputs are redirected to the standard output (console)
@@ -300,8 +301,8 @@ void PyInterp_Interp::initialize()
   \brief Initialize Python interpreter.
 
   In case if Python is not initialized, it sets program name, initializes the interpreter, sets program arguments,
-  initializes threads. 
-  Otherwise, it just obtains the global interpreter and thread states. This is important for light SALOME configuration, 
+  initializes threads.
+  Otherwise, it just obtains the global interpreter and thread states. This is important for light SALOME configuration,
   as in full SALOME this is done at SalomeApp level.
   \sa SalomeApp_PyInterp class
  */
@@ -341,15 +342,15 @@ std::string PyInterp_Interp::getbanner()
 
 /*!
   \brief Initialize run command.
-  This method is used to prepare interpreter for running 
+
+  This method is used to prepare interpreter for running
   Python commands.
-  
+
   \return \c true on success and \c false on error
 */
 bool PyInterp_Interp::initRun()
 {
-  // 
+  //
   // probably all below code isn't required
   //
   /*
@@ -357,7 +358,7 @@ bool PyInterp_Interp::initRun()
   PySys_SetObject("stdout",_vout);
 
   //PyObject *m = PyImport_GetModuleDict();
-  
+
   PySys_SetObject("stdout",PySys_GetObject("__stdout__"));
   PySys_SetObject("stderr",PySys_GetObject("__stderr__"));
   */
@@ -365,7 +366,7 @@ bool PyInterp_Interp::initRun()
 }
 
 /*!
-  \brief Compile Python command and evaluate it in the 
+  \brief Compile Python command and evaluate it in the
          python dictionary context if possible.
   \internal
   \param command Python command string
@@ -373,7 +374,7 @@ bool PyInterp_Interp::initRun()
   \return -1 on fatal error, 1 if command is incomplete and 0
          if command is executed successfully
  */
-static int compile_command(const char *command,PyObject *context)
+static int run_command(const char *command, PyObject *context)
 {
   PyObject *m = PyImport_AddModule("codeop");
   if(!m) { // Fatal error. No way to go on.
@@ -411,6 +412,59 @@ static int compile_command(const char *command,PyObject *context)
   }
 }
 
+void replaceAll(std::string& str, const std::string& from, const std::string& to) {
+    if(from.empty())
+        return;
+    size_t start_pos = 0;
+    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
+        str.replace(start_pos, from.length(), to);
+        start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
+    }
+}
+/*!
+  \brief Compile Python command and evaluate it in the
+         python dictionary context if possible. Command might correspond to
+         the execution of a script with optional arguments.
+         In this case, command is:
+         execfile(r"/absolute/path/to/script.py [args:arg1,...,argn]")
+  \internal
+  \param command Python command string
+  \param context Python context (dictionary)
+  \return -1 on fatal error, 1 if command is incomplete and 0
+         if command is executed successfully
+ */
+static int compile_command(const char *command,PyObject *context)
+{
+  // First guess if command is execution of a script with args, or a simple Python command
+  std::string singleCommand = command;
+  std::string commandArgs = "";
+
+  std::size_t pos = std::string(command).find("args:");
+  if (pos != std::string::npos) {
+    commandArgs = singleCommand.substr(pos+5);
+    commandArgs = commandArgs.substr(0, commandArgs.length()-3);
+    singleCommand = singleCommand.substr(0, pos-1)+"\")";
+  }
+
+  if (commandArgs.empty()) {
+    // process command: expression
+    // process command: execfile(r"/absolute/path/to/script.py") (no args)
+    return run_command(singleCommand.c_str(), context);
+  }
+  else {
+    // process command: execfile(r"/absolute/path/to/script.py [args:arg1,...,argn]")
+    std::string script = singleCommand.substr(11); // remove leading execfile(r"
+    script = script.substr(0, script.length()-2); // remove trailing ")
+
+    std::string preCommandBegin = "import sys; save_argv = sys.argv; sys.argv=[";
+    std::string preCommandEnd = "];";
+    replaceAll(commandArgs, ",", "\",\"");
+    commandArgs = "\""+commandArgs+"\"";
+    std::string completeCommand = preCommandBegin+"\""+script+"\","+commandArgs+preCommandEnd+singleCommand+";sys.argv=save_argv";
+    return run_command(completeCommand.c_str(), context);
+  }
+}
+
 /*!
   \brief Run Python command.
   \param command Python command
@@ -487,7 +541,7 @@ const char * PyInterp_Interp::getNext()
   \param data callback function parameters
 */
 void PyInterp_Interp::setvoutcb(PyOutChanged* cb, void* data)
-{  
+{
   ((PyStdOut*)_vout)->_cb=cb;
   ((PyStdOut*)_vout)->_data=data;
 }
@@ -498,7 +552,7 @@ void PyInterp_Interp::setvoutcb(PyOutChanged* cb, void* data)
   \param data callback function parameters
 */
 void PyInterp_Interp::setverrcb(PyOutChanged* cb, void* data)
-{  
+{
   ((PyStdOut*)_verr)->_cb=cb;
   ((PyStdOut*)_verr)->_data=data;
 }
index 03eb095bb92515b33f4b8601911dfe85ecb0e0c4..3bbe372618f3f29ce77d5d4f1e00db7927fa9e48 100644 (file)
@@ -199,13 +199,22 @@ void SalomeApp_Application::start()
           hdffile = fi.absoluteFilePath();
       }
       else {
-        QRegExp rxp ("--pyscript=(.+)");
+        QRegExp rxp ("--pyscript=\\[(.+)\\]");
         if ( rxp.indexIn( QString(qApp->argv()[i]) ) >= 0 && rxp.capturedTexts().count() > 1 ) {
-          QStringList files = rxp.capturedTexts()[1].split(",",QString::SkipEmptyParts);
-          pyfiles += files;
+          QStringList dictList = rxp.capturedTexts()[1].split("},", QString::SkipEmptyParts);
+          for (int k = 0; k < dictList.count(); ++k) {
+            QRegExp rxd ("[\\s]*\\{?([^\\{\\}]+)\\}?[\\s]*");
+            if ( rxd.indexIn( dictList[k] ) >= 0 && rxd.capturedTexts().count() > 1 ) {
+              for (int m = 1; m < rxd.capturedTexts().count(); ++m) {
+                pyfiles += rxd.capturedTexts()[m];
+              }
+            }
+          }
         }
       }
     }
+    // Here pyfiles elements are: "script_name": [list_of_"arg"s]
+    // For example: "/absolute/path/to/my_script.py": ["1", "2"]
 
     if ( !hdffile.isEmpty() )       // open hdf file given as parameter
       onOpenDoc( hdffile );
@@ -219,43 +228,30 @@ void SalomeApp_Application::start()
       if ( appStudy && pyConsole ) {
         _PTR(Study) aStudy = appStudy->studyDS();
         if ( !aStudy->GetProperties()->IsLocked() ) {
+          // pyfiles[j] is a dictionary: {"/absolute/path/to/script.py": [script_args]}
+          // Path is absolute, script has .py extension
           for (uint j = 0; j < pyfiles.count(); j++ ) {
-            QFileInfo fi ( pyfiles[j] );
-            QFileInfo fipy ( pyfiles[j] + ".py" );
-            QString command = QString( "execfile(r\"%1\")" );
-            if ( fi.isAbsolute() ) {
-              if ( fi.exists() )
-                pyConsole->exec( command.arg( fi.absoluteFilePath() ) );
-              else if ( fipy.exists() )
-                pyConsole->exec( command.arg( fipy.absoluteFilePath() ) );
-              else
-                qDebug() << "Can't execute file" << pyfiles[j];
-            }
-            else {
-              bool found = false;
-              QStringList dirs;
-              dirs << QDir::currentPath();
-              if ( ::getenv( "PYTHONPATH" ) )
-                dirs += QString( ::getenv( "PYTHONPATH" ) ).split( QRegExp( "[:|;]" ) );
-              foreach( QString dir, dirs ) {
-                qDebug() << "try" << QFileInfo( dir, pyfiles[j] ).absoluteFilePath();
-                qDebug() << "try" << QFileInfo( dir, pyfiles[j] + ".py" ).absoluteFilePath();
-                if ( QFileInfo( dir, pyfiles[j] ).exists() ) {
-                  pyConsole->exec( command.arg( QFileInfo( dir, pyfiles[j] ).absoluteFilePath() ) );
-                  found = true;
-                  break;
-                }
-                else if ( QFileInfo( dir, pyfiles[j] + ".py" ).exists() ) {
-                  pyConsole->exec( command.arg( QFileInfo( dir, pyfiles[j] + ".py" ).absoluteFilePath() ) );
-                  found = true;
-                  break;
-                }
+            // Extract scripts and their arguments, if any
+            QRegExp rxp ("\"(.+)\":[\\s]*\\[(.*)\\]");
+            if ( rxp.indexIn( pyfiles[j] ) >= 0 && rxp.capturedTexts().count() == 3 ) {
+              QString script = rxp.capturedTexts()[1];
+              QString args = "";
+              QStringList argList = rxp.capturedTexts()[2].split(",", QString::SkipEmptyParts);
+              for (uint k = 0; k < argList.count(); k++ ) {
+                QString arg = argList[k].trimmed();
+                arg.remove( QRegExp("^[\"]") );
+                arg.remove( QRegExp("[\"]$") );
+                args += arg+",";
               }
-              if ( !found ) {
-                qDebug() << "Can't execute file" << pyfiles[j];
+              args.remove( QRegExp("[,]$") );
+              if (!args.isEmpty()) {
+                args = "args:"+args;
               }
+              QString cmd = script+" "+args;
+              QString command = QString( "execfile(r\"%1\")" ).arg(cmd.trimmed());
+              pyConsole->exec(command);
             }
-          }
+          } // end for loop on pyfiles QStringList
         }
       }
     }
@@ -279,7 +275,7 @@ void SalomeApp_Application::createActions()
   createAction( DumpStudyId, tr( "TOT_DESK_FILE_DUMP_STUDY" ), QIcon(),
                 tr( "MEN_DESK_FILE_DUMP_STUDY" ), tr( "PRP_DESK_FILE_DUMP_STUDY" ),
                 Qt::CTRL+Qt::Key_D, desk, false, this, SLOT( onDumpStudy() ) );
-  
+
   //! Load script
   createAction( LoadScriptId, tr( "TOT_DESK_FILE_LOAD_SCRIPT" ), QIcon(),
                 tr( "MEN_DESK_FILE_LOAD_SCRIPT" ), tr( "PRP_DESK_FILE_LOAD_SCRIPT" ),
@@ -660,7 +656,7 @@ SUIT_Study* SalomeApp_Application::createNewStudy()
   connect( aStudy, SIGNAL( closed ( SUIT_Study* ) ), this, SLOT( onStudyClosed ( SUIT_Study* ) ) );
 
   //to receive signal in application that NoteBook's variable was modified
-  connect( aStudy, SIGNAL(notebookVarUpdated(QString)), 
+  connect( aStudy, SIGNAL(notebookVarUpdated(QString)),
            this, SIGNAL(notebookVarUpdated(QString)) );
 
   return aStudy;
@@ -795,7 +791,7 @@ void SalomeApp_Application::onDumpStudy( )
       QFileInfo aFileInfo(aFileName);
       if( aFileInfo.isDir() ) // IPAL19257
         return;
-      
+
       // Issue 21377 - dump study implementation moved to SalomeApp_Study class
       bool res;
       {
@@ -950,7 +946,7 @@ QWidget* SalomeApp_Application::createWindow( const int flag )
       _PTR(Study) aStudy = appStudy->studyDS();
       setNoteBook( new SalomeApp_NoteBook( desktop(), aStudy ) );
       //to receive signal in NoteBook that it's variable was modified
-      connect( this, SIGNAL( notebookVarUpdated( QString ) ), 
+      connect( this, SIGNAL( notebookVarUpdated( QString ) ),
                getNoteBook(), SLOT( onVarUpdate( QString ) ) );
     }
     wid = getNoteBook();
@@ -1381,7 +1377,7 @@ void SalomeApp_Application::onDblClick( SUIT_DataObject* theObj )
     SUIT_DataOwnerPtrList aList;
     aList.append( new LightApp_DataOwner( entry ) );
     selectionMgr()->setSelected( aList, false );
-    
+
     SUIT_DataBrowser* ob = objectBrowser();
 
     QModelIndexList aSelectedIndexes = ob->selectedIndexes();
@@ -1446,7 +1442,7 @@ void SalomeApp_Application::onStudyCreated( SUIT_Study* study )
 {
   LightApp_Application::onStudyCreated( study );
 
-  desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ), 
+  desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
                                windowDock( getWindow( WT_ObjectBrowser ) ) );
 
   loadDockWindowsState();
@@ -1475,7 +1471,7 @@ void SalomeApp_Application::onStudyOpened( SUIT_Study* study )
 {
   LightApp_Application::onStudyOpened( study );
 
-  desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ), 
+  desktop()->tabifyDockWidget( windowDock( getWindow( WT_NoteBook ) ),
                                windowDock( getWindow( WT_ObjectBrowser ) ) );
 
   loadDockWindowsState();
@@ -1499,7 +1495,7 @@ void SalomeApp_Application::updateSavePointDataObjects( SalomeApp_Study* study )
   SUIT_DataBrowser* ob = objectBrowser();
   LightApp_SelectionMgr* selMgr = selectionMgr();
 
-  if ( !study || !ob || !selMgr ) 
+  if ( !study || !ob || !selMgr )
     return;
 
   // find GUI states root object
@@ -1522,7 +1518,7 @@ void SalomeApp_Application::updateSavePointDataObjects( SalomeApp_Study* study )
     selMgr->clearSelected();
     ob->setAutoUpdate(true);
     DataObjectList ch = guiRootObj->children();
-    for( int i = 0; i < ch.size(); i++ ) 
+    for( int i = 0; i < ch.size(); i++ )
       delete ch[i];
     delete guiRootObj;
     ob->setAutoUpdate(isAutoUpdate);
@@ -1830,7 +1826,7 @@ bool SalomeApp_Application::renameObject( const QString& entry, const QString& n
 void SalomeApp_Application::defaultWindows( QMap<int, int>& aMap ) const
 {
   LightApp_Application::defaultWindows(aMap);
-  if ( !aMap.contains( WT_NoteBook ) ) { 
+  if ( !aMap.contains( WT_NoteBook ) ) {
     if ( !myNoteBook ) {
       aMap.insert( WT_NoteBook, Qt::LeftDockWidgetArea );
     }
@@ -1859,7 +1855,7 @@ void SalomeApp_Application::onUpdateStudy()
 
   if( !updateStudy() )
     SUIT_MessageBox::warning( desktop(), tr( "ERROR" ), tr( "ERR_UPDATE_STUDY_FAILED" ) );
-    
+
   QApplication::restoreOverrideCursor();
 }
 
@@ -1915,7 +1911,7 @@ bool SalomeApp_Application::updateStudy()
   int anIndex = aList.indexOf( this );
 
   // Disconnect dialog from application desktop in case if:
-  // 1) Application is not the first application in the session 
+  // 1) Application is not the first application in the session
   // 2) Application is the first application in session but not the only.
   bool changeDesktop = ((anIndex > 0) || (anIndex == 0 && aList.count() > 1));
   if( changeDesktop ) {
@@ -1941,7 +1937,7 @@ bool SalomeApp_Application::updateStudy()
   onCloseDoc( false );
 
   if( !changeDesktop ) {
-    ok = onRestoreStudy( aDumpScript, 
+    ok = onRestoreStudy( aDumpScript,
                          aStudyName,
                          isStudySaved );
   }
@@ -1954,8 +1950,8 @@ bool SalomeApp_Application::updateStudy()
  *  Purpose  : Load the dumped study from Python script
  */
 //============================================================================
-bool SalomeApp_Application::onRestoreStudy( const QString& theDumpScript, 
-                                            const QString& theStudyName, 
+bool SalomeApp_Application::onRestoreStudy( const QString& theDumpScript,
+                                            const QString& theStudyName,
                                             bool theIsStudySaved )
 {
   bool ok = true;
@@ -2005,8 +2001,8 @@ void SalomeApp_Application::afterCloseDoc()
 {
   // emit signal to restore study from Python script
   if ( myNoteBook ) {
-    emit dumpedStudyClosed( myNoteBook->getDumpedStudyScript(), 
-                            myNoteBook->getDumpedStudyName(), 
+    emit dumpedStudyClosed( myNoteBook->getDumpedStudyScript(),
+                            myNoteBook->getDumpedStudyName(),
                             myNoteBook->isDumpedStudySaved() );
   }
   LightApp_Application::afterCloseDoc();