1 // Copyright (C) 2007-2023 CEA, EDF, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 // File : PyConsole_Interp.cxx
23 // Author : Nicolas REJNERI (OPEN CASCADE), Adrien BRUNETON (CEA/DEN), Vadim SANDLER (OPEN CASCADE)
25 #include "PyConsole_Interp.h"
26 #include <QRegularExpression>
29 \class PyConsole_Interp
30 \brief Python interpreter to be embedded to the SALOME study's GUI.
32 There is only one Python interpreter for the whole SALOME environment.
34 Call the initialize() method defined in the base class PyInterp_Interp,
35 to initialize the interpreter after instance creation.
37 The method initialize() calls virtuals methods
38 - initPython() to initialize global Python interpreter
39 - initContext() to initialize interpreter internal context
40 - initRun() to prepare interpreter for running commands
42 See PyInterp_Interp class for more details.
48 Creates new python interpreter.
50 PyConsole_Interp::PyConsole_Interp()
58 PyConsole_Interp::~PyConsole_Interp()
63 \brief Performs specific actions before each Python command
65 Sets the variable "__IN_SALOME_GUI_CONSOLE" to True.
66 This is not attached to a module (like salome_iapp.IN_SALOME_GUI_CONSOLE)
67 since modules are shared across all interpreters in SALOME.
69 \note GIL is already acquired here.
71 int PyConsole_Interp::beforeRun()
73 return PyRun_SimpleString("__builtins__.__IN_SALOME_GUI_CONSOLE=True");
77 \brief Performs specific actions after each Python command
79 Sets the variable "__IN_SALOME_GUI_CONSOLE" to False.
82 \note GIL is already acquired here.
84 int PyConsole_Interp::afterRun()
86 return PyRun_SimpleString("__builtins__.__IN_SALOME_GUI_CONSOLE=False");
90 \brief Run Python dir() command to get matches.
92 \param dirArgument Python expression to pass to the dir command. The parsing of what the
93 user actually started typing is dedicated to the caller
94 \param startMatch string representing the beginning of the pattern to be completed. For example, when
95 the user types "a_string_variable.rsp <TAB>", this is "rsp".
96 \param[out] matches resulting list of matches
97 \param[out] docString resulting docstring of single match
98 \return \true if completion succeeded, \c false otherwise
100 bool PyConsole_Interp::runDirCommand( const QString& dirArgument, const QString& startMatch,
101 QStringList& matches, QString& docString )
103 static QStringList keywords;
104 if ( keywords.isEmpty() ) {
105 keywords << "and" << "as" << "assert" << "break" << "class" << "continue"
106 << "def" << "del" << "elif" << "else" << "except" << "exec"
107 << "finally" << "for" << "from" << "global" << "if" << "import"
108 << "in" << "is" << "lambda" << "not" << "or" << "pass" << "print" << "raise"
109 << "return" << "try" << "while" << "with" << "yield";
112 // run dir() command and extract completions
113 if ( !runDirAndExtract( dirArgument, startMatch, matches ) )
116 // If dirArgument is empty, we append the __builtins__
117 if ( dirArgument.isEmpty() ) {
118 if ( !runDirAndExtract( QString( "__builtins__" ), startMatch, matches, false ) )
121 // ... and we match on Python's keywords as well
122 foreach( QString keyword, keywords ) {
123 if ( keyword.startsWith( startMatch ) )
124 matches.append( keyword );
128 // Try to get doc string of the first match
129 if ( matches.size() > 0 ) {
130 QString cmd = QString( "%1.__doc__" ).arg( matches[0] );
131 if ( !dirArgument.trimmed().isEmpty() )
132 cmd.prepend( QString( "%1." ).arg( dirArgument ) );
134 PyObject* str = PyRun_String( cmd.toStdString().c_str(), Py_eval_input, _global_context, _local_context );
135 if ( !str || str == Py_None || !PyUnicode_Check( str ) )
141 docString = QString( PyUnicode_AsUTF8( str ) );
151 \param dirArgument see runDirCommand()
152 \param startMatch see runDirCommand()
153 \param[out] result resulting list of matches
154 \param discardSwig if \c true, a regular expression is used to discard all static method generated
155 by SWIG. Typically: MEDCouplingUMesh_Blabla
156 \return \c true if the call to dir() and parsing of the result succeeded, \false otherwise.
158 bool PyConsole_Interp::runDirAndExtract( const QString& dirArgument,
159 const QString& startMatch,
161 bool discardSwig ) const
163 QRegularExpression re( "^[A-Z].+_[A-Z]+[a-z]+.+$" ); // REX to discard SWIG static method, e.g. MEDCouplingUMesh_Blabla
165 // Execute dir() command
166 QString command( "dir(" + dirArgument + ")" );
167 PyObject* plst = PyRun_String( command.toStdString().c_str(), Py_eval_input, _global_context, _local_context );
168 if ( !plst || plst == Py_None ) {
176 if ( !PySequence_Check( plst ) ) {
177 // Should never happen ...
182 // Extract the returned list
183 int n = PySequence_Length( plst );
184 for ( int i = 0; i < n; i++ ) {
186 it = PySequence_GetItem( plst, i );
187 QString s( PyUnicode_AsUTF8( it ) );
188 // if the method is not from swig, not static (guessed from the reg exp) and matches
189 // what is already there
190 if ( s.startsWith( startMatch ) ) {
191 if ( !discardSwig || ( !re.match( s ).hasMatch() && !s.contains( "swig" ) ) )