2011-10-13 7 views
8

Sto lavorando per incorporare Python nella nostra suite di test. Lo scopo è di usare Python per eseguire diversi script di test per raccogliere dati e fare un report di test. Più script di test per un'esecuzione di test possono creare variabili e funzioni globali che possono essere utilizzate nello script successivo.Come reinizializzare un interprete Python integrato?

L'applicazione fornisce anche moduli di estensione importati nell'interprete incorporato e utilizzati per lo scambio di alcuni dati con l'applicazione.

Ma l'utente può anche eseguire più corse di prova. Non voglio condividere questi dati globali, le importazioni e i dati scambiati tra più esecuzioni di test. Devo essere sicuro di riavviare in uno stato originale per controllare l'ambiente di test e ottenere gli stessi risultati.

Come reinizializzare l'interprete?

Ho usato Py_Initialize() e Py_Finalize(), ma ottengo un'eccezione nella seconda esecuzione quando si inizializza una seconda volta i moduli di estensione che fornisco all'interprete. E la documentazione warns against using it more than once.

L'utilizzo di sub-interpreters sembra avere gli stessi avvertimenti con l'inizializzazione dei moduli di estensione.

Ho il sospetto che stia facendo qualcosa di sbagliato con l'inizializzazione dei miei moduli di estensione, ma temo che lo stesso problema si verifichi con i moduli di estensione di terze parti.

Forse è possibile farlo funzionare avviando l'interprete nel proprio processo, in modo da essere sicuri che tutta la memoria sia rilasciata.

A proposito, sto usando boost-python per questo, che avverte anche CONTRO l'uso di Py_Finalize!

Qualche suggerimento?

Grazie

risposta

4

Ecco un altro modo che ho trovato per ottenere ciò che voglio, iniziare con una lavagna pulita nell'interprete.

posso controllare namespace globali e locali che uso per eseguire il codice:

// get the dictionary from the main module 
// Get pointer to main module of python script 
object main_module = import("__main__"); 
// Get dictionary of main module (contains all variables and stuff) 
object main_namespace = main_module.attr("__dict__"); 

// define the dictionaries to use in the interpreter 
dict global_namespace; 
dict local_namespace; 

// add the builtins 
global_namespace["__builtins__"] = main_namespace["__builtins__"]; 

posso quindi utilizzare utilizzare i namespace per l'esecuzione del codice contenuto in pyCode:

exec(pyCode, global_namespace, lobaca_namespace); 

posso pulire gli spazi dei nomi quando voglio eseguire una nuova istanza del mio test, pulendo i dizionari:

// empty the interpreters namespaces 
global_namespace.clear(); 
local_namespace.clear();   

// Copy builtins to new global namespace 
global_namespace["__builtins__"] = main_namespace["__builtins__"]; 

A seconda del livello in cui voglio eseguire, posso utilizzare global = local

0

Come sull'utilizzo code.IteractiveInterpreter?

Qualcosa del genere dovrebbe farlo:

#include <boost/python.hpp> 
#include <string> 
#include <stdexcept> 

using namespace boost::python; 

std::string GetPythonError() 
{ 
    PyObject *ptype = NULL, *pvalue = NULL, *ptraceback = NULL; 
    PyErr_Fetch(&ptype, &pvalue, &ptraceback); 
    std::string message(""); 
    if(pvalue && PyString_Check(pvalue)) { 
     message = PyString_AsString(pvalue); 
    } 
    return message; 
} 

// Must be called after Py_Initialize() 
void RunInterpreter(std::string codeToRun) 
{ 
    object pymodule = object(handle<>(borrowed(PyImport_AddModule("__main__")))); 
    object pynamespace = pymodule.attr("__dict__"); 

    try { 
     // Initialize the embedded interpreter 
     object result = exec( "import code\n" 
           "__myInterpreter = code.InteractiveConsole() \n", 
           pynamespace); 
     // Run the code 
     str pyCode(codeToRun.c_str()); 
     pynamespace["__myCommand"] = pyCode; 
     result = eval("__myInterpreter.push(__myCommand)", pynamespace); 
    } catch(error_already_set) { 
     throw std::runtime_error(GetPythonError().c_str()); 
    } 
} 
+0

Quindi, in sostanza, dovrei creare un'istanza una volta che il mio interprete Python e utilizzare questo interprete per avviare subinterpretatori, che tutti hanno il loro spazio dei nomi molto personale? Sembra una soluzione valida, se questi subinterpreti non soffrono degli stessi avvertimenti di quelli creati con Py_NewInterpreter. Guarderò in dettaglio in quello e sperimenterò con esso. Grazie! – nab

+0

Hai capito. Instanciating un InteractiveInterpreter ti dà un ambiente fresco ogni volta. Non sono sicuro di quali siano le regole riguardanti ciò che viene ereditato dall'interprete principale, ma dovrebbe essere facile da controllare in entrambi i modi. –

+0

Sembra fare quello che voglio. Si noti che ha la stessa mancanza di inizializzazione dei moduli (vengono inizializzati solo una volta). Ma funziona bene per ripulire lo spazio dei nomi. Grazie! – nab

0

mi piacerebbe scrivere un altro script di shell eseguendo la sequenza di script di test con le nuove istanze di pitone ogni volta. O scrivere in pitone come

# run your tests in the process first 
# now run the user scripts, each in new process to have virgin env 
for script in userScript: 
    subprocess.call(['python',script])