Skip to content

Remove Utf8Marshaler, set PyScope base class to PyObject, added PyModule #1391

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion src/embed_tests/GlobalTestsSetup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Python.EmbeddingTest
// As the SetUpFixture, the OneTimeTearDown of this class is executed after
// all tests have run.
[SetUpFixture]
public class GlobalTestsSetup
public partial class GlobalTestsSetup
{
[OneTimeTearDown]
public void FinalCleanup()
Expand Down
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
2 changes: 1 addition & 1 deletion src/embed_tests/TestNativeTypeOffset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

using Python.Runtime;

namespace Python.EmbeddingPythonTest
namespace Python.EmbeddingTest
{
public class TestNativeTypeOffset
{
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
8 changes: 8 additions & 0 deletions src/runtime/BorrowedReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ public BorrowedReference(IntPtr pointer)
=> a.pointer == b.pointer;
public static bool operator !=(BorrowedReference a, BorrowedReference b)
=> a.pointer != b.pointer;
public static bool operator ==(BorrowedReference reference, NullOnly @null)
=> reference.IsNull;
public static bool operator !=(BorrowedReference reference, NullOnly @null)
=> !reference.IsNull;
public static bool operator ==(NullOnly @null, BorrowedReference reference)
=> reference.IsNull;
public static bool operator !=(NullOnly @null, BorrowedReference reference)
=> !reference.IsNull;

public override bool Equals(object obj) {
if (obj is IntPtr ptr)
Expand Down
45 changes: 0 additions & 45 deletions src/runtime/CustomMarshaler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,49 +189,4 @@ public static ICustomMarshaler GetInstance(string cookie)
return Instance;
}
}


/// <summary>
/// Custom Marshaler to deal with Managed String to Native
/// conversion on UTF-8. Use on functions that expect UTF-8 encoded
/// strings like `PyUnicode_FromStringAndSize`
/// </summary>
/// <remarks>
/// If instead we used `MarshalAs(UnmanagedType.LPWStr)` the output to
/// `foo` would be `f\x00o\x00o\x00`.
/// </remarks>
internal class Utf8Marshaler : MarshalerBase
{
private static readonly MarshalerBase Instance = new Utf8Marshaler();
private static readonly Encoding PyEncoding = Encoding.UTF8;

public override IntPtr MarshalManagedToNative(object managedObj)
{
var s = managedObj as string;

if (s == null)
{
return IntPtr.Zero;
}

byte[] bStr = PyEncoding.GetBytes(s + "\0");
IntPtr mem = Marshal.AllocHGlobal(bStr.Length);
try
{
Marshal.Copy(bStr, 0, mem, bStr.Length);
}
catch (Exception)
{
Marshal.FreeHGlobal(mem);
throw;
}

return mem;
}

public static ICustomMarshaler GetInstance(string cookie)
{
return Instance;
}
}
}
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
6 changes: 3 additions & 3 deletions src/runtime/importhook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public static unsafe NewReference GetCLRModule(BorrowedReference fromList = defa

// find any items from the from list and get them from the root if they're not
// already in the module dictionary
if (fromList != null && fromList != default)
if (fromList != null)
{
if (Runtime.PyTuple_Check(fromList))
{
Expand Down Expand Up @@ -224,7 +224,7 @@ public static IntPtr __import__(IntPtr self, IntPtr argsRaw, IntPtr kw)
if (num_args >= 4)
{
fromList = Runtime.PyTuple_GetItem(args, 3);
if (fromList != default &&
if (fromList != null &&
Runtime.PyObject_IsTrue(fromList) == 1)
{
fromlist = true;
Expand Down Expand Up @@ -297,7 +297,7 @@ public static IntPtr __import__(IntPtr self, IntPtr argsRaw, IntPtr kw)
BorrowedReference modules = Runtime.PyImport_GetModuleDict();
BorrowedReference module = Runtime.PyDict_GetItem(modules, py_mod_name);

if (module != default)
if (module != null)
{
if (fromlist)
{
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);
PythonException.ThrowIfIsNull(c);
NewReference m = Runtime.PyImport_ExecCodeModule(name, c);
PythonException.ThrowIfIsNull(m);
return new PyModule(ref m);
}
}
}
Loading