diff --git a/.travis.yml b/.travis.yml
index 2062a35da..ab76d3b27 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,6 +2,7 @@ dist: xenial
sudo: false
language: python
python:
+ - 3.9
- 3.8
- 3.7
- 3.6
diff --git a/AUTHORS.md b/AUTHORS.md
index 5dbf1e1ce..85812a47d 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -73,4 +73,5 @@
- ([@rmadsen-ks](https://github.com/rmadsen-ks))
- ([@stonebig](https://github.com/stonebig))
- ([@testrunner123](https://github.com/testrunner123))
+- ([@DanBarzilian](https://github.com/DanBarzilian))
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad4016450..e884341bd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,15 +5,25 @@ project adheres to [Semantic Versioning][].
This document follows the conventions laid out in [Keep a CHANGELOG][].
-## [Unreleased][]
-### Added
+## [2.5.2](https://github.com/pythonnet/pythonnet/releases/tag/v2.5.2) - 2021-02-05
-### Changed
+Bugfix release.
### Fixed
+- Fix `object[]` parameters taking precedence when should not in overload resolution
+- Empty parameter names (as can be generated from F#) do not cause crashes
+
+## [2.5.1](https://github.com/pythonnet/pythonnet/releases/tag/v2.5.1) - 2020-06-18
+
+Bugfix release.
+
+### Fixed
+
+- Fix incorrect dereference of wrapper object in `tp_repr`, which may result in a program crash
+- Fix incorrect dereference in params array handling
-## [2.5.0][] - 2020-06-14
+## [2.5.0](https://github.com/pythonnet/pythonnet/releases/tag/v2.5.0) - 2020-06-14
This version improves performance on benchmarks significantly compared to 2.3.
@@ -65,7 +75,7 @@ This version improves performance on benchmarks significantly compared to 2.3.
- Fixed issue with params methods that are not passed an array.
- Use UTF8 to encode strings passed to `PyRun_String` on Python 3
-## [2.4.0][] - 2019-05-15
+## [2.4.0](https://github.com/pythonnet/pythonnet/releases/tag/v2.4.0) - 2019-05-15
### Added
diff --git a/appveyor.yml b/appveyor.yml
index b58b72372..6268798af 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -2,8 +2,8 @@ version: '{branch}-{build}'
build: off
image:
- - Visual Studio 2017
-
+ - Visual Studio 2019
+
platform:
- x86
- x64
@@ -15,6 +15,8 @@ environment:
CODECOV_ENV: PYTHON_VERSION, PLATFORM
matrix:
+ - PYTHON_VERSION: 3.9
+ BUILD_OPTS: --xplat
- PYTHON_VERSION: 3.8
BUILD_OPTS: --xplat
- PYTHON_VERSION: 3.7
@@ -23,14 +25,14 @@ environment:
BUILD_OPTS: --xplat
- PYTHON_VERSION: 3.5
BUILD_OPTS: --xplat
- - PYTHON_VERSION: 2.7
+ - PYTHON_VERSION: 2.7
BUILD_OPTS: --xplat
+ - PYTHON_VERSION: 3.9
- PYTHON_VERSION: 3.8
- PYTHON_VERSION: 3.7
- PYTHON_VERSION: 3.6
- PYTHON_VERSION: 3.5
- PYTHON_VERSION: 2.7
-
init:
# Update Environment Variables based on matrix/platform
- set PY_VER=%PYTHON_VERSION:.=%
diff --git a/requirements.txt b/requirements.txt
index 29c2e4566..78570cb95 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
# Requirements for both Travis and AppVeyor
-pytest==3.2.5
+pytest
coverage
psutil
@@ -7,6 +7,5 @@ psutil
codecov
# Platform specific requirements
-# pip; sys_platform == 'win32'
-wheel; sys_platform == 'win32'
-pycparser; sys_platform != 'win32'
+wheel
+pycparser
diff --git a/setup.py b/setup.py
index 216620211..9dd4ec990 100644
--- a/setup.py
+++ b/setup.py
@@ -13,10 +13,10 @@
import subprocess
import sys
import sysconfig
-from distutils import spawn
-from distutils.command import install, build, build_ext, install_data, install_lib
from setuptools import Extension, setup
+from distutils import spawn
+from distutils.command import install, build, build_ext, install_data, install_lib
try:
from wheel import bdist_wheel
@@ -629,7 +629,7 @@ def run(self):
setup(
name="pythonnet",
- version="2.5.0",
+ version="2.5.2",
description=".Net and Mono integration for Python",
url="https://pythonnet.github.io/",
license="MIT",
@@ -652,6 +652,7 @@ def run(self):
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux",
"Operating System :: MacOS :: MacOS X",
diff --git a/src/SharedAssemblyInfo.cs b/src/SharedAssemblyInfo.cs
index dd057b144..2b96e0daf 100644
--- a/src/SharedAssemblyInfo.cs
+++ b/src/SharedAssemblyInfo.cs
@@ -25,4 +25,4 @@
// Version Information. Keeping it simple. May need to revisit for Nuget
// See: https://codingforsmarties.wordpress.com/2016/01/21/how-to-version-assemblies-destined-for-nuget/
// AssemblyVersion can only be numeric
-[assembly: AssemblyVersion("2.5.0")]
+[assembly: AssemblyVersion("2.5.2")]
diff --git a/src/clrmodule/ClrModule.cs b/src/clrmodule/ClrModule.cs
index 377be66d1..e8ef1ae77 100644
--- a/src/clrmodule/ClrModule.cs
+++ b/src/clrmodule/ClrModule.cs
@@ -53,7 +53,7 @@ public static void initclr()
{
#if USE_PYTHON_RUNTIME_VERSION
// Has no effect until SNK works. Keep updated anyways.
- Version = new Version("2.5.0"),
+ Version = new Version("2.5.2"),
#endif
CultureInfo = CultureInfo.InvariantCulture
};
diff --git a/src/clrmodule/clrmodule.15.csproj b/src/clrmodule/clrmodule.15.csproj
index 7fc9c2dda..eba365b76 100644
--- a/src/clrmodule/clrmodule.15.csproj
+++ b/src/clrmodule/clrmodule.15.csproj
@@ -9,7 +9,7 @@
clrmodule
clrmodule
clrmodule
- 2.5.0
+ 2.5.2
false
false
false
diff --git a/src/console/Console.15.csproj b/src/console/Console.15.csproj
index 3c51caa31..4397feb7b 100644
--- a/src/console/Console.15.csproj
+++ b/src/console/Console.15.csproj
@@ -8,7 +8,7 @@
nPython
Python.Runtime
nPython
- 2.5.0
+ 2.5.2
false
false
false
diff --git a/src/embed_tests/Python.EmbeddingTest.15.csproj b/src/embed_tests/Python.EmbeddingTest.15.csproj
index eb6957656..8fed4dc99 100644
--- a/src/embed_tests/Python.EmbeddingTest.15.csproj
+++ b/src/embed_tests/Python.EmbeddingTest.15.csproj
@@ -10,7 +10,7 @@
Python.EmbeddingTest
Python.EmbeddingTest
Python.EmbeddingTest
- 2.5.0
+ 2.5.2
false
false
false
diff --git a/src/runtime/Python.Runtime.15.csproj b/src/runtime/Python.Runtime.15.csproj
index d753a5dff..a9a86536f 100644
--- a/src/runtime/Python.Runtime.15.csproj
+++ b/src/runtime/Python.Runtime.15.csproj
@@ -8,7 +8,7 @@
Python.Runtime
Python.Runtime
pythonnet
- 2.5.0
+ 2.5.2
true
false
Python.NET
diff --git a/src/runtime/Python.Runtime.csproj b/src/runtime/Python.Runtime.csproj
index 75f5e2fab..9f8acff92 100644
--- a/src/runtime/Python.Runtime.csproj
+++ b/src/runtime/Python.Runtime.csproj
@@ -1,4 +1,4 @@
-
+
Debug
@@ -165,6 +165,7 @@
+
diff --git a/src/runtime/classbase.cs b/src/runtime/classbase.cs
index 144bac9d3..43ec6ea72 100644
--- a/src/runtime/classbase.cs
+++ b/src/runtime/classbase.cs
@@ -266,6 +266,7 @@ public static IntPtr tp_repr(IntPtr ob)
//otherwise use the standard object.__repr__(inst)
IntPtr args = Runtime.PyTuple_New(1);
+ Runtime.XIncref(ob);
Runtime.PyTuple_SetItem(args, 0, ob);
IntPtr reprFunc = Runtime.PyObject_GetAttrString(Runtime.PyBaseObjectType, "__repr__");
var output = Runtime.PyObject_Call(reprFunc, args, IntPtr.Zero);
diff --git a/src/runtime/interop39.cs b/src/runtime/interop39.cs
new file mode 100644
index 000000000..e042cb57d
--- /dev/null
+++ b/src/runtime/interop39.cs
@@ -0,0 +1,244 @@
+
+// Auto-generated by geninterop.py.
+// DO NOT MODIFY BY HAND.
+
+
+#if PYTHON39
+using System;
+using System.Collections;
+using System.Collections.Specialized;
+using System.Runtime.InteropServices;
+using System.Reflection;
+using System.Text;
+
+namespace Python.Runtime
+{
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal static class TypeOffset
+ {
+ static TypeOffset()
+ {
+ Type type = typeof(TypeOffset);
+ FieldInfo[] fi = type.GetFields();
+ int size = IntPtr.Size;
+ for (int i = 0; i < fi.Length; i++)
+ {
+ fi[i].SetValue(null, i * size);
+ }
+ }
+
+ public static int magic()
+ {
+ return ob_size;
+ }
+
+ // Auto-generated from PyHeapTypeObject in Python.h
+ public static int ob_refcnt = 0;
+ public static int ob_type = 0;
+ public static int ob_size = 0;
+ public static int tp_name = 0;
+ public static int tp_basicsize = 0;
+ public static int tp_itemsize = 0;
+ public static int tp_dealloc = 0;
+ public static int tp_vectorcall_offset = 0;
+ public static int tp_getattr = 0;
+ public static int tp_setattr = 0;
+ public static int tp_as_async = 0;
+ public static int tp_repr = 0;
+ public static int tp_as_number = 0;
+ public static int tp_as_sequence = 0;
+ public static int tp_as_mapping = 0;
+ public static int tp_hash = 0;
+ public static int tp_call = 0;
+ public static int tp_str = 0;
+ public static int tp_getattro = 0;
+ public static int tp_setattro = 0;
+ public static int tp_as_buffer = 0;
+ public static int tp_flags = 0;
+ public static int tp_doc = 0;
+ public static int tp_traverse = 0;
+ public static int tp_clear = 0;
+ public static int tp_richcompare = 0;
+ public static int tp_weaklistoffset = 0;
+ public static int tp_iter = 0;
+ public static int tp_iternext = 0;
+ public static int tp_methods = 0;
+ public static int tp_members = 0;
+ public static int tp_getset = 0;
+ public static int tp_base = 0;
+ public static int tp_dict = 0;
+ public static int tp_descr_get = 0;
+ public static int tp_descr_set = 0;
+ public static int tp_dictoffset = 0;
+ public static int tp_init = 0;
+ public static int tp_alloc = 0;
+ public static int tp_new = 0;
+ public static int tp_free = 0;
+ public static int tp_is_gc = 0;
+ public static int tp_bases = 0;
+ public static int tp_mro = 0;
+ public static int tp_cache = 0;
+ public static int tp_subclasses = 0;
+ public static int tp_weaklist = 0;
+ public static int tp_del = 0;
+ public static int tp_version_tag = 0;
+ public static int tp_finalize = 0;
+ public static int tp_vectorcall = 0;
+ public static int am_await = 0;
+ public static int am_aiter = 0;
+ public static int am_anext = 0;
+ public static int nb_add = 0;
+ public static int nb_subtract = 0;
+ public static int nb_multiply = 0;
+ public static int nb_remainder = 0;
+ public static int nb_divmod = 0;
+ public static int nb_power = 0;
+ public static int nb_negative = 0;
+ public static int nb_positive = 0;
+ public static int nb_absolute = 0;
+ public static int nb_bool = 0;
+ public static int nb_invert = 0;
+ public static int nb_lshift = 0;
+ public static int nb_rshift = 0;
+ public static int nb_and = 0;
+ public static int nb_xor = 0;
+ public static int nb_or = 0;
+ public static int nb_int = 0;
+ public static int nb_reserved = 0;
+ public static int nb_float = 0;
+ public static int nb_inplace_add = 0;
+ public static int nb_inplace_subtract = 0;
+ public static int nb_inplace_multiply = 0;
+ public static int nb_inplace_remainder = 0;
+ public static int nb_inplace_power = 0;
+ public static int nb_inplace_lshift = 0;
+ public static int nb_inplace_rshift = 0;
+ public static int nb_inplace_and = 0;
+ public static int nb_inplace_xor = 0;
+ public static int nb_inplace_or = 0;
+ public static int nb_floor_divide = 0;
+ public static int nb_true_divide = 0;
+ public static int nb_inplace_floor_divide = 0;
+ public static int nb_inplace_true_divide = 0;
+ public static int nb_index = 0;
+ public static int nb_matrix_multiply = 0;
+ public static int nb_inplace_matrix_multiply = 0;
+ public static int mp_length = 0;
+ public static int mp_subscript = 0;
+ public static int mp_ass_subscript = 0;
+ public static int sq_length = 0;
+ public static int sq_concat = 0;
+ public static int sq_repeat = 0;
+ public static int sq_item = 0;
+ public static int was_sq_slice = 0;
+ public static int sq_ass_item = 0;
+ public static int was_sq_ass_slice = 0;
+ public static int sq_contains = 0;
+ public static int sq_inplace_concat = 0;
+ public static int sq_inplace_repeat = 0;
+ public static int bf_getbuffer = 0;
+ public static int bf_releasebuffer = 0;
+ public static int name = 0;
+ public static int ht_slots = 0;
+ public static int qualname = 0;
+ public static int ht_cached_keys = 0;
+ public static int ht_module = 0;
+
+ /* here are optional user slots, followed by the members. */
+ public static int members = 0;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyNumberMethods
+ {
+ public IntPtr nb_add;
+ public IntPtr nb_subtract;
+ public IntPtr nb_multiply;
+ public IntPtr nb_remainder;
+ public IntPtr nb_divmod;
+ public IntPtr nb_power;
+ public IntPtr nb_negative;
+ public IntPtr nb_positive;
+ public IntPtr nb_absolute;
+ public IntPtr nb_bool;
+ public IntPtr nb_invert;
+ public IntPtr nb_lshift;
+ public IntPtr nb_rshift;
+ public IntPtr nb_and;
+ public IntPtr nb_xor;
+ public IntPtr nb_or;
+ public IntPtr nb_int;
+ public IntPtr nb_reserved;
+ public IntPtr nb_float;
+ public IntPtr nb_inplace_add;
+ public IntPtr nb_inplace_subtract;
+ public IntPtr nb_inplace_multiply;
+ public IntPtr nb_inplace_remainder;
+ public IntPtr nb_inplace_power;
+ public IntPtr nb_inplace_lshift;
+ public IntPtr nb_inplace_rshift;
+ public IntPtr nb_inplace_and;
+ public IntPtr nb_inplace_xor;
+ public IntPtr nb_inplace_or;
+ public IntPtr nb_floor_divide;
+ public IntPtr nb_true_divide;
+ public IntPtr nb_inplace_floor_divide;
+ public IntPtr nb_inplace_true_divide;
+ public IntPtr nb_index;
+ public IntPtr nb_matrix_multiply;
+ public IntPtr nb_inplace_matrix_multiply;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PySequenceMethods
+ {
+ public IntPtr sq_length;
+ public IntPtr sq_concat;
+ public IntPtr sq_repeat;
+ public IntPtr sq_item;
+ public IntPtr was_sq_slice;
+ public IntPtr sq_ass_item;
+ public IntPtr was_sq_ass_slice;
+ public IntPtr sq_contains;
+ public IntPtr sq_inplace_concat;
+ public IntPtr sq_inplace_repeat;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyMappingMethods
+ {
+ public IntPtr mp_length;
+ public IntPtr mp_subscript;
+ public IntPtr mp_ass_subscript;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyAsyncMethods
+ {
+ public IntPtr am_await;
+ public IntPtr am_aiter;
+ public IntPtr am_anext;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PyBufferProcs
+ {
+ public IntPtr bf_getbuffer;
+ public IntPtr bf_releasebuffer;
+ }
+
+ internal static partial class SlotTypes
+ {
+ public static readonly Type[] Types = {
+ typeof(PyNumberMethods),
+ typeof(PySequenceMethods),
+ typeof(PyMappingMethods),
+ typeof(PyAsyncMethods),
+ typeof(PyBufferProcs),
+ };
+ }
+
+}
+#endif
+
diff --git a/src/runtime/methodbinder.cs b/src/runtime/methodbinder.cs
index b74f21754..077297a90 100644
--- a/src/runtime/methodbinder.cs
+++ b/src/runtime/methodbinder.cs
@@ -203,6 +203,16 @@ internal static int ArgPrecedence(Type t)
return 3000;
}
+ if (t.IsArray)
+ {
+ Type e = t.GetElementType();
+ if (e == objectType)
+ {
+ return 2500;
+ }
+ return 100 + ArgPrecedence(e);
+ }
+
TypeCode tc = Type.GetTypeCode(t);
// TODO: Clean up
switch (tc)
@@ -250,16 +260,6 @@ internal static int ArgPrecedence(Type t)
return 40;
}
- if (t.IsArray)
- {
- Type e = t.GetElementType();
- if (e == objectType)
- {
- return 2500;
- }
- return 100 + ArgPrecedence(e);
- }
-
return 2000;
}
@@ -372,7 +372,7 @@ internal Binding Bind(IntPtr inst, IntPtr args, IntPtr kw, MethodBase info, Meth
static IntPtr HandleParamsArray(IntPtr args, int arrayStart, int pyArgCount, out bool isNewReference)
{
isNewReference = false;
- IntPtr op;
+ IntPtr op;
// for a params method, we may have a sequence or single/multiple items
// here we look to see if the item at the paramIndex is there or not
// and then if it is a sequence itself.
@@ -390,10 +390,6 @@ static IntPtr HandleParamsArray(IntPtr args, int arrayStart, int pyArgCount, out
{
isNewReference = true;
op = Runtime.PyTuple_GetSlice(args, arrayStart, pyArgCount);
- if (item != IntPtr.Zero)
- {
- Runtime.XDecref(item);
- }
}
}
else
@@ -431,7 +427,7 @@ static object[] TryConvertArguments(ParameterInfo[] pi, bool paramsArray,
for (int paramIndex = 0; paramIndex < pi.Length; paramIndex++)
{
var parameter = pi[paramIndex];
- bool hasNamedParam = kwargDict.ContainsKey(parameter.Name);
+ bool hasNamedParam = parameter.Name != null ? kwargDict.ContainsKey(parameter.Name) : false;
bool isNewReference = false;
if (paramIndex >= pyArgCount && !(hasNamedParam || (paramsArray && paramIndex == arrayStart)))
diff --git a/src/runtime/resources/clr.py b/src/runtime/resources/clr.py
index 78fae0d3a..18b4ba93f 100644
--- a/src/runtime/resources/clr.py
+++ b/src/runtime/resources/clr.py
@@ -2,7 +2,7 @@
Code in this module gets loaded into the main clr module.
"""
-__version__ = "2.5.0"
+__version__ = "2.5.1"
class clrproperty(object):
diff --git a/src/runtime/runtime.cs b/src/runtime/runtime.cs
index 8cf24ba70..8db9c3d13 100644
--- a/src/runtime/runtime.cs
+++ b/src/runtime/runtime.cs
@@ -68,8 +68,11 @@ public class Runtime
#elif PYTHON38
internal const string _pyversion = "3.8";
internal const string _pyver = "38";
+#elif PYTHON39
+ internal const string _pyversion = "3.9";
+ internal const string _pyver = "39";
#else
-#error You must define one of PYTHON34 to PYTHON38 or PYTHON27
+#error You must define one of PYTHON34 to PYTHON39 or PYTHON27
#endif
#if MONO_LINUX || MONO_OSX // Linux/macOS use dotted version string
diff --git a/src/testing/Python.Test.15.csproj b/src/testing/Python.Test.15.csproj
index 0e19adf91..75a26bd1c 100644
--- a/src/testing/Python.Test.15.csproj
+++ b/src/testing/Python.Test.15.csproj
@@ -7,7 +7,7 @@
Python.Test
Python.Test
Python.Test
- 2.5.0
+ 2.5.2
bin\
false
$(OutputPath)\$(AssemblyName).xml
diff --git a/src/testing/methodtest.cs b/src/testing/methodtest.cs
index 91836b727..9a4c408d6 100644
--- a/src/testing/methodtest.cs
+++ b/src/testing/methodtest.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+using System.Linq;
using System.Runtime.InteropServices;
namespace Python.Test
@@ -84,7 +85,7 @@ public Type[] TestNullArrayConversion(Type[] v)
public static string[] TestStringParamsArg(params string[] args)
{
- return args;
+ return args.Concat(new []{"tail"}).ToArray();
}
public static object[] TestObjectParamsArg(params object[] args)
diff --git a/src/tests/test_method.py b/src/tests/test_method.py
index 69f1b5e72..8456ddc9f 100644
--- a/src/tests/test_method.py
+++ b/src/tests/test_method.py
@@ -208,17 +208,20 @@ def test_null_array_conversion():
def test_string_params_args():
"""Test use of string params."""
result = MethodTest.TestStringParamsArg('one', 'two', 'three')
- assert result.Length == 3
- assert len(result) == 3, result
+ assert result.Length == 4
+ assert len(result) == 4, result
assert result[0] == 'one'
assert result[1] == 'two'
assert result[2] == 'three'
+ # ensures params string[] overload takes precedence over params object[]
+ assert result[3] == 'tail'
result = MethodTest.TestStringParamsArg(['one', 'two', 'three'])
- assert len(result) == 3
+ assert len(result) == 4
assert result[0] == 'one'
assert result[1] == 'two'
assert result[2] == 'three'
+ assert result[3] == 'tail'
def test_object_params_args():
diff --git a/tools/geninterop/geninterop.py b/tools/geninterop/geninterop.py
index 902296229..aed646dd4 100644
--- a/tools/geninterop/geninterop.py
+++ b/tools/geninterop/geninterop.py
@@ -21,6 +21,11 @@
import sysconfig
import subprocess
+if sys.version_info.major > 2:
+ from io import StringIO
+else:
+ from StringIO import StringIO
+
from pycparser import c_ast, c_parser
_log = logging.getLogger()
@@ -48,22 +53,25 @@ class AstParser(object):
"""Walk an AST and determine the members of all structs"""
def __init__(self):
- self.__typedefs = {}
- self.__typedecls = {}
- self.__structs = {}
- self.__struct_stack = []
- self.__struct_members_stack = []
- self.__ptr_decl_depth = 0
- self.__struct_members = {}
+ self._typedefs = {}
+ self._typedecls = {}
+ self._structs = {}
+ self._struct_stack = []
+ self._struct_members_stack = []
+ self._ptr_decl_depth = 0
+ self._struct_members = {}
+ self._decl_names = {}
def get_struct_members(self, name):
"""return a list of (name, type) of struct members"""
- if name in self.__typedefs:
- node = self.__get_leaf_node(self.__typedefs[name])
- name = node.name
- if name not in self.__struct_members:
- raise Exception("Unknown struct '%s'" % name)
- return self.__struct_members[name]
+ defs = self._typedefs.get(name)
+ if defs is None:
+ return None
+ node = self._get_leaf_node(defs)
+ name = node.name
+ if name is None:
+ name = defs.declname
+ return self._struct_members.get(name)
def visit(self, node):
if isinstance(node, c_ast.FileAST):
@@ -88,26 +96,27 @@ def visit_ast(self, ast):
self.visit(node)
def visit_typedef(self, typedef):
- self.__typedefs[typedef.name] = typedef.type
+ self._typedefs[typedef.name] = typedef.type
self.visit(typedef.type)
def visit_typedecl(self, typedecl):
+ self._decl_names[typedecl.type] = typedecl.declname
self.visit(typedecl.type)
def visit_struct(self, struct):
- self.__structs[self.__get_struct_name(struct)] = struct
if struct.decls:
+ self._structs[self._get_struct_name(struct)] = struct
# recurse into the struct
- self.__struct_stack.insert(0, struct)
+ self._struct_stack.insert(0, struct)
for decl in struct.decls:
- self.__struct_members_stack.insert(0, decl.name)
+ self._struct_members_stack.insert(0, decl.name)
self.visit(decl)
- self.__struct_members_stack.pop(0)
- self.__struct_stack.pop(0)
- elif self.__ptr_decl_depth:
+ self._struct_members_stack.pop(0)
+ self._struct_stack.pop(0)
+ elif self._ptr_decl_depth:
# the struct is empty, but add it as a member to the current
# struct as the current member maybe a pointer to it.
- self.__add_struct_member(struct.name)
+ self._add_struct_member(struct.name)
def visit_decl(self, decl):
self.visit(decl.type)
@@ -116,51 +125,72 @@ def visit_funcdecl(self, funcdecl):
self.visit(funcdecl.type)
def visit_ptrdecl(self, ptrdecl):
- self.__ptr_decl_depth += 1
+ self._ptr_decl_depth += 1
self.visit(ptrdecl.type)
- self.__ptr_decl_depth -= 1
+ self._ptr_decl_depth -= 1
def visit_identifier(self, identifier):
type_name = " ".join(identifier.names)
- self.__add_struct_member(type_name)
+ self._add_struct_member(type_name)
- def __add_struct_member(self, type_name):
- if not (self.__struct_stack and self.__struct_members_stack):
+ def _add_struct_member(self, type_name):
+ if not (self._struct_stack and self._struct_members_stack):
return
# add member to current struct
- current_struct = self.__struct_stack[0]
- member_name = self.__struct_members_stack[0]
- struct_members = self.__struct_members.setdefault(
- self.__get_struct_name(current_struct), [])
+ current_struct = self._struct_stack[0]
+ member_name = self._struct_members_stack[0]
+ struct_members = self._struct_members.setdefault(
+ self._get_struct_name(current_struct), [])
# get the node associated with this type
node = None
- if type_name in self.__typedefs:
- node = self.__get_leaf_node(self.__typedefs[type_name])
- elif type_name in self.__structs:
- node = self.__structs[type_name]
+ if type_name in self._typedefs:
+ node = self._get_leaf_node(self._typedefs[type_name])
+ # If the struct was only declared when the typedef was created, its member
+ # information will not have been recorded and we have to look it up in the
+ # structs
+ if isinstance(node, c_ast.Struct) and node.decls is None:
+ if node.name in self._structs:
+ node = self._structs[node.name]
+ elif type_name in self._structs:
+ node = self._structs[type_name]
# If it's a struct (and not a pointer to a struct) expand
# it into the current struct definition
- if not self.__ptr_decl_depth and isinstance(node, c_ast.Struct):
+ if not self._ptr_decl_depth and isinstance(node, c_ast.Struct):
for decl in node.decls or []:
- self.__struct_members_stack.insert(0, decl.name)
+ self._struct_members_stack.insert(0, decl.name)
self.visit(decl)
- self.__struct_members_stack.pop(0)
+ self._struct_members_stack.pop(0)
else:
# otherwise add it as a single member
struct_members.append((member_name, type_name))
- def __get_leaf_node(self, node):
+ def _get_leaf_node(self, node):
if isinstance(node, c_ast.Typedef):
- return self.__get_leaf_node(node.type)
+ return self._get_leaf_node(node.type)
if isinstance(node, c_ast.TypeDecl):
- return self.__get_leaf_node(node.type)
+ return self._get_leaf_node(node.type)
return node
- def __get_struct_name(self, node):
- return node.name or "_struct_%d" % id(node)
+ def _get_struct_name(self, node):
+ return node.name or self._decl_names.get(node) or "_struct_%d" % id(node)
+
+
+class Writer(object):
+
+ def __init__(self):
+ self._stream = StringIO()
+
+ def append(self, indent=0, code=""):
+ self._stream.write("%s%s\n" % (indent * " ", code))
+
+ def extend(self, s):
+ self._stream.write(s)
+
+ def to_string(self):
+ return self._stream.getvalue()
def preprocess_python_headers():
@@ -188,6 +218,7 @@ def preprocess_python_headers():
defines.extend([
"-D", "__inline=inline",
"-D", "__ptr32=",
+ "-D", "__ptr64=",
"-D", "__declspec(x)=",
])
@@ -211,9 +242,8 @@ def preprocess_python_headers():
return "\n".join(lines)
-def gen_interop_code(members):
- """Generate the TypeOffset C# class"""
+def gen_interop_head(writer):
defines = [
"PYTHON{0}{1}".format(PY_MAJOR, PY_MINOR)
]
@@ -243,8 +273,23 @@ def gen_interop_code(members):
namespace Python.Runtime
{
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
- internal class TypeOffset
+""" % (filename, defines_str)
+ writer.extend(class_definition)
+
+
+def gen_interop_tail(writer):
+ tail = """}
+#endif
+"""
+ writer.extend(tail)
+
+
+def gen_heap_type_members(parser, writer):
+ """Generate the TypeOffset C# class"""
+ members = parser.get_struct_members("PyHeapTypeObject")
+ class_definition = """
+ [StructLayout(LayoutKind.Sequential)]
+ internal static class TypeOffset
{
static TypeOffset()
{
@@ -263,7 +308,7 @@ def gen_interop_code(members):
}
// Auto-generated from PyHeapTypeObject in Python.h
-""" % (filename, defines_str)
+"""
# All the members are sizeof(void*) so we don't need to do any
# extra work to determine the size based on the type.
@@ -275,11 +320,36 @@ def gen_interop_code(members):
/* here are optional user slots, followed by the members. */
public static int members = 0;
}
-}
-#endif
"""
- return class_definition
+ writer.extend(class_definition)
+
+
+def gen_structure_code(parser, writer, type_name, indent):
+ members = parser.get_struct_members(type_name)
+ if members is None:
+ return False
+ out = writer.append
+ out(indent, "[StructLayout(LayoutKind.Sequential)]")
+ out(indent, "internal struct %s" % type_name)
+ out(indent, "{")
+ for name, tpy in members:
+ out(indent + 1, "public IntPtr %s;" % name)
+ out(indent, "}")
+ out()
+ return True
+
+
+def gen_supported_slot_record(writer, types, indent):
+ out = writer.append
+ out(indent, "internal static partial class SlotTypes")
+ out(indent, "{")
+ out(indent + 1, "public static readonly Type[] Types = {")
+ for name in types:
+ out(indent + 2, "typeof(%s)," % name)
+ out(indent + 1, "};")
+ out(indent, "}")
+ out()
def main():
@@ -292,10 +362,29 @@ def main():
ast_parser = AstParser()
ast_parser.visit(ast)
+ writer = Writer()
# generate the C# code
- members = ast_parser.get_struct_members("PyHeapTypeObject")
- interop_cs = gen_interop_code(members)
+ gen_interop_head(writer)
+
+ gen_heap_type_members(ast_parser, writer)
+ slots_types = [
+ "PyNumberMethods",
+ "PySequenceMethods",
+ "PyMappingMethods",
+ "PyAsyncMethods",
+ "PyBufferProcs",
+ ]
+ supported_types = []
+ indent = 1
+ for type_name in slots_types:
+ if not gen_structure_code(ast_parser, writer, type_name, indent):
+ continue
+ supported_types.append(type_name)
+ gen_supported_slot_record(writer, supported_types, indent)
+
+ gen_interop_tail(writer)
+ interop_cs = writer.to_string()
if len(sys.argv) > 1:
with open(sys.argv[1], "w") as fh:
fh.write(interop_cs)