| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,9 +8,14 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. | |||
| 8 | 8 | ## Unreleased | |
| 9 | 9 | ||
| 10 | 10 | ### Added | |
| 11 | + | ||
| 12 | + - Support `del obj[...]` for types derived from `IList<T>` and `IDictionary<K, V>` | ||
| 13 | + | ||
| 11 | 14 | ### Changed | |
| 12 | 15 | ### Fixed | |
| 13 | 16 | ||
| 17 | + - Fixed crash when trying to `del clrObj[...]` for non-arrays | ||
| 18 | + | ||
| 14 | 19 | ## [3.0.5](https://github.com/pythonnet/pythonnet/releases/tag/v3.0.5) - 2024-12-13 | |
| 15 | 20 | ||
| 16 | 21 | ### Added | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -213,6 +213,7 @@ internal static void InitClassBase(Type type, ClassBase impl, ReflectedClrType p | |||
| 213 | 213 | ClassInfo info = GetClassInfo(type, impl); | |
| 214 | 214 | ||
| 215 | 215 | impl.indexer = info.indexer; | |
| 216 | + impl.del = info.del; | ||
| 216 | 217 | impl.richcompare.Clear(); | |
| 217 | 218 | ||
| 218 | 219 | ||
@@ -538,6 +539,21 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl) | |||
| 538 | 539 | ||
| 539 | 540 | ob = new MethodObject(type, name, mlist); | |
| 540 | 541 | ci.members[name] = ob.AllocObject(); | |
| 542 | + if (name == nameof(IDictionary<int, int>.Remove) | ||
| 543 | + && mlist.Any(m => m.DeclaringType?.GetInterfaces() | ||
| 544 | + .Any(i => i.TryGetGenericDefinition() == typeof(IDictionary<,>)) is true)) | ||
| 545 | + { | ||
| 546 | + ci.del = new(); | ||
| 547 | + ci.del.AddRange(mlist.Where(m => !m.IsStatic)); | ||
| 548 | + } | ||
| 549 | + else if (name == nameof(IList<int>.RemoveAt) | ||
| 550 | + && mlist.Any(m => m.DeclaringType?.GetInterfaces() | ||
| 551 | + .Any(i => i.TryGetGenericDefinition() == typeof(IList<>)) is true)) | ||
| 552 | + { | ||
| 553 | + ci.del = new(); | ||
| 554 | + ci.del.AddRange(mlist.Where(m => !m.IsStatic)); | ||
| 555 | + } | ||
| 556 | + | ||
| 541 | 557 | if (mlist.Any(OperatorMethod.IsOperatorMethod)) | |
| 542 | 558 | { | |
| 543 | 559 | string pyName = OperatorMethod.GetPyMethodName(name); | |
@@ -581,6 +597,7 @@ private static ClassInfo GetClassInfo(Type type, ClassBase impl) | |||
| 581 | 597 | private class ClassInfo | |
| 582 | 598 | { | |
| 583 | 599 | public Indexer? indexer; | |
| 600 | + public MethodBinder? del; | ||
| 584 | 601 | public readonly Dictionary<string, PyObject> members = new(); | |
| 585 | 602 | ||
| 586 | 603 | internal ClassInfo() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -54,6 +54,11 @@ internal void AddMethod(MethodBase m) | |||
| 54 | 54 | list.Add(m); | |
| 55 | 55 | } | |
| 56 | 56 | ||
| 57 | + internal void AddRange(IEnumerable<MethodBase> methods) | ||
| 58 | + { | ||
| 59 | + list.AddRange(methods.Select(m => new MaybeMethodBase(m))); | ||
| 60 | + } | ||
| 61 | + | ||
| 57 | 62 | /// <summary> | |
| 58 | 63 | /// Given a sequence of MethodInfo and a sequence of types, return the | |
| 59 | 64 | /// MethodInfo that matches the signature represented by those types. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -247,6 +247,12 @@ public static NewReference mp_subscript(BorrowedReference ob, BorrowedReference | |||
| 247 | 247 | /// </summary> | |
| 248 | 248 | public static int mp_ass_subscript(BorrowedReference ob, BorrowedReference idx, BorrowedReference v) | |
| 249 | 249 | { | |
| 250 | + if (v.IsNull) | ||
| 251 | + { | ||
| 252 | + Exceptions.RaiseTypeError("'System.Array' object does not support item deletion"); | ||
| 253 | + return -1; | ||
| 254 | + } | ||
| 255 | + | ||
| 250 | 256 | var obj = (CLRObject)GetManagedObject(ob)!; | |
| 251 | 257 | var items = (Array)obj.inst; | |
| 252 | 258 | Type itemType = obj.inst.GetType().GetElementType(); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,6 +25,7 @@ internal class ClassBase : ManagedType, IDeserializationCallback | |||
| 25 | 25 | [NonSerialized] | |
| 26 | 26 | internal List<string> dotNetMembers = new(); | |
| 27 | 27 | internal Indexer? indexer; | |
| 28 | + internal MethodBinder? del; | ||
| 28 | 29 | internal readonly Dictionary<int, MethodObject> richcompare = new(); | |
| 29 | 30 | internal MaybeType type; | |
| 30 | 31 | ||
@@ -465,6 +466,11 @@ static int mp_ass_subscript_impl(BorrowedReference ob, BorrowedReference idx, Bo | |||
| 465 | 466 | // with the index arg (method binders expect arg tuples). | |
| 466 | 467 | NewReference argsTuple = default; | |
| 467 | 468 | ||
| 469 | + if (v.IsNull) | ||
| 470 | + { | ||
| 471 | + return DelImpl(ob, idx, cls); | ||
| 472 | + } | ||
| 473 | + | ||
| 468 | 474 | if (!Runtime.PyTuple_Check(idx)) | |
| 469 | 475 | { | |
| 470 | 476 | argsTuple = Runtime.PyTuple_New(1); | |
@@ -501,6 +507,44 @@ static int mp_ass_subscript_impl(BorrowedReference ob, BorrowedReference idx, Bo | |||
| 501 | 507 | return result.IsNull() ? -1 : 0; | |
| 502 | 508 | } | |
| 503 | 509 | ||
| 510 | + /// Implements __delitem__ (del x[...]) for IList<T> and IDictionary<TKey, TValue>. | ||
| 511 | + private static int DelImpl(BorrowedReference ob, BorrowedReference idx, ClassBase cls) | ||
| 512 | + { | ||
| 513 | + if (cls.del is null) | ||
| 514 | + { | ||
| 515 | + Exceptions.SetError(Exceptions.TypeError, "object does not support item deletion"); | ||
| 516 | + return -1; | ||
| 517 | + } | ||
| 518 | + | ||
| 519 | + if (Runtime.PyTuple_Check(idx)) | ||
| 520 | + { | ||
| 521 | + Exceptions.SetError(Exceptions.TypeError, "indices must be integers"); | ||
| 522 | + return -1; | ||
| 523 | + } | ||
| 524 | + | ||
| 525 | + using var argsTuple = Runtime.PyTuple_New(1); | ||
| 526 | + Runtime.PyTuple_SetItem(argsTuple.Borrow(), 0, idx); | ||
| 527 | + using var result = cls.del.Invoke(ob, argsTuple.Borrow(), kw: null); | ||
| 528 | + if (result.IsNull()) | ||
| 529 | + return -1; | ||
| 530 | + | ||
| 531 | + if (Runtime.PyBool_CheckExact(result.Borrow())) | ||
| 532 | + { | ||
| 533 | + if (Runtime.PyObject_IsTrue(result.Borrow()) != 0) | ||
| 534 | + return 0; | ||
| 535 | + | ||
| 536 | + Exceptions.SetError(Exceptions.KeyError, "key not found"); | ||
| 537 | + return -1; | ||
| 538 | + } | ||
| 539 | + | ||
| 540 | + if (!result.IsNone()) | ||
| 541 | + { | ||
| 542 | + Exceptions.warn("unsupported return type for __delitem__", Exceptions.TypeError); | ||
| 543 | + } | ||
| 544 | + | ||
| 545 | + return 0; | ||
| 546 | + } | ||
| 547 | + | ||
| 504 | 548 | static NewReference tp_call_impl(BorrowedReference ob, BorrowedReference args, BorrowedReference kw) | |
| 505 | 549 | { | |
| 506 | 550 | BorrowedReference tp = Runtime.PyObject_TYPE(ob); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -53,4 +53,11 @@ public static BindingFlags GetBindingFlags(this PropertyInfo property) | |||
| 53 | 53 | flags |= accessor.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic; | |
| 54 | 54 | return flags; | |
| 55 | 55 | } | |
| 56 | + | ||
| 57 | + public static Type? TryGetGenericDefinition(this Type type) | ||
| 58 | + { | ||
| 59 | + if (type is null) throw new ArgumentNullException(nameof(type)); | ||
| 60 | + | ||
| 61 | + return type.IsConstructedGenericType ? type.GetGenericTypeDefinition() : null; | ||
| 62 | + } | ||
| 56 | 63 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -668,3 +668,39 @@ def test_public_inherited_overloaded_indexer(): | |||
| 668 | 668 | ||
| 669 | 669 | with pytest.raises(TypeError): | |
| 670 | 670 | ob[[]] | |
| 671 | + | ||
| 672 | + def test_del_indexer_dict(): | ||
| 673 | + """Test deleting indexers (__delitem__).""" | ||
| 674 | + from System.Collections.Generic import Dictionary, KeyNotFoundException | ||
| 675 | + d = Dictionary[str, str]() | ||
| 676 | + d["delme"] = "1" | ||
| 677 | + with pytest.raises(KeyError): | ||
| 678 | + del d["nonexistent"] | ||
| 679 | + del d["delme"] | ||
| 680 | + with pytest.raises(KeyError): | ||
| 681 | + del d["delme"] | ||
| 682 | + | ||
| 683 | + def test_del_indexer_list(): | ||
| 684 | + """Test deleting indexers (__delitem__).""" | ||
| 685 | + from System import ArgumentOutOfRangeException | ||
| 686 | + from System.Collections.Generic import List | ||
| 687 | + l = List[str]() | ||
| 688 | + l.Add("1") | ||
| 689 | + with pytest.raises(ArgumentOutOfRangeException): | ||
| 690 | + del l[3] | ||
| 691 | + del l[0] | ||
| 692 | + assert len(l) == 0 | ||
| 693 | + | ||
| 694 | + def test_del_indexer_array(): | ||
| 695 | + """Test deleting indexers (__delitem__).""" | ||
| 696 | + from System import Array | ||
| 697 | + l = Array[str](0) | ||
| 698 | + with pytest.raises(TypeError): | ||
| 699 | + del l[0] | ||
| 700 | + | ||
| 701 | + def test_del_indexer_absent(): | ||
| 702 | + """Test deleting indexers (__delitem__).""" | ||
| 703 | + from System import Uri | ||
| 704 | + l = Uri("http://www.example.com") | ||
| 705 | + with pytest.raises(TypeError): | ||
| 706 | + del l[0] | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments