From c422abdba471a114f04519c08abc7bba71163afb Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sun, 18 Sep 2022 16:00:02 +0200 Subject: [PATCH 01/14] Add Python 3.11 type offsets --- src/runtime/Native/TypeOffset311.cs | 141 ++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 src/runtime/Native/TypeOffset311.cs diff --git a/src/runtime/Native/TypeOffset311.cs b/src/runtime/Native/TypeOffset311.cs new file mode 100644 index 000000000..7691236c9 --- /dev/null +++ b/src/runtime/Native/TypeOffset311.cs @@ -0,0 +1,141 @@ + +// Auto-generated by geninterop.py. +// DO NOT MODIFY BY HAND. + +// Python 3.11: ABI flags: '' + +// ReSharper disable InconsistentNaming +// ReSharper disable IdentifierTypo + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +using Python.Runtime.Native; + +namespace Python.Runtime +{ + [SuppressMessage("Style", "IDE1006:Naming Styles", + Justification = "Following CPython", + Scope = "type")] + + [StructLayout(LayoutKind.Sequential)] + internal class TypeOffset311 : GeneratedTypeOffsets, ITypeOffsets + { + public TypeOffset311() { } + // Auto-generated from PyHeapTypeObject in Python.h + public int ob_refcnt { get; private set; } + public int ob_type { get; private set; } + public int ob_size { get; private set; } + public int tp_name { get; private set; } + public int tp_basicsize { get; private set; } + public int tp_itemsize { get; private set; } + public int tp_dealloc { get; private set; } + public int tp_vectorcall_offset { get; private set; } + public int tp_getattr { get; private set; } + public int tp_setattr { get; private set; } + public int tp_as_async { get; private set; } + public int tp_repr { get; private set; } + public int tp_as_number { get; private set; } + public int tp_as_sequence { get; private set; } + public int tp_as_mapping { get; private set; } + public int tp_hash { get; private set; } + public int tp_call { get; private set; } + public int tp_str { get; private set; } + public int tp_getattro { get; private set; } + public int tp_setattro { get; private set; } + public int tp_as_buffer { get; private set; } + public int tp_flags { get; private set; } + public int tp_doc { get; private set; } + public int tp_traverse { get; private set; } + public int tp_clear { get; private set; } + public int tp_richcompare { get; private set; } + public int tp_weaklistoffset { get; private set; } + public int tp_iter { get; private set; } + public int tp_iternext { get; private set; } + public int tp_methods { get; private set; } + public int tp_members { get; private set; } + public int tp_getset { get; private set; } + public int tp_base { get; private set; } + public int tp_dict { get; private set; } + public int tp_descr_get { get; private set; } + public int tp_descr_set { get; private set; } + public int tp_dictoffset { get; private set; } + public int tp_init { get; private set; } + public int tp_alloc { get; private set; } + public int tp_new { get; private set; } + public int tp_free { get; private set; } + public int tp_is_gc { get; private set; } + public int tp_bases { get; private set; } + public int tp_mro { get; private set; } + public int tp_cache { get; private set; } + public int tp_subclasses { get; private set; } + public int tp_weaklist { get; private set; } + public int tp_del { get; private set; } + public int tp_version_tag { get; private set; } + public int tp_finalize { get; private set; } + public int tp_vectorcall { get; private set; } + public int am_await { get; private set; } + public int am_aiter { get; private set; } + public int am_anext { get; private set; } + public int am_send { get; private set; } + public int nb_add { get; private set; } + public int nb_subtract { get; private set; } + public int nb_multiply { get; private set; } + public int nb_remainder { get; private set; } + public int nb_divmod { get; private set; } + public int nb_power { get; private set; } + public int nb_negative { get; private set; } + public int nb_positive { get; private set; } + public int nb_absolute { get; private set; } + public int nb_bool { get; private set; } + public int nb_invert { get; private set; } + public int nb_lshift { get; private set; } + public int nb_rshift { get; private set; } + public int nb_and { get; private set; } + public int nb_xor { get; private set; } + public int nb_or { get; private set; } + public int nb_int { get; private set; } + public int nb_reserved { get; private set; } + public int nb_float { get; private set; } + public int nb_inplace_add { get; private set; } + public int nb_inplace_subtract { get; private set; } + public int nb_inplace_multiply { get; private set; } + public int nb_inplace_remainder { get; private set; } + public int nb_inplace_power { get; private set; } + public int nb_inplace_lshift { get; private set; } + public int nb_inplace_rshift { get; private set; } + public int nb_inplace_and { get; private set; } + public int nb_inplace_xor { get; private set; } + public int nb_inplace_or { get; private set; } + public int nb_floor_divide { get; private set; } + public int nb_true_divide { get; private set; } + public int nb_inplace_floor_divide { get; private set; } + public int nb_inplace_true_divide { get; private set; } + public int nb_index { get; private set; } + public int nb_matrix_multiply { get; private set; } + public int nb_inplace_matrix_multiply { get; private set; } + public int mp_length { get; private set; } + public int mp_subscript { get; private set; } + public int mp_ass_subscript { get; private set; } + public int sq_length { get; private set; } + public int sq_concat { get; private set; } + public int sq_repeat { get; private set; } + public int sq_item { get; private set; } + public int was_sq_slice { get; private set; } + public int sq_ass_item { get; private set; } + public int was_sq_ass_slice { get; private set; } + public int sq_contains { get; private set; } + public int sq_inplace_concat { get; private set; } + public int sq_inplace_repeat { get; private set; } + public int bf_getbuffer { get; private set; } + public int bf_releasebuffer { get; private set; } + public int name { get; private set; } + public int ht_slots { get; private set; } + public int qualname { get; private set; } + public int ht_cached_keys { get; private set; } + public int ht_module { get; private set; } + public int _ht_tpname { get; private set; } + } +} + From 2b5291000e0fae50bdf3db3e9a7a38157b319ba6 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sun, 18 Sep 2022 16:03:40 +0200 Subject: [PATCH 02/14] Add Python 3.11 to metadata and workflows --- .github/workflows/main.yml | 2 +- pyproject.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2cc793621..ea93ce18c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: os: [windows, ubuntu, macos] - python: ["3.7", "3.8", "3.9", "3.10"] + python: ["3.7", "3.8", "3.9", "3.10", "3.11"] platform: [x64, x86] exclude: - os: ubuntu diff --git a/pyproject.toml b/pyproject.toml index 91f386fc6..d0512cc45 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ classifiers = [ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX :: Linux", "Operating System :: MacOS :: MacOS X", From c0b4eb285ef3af1fdf90b0461e14c26b9969cf7a Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 19 Sep 2022 11:09:01 +0200 Subject: [PATCH 03/14] Improve geninterop script to handle new case in 3.11 --- tools/geninterop/geninterop.py | 144 +++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 61 deletions(-) mode change 100644 => 100755 tools/geninterop/geninterop.py diff --git a/tools/geninterop/geninterop.py b/tools/geninterop/geninterop.py old mode 100644 new mode 100755 index 0c80c1904..78e4d45c2 --- a/tools/geninterop/geninterop.py +++ b/tools/geninterop/geninterop.py @@ -13,40 +13,26 @@ - clang """ -from __future__ import print_function - -import logging import os +import shutil import sys import sysconfig import subprocess -if sys.version_info.major > 2: - from io import StringIO -else: - from StringIO import StringIO - +from io import StringIO +from pathlib import Path from pycparser import c_ast, c_parser -_log = logging.getLogger() -logging.basicConfig(level=logging.DEBUG) - -PY_MAJOR = sys.version_info[0] -PY_MINOR = sys.version_info[1] - # rename some members from their C name when generating the C# _typeoffset_member_renames = { "ht_name": "name", - "ht_qualname": "qualname" + "ht_qualname": "qualname", + "getitem": "spec_cache_getitem", } def _check_output(*args, **kwargs): - """Check output wrapper for py2/py3 compatibility""" - output = subprocess.check_output(*args, **kwargs) - if PY_MAJOR == 2: - return output - return output.decode("ascii") + return subprocess.check_output(*args, **kwargs, encoding="utf8") class AstParser(object): @@ -92,7 +78,7 @@ def visit(self, node): self.visit_identifier(node) def visit_ast(self, ast): - for name, node in ast.children(): + for _name, node in ast.children(): self.visit(node) def visit_typedef(self, typedef): @@ -113,7 +99,7 @@ def visit_struct(self, struct): self.visit(decl) self._struct_members_stack.pop(0) self._struct_stack.pop(0) - elif self._ptr_decl_depth: + elif self._ptr_decl_depth or self._struct_members_stack: # 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) @@ -141,7 +127,8 @@ def _add_struct_member(self, type_name): 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), []) + self._get_struct_name(current_struct), [] + ) # get the node associated with this type node = None @@ -179,7 +166,6 @@ def _get_struct_name(self, node): class Writer(object): - def __init__(self): self._stream = StringIO() @@ -193,34 +179,47 @@ def to_string(self): return self._stream.getvalue() -def preprocess_python_headers(): +def preprocess_python_headers(*, cc=None, include_py=None): """Return Python.h pre-processed, ready for parsing. Requires clang. """ - fake_libc_include = os.path.join(os.path.dirname(__file__), - "fake_libc_include") + this_path = Path(__file__).parent + + fake_libc_include = this_path / "fake_libc_include" include_dirs = [fake_libc_include] - include_py = sysconfig.get_config_var("INCLUDEPY") + if cc is None: + cc = shutil.which("clang") + if cc is None: + cc = shutil.which("gcc") + if cc is None: + raise RuntimeError("No suitable C compiler found, need clang or gcc") + + if include_py is None: + include_py = sysconfig.get_config_var("INCLUDEPY") + include_py = Path(include_py) + include_dirs.append(include_py) - include_args = [c for p in include_dirs for c in ["-I", p]] + include_args = [c for p in include_dirs for c in ["-I", str(p)]] + # fmt: off defines = [ "-D", "__attribute__(x)=", "-D", "__inline__=inline", "-D", "__asm__=;#pragma asm", "-D", "__int64=long long", - "-D", "_POSIX_THREADS" + "-D", "_POSIX_THREADS", ] - if os.name == 'nt': + if sys.platform == "win32": defines.extend([ "-D", "__inline=inline", "-D", "__ptr32=", "-D", "__ptr64=", "-D", "__declspec(x)=", ]) + #fmt: on if hasattr(sys, "abiflags"): if "d" in sys.abiflags: @@ -228,8 +227,8 @@ def preprocess_python_headers(): if "u" in sys.abiflags: defines.extend(("-D", "PYTHON_WITH_WIDE_UNICODE")) - python_h = os.path.join(include_py, "Python.h") - cmd = ["clang", "-pthread"] + include_args + defines + ["-E", python_h] + python_h = include_py / "Python.h" + cmd = [cc, "-pthread"] + include_args + defines + ["-E", str(python_h)] # normalize as the parser doesn't like windows line endings. lines = [] @@ -240,16 +239,13 @@ def preprocess_python_headers(): return "\n".join(lines) - -def gen_interop_head(writer): +def gen_interop_head(writer, version, abi_flags): filename = os.path.basename(__file__) - abi_flags = getattr(sys, "abiflags", "").replace("m", "") - py_ver = "{0}.{1}".format(PY_MAJOR, PY_MINOR) - class_definition = """ -// Auto-generated by %s. + class_definition = f""" +// Auto-generated by {filename}. // DO NOT MODIFY BY HAND. -// Python %s: ABI flags: '%s' +// Python {".".join(version[:2])}: ABI flags: '{abi_flags}' // ReSharper disable InconsistentNaming // ReSharper disable IdentifierTypo @@ -261,7 +257,7 @@ def gen_interop_head(writer): using Python.Runtime.Native; namespace Python.Runtime -{""" % (filename, py_ver, abi_flags) +{{""" writer.extend(class_definition) @@ -271,25 +267,24 @@ def gen_interop_tail(writer): writer.extend(tail) -def gen_heap_type_members(parser, writer, type_name = None): +def gen_heap_type_members(parser, writer, type_name): """Generate the TypeOffset C# class""" members = parser.get_struct_members("PyHeapTypeObject") - type_name = type_name or "TypeOffset{0}{1}".format(PY_MAJOR, PY_MINOR) - class_definition = """ + class_definition = f""" [SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Following CPython", Scope = "type")] [StructLayout(LayoutKind.Sequential)] - internal class {0} : GeneratedTypeOffsets, ITypeOffsets + internal class {type_name} : GeneratedTypeOffsets, ITypeOffsets {{ - public {0}() {{ }} + public {type_name}() {{ }} // Auto-generated from PyHeapTypeObject in Python.h -""".format(type_name) +""" # All the members are sizeof(void*) so we don't need to do any # extra work to determine the size based on the type. - for name, tpy in members: + for name, _type in members: name = _typeoffset_member_renames.get(name, name) class_definition += " public int %s { get; private set; }\n" % name @@ -304,17 +299,18 @@ def gen_structure_code(parser, writer, type_name, indent): return False out = writer.append out(indent, "[StructLayout(LayoutKind.Sequential)]") - out(indent, "internal struct %s" % type_name) + out(indent, f"internal struct {type_name}") out(indent, "{") - for name, tpy in members: - out(indent + 1, "public IntPtr %s;" % name) + for name, _type in members: + out(indent + 1, f"public IntPtr {name};") out(indent, "}") out() return True -def main(): + +def main(*, cc=None, include_py=None, version=None, out=None): # preprocess Python.h and build the AST - python_h = preprocess_python_headers() + python_h = preprocess_python_headers(cc=cc, include_py=include_py) parser = c_parser.CParser() ast = parser.parse(python_h) @@ -323,21 +319,47 @@ def main(): ast_parser.visit(ast) writer = Writer() + + if include_py and not version: + raise RuntimeError("If the include path is overridden, version must be " + "defined" + ) + + if version: + version = version.split('.') + else: + version = sys.version_info + # generate the C# code - offsets_type_name = "NativeTypeOffset" if len(sys.argv) > 1 else None - gen_interop_head(writer) + abi_flags = getattr(sys, "abiflags", "").replace("m", "") + gen_interop_head(writer, version, abi_flags) - gen_heap_type_members(ast_parser, writer, type_name = offsets_type_name) + type_name = f"TypeOffset{version[0]}{version[1]}{abi_flags}" + gen_heap_type_members(ast_parser, writer, type_name) 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) - else: + if not out or out == "-": print(interop_cs) + else: + with open(out, "w") as fh: + fh.write(interop_cs) if __name__ == "__main__": - sys.exit(main()) + import argparse + + a = argparse.ArgumentParser("Interop file generator for Python.NET") + a.add_argument("--cc", help="C compiler to use, either clang or gcc") + a.add_argument("--include-py", help="Include path of Python") + a.add_argument("--version", help="Python version") + a.add_argument("--out", help="Output path", default="-") + args = a.parse_args() + + sys.exit(main( + cc=args.cc, + include_py=args.include_py, + out=args.out, + version=args.version + )) From cc8606856278a511a2965c8224032cab40c6958d Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 19 Sep 2022 11:09:18 +0200 Subject: [PATCH 04/14] Fix offsets for 3.11 --- src/runtime/Native/TypeOffset311.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/Native/TypeOffset311.cs b/src/runtime/Native/TypeOffset311.cs index 7691236c9..de5afacb9 100644 --- a/src/runtime/Native/TypeOffset311.cs +++ b/src/runtime/Native/TypeOffset311.cs @@ -136,6 +136,6 @@ public TypeOffset311() { } public int ht_cached_keys { get; private set; } public int ht_module { get; private set; } public int _ht_tpname { get; private set; } + public int spec_cache_getitem { get; private set; } } } - From 5636262b27f883c484369c89ecdbcd54afa97a56 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sat, 15 Oct 2022 21:41:55 +0200 Subject: [PATCH 05/14] Update requires-python --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d0512cc45..52f1adb18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ dependencies = [ "clr_loader>=0.2.2,<0.3.0" ] -requires-python = ">=3.7, <3.11" +requires-python = ">=3.7, <3.12" classifiers = [ "Development Status :: 5 - Production/Stable", From 8668579834623a1ef196840b8f2a93407f52701b Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sat, 15 Oct 2022 22:13:30 +0200 Subject: [PATCH 06/14] Define slots before initialization --- src/runtime/PythonTypes/PyType.cs | 1 + src/runtime/TypeManager.cs | 23 +++++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/runtime/PythonTypes/PyType.cs b/src/runtime/PythonTypes/PyType.cs index 260800592..af796a5c5 100644 --- a/src/runtime/PythonTypes/PyType.cs +++ b/src/runtime/PythonTypes/PyType.cs @@ -155,6 +155,7 @@ private static StolenReference FromSpec(TypeSpec spec, PyTuple? bases = null) using var nativeSpec = new NativeTypeSpec(spec); var basesRef = bases is null ? default : bases.Reference; var result = Runtime.PyType_FromSpecWithBases(in nativeSpec, basesRef); + // Runtime.PyErr_Print(); return result.StealOrThrow(); } } diff --git a/src/runtime/TypeManager.cs b/src/runtime/TypeManager.cs index 217b4820e..6170f820f 100644 --- a/src/runtime/TypeManager.cs +++ b/src/runtime/TypeManager.cs @@ -475,17 +475,20 @@ internal static PyType CreateMetatypeWithGCHandleOffset() int size = Util.ReadInt32(Runtime.PyTypeType, TypeOffset.tp_basicsize) + IntPtr.Size // tp_clr_inst_offset ; - var result = new PyType(new TypeSpec("clr._internal.GCOffsetBase", basicSize: size, - new TypeSpec.Slot[] - { - - }, - TypeFlags.Default | TypeFlags.HeapType | TypeFlags.HaveGC), - bases: new PyTuple(new[] { py_type })); - SetRequiredSlots(result, seen: new HashSet()); - - Runtime.PyType_Modified(result); + var slots = new[] { + new TypeSpec.Slot(TypeSlotID.tp_traverse, subtype_traverse), + new TypeSpec.Slot(TypeSlotID.tp_clear, subtype_clear) + }; + var result = new PyType( + new TypeSpec( + "clr._internal.GCOffsetBase", + basicSize: size, + slots: slots, + TypeFlags.Default | TypeFlags.HeapType | TypeFlags.HaveGC + ), + bases: new PyTuple(new[] { py_type }) + ); return result; } From ddf5a701d2542c81b6bf0dcc6ad4a5a440713408 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sun, 30 Oct 2022 21:36:03 +0100 Subject: [PATCH 07/14] Only clear dict if tp_dictoffset > 0 --- src/runtime/Types/ManagedType.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/runtime/Types/ManagedType.cs b/src/runtime/Types/ManagedType.cs index 2ed9d7970..97a19497c 100644 --- a/src/runtime/Types/ManagedType.cs +++ b/src/runtime/Types/ManagedType.cs @@ -148,8 +148,9 @@ protected static void ClearObjectDict(BorrowedReference ob) { BorrowedReference type = Runtime.PyObject_TYPE(ob); int instanceDictOffset = Util.ReadInt32(type, TypeOffset.tp_dictoffset); - Debug.Assert(instanceDictOffset > 0); - Runtime.Py_CLEAR(ob, instanceDictOffset); + // Debug.Assert(instanceDictOffset > 0); + if (instanceDictOffset > 0) + Runtime.Py_CLEAR(ob, instanceDictOffset); } protected static BorrowedReference GetObjectDict(BorrowedReference ob) From d3b56ffeeb35091365676362665460268d087a87 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sun, 30 Oct 2022 21:40:26 +0100 Subject: [PATCH 08/14] Ensure that sub-processes in tests use the same runtime settings --- tests/conftest.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index e61e3680e..1ac20e1dd 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -79,9 +79,10 @@ def pytest_configure(config): ["dotnet", "publish", "-f", fw, "-o", str(bin_path), str(test_proj_path)] ) - from pythonnet import load - - load(runtime_opt, **runtime_params) + import os + os.environ["PYTHONNET_RUNTIME"] = runtime_opt + for k, v in runtime_params.items(): + os.environ[f"PYTHONNET_{runtime_opt.upper()}_{k.upper()}"] = v import clr From a6efeaee77404b75e557bae5cf795cbdd704bd00 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sun, 30 Oct 2022 21:40:42 +0100 Subject: [PATCH 09/14] Update MaxSupportedVersion --- src/runtime/PythonEngine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/PythonEngine.cs b/src/runtime/PythonEngine.cs index e5879ae67..ddf597c4f 100644 --- a/src/runtime/PythonEngine.cs +++ b/src/runtime/PythonEngine.cs @@ -128,7 +128,7 @@ public static string PythonPath } public static Version MinSupportedVersion => new(3, 7); - public static Version MaxSupportedVersion => new(3, 10, int.MaxValue, int.MaxValue); + public static Version MaxSupportedVersion => new(3, 11, int.MaxValue, int.MaxValue); public static bool IsSupportedVersion(Version version) => version >= MinSupportedVersion && version <= MaxSupportedVersion; public static string Version From da082ac487190aed5d325a346b0a6c268c870020 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Sun, 30 Oct 2022 23:16:57 +0100 Subject: [PATCH 10/14] Enforce tp_traverse/clear in AllocateTypeObject --- src/runtime/TypeManager.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/runtime/TypeManager.cs b/src/runtime/TypeManager.cs index 6170f820f..e0a78ba49 100644 --- a/src/runtime/TypeManager.cs +++ b/src/runtime/TypeManager.cs @@ -620,6 +620,11 @@ internal static PyType AllocateTypeObject(string name, PyType metatype) Util.WriteRef(type, TypeOffset.name, new NewReference(temp).Steal()); Util.WriteRef(type, TypeOffset.qualname, temp.Steal()); + // Ensure that tp_traverse and tp_clear are always set, since their + // existence is enforced in newer Python versions in PyType_Ready + Util.WriteIntPtr(type, TypeOffset.tp_traverse, subtype_traverse); + Util.WriteIntPtr(type, TypeOffset.tp_clear, subtype_clear); + InheritSubstructs(type.Reference.DangerousGetAddress()); return type; From e9283e3ec35184c7c4464540756f3d20c1301bfd Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Mon, 31 Oct 2022 14:51:18 +0100 Subject: [PATCH 11/14] Ensure that Python is initialized before probing properties --- src/embed_tests/TestPythonEngineProperties.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/embed_tests/TestPythonEngineProperties.cs b/src/embed_tests/TestPythonEngineProperties.cs index ca9164a1d..bbaff047f 100644 --- a/src/embed_tests/TestPythonEngineProperties.cs +++ b/src/embed_tests/TestPythonEngineProperties.cs @@ -9,6 +9,7 @@ public class TestPythonEngineProperties [Test] public static void GetBuildinfoDoesntCrash() { + PythonEngine.Initialize(); using (Py.GIL()) { string s = PythonEngine.BuildInfo; @@ -21,6 +22,7 @@ public static void GetBuildinfoDoesntCrash() [Test] public static void GetCompilerDoesntCrash() { + PythonEngine.Initialize(); using (Py.GIL()) { string s = PythonEngine.Compiler; @@ -34,6 +36,7 @@ public static void GetCompilerDoesntCrash() [Test] public static void GetCopyrightDoesntCrash() { + PythonEngine.Initialize(); using (Py.GIL()) { string s = PythonEngine.Copyright; @@ -46,6 +49,7 @@ public static void GetCopyrightDoesntCrash() [Test] public static void GetPlatformDoesntCrash() { + PythonEngine.Initialize(); using (Py.GIL()) { string s = PythonEngine.Platform; @@ -58,6 +62,7 @@ public static void GetPlatformDoesntCrash() [Test] public static void GetVersionDoesntCrash() { + PythonEngine.Initialize(); using (Py.GIL()) { string s = PythonEngine.Version; From cc97b8a49646e952a774e105f77d00f21761d864 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 1 Nov 2022 14:46:39 +0100 Subject: [PATCH 12/14] Add an Action variant of TryUsingDll --- src/runtime/Runtime.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/runtime/Runtime.cs b/src/runtime/Runtime.cs index 6238119ff..26e83a5f9 100644 --- a/src/runtime/Runtime.cs +++ b/src/runtime/Runtime.cs @@ -672,6 +672,9 @@ internal static unsafe nint Refcount(BorrowedReference op) [Pure] internal static int Refcount32(BorrowedReference op) => checked((int)Refcount(op)); + internal static void TryUsingDll(Action op) => + TryUsingDll(() => { op(); return 0; }); + /// /// Call specified function, and handle PythonDLL-related failures. /// From 096f50a21e235b6a8a15c011d0a390468901daa2 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Wed, 2 Nov 2022 06:36:34 +0100 Subject: [PATCH 13/14] Adjust code a bit and skip PythonHome tests for empty strings --- src/embed_tests/TestPythonEngineProperties.cs | 49 ++++++++++++------- src/runtime/PythonEngine.cs | 15 ++++-- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/embed_tests/TestPythonEngineProperties.cs b/src/embed_tests/TestPythonEngineProperties.cs index bbaff047f..be91d7f45 100644 --- a/src/embed_tests/TestPythonEngineProperties.cs +++ b/src/embed_tests/TestPythonEngineProperties.cs @@ -96,9 +96,6 @@ public static void GetProgramNameDefault() /// Test default behavior of PYTHONHOME. If ENVVAR is set it will /// return the same value. If not, returns EmptyString. /// - /// - /// AppVeyor.yml has been update to tests with ENVVAR set. - /// [Test] public static void GetPythonHomeDefault() { @@ -114,22 +111,19 @@ public static void GetPythonHomeDefault() [Test] public void SetPythonHome() { - // We needs to ensure that engine was started and shutdown at least once before setting dummy home. - // Otherwise engine will not run with dummy path with random problem. - if (!PythonEngine.IsInitialized) - { - PythonEngine.Initialize(); - } - + PythonEngine.Initialize(); + var pythonHomeBackup = PythonEngine.PythonHome; PythonEngine.Shutdown(); - var pythonHomeBackup = PythonEngine.PythonHome; + if (pythonHomeBackup == "") + Assert.Inconclusive("Can't reset PythonHome to empty string, skipping"); var pythonHome = "/dummypath/"; PythonEngine.PythonHome = pythonHome; PythonEngine.Initialize(); + Assert.AreEqual(pythonHome, PythonEngine.PythonHome); PythonEngine.Shutdown(); // Restoring valid pythonhome. @@ -139,15 +133,12 @@ public void SetPythonHome() [Test] public void SetPythonHomeTwice() { - // We needs to ensure that engine was started and shutdown at least once before setting dummy home. - // Otherwise engine will not run with dummy path with random problem. - if (!PythonEngine.IsInitialized) - { - PythonEngine.Initialize(); - } + PythonEngine.Initialize(); + var pythonHomeBackup = PythonEngine.PythonHome; PythonEngine.Shutdown(); - var pythonHomeBackup = PythonEngine.PythonHome; + if (pythonHomeBackup == "") + Assert.Inconclusive("Can't reset PythonHome to empty string, skipping"); var pythonHome = "/dummypath/"; @@ -161,6 +152,26 @@ public void SetPythonHomeTwice() PythonEngine.PythonHome = pythonHomeBackup; } + [Test] + [Ignore("Currently buggy in Python")] + public void SetPythonHomeEmptyString() + { + PythonEngine.Initialize(); + + var backup = PythonEngine.PythonHome; + if (backup == "") + { + PythonEngine.Shutdown(); + Assert.Inconclusive("Can't reset PythonHome to empty string, skipping"); + } + PythonEngine.PythonHome = ""; + + Assert.AreEqual("", PythonEngine.PythonHome); + + PythonEngine.PythonHome = backup; + PythonEngine.Shutdown(); + } + [Test] public void SetProgramName() { @@ -207,7 +218,7 @@ public void SetPythonPath() // The list sys.path is initialized with this value on interpreter startup; // it can be (and usually is) modified later to change the search path for loading modules. // See https://docs.python.org/3/c-api/init.html#c.Py_GetPath - // After PythonPath is set, then PythonEngine.PythonPath will correctly return the full search path. + // After PythonPath is set, then PythonEngine.PythonPath will correctly return the full search path. PythonEngine.Shutdown(); diff --git a/src/runtime/PythonEngine.cs b/src/runtime/PythonEngine.cs index ddf597c4f..4ed45b9e9 100644 --- a/src/runtime/PythonEngine.cs +++ b/src/runtime/PythonEngine.cs @@ -47,6 +47,14 @@ public static bool IsInitialized get { return initialized; } } + private static void EnsureInitialized() + { + if (!IsInitialized) + throw new InvalidOperationException( + "Python must be initialized for this operation" + ); + } + /// Set to true to enable GIL debugging assistance. public static bool DebugGIL { get; set; } = false; @@ -96,6 +104,7 @@ public static string PythonHome { get { + EnsureInitialized(); IntPtr p = Runtime.TryUsingDll(() => Runtime.Py_GetPythonHome()); return UcsMarshaler.PtrToPy3UnicodePy2String(p) ?? ""; } @@ -103,10 +112,8 @@ public static string PythonHome { // this value is null in the beginning Marshal.FreeHGlobal(_pythonHome); - _pythonHome = Runtime.TryUsingDll( - () => UcsMarshaler.Py3UnicodePy2StringtoPtr(value) - ); - Runtime.Py_SetPythonHome(_pythonHome); + _pythonHome = UcsMarshaler.Py3UnicodePy2StringtoPtr(value); + Runtime.TryUsingDll(() => Runtime.Py_SetPythonHome(_pythonHome)); } } From 15e2e9596ccf6c23ea1bc27db4977055455c170d Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Wed, 2 Nov 2022 08:29:02 +0100 Subject: [PATCH 14/14] Set PYTHONHOME for tests --- .github/workflows/main.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ea93ce18c..93963c70a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -54,15 +54,17 @@ jobs: run: | pip install -v . - - name: Set Python DLL path (non Windows) + - name: Set Python DLL path and PYTHONHOME (non Windows) if: ${{ matrix.os != 'windows' }} run: | echo PYTHONNET_PYDLL=$(python -m find_libpython) >> $GITHUB_ENV + echo PYTHONHOME=$(python -c 'import sys; print(sys.prefix)') >> $GITHUB_ENV - - name: Set Python DLL path (Windows) + - name: Set Python DLL path and PYTHONHOME (Windows) if: ${{ matrix.os == 'windows' }} run: | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append -InputObject "PYTHONNET_PYDLL=$(python -m find_libpython)" + Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append -InputObject "PYTHONHOME=$(python -c 'import sys; print(sys.prefix)')" - name: Embedding tests run: dotnet test --runtime any-${{ matrix.platform }} --logger "console;verbosity=detailed" src/embed_tests/ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy