| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 5f56ebc commit ba5127a
9 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,6 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | 13 | ## Contributors | |
| 14 | 14 | ||
| 15 | + - Alex Earl ([@slide](https://github.com/slide)) | ||
| 15 | 16 | - Alex Helms ([@alexhelms](https://github.com/alexhelms)) | |
| 16 | 17 | - Alexandre Catarino([@AlexCatarino](https://github.com/AlexCatarino)) | |
| 17 | 18 | - Arvid JB ([@ArvidJB](https://github.com/ArvidJB)) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,6 +12,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. | |||
| 12 | 12 | - Added automatic NuGet package generation in appveyor and local builds | |
| 13 | 13 | - Added function that sets Py_NoSiteFlag to 1. | |
| 14 | 14 | - Added support for Jetson Nano. | |
| 15 | + - Added support for __len__ for .NET classes that implement ICollection | ||
| 15 | 16 | ||
| 16 | 17 | ### Changed | |
| 17 | 18 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -142,6 +142,7 @@ | |||
| 142 | 142 | <Compile Include="Util.cs" /> | |
| 143 | 143 | <Compile Include="platform\Types.cs" /> | |
| 144 | 144 | <Compile Include="platform\LibraryLoader.cs" /> | |
| 145 | + <Compile Include="slots\mp_length.cs" /> | ||
| 145 | 146 | </ItemGroup> | |
| 146 | 147 | <ItemGroup Condition=" '$(PythonInteropFile)' != '' "> | |
| 147 | 148 | <Compile Include="$(PythonInteropFile)" /> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -244,16 +244,5 @@ public static int sq_contains(IntPtr ob, IntPtr v) | |||
| 244 | 244 | ||
| 245 | 245 | return 0; | |
| 246 | 246 | } | |
| 247 | - | ||
| 248 | - | ||
| 249 | - /// <summary> | ||
| 250 | - /// Implements __len__ for array types. | ||
| 251 | - /// </summary> | ||
| 252 | - public static int mp_length(IntPtr ob) | ||
| 253 | - { | ||
| 254 | - var self = (CLRObject)GetManagedObject(ob); | ||
| 255 | - var items = self.inst as Array; | ||
| 256 | - return items.Length; | ||
| 257 | - } | ||
| 258 | 247 | } | |
| 259 | 248 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,50 @@ | |||
| 1 | + using System; | ||
| 2 | + using System.Collections; | ||
| 3 | + using System.Collections.Generic; | ||
| 4 | + using System.Linq; | ||
| 5 | + using System.Reflection; | ||
| 6 | + | ||
| 7 | + namespace Python.Runtime.Slots | ||
| 8 | + { | ||
| 9 | + internal static class mp_length_slot | ||
| 10 | + { | ||
| 11 | + /// <summary> | ||
| 12 | + /// Implements __len__ for classes that implement ICollection | ||
| 13 | + /// (this includes any IList implementer or Array subclass) | ||
| 14 | + /// </summary> | ||
| 15 | + public static int mp_length(IntPtr ob) | ||
| 16 | + { | ||
| 17 | + var co = ManagedType.GetManagedObject(ob) as CLRObject; | ||
| 18 | + if (co == null) | ||
| 19 | + { | ||
| 20 | + Exceptions.RaiseTypeError("invalid object"); | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + // first look for ICollection implementation directly | ||
| 24 | + if (co.inst is ICollection c) | ||
| 25 | + { | ||
| 26 | + return c.Count; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + Type clrType = co.inst.GetType(); | ||
| 30 | + | ||
| 31 | + // now look for things that implement ICollection<T> directly (non-explicitly) | ||
| 32 | + PropertyInfo p = clrType.GetProperty("Count"); | ||
| 33 | + if (p != null && clrType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>))) | ||
| 34 | + { | ||
| 35 | + return (int)p.GetValue(co.inst, null); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + // finally look for things that implement the interface explicitly | ||
| 39 | + var iface = clrType.GetInterfaces().FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)); | ||
| 40 | + if (iface != null) | ||
| 41 | + { | ||
| 42 | + p = iface.GetProperty(nameof(ICollection<int>.Count)); | ||
| 43 | + return (int)p.GetValue(co.inst, null); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + Exceptions.SetError(Exceptions.TypeError, $"object of type '{clrType.Name}' has no len()"); | ||
| 47 | + return -1; | ||
| 48 | + } | ||
| 49 | + } | ||
| 50 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,9 +1,11 @@ | |||
| 1 | 1 | using System; | |
| 2 | 2 | using System.Collections; | |
| 3 | 3 | using System.Collections.Generic; | |
| 4 | + using System.Linq; | ||
| 4 | 5 | using System.Reflection; | |
| 5 | 6 | using System.Runtime.InteropServices; | |
| 6 | 7 | using Python.Runtime.Platform; | |
| 8 | + using Python.Runtime.Slots; | ||
| 7 | 9 | ||
| 8 | 10 | namespace Python.Runtime | |
| 9 | 11 | { | |
@@ -153,6 +155,13 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) | |||
| 153 | 155 | Marshal.WriteIntPtr(type, TypeOffset.tp_itemsize, IntPtr.Zero); | |
| 154 | 156 | Marshal.WriteIntPtr(type, TypeOffset.tp_dictoffset, (IntPtr)tp_dictoffset); | |
| 155 | 157 | ||
| 158 | + // add a __len__ slot for inheritors of ICollection and ICollection<> | ||
| 159 | + if (typeof(ICollection).IsAssignableFrom(clrType) || clrType.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>))) | ||
| 160 | + { | ||
| 161 | + InitializeSlot(type, TypeOffset.mp_length, typeof(mp_length_slot).GetMethod(nameof(mp_length_slot.mp_length))); | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + // we want to do this after the slot stuff above in case the class itself implements a slot method | ||
| 156 | 165 | InitializeSlots(type, impl.GetType()); | |
| 157 | 166 | ||
| 158 | 167 | if (base_ != IntPtr.Zero) | |
@@ -193,6 +202,12 @@ internal static IntPtr CreateType(ManagedType impl, Type clrType) | |||
| 193 | 202 | return type; | |
| 194 | 203 | } | |
| 195 | 204 | ||
| 205 | + static void InitializeSlot(IntPtr type, int slotOffset, MethodInfo method) | ||
| 206 | + { | ||
| 207 | + IntPtr thunk = Interop.GetThunk(method); | ||
| 208 | + Marshal.WriteIntPtr(type, slotOffset, thunk); | ||
| 209 | + } | ||
| 210 | + | ||
| 196 | 211 | internal static IntPtr CreateSubType(IntPtr py_name, IntPtr py_base_type, IntPtr py_dict) | |
| 197 | 212 | { | |
| 198 | 213 | // Utility to create a subtype of a managed type with the ability for the | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -92,6 +92,7 @@ | |||
| 92 | 92 | <Compile Include="doctest.cs" /> | |
| 93 | 93 | <Compile Include="subclasstest.cs" /> | |
| 94 | 94 | <Compile Include="ReprTest.cs" /> | |
| 95 | + <Compile Include="mp_lengthtest.cs" /> | ||
| 95 | 96 | </ItemGroup> | |
| 96 | 97 | <ItemGroup> | |
| 97 | 98 | <Reference Include="Microsoft.CSharp" /> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,171 @@ | |||
| 1 | + using System; | ||
| 2 | + using System.Collections; | ||
| 3 | + using System.Collections.Generic; | ||
| 4 | + | ||
| 5 | + namespace Python.Test | ||
| 6 | + { | ||
| 7 | + public class MpLengthCollectionTest : ICollection | ||
| 8 | + { | ||
| 9 | + private readonly List<int> items; | ||
| 10 | + | ||
| 11 | + public MpLengthCollectionTest() | ||
| 12 | + { | ||
| 13 | + SyncRoot = new object(); | ||
| 14 | + items = new List<int> | ||
| 15 | + { | ||
| 16 | + 1, | ||
| 17 | + 2, | ||
| 18 | + 3 | ||
| 19 | + }; | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + public int Count => items.Count; | ||
| 23 | + | ||
| 24 | + public object SyncRoot { get; private set; } | ||
| 25 | + | ||
| 26 | + public bool IsSynchronized => false; | ||
| 27 | + | ||
| 28 | + public void CopyTo(Array array, int index) | ||
| 29 | + { | ||
| 30 | + throw new NotImplementedException(); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + public IEnumerator GetEnumerator() | ||
| 34 | + { | ||
| 35 | + throw new NotImplementedException(); | ||
| 36 | + } | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + public class MpLengthExplicitCollectionTest : ICollection | ||
| 40 | + { | ||
| 41 | + private readonly List<int> items; | ||
| 42 | + private readonly object syncRoot; | ||
| 43 | + | ||
| 44 | + public MpLengthExplicitCollectionTest() | ||
| 45 | + { | ||
| 46 | + syncRoot = new object(); | ||
| 47 | + items = new List<int> | ||
| 48 | + { | ||
| 49 | + 9, | ||
| 50 | + 10 | ||
| 51 | + }; | ||
| 52 | + } | ||
| 53 | + int ICollection.Count => items.Count; | ||
| 54 | + | ||
| 55 | + object ICollection.SyncRoot => syncRoot; | ||
| 56 | + | ||
| 57 | + bool ICollection.IsSynchronized => false; | ||
| 58 | + | ||
| 59 | + void ICollection.CopyTo(Array array, int index) | ||
| 60 | + { | ||
| 61 | + throw new NotImplementedException(); | ||
| 62 | + } | ||
| 63 | + | ||
| 64 | + IEnumerator IEnumerable.GetEnumerator() | ||
| 65 | + { | ||
| 66 | + throw new NotImplementedException(); | ||
| 67 | + } | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + public class MpLengthGenericCollectionTest<T> : ICollection<T> | ||
| 71 | + { | ||
| 72 | + private readonly List<T> items; | ||
| 73 | + | ||
| 74 | + public MpLengthGenericCollectionTest() { | ||
| 75 | + SyncRoot = new object(); | ||
| 76 | + items = new List<T>(); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + public int Count => items.Count; | ||
| 80 | + | ||
| 81 | + public object SyncRoot { get; private set; } | ||
| 82 | + | ||
| 83 | + public bool IsSynchronized => false; | ||
| 84 | + | ||
| 85 | + public bool IsReadOnly => false; | ||
| 86 | + | ||
| 87 | + public void Add(T item) | ||
| 88 | + { | ||
| 89 | + items.Add(item); | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + public void Clear() | ||
| 93 | + { | ||
| 94 | + items.Clear(); | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + public bool Contains(T item) | ||
| 98 | + { | ||
| 99 | + return items.Contains(item); | ||
| 100 | + } | ||
| 101 | + | ||
| 102 | + public void CopyTo(T[] array, int arrayIndex) | ||
| 103 | + { | ||
| 104 | + items.CopyTo(array, arrayIndex); | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + public IEnumerator GetEnumerator() | ||
| 108 | + { | ||
| 109 | + return ((IEnumerable)items).GetEnumerator(); | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + public bool Remove(T item) | ||
| 113 | + { | ||
| 114 | + return items.Remove(item); | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + IEnumerator<T> IEnumerable<T>.GetEnumerator() | ||
| 118 | + { | ||
| 119 | + return items.GetEnumerator(); | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + public class MpLengthExplicitGenericCollectionTest<T> : ICollection<T> | ||
| 124 | + { | ||
| 125 | + private readonly List<T> items; | ||
| 126 | + | ||
| 127 | + public MpLengthExplicitGenericCollectionTest() | ||
| 128 | + { | ||
| 129 | + items = new List<T>(); | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + int ICollection<T>.Count => items.Count; | ||
| 133 | + | ||
| 134 | + bool ICollection<T>.IsReadOnly => false; | ||
| 135 | + | ||
| 136 | + public void Add(T item) | ||
| 137 | + { | ||
| 138 | + items.Add(item); | ||
| 139 | + } | ||
| 140 | + | ||
| 141 | + void ICollection<T>.Clear() | ||
| 142 | + { | ||
| 143 | + items.Clear(); | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + bool ICollection<T>.Contains(T item) | ||
| 147 | + { | ||
| 148 | + return items.Contains(item); | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + void ICollection<T>.CopyTo(T[] array, int arrayIndex) | ||
| 152 | + { | ||
| 153 | + items.CopyTo(array, arrayIndex); | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + IEnumerator<T> IEnumerable<T>.GetEnumerator() | ||
| 157 | + { | ||
| 158 | + return items.GetEnumerator(); | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + IEnumerator IEnumerable.GetEnumerator() | ||
| 162 | + { | ||
| 163 | + return ((IEnumerable)items).GetEnumerator(); | ||
| 164 | + } | ||
| 165 | + | ||
| 166 | + bool ICollection<T>.Remove(T item) | ||
| 167 | + { | ||
| 168 | + return items.Remove(item); | ||
| 169 | + } | ||
| 170 | + } | ||
| 171 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,49 @@ | |||
| 1 | + # -*- coding: utf-8 -*- | ||
| 2 | + | ||
| 3 | + """Test __len__ for .NET classes implementing ICollection/ICollection<T>.""" | ||
| 4 | + | ||
| 5 | + import System | ||
| 6 | + import pytest | ||
| 7 | + from Python.Test import MpLengthCollectionTest, MpLengthExplicitCollectionTest, MpLengthGenericCollectionTest, MpLengthExplicitGenericCollectionTest | ||
| 8 | + | ||
| 9 | + def test_simple___len__(): | ||
| 10 | + """Test __len__ for simple ICollection implementers""" | ||
| 11 | + import System | ||
| 12 | + import System.Collections.Generic | ||
| 13 | + l = System.Collections.Generic.List[int]() | ||
| 14 | + assert len(l) == 0 | ||
| 15 | + l.Add(5) | ||
| 16 | + l.Add(6) | ||
| 17 | + assert len(l) == 2 | ||
| 18 | + | ||
| 19 | + d = System.Collections.Generic.Dictionary[int, int]() | ||
| 20 | + assert len(d) == 0 | ||
| 21 | + d.Add(4, 5) | ||
| 22 | + assert len(d) == 1 | ||
| 23 | + | ||
| 24 | + a = System.Array[int]([0,1,2,3]) | ||
| 25 | + assert len(a) == 4 | ||
| 26 | + | ||
| 27 | + def test_custom_collection___len__(): | ||
| 28 | + """Test __len__ for custom collection class""" | ||
| 29 | + s = MpLengthCollectionTest() | ||
| 30 | + assert len(s) == 3 | ||
| 31 | + | ||
| 32 | + def test_custom_collection_explicit___len__(): | ||
| 33 | + """Test __len__ for custom collection class that explicitly implements ICollection""" | ||
| 34 | + s = MpLengthExplicitCollectionTest() | ||
| 35 | + assert len(s) == 2 | ||
| 36 | + | ||
| 37 | + def test_custom_generic_collection___len__(): | ||
| 38 | + """Test __len__ for custom generic collection class""" | ||
| 39 | + s = MpLengthGenericCollectionTest[int]() | ||
| 40 | + s.Add(1) | ||
| 41 | + s.Add(2) | ||
| 42 | + assert len(s) == 2 | ||
| 43 | + | ||
| 44 | + def test_custom_generic_collection_explicit___len__(): | ||
| 45 | + """Test __len__ for custom generic collection that explicity implements ICollection<T>""" | ||
| 46 | + s = MpLengthExplicitGenericCollectionTest[int]() | ||
| 47 | + s.Add(1) | ||
| 48 | + s.Add(10) | ||
| 49 | + assert len(s) == 2 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments