using System;
using System.Threading;
using NUnit.Framework;
using Python.Runtime;
namespace Python.EmbeddingTest
{
public class Modules
{
private PyModule ps;
[SetUp]
public void SetUp()
{
using (Py.GIL())
{
ps = Py.CreateScope("test");
}
}
[TearDown]
public void Dispose()
{
using (Py.GIL())
{
ps.Dispose();
ps = null;
}
}
///
/// Eval a Python expression and obtain its return value.
///
[Test]
public void TestEval()
{
using (Py.GIL())
{
ps.Set("a", 1);
var result = ps.Eval("a + 2");
Assert.That(result, Is.EqualTo(3));
}
}
///
/// Exec Python statements and obtain the variables created.
///
[Test]
public void TestExec()
{
using (Py.GIL())
{
ps.Set("bb", 100); //declare a global variable
ps.Set("cc", 10); //declare a local variable
ps.Exec("aa = bb + cc + 3");
var result = ps.Get("aa");
Assert.That(result, Is.EqualTo(113));
}
}
///
/// Compile an expression into an ast object;
/// Execute the ast and obtain its return value.
///
[Test]
public void TestCompileExpression()
{
using (Py.GIL())
{
ps.Set("bb", 100); //declare a global variable
ps.Set("cc", 10); //declare a local variable
PyObject script = PythonEngine.Compile("bb + cc + 3", "", RunFlagType.Eval);
var result = ps.Execute(script);
Assert.That(result, Is.EqualTo(113));
}
}
///
/// Compile Python statements into an ast object;
/// Execute the ast;
/// Obtain the local variables created.
///
[Test]
public void TestCompileStatements()
{
using (Py.GIL())
{
ps.Set("bb", 100); //declare a global variable
ps.Set("cc", 10); //declare a local variable
PyObject script = PythonEngine.Compile("aa = bb + cc + 3", "", RunFlagType.File);
ps.Execute(script);
var result = ps.Get("aa");
Assert.That(result, Is.EqualTo(113));
}
}
///
/// Create a function in the scope, then the function can read variables in the scope.
/// It cannot write the variables unless it uses the 'global' keyword.
///
[Test]
public void TestScopeFunction()
{
using (Py.GIL())
{
ps.Set("bb", 100);
ps.Set("cc", 10);
ps.Exec(
"def func1():\n" +
" bb = cc + 10\n");
dynamic func1 = ps.Get("func1");
func1(); //call the function, it can be called any times
var result = ps.Get("bb");
Assert.That(result, Is.EqualTo(100));
ps.Set("bb", 100);
ps.Set("cc", 10);
ps.Exec(
"def func2():\n" +
" global bb\n" +
" bb = cc + 10\n");
dynamic func2 = ps.Get("func2");
func2();
result = ps.Get("bb");
Assert.That(result, Is.EqualTo(20));
}
}
///
/// Create a class in the scope, the class can read variables in the scope.
/// Its methods can write the variables with the help of 'global' keyword.
///
[Test]
public void TestScopeClass()
{
using (Py.GIL())
{
dynamic _ps = ps;
_ps.bb = 100;
ps.Exec(
"class Class1():\n" +
" def __init__(self, value):\n" +
" self.value = value\n" +
" def call(self, arg):\n" +
" return self.value + bb + arg\n" + //use scope variables
" def update(self, arg):\n" +
" global bb\n" +
" bb = self.value + arg\n" //update scope variable
);
dynamic obj1 = _ps.Class1(20);
var result = obj1.call(10).As();
Assert.AreEqual(130, result);
obj1.update(10);
result = ps.Get("bb");
Assert.AreEqual(30, result);
}
}
///
/// Create a class in the scope, the class can read variables in the scope.
/// Its methods can write the variables with the help of 'global' keyword.
///
[Test]
public void TestCreateVirtualPackageStructure()
{
using (Py.GIL())
{
using var _p1 = PyModule.FromString("test", "");
// Sub-module
using var _p2 = PyModule.FromString("test.scope",
"class Class1():\n" +
" def __init__(self, value):\n" +
" self.value = value\n" +
" def call(self, arg):\n" +
" return self.value + bb + arg\n" + // use scope variables
" def update(self, arg):\n" +
" global bb\n" +
" bb = self.value + arg\n", // update scope variable
"test"
);
dynamic ps2 = Py.Import("test.scope");
ps2.bb = 100;
dynamic obj1 = ps2.Class1(20);
var result = obj1.call(10).As();
Assert.AreEqual(130, result);
obj1.update(10);
result = ps2.Get("bb");
Assert.AreEqual(30, result);
}
}
///
/// Test setting the file attribute via a FromString parameter
///
[Test]
public void TestCreateModuleWithFilename()
{
using var _gil = Py.GIL();
using var mod = PyModule.FromString("mod", "");
using var modWithoutName = PyModule.FromString("mod_without_name", "", " ");
using var modNullName = PyModule.FromString("mod_null_name", "", null);
using var modWithName = PyModule.FromString("mod_with_name", "", "some_filename");
Assert.That(mod.Get("__file__"), Is.EqualTo("none"));
Assert.That(modWithoutName.Get("__file__"), Is.EqualTo("none"));
Assert.That(modNullName.Get("__file__"), Is.EqualTo("none"));
Assert.That(modWithName.Get("__file__"), Is.EqualTo("some_filename"));
}
///
/// Import a python module into the session.
/// Equivalent to the Python "import" statement.
///
[Test]
public void TestImportModule()
{
using (Py.GIL())
{
dynamic sys = ps.Import("sys");
Assert.That(ps.Contains("sys"), Is.True);
ps.Exec("sys.attr1 = 2");
var value1 = ps.Eval("sys.attr1");
var value2 = sys.attr1.As();
Assert.That(value1, Is.EqualTo(2));
Assert.AreEqual(2, value2);
//import as
ps.Import("sys", "sys1");
Assert.That(ps.Contains("sys1"), Is.True);
}
}
///
/// Create a scope and import variables from a scope,
/// exec Python statements in the scope then discard it.
///
[Test]
public void TestImportScope()
{
using (Py.GIL())
{
ps.Set("bb", 100);
ps.Set("cc", 10);
using (var scope = Py.CreateScope())
{
scope.Import(ps, "ps");
scope.Exec("aa = ps.bb + ps.cc + 3");
var result = scope.Get("aa");
Assert.That(result, Is.EqualTo(113));
}
Assert.That(ps.Contains("aa"), Is.False);
}
}
///
/// Create a scope and import variables from a scope,
/// exec Python statements in the scope then discard it.
///
[Test]
public void TestImportAllFromScope()
{
using (Py.GIL())
{
ps.Set("bb", 100);
ps.Set("cc", 10);
using (var scope = ps.NewScope())
{
scope.Exec("aa = bb + cc + 3");
var result = scope.Get("aa");
Assert.That(result, Is.EqualTo(113));
}
Assert.That(ps.Contains("aa"), Is.False);
}
}
///
/// Create a scope and import variables from a scope,
/// call the function imported.
///
[Test]
public void TestImportScopeFunction()
{
using (Py.GIL())
{
ps.Set("bb", 100);
ps.Set("cc", 10);
ps.Exec(
"def func1():\n" +
" return cc + bb\n");
using (var scope = ps.NewScope())
{
//'func1' is imported from the origion scope
scope.Exec(
"def func2():\n" +
" return func1() - cc - bb\n");
dynamic func2 = scope.Get("func2");
var result1 = func2().As();
Assert.AreEqual(0, result1);
scope.Set("cc", 20);//it has no effect on the globals of 'func1'
var result2 = func2().As();
Assert.AreEqual(-10, result2);
scope.Set("cc", 10); //rollback
ps.Set("cc", 20);
var result3 = func2().As();
Assert.AreEqual(10, result3);
ps.Set("cc", 10); //rollback
}
}
}
///
/// Use the locals() and globals() method just like in python module
///
[Test]
public void TestVariables()
{
using (Py.GIL())
{
(ps.Variables() as dynamic)["ee"] = new PyInt(200);
var a0 = ps.Get("ee");
Assert.That(a0, Is.EqualTo(200));
ps.Exec("locals()['ee'] = 210");
var a1 = ps.Get("ee");
Assert.That(a1, Is.EqualTo(210));
ps.Exec("globals()['ee'] = 220");
var a2 = ps.Get("ee");
Assert.That(a2, Is.EqualTo(220));
using (var item = ps.Variables())
{
item["ee"] = new PyInt(230);
}
var a3 = ps.Get("ee");
Assert.That(a3, Is.EqualTo(230));
}
}
///
/// Share a pyscope by multiple threads.
///
[Test]
public void TestThread()
{
//After the proposal here https://github.com/pythonnet/pythonnet/pull/419 complished,
//the BeginAllowThreads statement blow and the last EndAllowThreads statement
//should be removed.
dynamic _ps = ps;
var ts = PythonEngine.BeginAllowThreads();
try
{
using (Py.GIL())
{
_ps.res = 0;
_ps.bb = 100;
_ps.th_cnt = 0;
//add function to the scope
//can be call many times, more efficient than ast
ps.Exec(
"import threading\n"+
"lock = threading.Lock()\n"+
"def update():\n" +
" global res, th_cnt\n" +
" with lock:\n" +
" res += bb + 1\n" +
" th_cnt += 1\n"
);
}
int th_cnt = 100;
for (int i = 0; i < th_cnt; i++)
{
System.Threading.Thread th = new System.Threading.Thread(() =>
{
using (Py.GIL())
{
//ps.GetVariable("update")(); //call the scope function dynamicly
_ps.update();
}
});
th.Start();
}
//equivalent to Thread.Join, make the main thread join the GIL competition
int cnt = 0;
while (cnt != th_cnt)
{
using (Py.GIL())
{
cnt = ps.Get("th_cnt");
}
Thread.Yield();
}
using (Py.GIL())
{
var result = ps.Get("res");
Assert.That(result, Is.EqualTo(101 * th_cnt));
}
}
finally
{
PythonEngine.EndAllowThreads(ts);
}
}
[Test]
public void TestCreate()
{
using var scope = Py.CreateScope();
Assert.That(PyModule.SysModules.HasKey("testmod"), Is.False);
PyModule testmod = new PyModule("testmod");
testmod.SetAttr("testattr1", "True".ToPython());
PyModule.SysModules.SetItem("testmod", testmod);
using PyObject code = PythonEngine.Compile(
"import testmod\n" +
"x = testmod.testattr1"
);
scope.Execute(code);
Assert.That(scope.TryGet("x", out dynamic x), Is.True);
Assert.AreEqual("True", x.ToString());
}
[Test]
public void ImportClrNamespace()
{
Py.Import(GetType().Namespace);
}
}
}