diff --git a/src/CSnakes.Runtime/CPython/Import.cs b/src/CSnakes.Runtime/CPython/Import.cs
index ff8f4f0c..e8389179 100644
--- a/src/CSnakes.Runtime/CPython/Import.cs
+++ b/src/CSnakes.Runtime/CPython/Import.cs
@@ -15,9 +15,24 @@ internal static PyObject Import(string name)
nint pyName = AsPyUnicodeObject(name);
nint module = PyImport_Import(pyName);
Py_DecRefRaw(pyName);
- return new(module);
+ return PyObject.Create(module);
}
+ protected static nint GetBuiltin(string name)
+ {
+ nint pyName = AsPyUnicodeObject("builtins");
+ nint pyAttrName = AsPyUnicodeObject(name);
+ nint module = PyImport_Import(pyName);
+ nint attr = PyObject_GetAttrRaw(module, pyAttrName);
+ if (attr == IntPtr.Zero)
+ {
+ PyObject.ThrowPythonExceptionAsClrException();
+ }
+ Py_DecRefRaw(pyName);
+ Py_DecRefRaw(pyAttrName);
+ return attr;
+ }
+
///
/// Import and return a reference to the module `name`
///
diff --git a/src/CSnakes.Runtime/CPython/Init.cs b/src/CSnakes.Runtime/CPython/Init.cs
index c32d54e6..3119a98f 100644
--- a/src/CSnakes.Runtime/CPython/Init.cs
+++ b/src/CSnakes.Runtime/CPython/Init.cs
@@ -1,4 +1,5 @@
using CSnakes.Runtime.Python;
+using CSnakes.Runtime.Python.Interns;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
@@ -79,11 +80,7 @@ private void InitializeEmbeddedPython()
PyDictType = GetTypeRaw(PyDict_New());
PyBytesType = GetTypeRaw(PyBytes_FromByteSpan(new byte[] { }));
ItemsStrIntern = AsPyUnicodeObject("items");
-
- // Import builtins module
- var builtinsMod = Import("builtins");
- PyNone = GetAttr(builtinsMod, "None");
- Py_DecRef(builtinsMod);
+ PyNone = GetBuiltin("None");
}
PyEval_SaveThread();
}
diff --git a/src/CSnakes.Runtime/CPython/None.cs b/src/CSnakes.Runtime/CPython/None.cs
index eb3fb93c..a56ebac1 100644
--- a/src/CSnakes.Runtime/CPython/None.cs
+++ b/src/CSnakes.Runtime/CPython/None.cs
@@ -1,4 +1,5 @@
-namespace CSnakes.Runtime.CPython;
+using CSnakes.Runtime.Python;
+namespace CSnakes.Runtime.CPython;
internal unsafe partial class CPythonAPI
{
@@ -10,7 +11,16 @@ internal unsafe partial class CPythonAPI
/// A new reference to None. In newer versions of Python, None is immortal anyway.
internal static nint GetNone()
{
+ if (PyNone == IntPtr.Zero)
+ {
+ throw new InvalidOperationException("Python is not initialized. You cannot call this method outside of a Python Environment context.");
+ }
Py_IncRefRaw(PyNone);
return PyNone;
}
+
+ internal static bool IsNone(PyObject o)
+ {
+ return PyNone == o.DangerousGetHandle();
+ }
}
diff --git a/src/CSnakes.Runtime/CPython/Object.cs b/src/CSnakes.Runtime/CPython/Object.cs
index d4c59056..3fcc9a6e 100644
--- a/src/CSnakes.Runtime/CPython/Object.cs
+++ b/src/CSnakes.Runtime/CPython/Object.cs
@@ -106,6 +106,9 @@ internal static bool HasAttr(PyObject ob, string name)
[LibraryImport(PythonLibraryName)]
internal static partial IntPtr PyObject_GetAttr(PyObject ob, IntPtr attr);
+ [LibraryImport(PythonLibraryName, EntryPoint = "PyObject_GetAttr")]
+ private static partial IntPtr PyObject_GetAttrRaw(IntPtr ob, IntPtr attr);
+
///
/// Does the object ob have the attr `attr`?
///
diff --git a/src/CSnakes.Runtime/PyObjectTypeConverter.BigInteger.cs b/src/CSnakes.Runtime/PyObjectTypeConverter.BigInteger.cs
index f015442d..d532eca6 100644
--- a/src/CSnakes.Runtime/PyObjectTypeConverter.BigInteger.cs
+++ b/src/CSnakes.Runtime/PyObjectTypeConverter.BigInteger.cs
@@ -15,7 +15,7 @@ internal partial class PyObjectTypeConverter
private PyObject ConvertFromBigInteger(ITypeDescriptorContext? context, CultureInfo? culture, BigInteger integer)
{
- using PyObject pyUnicode = new PyObject(CPythonAPI.AsPyUnicodeObject(integer.ToString()));
- return new PyObject(CPythonAPI.PyLong_FromUnicodeObject(pyUnicode, 10));
+ using PyObject pyUnicode = PyObject.Create(CPythonAPI.AsPyUnicodeObject(integer.ToString()));
+ return PyObject.Create(CPythonAPI.PyLong_FromUnicodeObject(pyUnicode, 10));
}
}
\ No newline at end of file
diff --git a/src/CSnakes.Runtime/PyObjectTypeConverter.Dictionary.cs b/src/CSnakes.Runtime/PyObjectTypeConverter.Dictionary.cs
index d9d9caab..f737e059 100644
--- a/src/CSnakes.Runtime/PyObjectTypeConverter.Dictionary.cs
+++ b/src/CSnakes.Runtime/PyObjectTypeConverter.Dictionary.cs
@@ -11,42 +11,42 @@ internal partial class PyObjectTypeConverter
{
private object? ConvertToDictionary(PyObject pyObject, Type destinationType, ITypeDescriptorContext? context, CultureInfo? culture, bool useMappingProtocol = false)
{
- using PyObject items = useMappingProtocol ? new(CPythonAPI.PyMapping_Items(pyObject)) : new(CPythonAPI.PyDict_Items(pyObject));
-
- Type item1Type = destinationType.GetGenericArguments()[0];
- Type item2Type = destinationType.GetGenericArguments()[1];
-
- if (!knownDynamicTypes.TryGetValue(destinationType, out DynamicTypeInfo? typeInfo))
- {
- Type dictType = typeof(Dictionary<,>).MakeGenericType(item1Type, item2Type);
+ using PyObject items = useMappingProtocol ? PyObject.Create(CPythonAPI.PyMapping_Items(pyObject)) : PyObject.Create(CPythonAPI.PyDict_Items(pyObject));
+
+ Type item1Type = destinationType.GetGenericArguments()[0];
+ Type item2Type = destinationType.GetGenericArguments()[1];
+
+ if (!knownDynamicTypes.TryGetValue(destinationType, out DynamicTypeInfo? typeInfo))
+ {
+ Type dictType = typeof(Dictionary<,>).MakeGenericType(item1Type, item2Type);
Type returnType = typeof(ReadOnlyDictionary<,>).MakeGenericType(item1Type, item2Type);
-
- typeInfo = new(returnType.GetConstructor([dictType])!, dictType.GetConstructor([])!);
- knownDynamicTypes[destinationType] = typeInfo;
- }
+
+ typeInfo = new(returnType.GetConstructor([dictType])!, dictType.GetConstructor([])!);
+ knownDynamicTypes[destinationType] = typeInfo;
+ }
IDictionary dict = (IDictionary)typeInfo.TransientTypeConstructor!.Invoke([]);
nint itemsLength = CPythonAPI.PyList_Size(items);
for (nint i = 0; i < itemsLength; i++)
{
- using PyObject item = new(CPythonAPI.PyList_GetItem(items, i));
+ using PyObject item = PyObject.Create(CPythonAPI.PyList_GetItem(items, i));
- using PyObject item1 = new(CPythonAPI.PyTuple_GetItem(item, 0));
- using PyObject item2 = new(CPythonAPI.PyTuple_GetItem(item, 1));
+ using PyObject item1 = PyObject.Create(CPythonAPI.PyTuple_GetItem(item, 0));
+ using PyObject item2 = PyObject.Create(CPythonAPI.PyTuple_GetItem(item, 1));
object? convertedItem1 = ConvertTo(context, culture, item1, item1Type);
object? convertedItem2 = ConvertTo(context, culture, item2, item2Type);
dict.Add(convertedItem1!, convertedItem2);
- }
-
+ }
+
return typeInfo.ReturnTypeConstructor.Invoke([dict]);
}
private PyObject ConvertFromDictionary(ITypeDescriptorContext? context, CultureInfo? culture, IDictionary dictionary)
{
- PyObject pyDict = new(CPythonAPI.PyDict_New());
+ PyObject pyDict = PyObject.Create(CPythonAPI.PyDict_New());
foreach (DictionaryEntry kvp in dictionary)
{
diff --git a/src/CSnakes.Runtime/PyObjectTypeConverter.List.cs b/src/CSnakes.Runtime/PyObjectTypeConverter.List.cs
index 958f6581..b9ab3978 100644
--- a/src/CSnakes.Runtime/PyObjectTypeConverter.List.cs
+++ b/src/CSnakes.Runtime/PyObjectTypeConverter.List.cs
@@ -9,19 +9,19 @@ internal partial class PyObjectTypeConverter
{
private object? ConvertToList(PyObject pyObject, Type destinationType, ITypeDescriptorContext? context, CultureInfo? culture)
{
- Type genericArgument = destinationType.GetGenericArguments()[0];
-
- if (!knownDynamicTypes.TryGetValue(destinationType, out DynamicTypeInfo? typeInfo))
- {
- Type listType = typeof(List<>).MakeGenericType(genericArgument);
- typeInfo = new(listType.GetConstructor([])!);
- knownDynamicTypes[destinationType] = typeInfo;
- }
+ Type genericArgument = destinationType.GetGenericArguments()[0];
+
+ if (!knownDynamicTypes.TryGetValue(destinationType, out DynamicTypeInfo? typeInfo))
+ {
+ Type listType = typeof(List<>).MakeGenericType(genericArgument);
+ typeInfo = new(listType.GetConstructor([])!);
+ knownDynamicTypes[destinationType] = typeInfo;
+ }
IList list = (IList)typeInfo.ReturnTypeConstructor.Invoke([]);
for (var i = 0; i < CPythonAPI.PyList_Size(pyObject); i++)
{
- using PyObject item = new(CPythonAPI.PyList_GetItem(pyObject, i));
+ using PyObject item = PyObject.Create(CPythonAPI.PyList_GetItem(pyObject, i));
list.Add(ConvertTo(context, culture, item, genericArgument));
}
@@ -30,20 +30,20 @@ internal partial class PyObjectTypeConverter
private object? ConvertToListFromSequence(PyObject pyObject, Type destinationType, ITypeDescriptorContext? context, CultureInfo? culture)
{
- Type genericArgument = destinationType.GetGenericArguments()[0];
-
- if (!knownDynamicTypes.TryGetValue(destinationType, out DynamicTypeInfo? typeInfo))
- {
- Type listType = typeof(List<>).MakeGenericType(genericArgument);
- typeInfo = new(listType.GetConstructor([])!);
- knownDynamicTypes[destinationType] = typeInfo;
- }
+ Type genericArgument = destinationType.GetGenericArguments()[0];
- IList list = (IList)typeInfo.ReturnTypeConstructor.Invoke([]);
+ if (!knownDynamicTypes.TryGetValue(destinationType, out DynamicTypeInfo? typeInfo))
+ {
+ Type listType = typeof(List<>).MakeGenericType(genericArgument);
+ typeInfo = new(listType.GetConstructor([])!);
+ knownDynamicTypes[destinationType] = typeInfo;
+ }
+
+ IList list = (IList)typeInfo.ReturnTypeConstructor.Invoke([]);
for (var i = 0; i < CPythonAPI.PySequence_Size(pyObject); i++)
{
- using PyObject item = new(CPythonAPI.PySequence_GetItem(pyObject, i));
+ using PyObject item = PyObject.Create(CPythonAPI.PySequence_GetItem(pyObject, i));
list.Add(ConvertTo(context, culture, item, genericArgument));
}
@@ -52,7 +52,7 @@ internal partial class PyObjectTypeConverter
private PyObject ConvertFromList(ITypeDescriptorContext? context, CultureInfo? culture, IEnumerable e)
{
- PyObject pyList = new(CPythonAPI.PyList_New(0));
+ PyObject pyList = PyObject.Create(CPythonAPI.PyList_New(0));
foreach (var item in e)
{
diff --git a/src/CSnakes.Runtime/PyObjectTypeConverter.Tuple.cs b/src/CSnakes.Runtime/PyObjectTypeConverter.Tuple.cs
index ab552015..1460037c 100644
--- a/src/CSnakes.Runtime/PyObjectTypeConverter.Tuple.cs
+++ b/src/CSnakes.Runtime/PyObjectTypeConverter.Tuple.cs
@@ -35,7 +35,7 @@ private PyObject ConvertFromTuple(ITypeDescriptorContext? context, CultureInfo?
var tupleValues = new List();
for (nint i = 0; i < CPythonAPI.PyTuple_Size(pyObj); i++)
{
- PyObject value = new(CPythonAPI.PyTuple_GetItem(pyObj, i));
+ PyObject value = PyObject.Create(CPythonAPI.PyTuple_GetItem(pyObj, i));
tupleValues.Add(value);
}
diff --git a/src/CSnakes.Runtime/PyObjectTypeConverter.cs b/src/CSnakes.Runtime/PyObjectTypeConverter.cs
index 54ffb3b5..42c3e92f 100644
--- a/src/CSnakes.Runtime/PyObjectTypeConverter.cs
+++ b/src/CSnakes.Runtime/PyObjectTypeConverter.cs
@@ -124,17 +124,18 @@ internal partial class PyObjectTypeConverter : TypeConverter
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) =>
value switch
{
- string str => new PyObject(CPythonAPI.AsPyUnicodeObject(str)),
- byte[] bytes => new PyObject(CPythonAPI.PyBytes_FromByteSpan(bytes.AsSpan())),
- long l => new PyObject(CPythonAPI.PyLong_FromLongLong(l)),
- int i => new PyObject(CPythonAPI.PyLong_FromLong(i)),
- bool b => new PyObject(CPythonAPI.PyBool_FromLong(b ? 1 : 0)),
- double d => new PyObject(CPythonAPI.PyFloat_FromDouble(d)),
+ string str => PyObject.Create(CPythonAPI.AsPyUnicodeObject(str)),
+ byte[] bytes => PyObject.Create(CPythonAPI.PyBytes_FromByteSpan(bytes.AsSpan())),
+ long l => PyObject.Create(CPythonAPI.PyLong_FromLongLong(l)),
+ int i => PyObject.Create(CPythonAPI.PyLong_FromLong(i)),
+ bool b => PyObject.Create(CPythonAPI.PyBool_FromLong(b ? 1 : 0)),
+ double d => PyObject.Create(CPythonAPI.PyFloat_FromDouble(d)),
IDictionary dictionary => ConvertFromDictionary(context, culture, dictionary),
ITuple t => ConvertFromTuple(context, culture, t),
IEnumerable e => ConvertFromList(context, culture, e),
BigInteger b => ConvertFromBigInteger(context, culture, b),
- null => new PyObject(CPythonAPI.GetNone()),
+ PyObject pyObject => pyObject.Clone(),
+ null => PyObject.None,
_ => base.ConvertFrom(context, culture, value)
};
@@ -177,7 +178,7 @@ private PyObject ToPython(object? o, ITypeDescriptorContext? context, CultureInf
{
if (o is null)
{
- return new PyObject(CPythonAPI.GetNone());
+ return PyObject.None;
}
var result = ConvertFrom(context, culture, o);
diff --git a/src/CSnakes.Runtime/Python/Interns/ImmortalPyObject.cs b/src/CSnakes.Runtime/Python/Interns/ImmortalPyObject.cs
new file mode 100644
index 00000000..013b9bfa
--- /dev/null
+++ b/src/CSnakes.Runtime/Python/Interns/ImmortalPyObject.cs
@@ -0,0 +1,16 @@
+namespace CSnakes.Runtime.Python.Interns;
+
+internal class ImmortalPyObject : PyObject
+{
+ internal ImmortalPyObject(nint handle) : base(handle)
+ {
+ }
+
+ protected override bool ReleaseHandle() => true;
+
+
+ protected override void Dispose(bool disposing)
+ {
+ // I am immortal!!
+ }
+}
diff --git a/src/CSnakes.Runtime/Python/Interns/PyNoneObject.cs b/src/CSnakes.Runtime/Python/Interns/PyNoneObject.cs
new file mode 100644
index 00000000..967e2d7a
--- /dev/null
+++ b/src/CSnakes.Runtime/Python/Interns/PyNoneObject.cs
@@ -0,0 +1,18 @@
+using CSnakes.Runtime.CPython;
+
+namespace CSnakes.Runtime.Python.Interns;
+
+internal sealed class PyNoneObject : ImmortalPyObject
+{
+ public PyNoneObject() : base(CPythonAPI.GetNone())
+ {
+ }
+
+ public override bool IsNone() => true;
+
+ public override string GetRepr() => ToString();
+
+ public override string ToString() => "None";
+
+ internal override PyObject Clone() => this;
+}
diff --git a/src/CSnakes.Runtime/Python/PyObject.cs b/src/CSnakes.Runtime/Python/PyObject.cs
index d9cf334f..1f5e26dc 100644
--- a/src/CSnakes.Runtime/Python/PyObject.cs
+++ b/src/CSnakes.Runtime/Python/PyObject.cs
@@ -1,4 +1,5 @@
using CSnakes.Runtime.CPython;
+using CSnakes.Runtime.Python.Interns;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
@@ -12,7 +13,7 @@ public class PyObject : SafeHandle
{
private static readonly TypeConverter td = TypeDescriptor.GetConverter(typeof(PyObject));
- internal PyObject(IntPtr pyObject, bool ownsHandle = true) : base(pyObject, ownsHandle)
+ protected PyObject(IntPtr pyObject, bool ownsHandle = true) : base(pyObject, ownsHandle)
{
if (pyObject == IntPtr.Zero)
{
@@ -20,6 +21,13 @@ internal PyObject(IntPtr pyObject, bool ownsHandle = true) : base(pyObject, owns
}
}
+ internal static PyObject Create(IntPtr ptr)
+ {
+ if (None.DangerousGetHandle() == ptr)
+ return None;
+ return new PyObject(ptr);
+ }
+
public override bool IsInvalid => handle == IntPtr.Zero;
protected override bool ReleaseHandle()
@@ -63,13 +71,13 @@ internal static void ThrowPythonExceptionAsClrException()
throw new InvalidDataException("An error occurred in Python, but no exception was set.");
}
- using var pyExceptionType = new PyObject(excType);
+ using var pyExceptionType = Create(excType);
PyObject? pyExceptionTraceback = excTraceback == IntPtr.Zero ? null : new PyObject(excTraceback);
var pyExceptionStr = string.Empty;
if (excValue != IntPtr.Zero)
{
- using PyObject pyExceptionValue = new PyObject(excValue);
+ using PyObject pyExceptionValue = Create(excValue);
pyExceptionStr = pyExceptionValue.ToString();
}
;
@@ -97,7 +105,7 @@ private static void RaiseOnPythonNotInitialized()
/// Get the type for the object.
///
/// A new reference to the type field.
- public PyObject GetPythonType()
+ public virtual PyObject GetPythonType()
{
RaiseOnPythonNotInitialized();
using (GIL.Acquire())
@@ -111,16 +119,16 @@ public PyObject GetPythonType()
///
///
/// Attribute object (new ref)
- public PyObject GetAttr(string name)
+ public virtual PyObject GetAttr(string name)
{
RaiseOnPythonNotInitialized();
using (GIL.Acquire())
{
- return new PyObject(CPythonAPI.GetAttr(this, name));
+ return Create(CPythonAPI.GetAttr(this, name));
}
}
- public bool HasAttr(string name)
+ public virtual bool HasAttr(string name)
{
RaiseOnPythonNotInitialized();
using (GIL.Acquire())
@@ -133,12 +141,12 @@ public bool HasAttr(string name)
/// Get the iterator for the object. This is equivalent to iter(obj) in Python.
///
/// The iterator object (new ref)
- public PyObject GetIter()
+ public virtual PyObject GetIter()
{
RaiseOnPythonNotInitialized();
using (GIL.Acquire())
{
- return new PyObject(CPythonAPI.PyObject_GetIter(this));
+ return Create(CPythonAPI.PyObject_GetIter(this));
}
}
@@ -146,7 +154,7 @@ public PyObject GetIter()
/// Get the results of the repr() function on the object.
///
///
- public string GetRepr()
+ public virtual string GetRepr()
{
RaiseOnPythonNotInitialized();
using (GIL.Acquire())
@@ -157,6 +165,14 @@ public string GetRepr()
}
}
+ ///
+ /// Is the Python object None?
+ ///
+ /// true if None, else false
+ public virtual bool IsNone() => CPythonAPI.IsNone(this);
+
+ public static PyObject None { get; } = new PyNoneObject();
+
///
/// Call the object. Equivalent to (__call__)(args)
/// All arguments are treated as positional.
@@ -188,7 +204,7 @@ public PyObject CallWithArgs(PyObject[]? args = null)
{
using (GIL.Acquire())
{
- return new PyObject(CPythonAPI.Call(this, argHandles));
+ return Create(CPythonAPI.Call(this, argHandles));
}
} finally
{
@@ -234,7 +250,7 @@ public PyObject CallWithKeywordArguments(PyObject[]? args = null, string[]? kwna
{
using (GIL.Acquire())
{
- return new PyObject(CPythonAPI.Call(this, argHandles, kwnames, kwargHandles));
+ return Create(CPythonAPI.Call(this, argHandles, kwnames, kwargHandles));
}
}
finally
@@ -292,12 +308,12 @@ public T As()
using (GIL.Acquire())
{
return value is null ?
- new PyObject(CPythonAPI.GetNone()) :
+ PyObject.None :
(PyObject?)td.ConvertFrom(value);
}
}
- internal PyObject Clone()
+ internal virtual PyObject Clone()
{
CPythonAPI.Py_IncRefRaw(handle);
return new PyObject(handle);
diff --git a/src/CSnakes.Runtime/Python/PyTuple.cs b/src/CSnakes.Runtime/Python/PyTuple.cs
index 5d9fd633..c7484a47 100644
--- a/src/CSnakes.Runtime/Python/PyTuple.cs
+++ b/src/CSnakes.Runtime/Python/PyTuple.cs
@@ -18,7 +18,7 @@ public static PyObject CreateTuple(IEnumerable items)
marshallers.Add(m);
handles.Add(m.ToUnmanaged());
}
- return new(CPythonAPI.PackTuple(handles.ToArray()));
+ return PyObject.Create(CPythonAPI.PackTuple(handles.ToArray()));
}
finally
{
diff --git a/src/CSnakes/Reflection/MethodReflection.cs b/src/CSnakes/Reflection/MethodReflection.cs
index 58244ff5..a2fbb3b1 100644
--- a/src/CSnakes/Reflection/MethodReflection.cs
+++ b/src/CSnakes/Reflection/MethodReflection.cs
@@ -51,6 +51,21 @@ public static MethodDefinition FromMethod(PythonFunctionDefinition function, str
{
continue;
}
+ bool needsConversion = true; // TODO: Skip .From for PyObject arguments.
+ ExpressionSyntax rhs = IdentifierName(parameter.cSharpParameter.Identifier);
+ if (needsConversion)
+ rhs =
+ InvocationExpression(
+ MemberAccessExpression(
+ SyntaxKind.SimpleMemberAccessExpression,
+ IdentifierName("PyObject"),
+ IdentifierName("From")))
+ .WithArgumentList(
+ ArgumentList(
+ SingletonSeparatedList(
+ Argument(
+ IdentifierName(parameter.cSharpParameter.Identifier)))));
+
pythonConversionStatements.Add(
LocalDeclarationStatement(
VariableDeclaration(
@@ -63,16 +78,8 @@ public static MethodDefinition FromMethod(PythonFunctionDefinition function, str
EqualsValueClause(
PostfixUnaryExpression(
SyntaxKind.SuppressNullableWarningExpression,
- InvocationExpression(
- MemberAccessExpression(
- SyntaxKind.SimpleMemberAccessExpression,
- IdentifierName("PyObject"),
- IdentifierName("From")))
- .WithArgumentList(
- ArgumentList(
- SingletonSeparatedList(
- Argument(
- IdentifierName(parameter.cSharpParameter.Identifier)))))))))))
+ rhs
+ ))))))
.WithUsingKeyword(
Token(SyntaxKind.UsingKeyword)));
}
diff --git a/src/CSnakes/Reflection/TypeReflection.cs b/src/CSnakes/Reflection/TypeReflection.cs
index 4941737a..36b6610f 100644
--- a/src/CSnakes/Reflection/TypeReflection.cs
+++ b/src/CSnakes/Reflection/TypeReflection.cs
@@ -26,7 +26,7 @@ public static TypeSyntax AsPredefinedType(PythonTypeSpec pythonType)
"Optional" => AsPredefinedType(pythonType.Arguments[0]),
"Generator" => CreateGeneratorType(pythonType.Arguments[0], pythonType.Arguments[1], pythonType.Arguments[2]),
// Todo more types... see https://docs.python.org/3/library/stdtypes.html#standard-generic-classes
- _ => SyntaxFactory.ParseTypeName("PyObject"),// TODO : Should be nullable?
+ _ => SyntaxFactory.ParseTypeName("PyObject"),
};
}
return pythonType.Name switch
@@ -36,8 +36,7 @@ public static TypeSyntax AsPredefinedType(PythonTypeSpec pythonType)
"float" => SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.DoubleKeyword)),
"bool" => SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.BoolKeyword)),
"bytes" => SyntaxFactory.ParseTypeName("byte[]"),
- // Todo more types...
- _ => SyntaxFactory.ParseTypeName("PyObject"),// TODO : Should be nullable?
+ _ => SyntaxFactory.ParseTypeName("PyObject"),
};
}
diff --git a/src/Integration.Tests/Integration.Tests.csproj b/src/Integration.Tests/Integration.Tests.csproj
index 83421d99..8ede156a 100644
--- a/src/Integration.Tests/Integration.Tests.csproj
+++ b/src/Integration.Tests/Integration.Tests.csproj
@@ -17,6 +17,7 @@
+
@@ -49,6 +50,9 @@
Always
+
+ Always
+
Always
diff --git a/src/Integration.Tests/NoneTests.cs b/src/Integration.Tests/NoneTests.cs
new file mode 100644
index 00000000..9a5b13fc
--- /dev/null
+++ b/src/Integration.Tests/NoneTests.cs
@@ -0,0 +1,23 @@
+using CSnakes.Runtime.Python;
+
+namespace Integration.Tests;
+public class NoneTests : IntegrationTestBase
+{
+ [Fact]
+ public void TestReturnsNoneIsNone()
+ {
+ var mod = Env.TestNone();
+ using PyObject result = mod.ReturnsNone();
+ Assert.True(result.IsNone());
+ }
+
+ [Fact]
+ public void TestNullArgAsNone()
+ {
+ PyObject none = PyObject.None;
+ Assert.True(none.IsNone());
+ // Give to function
+ var mod = Env.TestNone();
+ Assert.True(mod.TestNoneResult(none));
+ }
+}
diff --git a/src/Integration.Tests/python/test_none.py b/src/Integration.Tests/python/test_none.py
new file mode 100644
index 00000000..3806a627
--- /dev/null
+++ b/src/Integration.Tests/python/test_none.py
@@ -0,0 +1,5 @@
+def returns_none():
+ return None
+
+def test_none_result(arg) -> bool:
+ return arg is None