Reset environment/scope between multiple function calls in Python.Net
Question:
I want to execute the same Python function several times and I want each execution to be completely isolated from the previous one.
But the problem is that the first execution of the method changes a global variable and when I execute the method the second time the global variable has the value left by the first execution.
I want that from one call to another the environment is reset and is like the first time.
var1=1
def Met(n) :
global var1
if n!=1 : var1=n
print(f"Py: {var1}")
return var1
if __name__ == "__main__":
args = sys.argv
globals()[args[1]](*args[2:])
If I execute it from command line:
python .Test1.py Met 2
python .Test1.py Met 1
The output is (correct):
Py: 2
Py: 1
What I want, OK.
But if I run it from Pythonet I get:
Py: 2
Py: 2
Incorrect!
That is, the second call, the value of var1 is the value of first call.
The code (simplifying):
public string Met2(int n) {
dynamic txtResul="X";
using ( Py.GIL() ) {
using (var scope=gbl.NewScope() ) { //Witout this, it doesn't work too
dynamic f=Py.Import(NOM_FICH_Py);
txtResul=f.Met(n);
}
}
return txtResul.ToString();
}
I have tried initialising with and without this:
PythonEngine.Initialize();
gbl = Py.CreateScope(nombre);
pyPtr=PythonEngine.BeginAllowThreads();
I have also tried to finalize everything and run it, with and without this:
public void Fin() {
PythonEngine.EndAllowThreads(pyPtr);
PythonEngine.Shutdown();
}
I have also tried running each function call as a separate script:
using (Py.GIL()) {
dynamic sys = Py.Import("sys");
string script ="import sysn"+ "from io import StringIOn"+
$"from {NOM_FICH_Py} import {funcion.Split('(')[0]}n"+
"sys.stdout=StringIO()n"+ "sys.stdout.flush()n"+
"sys.stderr=StringIO()n"+ "sys.stderr.flush()n"+
funcion;
var scope = Py.CreateScope();
var vars=scope.Variables();
scope.Exec(script, new PyDict());
salida=sys.stdout.getvalue();
scope.Dispose();
}
I use Python 3.10 and Python .Net 3.0.0-rc 4 and Python.Net 3.0.0-preview2022-03-03 (in other computer) and I cann’t get the ‘reset’ as I executed it like command line.
Thanks in advance.
Extra question, is there any wiki/documentacion to read the detail of functions like CreateScope, NewScope, Exec…? In https://github.com/pythonnet/pythonnet/wiki I haven’t found the function’s detail.
Answers:
The only supported way to fully reset Python environment with Python.NET is to restart the process.
At the end, the only posible solution it’s to exectue like a script in cmd.
It’s not the best solution (it’s not posible to debug) but it’s the only one I know.
public string ExecCmd(string funcion, string args) {
Process p = new Process();
p.StartInfo=new ProcessStartInfo(PYTHON_EXE, $"{NombreFichApp} {funcion} {args}") {
RedirectStandardOutput=true,
UseShellExecute=false,
CreateNoWindow=true
};
p.Start();
string salida = p.StandardOutput.ReadToEnd();
p.WaitForExit();
return salida;
}
Try enclosing your code with another using block for PythonEngine, you can also use PythonEngine.Shutdown and then reinitialise it every time a method call is made
using(var pyEngine=new PythonEngine())
{
using ( Py.GIL() ) {
using (var scope=gbl.NewScope() ) { //Witout this, it doesn't work too
dynamic f=Py.Import(NOM_FICH_Py);
txtResul=f.Met(n);
}
}
}
I want to execute the same Python function several times and I want each execution to be completely isolated from the previous one.
But the problem is that the first execution of the method changes a global variable and when I execute the method the second time the global variable has the value left by the first execution.
I want that from one call to another the environment is reset and is like the first time.
var1=1
def Met(n) :
global var1
if n!=1 : var1=n
print(f"Py: {var1}")
return var1
if __name__ == "__main__":
args = sys.argv
globals()[args[1]](*args[2:])
If I execute it from command line:
python .Test1.py Met 2
python .Test1.py Met 1
The output is (correct):
Py: 2
Py: 1
What I want, OK.
But if I run it from Pythonet I get:
Py: 2
Py: 2
Incorrect!
That is, the second call, the value of var1 is the value of first call.
The code (simplifying):
public string Met2(int n) {
dynamic txtResul="X";
using ( Py.GIL() ) {
using (var scope=gbl.NewScope() ) { //Witout this, it doesn't work too
dynamic f=Py.Import(NOM_FICH_Py);
txtResul=f.Met(n);
}
}
return txtResul.ToString();
}
I have tried initialising with and without this:
PythonEngine.Initialize();
gbl = Py.CreateScope(nombre);
pyPtr=PythonEngine.BeginAllowThreads();
I have also tried to finalize everything and run it, with and without this:
public void Fin() {
PythonEngine.EndAllowThreads(pyPtr);
PythonEngine.Shutdown();
}
I have also tried running each function call as a separate script:
using (Py.GIL()) {
dynamic sys = Py.Import("sys");
string script ="import sysn"+ "from io import StringIOn"+
$"from {NOM_FICH_Py} import {funcion.Split('(')[0]}n"+
"sys.stdout=StringIO()n"+ "sys.stdout.flush()n"+
"sys.stderr=StringIO()n"+ "sys.stderr.flush()n"+
funcion;
var scope = Py.CreateScope();
var vars=scope.Variables();
scope.Exec(script, new PyDict());
salida=sys.stdout.getvalue();
scope.Dispose();
}
I use Python 3.10 and Python .Net 3.0.0-rc 4 and Python.Net 3.0.0-preview2022-03-03 (in other computer) and I cann’t get the ‘reset’ as I executed it like command line.
Thanks in advance.
Extra question, is there any wiki/documentacion to read the detail of functions like CreateScope, NewScope, Exec…? In https://github.com/pythonnet/pythonnet/wiki I haven’t found the function’s detail.
The only supported way to fully reset Python environment with Python.NET is to restart the process.
At the end, the only posible solution it’s to exectue like a script in cmd.
It’s not the best solution (it’s not posible to debug) but it’s the only one I know.
public string ExecCmd(string funcion, string args) {
Process p = new Process();
p.StartInfo=new ProcessStartInfo(PYTHON_EXE, $"{NombreFichApp} {funcion} {args}") {
RedirectStandardOutput=true,
UseShellExecute=false,
CreateNoWindow=true
};
p.Start();
string salida = p.StandardOutput.ReadToEnd();
p.WaitForExit();
return salida;
}
Try enclosing your code with another using block for PythonEngine, you can also use PythonEngine.Shutdown and then reinitialise it every time a method call is made
using(var pyEngine=new PythonEngine())
{
using ( Py.GIL() ) {
using (var scope=gbl.NewScope() ) { //Witout this, it doesn't work too
dynamic f=Py.Import(NOM_FICH_Py);
txtResul=f.Met(n);
}
}
}