Skip to content

Commit 1d107df

Browse files
authored
Merge branch 'master' into modernize-import-hook
2 parents 4d27f6e + 0775458 commit 1d107df

33 files changed

+820
-299
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
1313
- Python operator method will call C# operator method for supported binary and unary operators ([#1324][p1324]).
1414
- Add GetPythonThreadID and Interrupt methods in PythonEngine
1515
- Ability to implement delegates with `ref` and `out` parameters in Python, by returning the modified parameter values in a tuple. ([#1355][i1355])
16+
- `PyType` - a wrapper for Python type objects, that also permits creating new heap types from `TypeSpec`
1617

1718
### Changed
1819
- Drop support for Python 2, 3.4, and 3.5
@@ -37,9 +38,13 @@ when .NET expects an integer [#1342][i1342]
3738
- BREAKING: to call Python from .NET `Runtime.PythonDLL` property must be set to Python DLL name
3839
or the DLL must be loaded in advance. This must be done before calling any other Python.NET functions.
3940
- BREAKING: `PyObject.Length()` now raises a `PythonException` when object does not support a concept of length.
41+
- BREAKING: disabled implicit conversion from C# enums to Python `int` and back.
42+
One must now either use enum members (e.g. `MyEnum.Option`), or use enum constructor
43+
(e.g. `MyEnum(42)` or `MyEnum(42, True)` when `MyEnum` does not have a member with value 42).
4044
- Sign Runtime DLL with a strong name
4145
- Implement loading through `clr_loader` instead of the included `ClrModule`, enables
4246
support for .NET Core
47+
- BREAKING: custom encoders are no longer called for instances of `System.Type`
4348
- Replaced the old `__import__` hook hack with a PEP302-style Meta Path Loader.
4449

4550
### Fixed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<AssemblyCopyright>Copyright (c) 2006-2020 The Contributors of the Python.NET Project</AssemblyCopyright>
55
<AssemblyCompany>pythonnet</AssemblyCompany>
66
<AssemblyProduct>Python.NET</AssemblyProduct>
7-
<LangVersion>7.3</LangVersion>
7+
<LangVersion>9.0</LangVersion>
88
<IsPackable>false</IsPackable>
99
</PropertyGroup>
1010
<ItemGroup>

README.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ module:
4545
Embedding Python in .NET
4646
------------------------
4747

48-
- You must set `Runtime.PythonDLL` or `PYTHONNET_PYDLL` environment variable
48+
- You must set `Runtime.PythonDLL` property or `PYTHONNET_PYDLL` environment variable
4949
starting with version 3.0, otherwise you will receive `TypeInitializationException`.
50+
Typical values are `python38.dll` (Windows), `libpython3.8.dylib` (Mac),
51+
`libpython3.8.so` (most other *nix).
5052
- All calls to python should be inside a
5153
``using (Py.GIL()) {/* Your code here */}`` block.
5254
- Import python modules using ``dynamic mod = Py.Import("mod")``, then

pythonnet/__init__.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ def load():
2929
if _LOADED:
3030
return
3131

32-
from .find_libpython import linked_libpython
3332
from os.path import join, dirname
3433

3534
if _RUNTIME is None:
@@ -38,21 +37,11 @@ def load():
3837
set_default_runtime()
3938

4039
dll_path = join(dirname(__file__), "runtime", "Python.Runtime.dll")
41-
libpython = linked_libpython()
42-
43-
if libpython and _FFI is None and sys.platform != "win32":
44-
# Load and leak libpython handle s.t. the .NET runtime doesn't dlcloses
45-
# it
46-
import posix
47-
48-
import cffi
49-
_FFI = cffi.FFI()
50-
_FFI.dlopen(libpython, posix.RTLD_NODELETE | posix.RTLD_LOCAL)
51-
40+
5241
_LOADER_ASSEMBLY = _RUNTIME.get_assembly(dll_path)
5342

5443
func = _LOADER_ASSEMBLY["Python.Runtime.Loader.Initialize"]
55-
if func(f"{libpython or ''}".encode("utf8")) != 0:
44+
if func(''.encode("utf8")) != 0:
5645
raise RuntimeError("Failed to initialize Python.Runtime.dll")
5746

5847
import atexit

src/embed_tests/Codecs.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,32 @@ public void IterableDecoderTest()
296296
Assert.DoesNotThrow(() => { codec.TryDecode(pyList, out intEnumerable); });
297297
CollectionAssert.AreEqual(intEnumerable, new List<object> { 1, 2, 3 });
298298
}
299+
300+
// regression for https://github.com/pythonnet/pythonnet/issues/1427
301+
[Ignore("https://github.com/pythonnet/pythonnet/issues/1256")]
302+
[Test]
303+
public void PythonRegisteredDecoder_NoStackOverflowOnSystemType()
304+
{
305+
const string PyCode = @"
306+
import clr
307+
import System
308+
from Python.Runtime import PyObjectConversions
309+
from Python.Runtime.Codecs import RawProxyEncoder
310+
311+
312+
class ListAsRawEncoder(RawProxyEncoder):
313+
__namespace__ = 'Dummy'
314+
def CanEncode(self, clr_type):
315+
return clr_type.Name == 'IList`1' and clr_type.Namespace == 'System.Collections.Generic'
316+
317+
318+
list_encoder = ListAsRawEncoder()
319+
PyObjectConversions.RegisterEncoder(list_encoder)
320+
321+
system_type = list_encoder.GetType()";
322+
323+
PythonEngine.Exec(PyCode);
324+
}
299325
}
300326

301327
/// <summary>

src/embed_tests/TestOperator.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,14 @@ public void SymmetricalOperatorOverloads()
335335
");
336336
}
337337

338+
[Test]
339+
public void EnumOperator()
340+
{
341+
PythonEngine.Exec($@"
342+
from System.IO import FileAccess
343+
c = FileAccess.Read | FileAccess.Write");
344+
}
345+
338346
[Test]
339347
public void OperatorOverloadMissingArgument()
340348
{

src/embed_tests/TestPyType.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System.Text;
2+
3+
using NUnit.Framework;
4+
5+
using Python.Runtime;
6+
using Python.Runtime.Native;
7+
8+
namespace Python.EmbeddingTest
9+
{
10+
public class TestPyType
11+
{
12+
[OneTimeSetUp]
13+
public void SetUp()
14+
{
15+
PythonEngine.Initialize();
16+
}
17+
18+
[OneTimeTearDown]
19+
public void Dispose()
20+
{
21+
PythonEngine.Shutdown();
22+
}
23+
24+
[Test]
25+
public void CanCreateHeapType()
26+
{
27+
const string name = "nÁmæ";
28+
const string docStr = "dÁcæ";
29+
30+
using var doc = new StrPtr(docStr, Encoding.UTF8);
31+
var spec = new TypeSpec(
32+
name: name,
33+
basicSize: ObjectOffset.Size(Runtime.Runtime.PyTypeType),
34+
slots: new TypeSpec.Slot[] {
35+
new (TypeSlotID.tp_doc, doc.RawPointer),
36+
},
37+
TypeFlags.Default | TypeFlags.HeapType
38+
);
39+
40+
using var type = new PyType(spec);
41+
Assert.AreEqual(name, type.GetAttr("__name__").As<string>());
42+
Assert.AreEqual(docStr, type.GetAttr("__doc__").As<string>());
43+
}
44+
}
45+
}

src/runtime/Codecs/EnumPyLongCodec.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
3+
namespace Python.Runtime.Codecs
4+
{
5+
[Obsolete]
6+
public sealed class EnumPyLongCodec : IPyObjectEncoder, IPyObjectDecoder
7+
{
8+
public static EnumPyLongCodec Instance { get; } = new EnumPyLongCodec();
9+
10+
public bool CanDecode(PyObject objectType, Type targetType)
11+
{
12+
return targetType.IsEnum
13+
&& objectType.IsSubclass(new BorrowedReference(Runtime.PyLongType));
14+
}
15+
16+
public bool CanEncode(Type type)
17+
{
18+
return type == typeof(object) || type == typeof(ValueType) || type.IsEnum;
19+
}
20+
21+
public bool TryDecode<T>(PyObject pyObj, out T value)
22+
{
23+
value = default;
24+
if (!typeof(T).IsEnum) return false;
25+
26+
Type etype = Enum.GetUnderlyingType(typeof(T));
27+
28+
if (!PyLong.IsLongType(pyObj)) return false;
29+
30+
object result;
31+
try
32+
{
33+
result = pyObj.AsManagedObject(etype);
34+
}
35+
catch (InvalidCastException)
36+
{
37+
return false;
38+
}
39+
40+
if (Enum.IsDefined(typeof(T), result) || typeof(T).IsFlagsEnum())
41+
{
42+
value = (T)Enum.ToObject(typeof(T), result);
43+
return true;
44+
}
45+
46+
return false;
47+
}
48+
49+
public PyObject TryEncode(object value)
50+
{
51+
if (value is null) return null;
52+
53+
var enumType = value.GetType();
54+
if (!enumType.IsEnum) return null;
55+
56+
try
57+
{
58+
return new PyLong((long)value);
59+
}
60+
catch (InvalidCastException)
61+
{
62+
return new PyLong((ulong)value);
63+
}
64+
}
65+
66+
private EnumPyLongCodec() { }
67+
}
68+
}

src/runtime/TypeSpec.cs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#nullable enable
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Runtime.InteropServices;
6+
7+
namespace Python.Runtime
8+
{
9+
public class TypeSpec
10+
{
11+
public TypeSpec(string name, int basicSize, IEnumerable<Slot> slots, TypeFlags flags, int itemSize = 0)
12+
{
13+
this.Name = name ?? throw new ArgumentNullException(nameof(name));
14+
this.BasicSize = basicSize;
15+
this.Slots = slots.ToArray();
16+
this.Flags = flags;
17+
this.ItemSize = itemSize;
18+
}
19+
public string Name { get; }
20+
public int BasicSize { get; }
21+
public int ItemSize { get; }
22+
public TypeFlags Flags { get; }
23+
public IReadOnlyList<Slot> Slots { get; }
24+
25+
[StructLayout(LayoutKind.Sequential)]
26+
public struct Slot
27+
{
28+
public Slot(TypeSlotID id, IntPtr value)
29+
{
30+
ID = id;
31+
Value = value;
32+
}
33+
34+
public TypeSlotID ID { get; }
35+
public IntPtr Value { get; }
36+
}
37+
}
38+
39+
public enum TypeSlotID : int
40+
{
41+
mp_ass_subscript = 3,
42+
mp_length = 4,
43+
mp_subscript = 5,
44+
nb_absolute = 6,
45+
nb_add = 7,
46+
nb_and = 8,
47+
nb_bool = 9,
48+
nb_divmod = 10,
49+
nb_float = 11,
50+
nb_floor_divide = 12,
51+
nb_index = 13,
52+
nb_inplace_add = 14,
53+
nb_inplace_and = 15,
54+
nb_inplace_floor_divide = 16,
55+
nb_inplace_lshift = 17,
56+
nb_inplace_multiply = 18,
57+
nb_inplace_or = 19,
58+
nb_inplace_power = 20,
59+
nb_inplace_remainder = 21,
60+
nb_inplace_rshift = 22,
61+
nb_inplace_subtract = 23,
62+
nb_inplace_true_divide = 24,
63+
nb_inplace_xor = 25,
64+
nb_int = 26,
65+
nb_invert = 27,
66+
nb_lshift = 28,
67+
nb_multiply = 29,
68+
nb_negative = 30,
69+
nb_or = 31,
70+
nb_positive = 32,
71+
nb_power = 33,
72+
nb_remainder = 34,
73+
nb_rshift = 35,
74+
nb_subtract = 36,
75+
nb_true_divide = 37,
76+
nb_xor = 38,
77+
sq_ass_item = 39,
78+
sq_concat = 40,
79+
sq_contains = 41,
80+
sq_inplace_concat = 42,
81+
sq_inplace_repeat = 43,
82+
sq_item = 44,
83+
sq_length = 45,
84+
sq_repeat = 46,
85+
tp_alloc = 47,
86+
tp_base = 48,
87+
tp_bases = 49,
88+
tp_call = 50,
89+
tp_clear = 51,
90+
tp_dealloc = 52,
91+
tp_del = 53,
92+
tp_descr_get = 54,
93+
tp_descr_set = 55,
94+
tp_doc = 56,
95+
tp_getattr = 57,
96+
tp_getattro = 58,
97+
tp_hash = 59,
98+
tp_init = 60,
99+
tp_is_gc = 61,
100+
tp_iter = 62,
101+
tp_iternext = 63,
102+
tp_methods = 64,
103+
tp_new = 65,
104+
tp_repr = 66,
105+
tp_richcompare = 67,
106+
tp_setattr = 68,
107+
tp_setattro = 69,
108+
tp_str = 70,
109+
tp_traverse = 71,
110+
tp_members = 72,
111+
tp_getset = 73,
112+
tp_free = 74,
113+
nb_matrix_multiply = 75,
114+
nb_inplace_matrix_multiply = 76,
115+
am_await = 77,
116+
am_aiter = 78,
117+
am_anext = 79,
118+
/// <remarks>New in 3.5</remarks>
119+
tp_finalize = 80,
120+
}
121+
}

src/runtime/classmanager.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,17 @@ private static ClassInfo GetClassInfo(Type type)
403403
}
404404
}
405405

406+
// only [Flags] enums support bitwise operations
407+
if (type.IsEnum && type.IsFlagsEnum())
408+
{
409+
var opsImpl = typeof(EnumOps<>).MakeGenericType(type);
410+
foreach (var op in opsImpl.GetMethods(OpsHelper.BindingFlags))
411+
{
412+
local[op.Name] = 1;
413+
}
414+
info = info.Concat(opsImpl.GetMethods(OpsHelper.BindingFlags)).ToArray();
415+
}
416+
406417
// Now again to filter w/o losing overloaded member info
407418
for (i = 0; i < info.Length; i++)
408419
{

0 commit comments

Comments
 (0)