diff --git a/CHANGELOG.md b/CHANGELOG.md
index 52ee08484..9b5dd1816 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -45,6 +45,8 @@ details about the cause of the failure
able to access members that are part of the implementation class, but not the
interface. Use the new `__implementation__` or `__raw_implementation__` properties to
if you need to "downcast" to the implementation class.
+- BREAKING: `==` and `!=` operators on `PyObject` instances now use Python comparison
+ (previously was equivalent to `object.ReferenceEquals(,)`)
- BREAKING: Parameters marked with `ParameterAttributes.Out` are no longer returned in addition
to the regular method return value (unless they are passed with `ref` or `out` keyword).
- BREAKING: Drop support for the long-deprecated CLR.* prefix.
diff --git a/MANIFEST.in b/MANIFEST.in
index 4763ae70f..6458d5778 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,5 +1,6 @@
-recursive-include src/ *
+graft src/runtime
+prune src/runtime/obj
+prune src/runtime/bin
include Directory.Build.*
include pythonnet.sln
include version.txt
-global-exclude **/obj/* **/bin/*
diff --git a/pythonnet/__init__.py b/pythonnet/__init__.py
index fa6ed45cf..9876a0bec 100644
--- a/pythonnet/__init__.py
+++ b/pythonnet/__init__.py
@@ -3,7 +3,7 @@
from typing import Dict, Optional, Union
import clr_loader
-__all__ = ["set_runtime", "set_default_runtime", "load"]
+__all__ = ["set_runtime", "set_runtime_from_env", "load"]
_RUNTIME: Optional[clr_loader.Runtime] = None
_LOADER_ASSEMBLY: Optional[clr_loader.wrappers.Assembly] = None
@@ -30,7 +30,7 @@ def set_runtime(runtime: Union[clr_loader.Runtime, str], **params: str) -> None:
def _get_params_from_env(prefix: str) -> Dict[str, str]:
from os import environ
- full_prefix = f"PYTHONNET_{prefix.upper()}"
+ full_prefix = f"PYTHONNET_{prefix.upper()}_"
len_ = len(full_prefix)
env_vars = {
@@ -63,8 +63,8 @@ def _create_runtime_from_spec(
raise RuntimeError(f"Invalid runtime name: '{spec}'")
-def set_default_runtime() -> None:
- """Set up the default runtime
+def set_runtime_from_env() -> None:
+ """Set up the runtime using the environment
This will use the environment variable PYTHONNET_RUNTIME to decide the
runtime to use, which may be one of netfx, coreclr or mono. The parameters
@@ -80,16 +80,13 @@ def set_default_runtime() -> None:
"""
from os import environ
- print("Set default RUNTIME")
- raise RuntimeError("Shouldn't be called here")
-
spec = environ.get("PYTHONNET_RUNTIME", "default")
runtime = _create_runtime_from_spec(spec)
set_runtime(runtime)
def load(
- runtime: Union[clr_loader.Runtime, str] = "default", **params: Dict[str, str]
+ runtime: Union[clr_loader.Runtime, str, None] = None, **params: str
) -> None:
"""Load Python.NET in the specified runtime
@@ -102,7 +99,10 @@ def load(
return
if _RUNTIME is None:
- set_runtime(runtime, **params)
+ if runtime is None:
+ set_runtime_from_env()
+ else:
+ set_runtime(runtime, **params)
if _RUNTIME is None:
raise RuntimeError("No valid runtime selected")
diff --git a/src/embed_tests/dynamic.cs b/src/embed_tests/dynamic.cs
index 0a181231c..6e3bfc4cb 100644
--- a/src/embed_tests/dynamic.cs
+++ b/src/embed_tests/dynamic.cs
@@ -128,6 +128,28 @@ public void PassPyObjectInNet()
Assert.IsTrue(sys.testattr1.Equals(sys.testattr2));
}
+ // regression test for https://github.com/pythonnet/pythonnet/issues/1848
+ [Test]
+ public void EnumEquality()
+ {
+ using var scope = Py.CreateScope();
+ scope.Exec(@"
+import enum
+
+class MyEnum(enum.IntEnum):
+ OK = 1
+ ERROR = 2
+
+def get_status():
+ return MyEnum.OK
+"
+);
+
+ dynamic MyEnum = scope.Get("MyEnum");
+ dynamic status = scope.Get("get_status").Invoke();
+ Assert.IsTrue(status == MyEnum.OK);
+ }
+
// regression test for https://github.com/pythonnet/pythonnet/issues/1680
[Test]
public void ForEach()
diff --git a/src/embed_tests/packages.config b/src/embed_tests/packages.config
deleted file mode 100644
index 590eaef8c..000000000
--- a/src/embed_tests/packages.config
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/src/runtime/AssemblyManager.cs b/src/runtime/AssemblyManager.cs
index d09d2d76e..a8bbd1f6c 100644
--- a/src/runtime/AssemblyManager.cs
+++ b/src/runtime/AssemblyManager.cs
@@ -334,7 +334,7 @@ public static bool IsValidNamespace(string name)
}
///
- /// Returns an IEnumerable containing the namepsaces exported
+ /// Returns an enumerable collection containing the namepsaces exported
/// by loaded assemblies in the current app domain.
///
public static IEnumerable GetNamespaces ()
diff --git a/src/runtime/ClassManager.cs b/src/runtime/ClassManager.cs
index 6c5558f3a..79ab20e82 100644
--- a/src/runtime/ClassManager.cs
+++ b/src/runtime/ClassManager.cs
@@ -87,7 +87,7 @@ internal static ClassManagerState SaveRuntimeData()
if ((Runtime.PyDict_DelItemString(dict.Borrow(), member) == -1) &&
(Exceptions.ExceptionMatches(Exceptions.KeyError)))
{
- // Trying to remove a key that's not in the dictionary
+ // Trying to remove a key that's not in the dictionary
// raises an error. We don't care about it.
Runtime.PyErr_Clear();
}
@@ -215,7 +215,7 @@ internal static void InitClassBase(Type type, ClassBase impl, ReflectedClrType p
impl.indexer = info.indexer;
impl.richcompare.Clear();
-
+
// Finally, initialize the class __dict__ and return the object.
using var newDict = Runtime.PyObject_GenericGetDict(pyType.Reference);
BorrowedReference dict = newDict.Borrow();
@@ -271,6 +271,15 @@ internal static void InitClassBase(Type type, ClassBase impl, ReflectedClrType p
Runtime.PyDict_SetItem(dict, PyIdentifier.__doc__, doc.Borrow());
}
}
+
+ if (Runtime.PySequence_Contains(dict, PyIdentifier.__doc__) != 1)
+ {
+ // Ensure that at least some doc string is set
+ using var fallbackDoc = Runtime.PyString_FromString(
+ $"Python wrapper for .NET type {type}"
+ );
+ Runtime.PyDict_SetItem(dict, PyIdentifier.__doc__, fallbackDoc.Borrow());
+ }
}
doc.Dispose();
@@ -562,7 +571,7 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl)
return ci;
}
-
+
///
/// This class owns references to PyObjects in the `members` member.
/// The caller has responsibility to DECREF them.
diff --git a/src/runtime/Converter.cs b/src/runtime/Converter.cs
index f86ba7900..e1820f05b 100644
--- a/src/runtime/Converter.cs
+++ b/src/runtime/Converter.cs
@@ -361,7 +361,7 @@ internal static bool ToManagedValue(BorrowedReference value, Type obType,
// conversions (Python string -> managed string).
if (obType == objectType)
{
- if (Runtime.IsStringType(value))
+ if (Runtime.PyString_Check(value))
{
return ToPrimitive(value, stringType, out result, setError);
}
diff --git a/src/runtime/Finalizer.cs b/src/runtime/Finalizer.cs
index e796cacd1..f4b465ecb 100644
--- a/src/runtime/Finalizer.cs
+++ b/src/runtime/Finalizer.cs
@@ -106,6 +106,7 @@ internal IncorrectRefCountException(IntPtr ptr)
#endregion
+ [ForbidPythonThreads]
public void Collect() => this.DisposeAll();
internal void ThrottledCollect()
diff --git a/src/runtime/Native/NewReference.cs b/src/runtime/Native/NewReference.cs
index 25145fc4f..f7a030818 100644
--- a/src/runtime/Native/NewReference.cs
+++ b/src/runtime/Native/NewReference.cs
@@ -47,7 +47,7 @@ public PyObject MoveToPyObject()
///
public NewReference Move()
{
- var result = new NewReference(this);
+ var result = DangerousFromPointer(this.DangerousGetAddress());
this.pointer = default;
return result;
}
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index fad5b9da8..5072f23cd 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -23,6 +23,8 @@
true
snupkg
+ True
+
..\pythonnet.snk
true
diff --git a/src/runtime/PythonEngine.cs b/src/runtime/PythonEngine.cs
index 6e9c4d1f1..e5879ae67 100644
--- a/src/runtime/PythonEngine.cs
+++ b/src/runtime/PythonEngine.cs
@@ -228,6 +228,7 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true,
Assembly assembly = Assembly.GetExecutingAssembly();
// add the contents of clr.py to the module
string clr_py = assembly.ReadStringResource("clr.py");
+
Exec(clr_py, module_globals, locals.Reference);
LoadSubmodule(module_globals, "clr.interop", "interop.py");
@@ -237,14 +238,22 @@ public static void Initialize(IEnumerable args, bool setSysArgv = true,
// add the imported module to the clr module, and copy the API functions
// and decorators into the main clr module.
Runtime.PyDict_SetItemString(clr_dict, "_extras", module);
+
+ // append version
+ var version = typeof(PythonEngine)
+ .Assembly
+ .GetCustomAttribute()
+ .InformationalVersion;
+ using var versionObj = Runtime.PyString_FromString(version);
+ Runtime.PyDict_SetItemString(clr_dict, "__version__", versionObj.Borrow());
+
using var keys = locals.Keys();
foreach (PyObject key in keys)
{
- if (!key.ToString()!.StartsWith("_") || key.ToString()!.Equals("__version__"))
+ if (!key.ToString()!.StartsWith("_"))
{
- PyObject value = locals[key];
+ using PyObject value = locals[key];
Runtime.PyDict_SetItem(clr_dict, key.Reference, value.Reference);
- value.Dispose();
}
key.Dispose();
}
diff --git a/src/runtime/PythonTypes/PyObject.cs b/src/runtime/PythonTypes/PyObject.cs
index cfd3e7158..3d48e22ed 100644
--- a/src/runtime/PythonTypes/PyObject.cs
+++ b/src/runtime/PythonTypes/PyObject.cs
@@ -1075,12 +1075,9 @@ public virtual bool Equals(PyObject? other)
{
return true;
}
- int r = Runtime.PyObject_Compare(this, other);
- if (Exceptions.ErrorOccurred())
- {
- throw PythonException.ThrowLastAsClrException();
- }
- return r == 0;
+ int result = Runtime.PyObject_RichCompareBool(obj, other.obj, Runtime.Py_EQ);
+ if (result < 0) throw PythonException.ThrowLastAsClrException();
+ return result != 0;
}
@@ -1304,6 +1301,18 @@ public override bool TryConvert(ConvertBinder binder, out object? result)
return false;
}
+ private bool TryCompare(PyObject arg, int op, out object @out)
+ {
+ int result = Runtime.PyObject_RichCompareBool(this.obj, arg.obj, op);
+ @out = result != 0;
+ if (result < 0)
+ {
+ Exceptions.Clear();
+ return false;
+ }
+ return true;
+ }
+
public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object? result)
{
using var _ = Py.GIL();
@@ -1352,11 +1361,9 @@ public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg
res = Runtime.PyNumber_InPlaceXor(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.GreaterThan:
- result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) > 0;
- return true;
+ return this.TryCompare((PyObject)arg, Runtime.Py_GT, out result);
case ExpressionType.GreaterThanOrEqual:
- result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) >= 0;
- return true;
+ return this.TryCompare((PyObject)arg, Runtime.Py_GE, out result);
case ExpressionType.LeftShift:
res = Runtime.PyNumber_Lshift(this.obj, ((PyObject)arg).obj);
break;
@@ -1364,11 +1371,9 @@ public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg
res = Runtime.PyNumber_InPlaceLshift(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.LessThan:
- result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) < 0;
- return true;
+ return this.TryCompare((PyObject)arg, Runtime.Py_LT, out result);
case ExpressionType.LessThanOrEqual:
- result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) <= 0;
- return true;
+ return this.TryCompare((PyObject)arg, Runtime.Py_LE, out result);
case ExpressionType.Modulo:
res = Runtime.PyNumber_Remainder(this.obj, ((PyObject)arg).obj);
break;
@@ -1376,8 +1381,9 @@ public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg
res = Runtime.PyNumber_InPlaceRemainder(this.obj, ((PyObject)arg).obj);
break;
case ExpressionType.NotEqual:
- result = Runtime.PyObject_Compare(this.obj, ((PyObject)arg).obj) != 0;
- return true;
+ return this.TryCompare((PyObject)arg, Runtime.Py_NE, out result);
+ case ExpressionType.Equal:
+ return this.TryCompare((PyObject)arg, Runtime.Py_EQ, out result);
case ExpressionType.Or:
res = Runtime.PyNumber_Or(this.obj, ((PyObject)arg).obj);
break;
@@ -1402,6 +1408,40 @@ public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg
return true;
}
+ public static bool operator ==(PyObject? a, PyObject? b)
+ {
+ if (a is null && b is null)
+ {
+ return true;
+ }
+ if (a is null || b is null)
+ {
+ return false;
+ }
+
+ using var _ = Py.GIL();
+ int result = Runtime.PyObject_RichCompareBool(a.obj, b.obj, Runtime.Py_EQ);
+ if (result < 0) throw PythonException.ThrowLastAsClrException();
+ return result != 0;
+ }
+
+ public static bool operator !=(PyObject? a, PyObject? b)
+ {
+ if (a is null && b is null)
+ {
+ return false;
+ }
+ if (a is null || b is null)
+ {
+ return true;
+ }
+
+ using var _ = Py.GIL();
+ int result = Runtime.PyObject_RichCompareBool(a.obj, b.obj, Runtime.Py_NE);
+ if (result < 0) throw PythonException.ThrowLastAsClrException();
+ return result != 0;
+ }
+
// Workaround for https://bugzilla.xamarin.com/show_bug.cgi?id=41509
// See https://github.com/pythonnet/pythonnet/pull/219
internal static object? CheckNone(PyObject pyObj)
@@ -1436,14 +1476,17 @@ public override bool TryUnaryOperation(UnaryOperationBinder binder, out object?
case ExpressionType.Not:
r = Runtime.PyObject_Not(this.obj);
result = r == 1;
+ if (r == -1) Exceptions.Clear();
return r != -1;
case ExpressionType.IsFalse:
r = Runtime.PyObject_IsTrue(this.obj);
result = r == 0;
+ if (r == -1) Exceptions.Clear();
return r != -1;
case ExpressionType.IsTrue:
r = Runtime.PyObject_IsTrue(this.obj);
result = r == 1;
+ if (r == -1) Exceptions.Clear();
return r != -1;
case ExpressionType.Decrement:
case ExpressionType.Increment:
diff --git a/src/runtime/Resources/clr.py b/src/runtime/Resources/clr.py
index 2254e7430..d4330a4d5 100644
--- a/src/runtime/Resources/clr.py
+++ b/src/runtime/Resources/clr.py
@@ -2,8 +2,6 @@
Code in this module gets loaded into the main clr module.
"""
-__version__ = "3.0.0dev"
-
class clrproperty(object):
"""
diff --git a/src/runtime/Runtime.cs b/src/runtime/Runtime.cs
index 6ad1d459f..4dc904f43 100644
--- a/src/runtime/Runtime.cs
+++ b/src/runtime/Runtime.cs
@@ -59,6 +59,11 @@ private static string GetDefaultDllName(Version version)
internal static bool TypeManagerInitialized => _typesInitialized;
internal static readonly bool Is32Bit = IntPtr.Size == 4;
+ // Available in newer .NET Core versions (>= 5) as IntPtr.MaxValue etc.
+ internal static readonly long IntPtrMaxValue = Is32Bit ? Int32.MaxValue : Int64.MaxValue;
+ internal static readonly long IntPtrMinValue = Is32Bit ? Int32.MinValue : Int64.MinValue;
+ internal static readonly ulong UIntPtrMaxValue = Is32Bit ? UInt32.MaxValue : UInt64.MaxValue;
+
// .NET core: System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
internal static bool IsWindows = Environment.OSVersion.Platform == PlatformID.Win32NT;
@@ -94,6 +99,7 @@ internal static int GetRun()
internal static bool HostedInPython;
internal static bool ProcessIsTerminating;
+ ///
/// Initialize the runtime...
///
/// Always call this method from the Main thread. After the
@@ -354,6 +360,7 @@ static bool TryCollectingGarbage(int runs, bool forceBreakLoops)
///
/// Total number of GC loops to run
/// true if a steady state was reached upon the requested number of tries (e.g. on the last try no objects were collected).
+ [ForbidPythonThreads]
public static bool TryCollectingGarbage(int runs)
=> TryCollectingGarbage(runs, forceBreakLoops: false);
@@ -962,31 +969,6 @@ internal static IntPtr PyObject_CallObject(IntPtr pointer, IntPtr args)
internal static int PyObject_RichCompareBool(BorrowedReference value1, BorrowedReference value2, int opid) => Delegates.PyObject_RichCompareBool(value1, value2, opid);
- internal static int PyObject_Compare(BorrowedReference value1, BorrowedReference value2)
- {
- int res;
- res = PyObject_RichCompareBool(value1, value2, Py_LT);
- if (-1 == res)
- return -1;
- else if (1 == res)
- return -1;
-
- res = PyObject_RichCompareBool(value1, value2, Py_EQ);
- if (-1 == res)
- return -1;
- else if (1 == res)
- return 0;
-
- res = PyObject_RichCompareBool(value1, value2, Py_GT);
- if (-1 == res)
- return -1;
- else if (1 == res)
- return 1;
-
- Exceptions.SetError(Exceptions.SystemError, "Error comparing objects");
- return -1;
- }
-
internal static int PyObject_IsInstance(BorrowedReference ob, BorrowedReference type) => Delegates.PyObject_IsInstance(ob, type);
@@ -1306,13 +1288,6 @@ internal static bool PyFloat_Check(BorrowedReference ob)
//====================================================================
// Python string API
//====================================================================
- internal static bool IsStringType(BorrowedReference op)
- {
- BorrowedReference t = PyObject_TYPE(op);
- return (t == PyStringType)
- || (t == PyUnicodeType);
- }
-
internal static bool PyString_Check(BorrowedReference ob)
{
return PyObject_TYPE(ob) == PyStringType;
diff --git a/src/runtime/Types/ArrayObject.cs b/src/runtime/Types/ArrayObject.cs
index bda717e56..b95934baf 100644
--- a/src/runtime/Types/ArrayObject.cs
+++ b/src/runtime/Types/ArrayObject.cs
@@ -520,7 +520,7 @@ static IntPtr AllocateBufferProcs()
#endregion
///
- ///
+ ///
///
public static void InitializeSlots(PyType type, ISet initialized, SlotsHolder slotsHolder)
{
diff --git a/src/runtime/Types/ClassObject.cs b/src/runtime/Types/ClassObject.cs
index 474e9dd7b..cc42039e8 100644
--- a/src/runtime/Types/ClassObject.cs
+++ b/src/runtime/Types/ClassObject.cs
@@ -70,22 +70,9 @@ static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, Bo
// Primitive types do not have constructors, but they look like
// they do from Python. If the ClassObject represents one of the
// convertible primitive types, just convert the arg directly.
- if (type.IsPrimitive || type == typeof(string))
+ if (type.IsPrimitive)
{
- if (Runtime.PyTuple_Size(args) != 1)
- {
- Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments");
- return default;
- }
-
- BorrowedReference op = Runtime.PyTuple_GetItem(args, 0);
-
- if (!Converter.ToManaged(op, type, out var result, true))
- {
- return default;
- }
-
- return CLRObject.GetReference(result!, tp);
+ return NewPrimitive(tp, args, type);
}
if (type.IsAbstract)
@@ -99,6 +86,11 @@ static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, Bo
return NewEnum(type, args, tp);
}
+ if (type == typeof(string))
+ {
+ return NewString(args, tp);
+ }
+
if (IsGenericNullable(type))
{
// Nullable has special handling in .NET runtime.
@@ -112,6 +104,166 @@ static NewReference tp_new_impl(BorrowedReference tp, BorrowedReference args, Bo
return self.NewObjectToPython(obj, tp);
}
+ ///
+ /// Construct a new .NET String object from Python args
+ ///
+ /// This manual implementation of all individual relevant constructors
+ /// is required because System.String can't be allocated uninitialized.
+ ///
+ /// Additionally, it implements `String(pythonStr)`
+ ///
+ private static NewReference NewString(BorrowedReference args, BorrowedReference tp)
+ {
+ var argCount = Runtime.PyTuple_Size(args);
+
+ string? result = null;
+ if (argCount == 1)
+ {
+ BorrowedReference ob = Runtime.PyTuple_GetItem(args, 0);
+ if (Runtime.PyString_Check(ob))
+ {
+ if (Runtime.GetManagedString(ob) is string val)
+ result = val;
+ }
+ else if (Converter.ToManagedValue(ob, typeof(char[]), out object? arr, false))
+ {
+ result = new String((char[])arr!);
+ }
+ }
+ else if (argCount == 2)
+ {
+ BorrowedReference p1 = Runtime.PyTuple_GetItem(args, 0);
+ BorrowedReference p2 = Runtime.PyTuple_GetItem(args, 1);
+
+ if (
+ Converter.ToManagedValue(p1, typeof(char), out object? chr, false) &&
+ Converter.ToManagedValue(p2, typeof(int), out object? count, false)
+ )
+ {
+ result = new String((char)chr!, (int)count!);
+ }
+ }
+ else if (argCount == 3)
+ {
+ BorrowedReference p1 = Runtime.PyTuple_GetItem(args, 0);
+ BorrowedReference p2 = Runtime.PyTuple_GetItem(args, 1);
+ BorrowedReference p3 = Runtime.PyTuple_GetItem(args, 2);
+
+ if (
+ Converter.ToManagedValue(p1, typeof(char[]), out object? arr, false) &&
+ Converter.ToManagedValue(p2, typeof(int), out object? offset, false) &&
+ Converter.ToManagedValue(p3, typeof(int), out object? length, false)
+ )
+ {
+ result = new String((char[])arr!, (int)offset!, (int)length!);
+ }
+ }
+
+ if (result != null)
+ return CLRObject.GetReference(result!, tp);
+
+ Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments");
+ return default;
+ }
+
+ ///
+ /// Create a new Python object for a primitive type
+ ///
+ /// The primitive types are Boolean, Byte, SByte, Int16, UInt16,
+ /// Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double,
+ /// and Single.
+ ///
+ /// All numeric types and Boolean can be handled by a simple
+ /// conversion, (U)IntPtr has to be handled separately as we
+ /// do not want to convert them automically to/from integers.
+ ///
+ /// .NET type to construct
+ /// Corresponding Python type
+ /// Constructor arguments
+ private static NewReference NewPrimitive(BorrowedReference tp, BorrowedReference args, Type type)
+ {
+ // TODO: Handle IntPtr
+ if (Runtime.PyTuple_Size(args) != 1)
+ {
+ Exceptions.SetError(Exceptions.TypeError, "no constructors match given arguments");
+ return default;
+ }
+
+ BorrowedReference op = Runtime.PyTuple_GetItem(args, 0);
+ object? result = null;
+
+ if (type == typeof(IntPtr))
+ {
+ if (ManagedType.GetManagedObject(op) is CLRObject clrObject)
+ {
+ switch (clrObject.inst)
+ {
+ case nint val:
+ result = new IntPtr(val);
+ break;
+ case Int64 val:
+ result = new IntPtr(val);
+ break;
+ case Int32 val:
+ result = new IntPtr(val);
+ break;
+ }
+ }
+ else if (Runtime.PyInt_Check(op))
+ {
+ long? num = Runtime.PyLong_AsLongLong(op);
+ if (num is long n && n >= Runtime.IntPtrMinValue && n <= Runtime.IntPtrMaxValue)
+ {
+ result = new IntPtr(n);
+ }
+ else
+ {
+ Exceptions.SetError(Exceptions.OverflowError, "value not in range for IntPtr");
+ return default;
+ }
+ }
+ }
+
+ if (type == typeof(UIntPtr))
+ {
+ if (ManagedType.GetManagedObject(op) is CLRObject clrObject)
+ {
+ switch (clrObject.inst)
+ {
+ case nuint val:
+ result = new UIntPtr(val);
+ break;
+ case UInt64 val:
+ result = new UIntPtr(val);
+ break;
+ case UInt32 val:
+ result = new UIntPtr(val);
+ break;
+ }
+ }
+ else if (Runtime.PyInt_Check(op))
+ {
+ ulong? num = Runtime.PyLong_AsUnsignedLongLong(op);
+ if (num is ulong n && n <= Runtime.UIntPtrMaxValue)
+ {
+ result = new UIntPtr(n);
+ }
+ else
+ {
+ Exceptions.SetError(Exceptions.OverflowError, "value not in range for UIntPtr");
+ return default;
+ }
+ }
+ }
+
+ if (result == null && !Converter.ToManaged(op, type, out result, true))
+ {
+ return default;
+ }
+
+ return CLRObject.GetReference(result!, tp);
+ }
+
protected virtual void SetTypeNewSlot(BorrowedReference pyType, SlotsHolder slotsHolder)
{
TypeManager.InitializeSlotIfEmpty(pyType, TypeOffset.tp_new, new Interop.BBB_N(tp_new_impl), slotsHolder);
diff --git a/src/runtime/Types/EventBinding.cs b/src/runtime/Types/EventBinding.cs
index 9eb2382ec..5c47d4aab 100644
--- a/src/runtime/Types/EventBinding.cs
+++ b/src/runtime/Types/EventBinding.cs
@@ -70,7 +70,6 @@ public static NewReference nb_inplace_subtract(BorrowedReference ob, BorrowedRef
return new NewReference(ob);
}
- ///
public static int tp_descr_set(BorrowedReference ds, BorrowedReference ob, BorrowedReference val)
=> EventObject.tp_descr_set(ds, ob, val);
diff --git a/src/runtime/Types/ReflectedClrType.cs b/src/runtime/Types/ReflectedClrType.cs
index 2e8f95924..b787939be 100644
--- a/src/runtime/Types/ReflectedClrType.cs
+++ b/src/runtime/Types/ReflectedClrType.cs
@@ -22,7 +22,6 @@ internal ReflectedClrType(BorrowedReference original) : base(original) { }
///
///
/// Returned might be partially initialized.
- /// If you need fully initialized type, use
///
public static ReflectedClrType GetOrCreate(Type type)
{
diff --git a/src/testing/conversiontest.cs b/src/testing/conversiontest.cs
index 7a00f139e..272bb74c2 100644
--- a/src/testing/conversiontest.cs
+++ b/src/testing/conversiontest.cs
@@ -1,5 +1,6 @@
namespace Python.Test
{
+ using System;
using System.Collections.Generic;
///
@@ -26,6 +27,8 @@ public ConversionTest()
public ulong UInt64Field = 0;
public float SingleField = 0.0F;
public double DoubleField = 0.0;
+ public IntPtr IntPtrField = IntPtr.Zero;
+ public UIntPtr UIntPtrField = UIntPtr.Zero;
public decimal DecimalField = 0;
public string StringField;
public ShortEnum EnumField;
@@ -42,7 +45,7 @@ public ConversionTest()
}
-
+
public interface ISpam
{
@@ -63,7 +66,7 @@ public string GetValue()
return value;
}
}
-
+
public class UnicodeString
{
public string value = "안녕";
diff --git a/tests/test_constructors.py b/tests/test_constructors.py
index 8e7ef2794..3e0b1bb93 100644
--- a/tests/test_constructors.py
+++ b/tests/test_constructors.py
@@ -3,6 +3,7 @@
"""Test CLR class constructor support."""
import pytest
+import sys
import System
@@ -69,3 +70,33 @@ def test_default_constructor_fallback():
with pytest.raises(TypeError):
ob = DefaultConstructorMatching("2")
+
+
+def test_constructor_leak():
+ from System import Uri
+ from Python.Runtime import Runtime
+
+ uri = Uri("http://www.python.org")
+ Runtime.TryCollectingGarbage(20)
+ ref_count = sys.getrefcount(uri)
+
+ # check disabled due to GC uncertainty
+ # assert ref_count == 1
+
+
+
+def test_string_constructor():
+ from System import String, Char, Array
+
+ ob = String('A', 10)
+ assert ob == 'A' * 10
+
+ arr = Array[Char](10)
+ for i in range(10):
+ arr[i] = Char(str(i))
+
+ ob = String(arr)
+ assert ob == "0123456789"
+
+ ob = String(arr, 5, 4)
+ assert ob == "5678"
diff --git a/tests/test_conversion.py b/tests/test_conversion.py
index 6693d8000..a5b4c6fd9 100644
--- a/tests/test_conversion.py
+++ b/tests/test_conversion.py
@@ -25,7 +25,7 @@ def test_bool_conversion():
with pytest.raises(TypeError):
ob.BooleanField = 1
-
+
with pytest.raises(TypeError):
ob.BooleanField = 0
@@ -679,3 +679,37 @@ def test_iconvertible_conversion():
assert 1024 == change_type(1024, System.Int32)
assert 1024 == change_type(1024, System.Int64)
assert 1024 == change_type(1024, System.Int16)
+
+def test_intptr_construction():
+ from System import IntPtr, UIntPtr, Int64, UInt64
+ from ctypes import sizeof, c_void_p
+
+ ptr_size = sizeof(c_void_p)
+ max_intptr = 2 ** (ptr_size * 8 - 1) - 1
+ min_intptr = -max_intptr - 1
+ max_uintptr = 2 ** (ptr_size * 8) - 1
+ min_uintptr = 0
+
+ ob = ConversionTest()
+
+ assert ob.IntPtrField == IntPtr.Zero
+ assert ob.UIntPtrField == UIntPtr.Zero
+
+ for v in [0, -1, 1024, max_intptr, min_intptr]:
+ ob.IntPtrField = IntPtr(Int64(v))
+ assert ob.IntPtrField == IntPtr(v)
+ assert ob.IntPtrField.ToInt64() == v
+
+ for v in [min_intptr - 1, max_intptr + 1]:
+ with pytest.raises(OverflowError):
+ IntPtr(v)
+
+ for v in [0, 1024, min_uintptr, max_uintptr, max_intptr]:
+ ob.UIntPtrField = UIntPtr(UInt64(v))
+ assert ob.UIntPtrField == UIntPtr(v)
+ assert ob.UIntPtrField.ToUInt64() == v
+
+ for v in [min_uintptr - 1, max_uintptr + 1, min_intptr]:
+ with pytest.raises(OverflowError):
+ UIntPtr(v)
+
diff --git a/tests/test_docstring.py b/tests/test_docstring.py
index 640a61915..36c925a74 100644
--- a/tests/test_docstring.py
+++ b/tests/test_docstring.py
@@ -25,3 +25,10 @@ def test_doc_without_ctor():
assert DocWithoutCtorTest.__doc__ == 'DocWithoutCtorTest Class'
assert DocWithoutCtorTest.TestMethod.__doc__ == 'DocWithoutCtorTest TestMethod'
assert DocWithoutCtorTest.StaticTestMethod.__doc__ == 'DocWithoutCtorTest StaticTestMethod'
+
+
+def test_doc_primitve():
+ from System import Int64, String
+
+ assert Int64.__doc__ is not None
+ assert String.__doc__ is not None
diff --git a/tests/test_enum.py b/tests/test_enum.py
index 981fb735c..f24f95b36 100644
--- a/tests/test_enum.py
+++ b/tests/test_enum.py
@@ -15,7 +15,6 @@ def test_enum_standard_attrs():
assert DayOfWeek.__name__ == 'DayOfWeek'
assert DayOfWeek.__module__ == 'System'
assert isinstance(DayOfWeek.__dict__, DictProxyType)
- assert DayOfWeek.__doc__ is None
def test_enum_get_member():
@@ -139,7 +138,7 @@ def test_enum_undefined_value():
# This should fail because our test enum doesn't have it.
with pytest.raises(ValueError):
Test.FieldTest().EnumField = Test.ShortEnum(20)
-
+
# explicitly permit undefined values
Test.FieldTest().EnumField = Test.ShortEnum(20, True)
@@ -157,6 +156,6 @@ def test_enum_conversion():
with pytest.raises(TypeError):
Test.FieldTest().EnumField = "str"
-
+
with pytest.raises(TypeError):
Test.FieldTest().EnumField = 1
diff --git a/version.txt b/version.txt
index e16bcc8be..dc72b3783 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-3.0.0-dev1
+3.0.0-rc4