Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
introduced PyModule (inherits PyScope)
changed PyScope to inherit from PyObject
  • Loading branch information
lostmsu committed Feb 24, 2021
commit 50834ba7b50247ab71f57fcc0dd475027a531003
14 changes: 12 additions & 2 deletions src/embed_tests/TestFinalizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,17 @@ public void CollectOnShutdown()

PythonEngine.Shutdown();
garbage = Finalizer.Instance.GetCollectedObjects();
Assert.IsEmpty(garbage);

if (garbage.Count > 0)
{
PythonEngine.Initialize();
string objects = string.Join("\n", garbage.Select(ob =>
{
var obj = new PyObject(new BorrowedReference(ob));
return $"{obj} [{obj.GetPythonType()}@{obj.Handle}]";
}));
Assert.Fail("Garbage is not empty:\n" + objects);
}
}

[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] // ensure lack of references to obj
Expand Down Expand Up @@ -173,7 +183,7 @@ public void SimpleTestMemory()
bool oldState = Finalizer.Instance.Enable;
try
{
using (PyObject gcModule = PythonEngine.ImportModule("gc"))
using (PyModule gcModule = PyModule.Import("gc"))
using (PyObject pyCollect = gcModule.GetAttr("collect"))
{
long span1 = CompareWithFinalizerOn(pyCollect, false);
Expand Down
4 changes: 2 additions & 2 deletions src/embed_tests/TestPythonException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public void TestPythonErrorTypeName()
{
try
{
var module = PythonEngine.ImportModule("really____unknown___module");
var module = PyModule.Import("really____unknown___module");
Assert.Fail("Unknown module should not be loaded");
}
catch (PythonException ex)
Expand Down Expand Up @@ -95,7 +95,7 @@ public void TestPythonExceptionFormatNoTraceback()
{
try
{
var module = PythonEngine.ImportModule("really____unknown___module");
var module = PyModule.Import("really____unknown___module");
Assert.Fail("Unknown module should not be loaded");
}
catch (PythonException ex)
Expand Down
4 changes: 3 additions & 1 deletion src/embed_tests/TestRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public static void PyCheck_Iter_PyObject_IsIterable_ThreadingLock_Test()
// TypeFlags.HaveIter set in Python 2. This tests a different code path in PyObject_IsIterable and PyIter_Check.
var threading = Runtime.Runtime.PyImport_ImportModule("threading");
Exceptions.ErrorCheck(threading);
var threadingDict = Runtime.Runtime.PyModule_GetDict(new BorrowedReference(threading));
var threadingDict = Runtime.Runtime.PyModule_GetDict(threading);
Exceptions.ErrorCheck(threadingDict);
var lockType = Runtime.Runtime.PyDict_GetItemString(threadingDict, "Lock");
if (lockType.IsNull)
Expand All @@ -110,6 +110,8 @@ public static void PyCheck_Iter_PyObject_IsIterable_ThreadingLock_Test()
Assert.IsFalse(Runtime.Runtime.PyObject_IsIterable(lockInstance));
Assert.IsFalse(Runtime.Runtime.PyIter_Check(lockInstance));

threading.Dispose();

Runtime.Runtime.Py_Finalize();
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/embed_tests/pyimport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public void Dispose()
[Test]
public void TestDottedName()
{
PyObject module = PythonEngine.ImportModule("PyImportTest.test.one");
var module = PyModule.Import("PyImportTest.test.one");
Assert.IsNotNull(module);
}

Expand All @@ -62,7 +62,7 @@ public void TestDottedName()
[Test]
public void TestSysArgsImportException()
{
PyObject module = PythonEngine.ImportModule("PyImportTest.sysargv");
var module = PyModule.Import("PyImportTest.sysargv");
Assert.IsNotNull(module);
}

Expand Down
3 changes: 2 additions & 1 deletion src/embed_tests/pyinitialize.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@ public static void TestRunExitFuncs()
{
called = true;
};
atexit.InvokeMethod("register", callback.ToPython());
atexit.InvokeMethod("register", callback.ToPython()).Dispose();
atexit.Dispose();
Runtime.Runtime.Shutdown();
Assert.True(called);
}
Expand Down
21 changes: 8 additions & 13 deletions src/runtime/exceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,24 +95,21 @@ internal static Exception ToException(IntPtr ob)
/// </remarks>
public static class Exceptions
{
internal static IntPtr warnings_module;
internal static IntPtr exceptions_module;
internal static PyModule warnings_module;
internal static PyModule exceptions_module;

/// <summary>
/// Initialization performed on startup of the Python runtime.
/// </summary>
internal static void Initialize()
{
string exceptionsModuleName = "builtins";
exceptions_module = Runtime.PyImport_ImportModule(exceptionsModuleName);

Exceptions.ErrorCheck(exceptions_module);
warnings_module = Runtime.PyImport_ImportModule("warnings");
Exceptions.ErrorCheck(warnings_module);
exceptions_module = PyModule.Import(exceptionsModuleName);
warnings_module = PyModule.Import("warnings");
Type type = typeof(Exceptions);
foreach (FieldInfo fi in type.GetFields(BindingFlags.Public | BindingFlags.Static))
{
IntPtr op = Runtime.PyObject_GetAttrString(exceptions_module, fi.Name);
IntPtr op = Runtime.PyObject_GetAttrString(exceptions_module.obj, fi.Name);
if (op != IntPtr.Zero)
{
fi.SetValue(type, op);
Expand Down Expand Up @@ -147,8 +144,8 @@ internal static void Shutdown()
Runtime.XDecref(op);
fi.SetValue(null, IntPtr.Zero);
}
Runtime.Py_CLEAR(ref exceptions_module);
Runtime.Py_CLEAR(ref warnings_module);
exceptions_module.Dispose();
warnings_module.Dispose();
}

/// <summary>
Expand Down Expand Up @@ -348,9 +345,7 @@ public static void warn(string message, IntPtr exception, int stacklevel)
Exceptions.RaiseTypeError("Invalid exception");
}

Runtime.XIncref(warnings_module);
IntPtr warn = Runtime.PyObject_GetAttrString(warnings_module, "warn");
Runtime.XDecref(warnings_module);
IntPtr warn = Runtime.PyObject_GetAttrString(warnings_module.obj, "warn");
Exceptions.ErrorCheck(warn);

IntPtr args = Runtime.PyTuple_New(3);
Expand Down
41 changes: 41 additions & 0 deletions src/runtime/pymodule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;

namespace Python.Runtime
{
public class PyModule : PyScope
{
internal PyModule(ref NewReference reference) : base(ref reference, PyScopeManager.Global) { }
public PyModule(PyObject o) : base(o.Reference, PyScopeManager.Global) { }

/// <summary>
/// Given a module or package name, import the
/// module and return the resulting module object as a <see cref="PyModule"/>.
/// </summary>
/// <param name="name">Fully-qualified module or package name</param>
public static PyModule Import(string name)
{
NewReference op = Runtime.PyImport_ImportModule(name);
PythonException.ThrowIfIsNull(op);
return new PyModule(ref op);
}

/// <summary>
/// Reloads the module, and returns the updated object
/// </summary>
public PyModule Reload()
{
NewReference op = Runtime.PyImport_ReloadModule(this.Reference);
PythonException.ThrowIfIsNull(op);
return new PyModule(ref op);
}

public static PyModule FromString(string name, string code)
{
using NewReference c = Runtime.Py_CompileString(code, "none", (int)RunFlagType.File);
Comment thread
lostmsu marked this conversation as resolved.
PythonException.ThrowIfIsNull(c);
NewReference m = Runtime.PyImport_ExecCodeModule(name, c);
PythonException.ThrowIfIsNull(m);
return new PyModule(ref m);
}
}
}
51 changes: 27 additions & 24 deletions src/runtime/pyscope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,9 @@ public class PyGILAttribute : Attribute
}

[PyGIL]
public class PyScope : DynamicObject, IDisposable
public class PyScope : PyObject
{
public readonly string Name;

/// <summary>
/// the python Module object the scope associated with.
/// </summary>
readonly PyObject obj;
internal BorrowedReference Reference => obj.Reference;
public string Name { get; }

/// <summary>
/// the variable dict of the scope. Borrowed.
Expand All @@ -49,20 +43,24 @@ public class PyScope : DynamicObject, IDisposable
/// </summary>
public event Action<PyScope> OnDispose;

/// <summary>
/// Constructor
/// </summary>
/// <remarks>
/// Create a scope based on a Python Module.
/// </remarks>
internal PyScope(ref NewReference ptr, PyScopeManager manager)
/// <summary>Create a scope based on a Python Module.</summary>
internal PyScope(ref NewReference reference, PyScopeManager manager)
: this(reference.DangerousMoveToPointer(), manager) { }
/// <summary>Create a scope based on a Python Module.</summary>
internal PyScope(BorrowedReference reference, PyScopeManager manager)
: this(reference.DangerousGetAddress(), manager)
{
if (!Runtime.PyType_IsSubtype(Runtime.PyObject_TYPE(ptr), Runtime.PyModuleType))
Runtime.XIncref(reference.DangerousGetAddress());
}

/// <summary>Create a scope based on a Python Module.</summary>
private PyScope(IntPtr ptr, PyScopeManager manager) : base(ptr)
{
if (!Runtime.PyType_IsSubtype(Runtime.PyObject_TYPE(Reference), Runtime.PyModuleType))
{
throw new PyScopeException("object is not a module");
}
Manager = manager ?? PyScopeManager.Global;
obj = ptr.MoveToPyObject();
//Refcount of the variables not increase
variables = Runtime.PyModule_GetDict(Reference).DangerousGetAddress();
PythonException.ThrowIfIsNull(variables);
Expand All @@ -72,7 +70,8 @@ internal PyScope(ref NewReference ptr, PyScopeManager manager)
Runtime.PyEval_GetBuiltins()
);
PythonException.ThrowIfIsNotZero(res);
this.Name = this.Get<string>("__name__");
using var name = this.Get("__name__");
this.Name = name.As<string>();
}

/// <summary>
Expand Down Expand Up @@ -118,7 +117,7 @@ public dynamic Import(string name, string asname = null)
}
else
{
PyObject module = PythonEngine.ImportModule(name);
var module = PyModule.Import(name);
Import(module, asname);
return module;
}
Expand All @@ -132,7 +131,7 @@ public dynamic Import(string name, string asname = null)
/// </remarks>
public void Import(PyScope scope, string asname)
{
this.SetPyValue(asname, scope.obj.Handle);
this.SetPyValue(asname, scope.Handle);
}

/// <summary>
Expand Down Expand Up @@ -169,7 +168,7 @@ public void ImportAll(string name)
}
else
{
PyObject module = PythonEngine.ImportModule(name);
var module = PyModule.Import(name);
ImportAll(module);
}
}
Expand Down Expand Up @@ -503,16 +502,20 @@ public override bool TrySetMember(SetMemberBinder binder, object value)

private void Check()
{
if (this.obj.IsDisposed)
if (this.obj == IntPtr.Zero)
{
throw new PyScopeException($"The scope of name '{Name}' object has been disposed");
}
}

public void Dispose()
protected override void Dispose(bool disposing)
{
if (this.obj == IntPtr.Zero)
{
return;
}
base.Dispose(disposing);
this.OnDispose?.Invoke(this);
this.obj.Dispose();
}
}

Expand Down
65 changes: 16 additions & 49 deletions src/runtime/pythonengine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -468,62 +468,27 @@ public static void EndAllowThreads(IntPtr ts)
Runtime.PyEval_RestoreThread(ts);
}

[Obsolete("Use PyModule.Import")]
public static PyObject ImportModule(string name) => PyModule.Import(name);

/// <summary>
/// ImportModule Method
/// </summary>
/// <remarks>
/// Given a fully-qualified module or package name, import the
/// module and return the resulting module object as a PyObject
/// or null if an exception is raised.
/// </remarks>
public static PyObject ImportModule(string name)
Comment thread
filmor marked this conversation as resolved.
{
IntPtr op = Runtime.PyImport_ImportModule(name);
PythonException.ThrowIfIsNull(op);
return new PyObject(op);
}


/// <summary>
/// ReloadModule Method
/// </summary>
/// <remarks>
/// Given a PyObject representing a previously loaded module, reload
/// the module.
/// </remarks>
[Obsolete("Use PyModule.Reload")]
public static PyObject ReloadModule(PyObject module)
{
IntPtr op = Runtime.PyImport_ReloadModule(module.Handle);
PythonException.ThrowIfIsNull(op);
return new PyObject(op);
}
=> module is PyModule pyModule ? pyModule.Reload() : new PyModule(module).Reload();


/// <summary>
/// ModuleFromString Method
/// </summary>
/// <remarks>
/// Given a string module name and a string containing Python code,
/// execute the code in and return a module of the given name.
/// </remarks>
[Obsolete("Use PyModule.FromString")]
public static PyObject ModuleFromString(string name, string code)
{
IntPtr c = Runtime.Py_CompileString(code, "none", (int)RunFlagType.File);
PythonException.ThrowIfIsNull(c);
IntPtr m = Runtime.PyImport_ExecCodeModule(name, c);
PythonException.ThrowIfIsNull(m);
return new PyObject(m);
}
=> PyModule.FromString(name, code);


public static PyObject Compile(string code, string filename = "", RunFlagType mode = RunFlagType.File)
{
var flag = (int)mode;
IntPtr ptr = Runtime.Py_CompileString(code, filename, flag);
NewReference ptr = Runtime.Py_CompileString(code, filename, flag);
PythonException.ThrowIfIsNull(ptr);
return new PyObject(ptr);
return ptr.MoveToPyObject();
}


/// <summary>
/// Eval Method
/// </summary>
Expand Down Expand Up @@ -742,10 +707,12 @@ public static KeywordArguments kw(params object[] kv)
return dict;
}

public static PyObject Import(string name)
{
return PythonEngine.ImportModule(name);
}
/// <summary>
/// Given a module or package name, import the
/// module and return the resulting module object as a <see cref="PyModule"/>.
/// </summary>
/// <param name="name">Fully-qualified module or package name</param>
public static PyModule Import(string name) => PyModule.Import(name);

public static void SetArgv()
{
Expand Down
Loading