Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b1c9f5b

Browse files
committed
EventObject no longer used for static events. EventBinding is constructed directly instead.
Also fixes event_rename domain reload test case
1 parent 03f32cb commit b1c9f5b

5 files changed

Lines changed: 143 additions & 137 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
5+
namespace Python.Runtime;
6+
7+
internal class EventHandlerCollection: Dictionary<object, List<Handler>>
8+
{
9+
readonly EventInfo info;
10+
public EventHandlerCollection(EventInfo @event)
11+
{
12+
info = @event;
13+
}
14+
15+
/// <summary>
16+
/// Register a new Python object event handler with the event.
17+
/// </summary>
18+
internal bool AddEventHandler(BorrowedReference target, PyObject handler)
19+
{
20+
object? obj = null;
21+
if (target != null)
22+
{
23+
var co = (CLRObject)ManagedType.GetManagedObject(target)!;
24+
obj = co.inst;
25+
}
26+
27+
// Create a true delegate instance of the appropriate type to
28+
// wrap the Python handler. Note that wrapper delegate creation
29+
// always succeeds, though calling the wrapper may fail.
30+
Type type = info.EventHandlerType;
31+
Delegate d = PythonEngine.DelegateManager.GetDelegate(type, handler);
32+
33+
// Now register the handler in a mapping from instance to pairs
34+
// of (handler hash, delegate) so we can lookup to remove later.
35+
object key = obj ?? info.ReflectedType;
36+
if (!TryGetValue(key, out var list))
37+
{
38+
list = new List<Handler>();
39+
this[key] = list;
40+
}
41+
list.Add(new Handler(Runtime.PyObject_Hash(handler), d));
42+
43+
// Note that AddEventHandler helper only works for public events,
44+
// so we have to get the underlying add method explicitly.
45+
object[] args = { d };
46+
MethodInfo mi = info.GetAddMethod(true);
47+
mi.Invoke(obj, BindingFlags.Default, null, args, null);
48+
49+
return true;
50+
}
51+
52+
53+
/// <summary>
54+
/// Remove the given Python object event handler.
55+
/// </summary>
56+
internal bool RemoveEventHandler(BorrowedReference target, BorrowedReference handler)
57+
{
58+
object? obj = null;
59+
if (target != null)
60+
{
61+
var co = (CLRObject)ManagedType.GetManagedObject(target)!;
62+
obj = co.inst;
63+
}
64+
65+
nint hash = Runtime.PyObject_Hash(handler);
66+
if (hash == -1 && Exceptions.ErrorOccurred())
67+
{
68+
return false;
69+
}
70+
71+
object key = obj ?? info.ReflectedType;
72+
73+
if (!TryGetValue(key, out var list))
74+
{
75+
Exceptions.SetError(Exceptions.ValueError, "unknown event handler");
76+
return false;
77+
}
78+
79+
object?[] args = { null };
80+
MethodInfo mi = info.GetRemoveMethod(true);
81+
82+
for (var i = 0; i < list.Count; i++)
83+
{
84+
var item = (Handler)list[i];
85+
if (item.hash != hash)
86+
{
87+
continue;
88+
}
89+
args[0] = item.del;
90+
try
91+
{
92+
mi.Invoke(obj, BindingFlags.Default, null, args, null);
93+
}
94+
catch
95+
{
96+
continue;
97+
}
98+
list.RemoveAt(i);
99+
return true;
100+
}
101+
102+
Exceptions.SetError(Exceptions.ValueError, "unknown event handler");
103+
return false;
104+
}
105+
}

src/runtime/classmanager.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,9 @@ private static ClassInfo GetClassInfo(Type type)
491491
{
492492
continue;
493493
}
494-
ob = new EventObject(ei);
494+
ob = ei.AddMethod.IsStatic
495+
? new EventBinding(ei)
496+
: new EventObject(ei);
495497
ci.members[ei.Name] = ob;
496498
continue;
497499

src/runtime/eventbinding.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Diagnostics;
3+
using System.Reflection;
24

35
namespace Python.Runtime
46
{
@@ -8,15 +10,22 @@ namespace Python.Runtime
810
[Serializable]
911
internal class EventBinding : ExtensionType
1012
{
11-
private EventObject e;
13+
private readonly string name;
14+
private readonly EventHandlerCollection e;
1215
private PyObject? target;
1316

14-
public EventBinding(EventObject e, PyObject? target)
17+
public EventBinding(string name, EventHandlerCollection e, PyObject? target)
1518
{
19+
this.name = name;
1620
this.target = target;
1721
this.e = e;
1822
}
1923

24+
public EventBinding(EventInfo @event) : this(@event.Name, new EventHandlerCollection(@event), target: null)
25+
{
26+
Debug.Assert(@event.AddMethod.IsStatic);
27+
}
28+
2029

2130
/// <summary>
2231
/// EventBinding += operator implementation.
@@ -61,6 +70,10 @@ public static NewReference nb_inplace_subtract(BorrowedReference ob, BorrowedRef
6170
return new NewReference(ob);
6271
}
6372

73+
/// </summary>
74+
public static int tp_descr_set(BorrowedReference ds, BorrowedReference ob, BorrowedReference val)
75+
=> EventObject.tp_descr_set(ds, ob, val);
76+
6477

6578
/// <summary>
6679
/// EventBinding __hash__ implementation.
@@ -91,7 +104,7 @@ public static NewReference tp_repr(BorrowedReference ob)
91104
{
92105
var self = (EventBinding)GetManagedObject(ob)!;
93106
string type = self.target == null ? "unbound" : "bound";
94-
string s = string.Format("<{0} event '{1}'>", type, self.e.name);
107+
string s = string.Format("<{0} event '{1}'>", type, self.name);
95108
return Runtime.PyString_FromString(s);
96109
}
97110
}

src/runtime/eventobject.cs

Lines changed: 7 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections;
3+
using System.Diagnostics;
34
using System.Reflection;
45

56
namespace Python.Runtime
@@ -10,124 +11,16 @@ namespace Python.Runtime
1011
[Serializable]
1112
internal class EventObject : ExtensionType
1213
{
13-
internal string name;
14-
internal PyObject? unbound;
15-
internal EventInfo info;
16-
internal Hashtable? reg;
14+
internal readonly string name;
15+
internal readonly EventHandlerCollection reg;
1716

1817
public EventObject(EventInfo info)
1918
{
19+
Debug.Assert(!info.AddMethod.IsStatic);
2020
this.name = info.Name;
21-
this.info = info;
21+
this.reg = new EventHandlerCollection(info);
2222
}
2323

24-
25-
/// <summary>
26-
/// Register a new Python object event handler with the event.
27-
/// </summary>
28-
internal bool AddEventHandler(BorrowedReference target, PyObject handler)
29-
{
30-
object? obj = null;
31-
if (target != null)
32-
{
33-
var co = (CLRObject)GetManagedObject(target)!;
34-
obj = co.inst;
35-
}
36-
37-
// Create a true delegate instance of the appropriate type to
38-
// wrap the Python handler. Note that wrapper delegate creation
39-
// always succeeds, though calling the wrapper may fail.
40-
Type type = info.EventHandlerType;
41-
Delegate d = PythonEngine.DelegateManager.GetDelegate(type, handler);
42-
43-
// Now register the handler in a mapping from instance to pairs
44-
// of (handler hash, delegate) so we can lookup to remove later.
45-
// All this is done lazily to avoid overhead until an event is
46-
// actually subscribed to by a Python event handler.
47-
if (reg == null)
48-
{
49-
reg = new Hashtable();
50-
}
51-
object key = obj ?? info.ReflectedType;
52-
var list = reg[key] as ArrayList;
53-
if (list == null)
54-
{
55-
list = new ArrayList();
56-
reg[key] = list;
57-
}
58-
list.Add(new Handler(Runtime.PyObject_Hash(handler), d));
59-
60-
// Note that AddEventHandler helper only works for public events,
61-
// so we have to get the underlying add method explicitly.
62-
object[] args = { d };
63-
MethodInfo mi = info.GetAddMethod(true);
64-
mi.Invoke(obj, BindingFlags.Default, null, args, null);
65-
66-
return true;
67-
}
68-
69-
70-
/// <summary>
71-
/// Remove the given Python object event handler.
72-
/// </summary>
73-
internal bool RemoveEventHandler(BorrowedReference target, BorrowedReference handler)
74-
{
75-
if (reg == null)
76-
{
77-
Exceptions.SetError(Exceptions.ValueError, "unknown event handler");
78-
return false;
79-
}
80-
81-
object? obj = null;
82-
if (target != null)
83-
{
84-
var co = (CLRObject)GetManagedObject(target)!;
85-
obj = co.inst;
86-
}
87-
88-
nint hash = Runtime.PyObject_Hash(handler);
89-
if (hash == -1 && Exceptions.ErrorOccurred())
90-
{
91-
return false;
92-
}
93-
94-
object key = obj ?? info.ReflectedType;
95-
var list = reg[key] as ArrayList;
96-
97-
if (list == null)
98-
{
99-
Exceptions.SetError(Exceptions.ValueError, "unknown event handler");
100-
return false;
101-
}
102-
103-
object?[] args = { null };
104-
MethodInfo mi = info.GetRemoveMethod(true);
105-
106-
for (var i = 0; i < list.Count; i++)
107-
{
108-
var item = (Handler)list[i];
109-
if (item.hash != hash)
110-
{
111-
continue;
112-
}
113-
args[0] = item.del;
114-
try
115-
{
116-
mi.Invoke(obj, BindingFlags.Default, null, args, null);
117-
}
118-
catch
119-
{
120-
continue;
121-
}
122-
list.RemoveAt(i);
123-
return true;
124-
}
125-
126-
Exceptions.SetError(Exceptions.ValueError, "unknown event handler");
127-
return false;
128-
}
129-
130-
13124
/// <summary>
13225
/// Descriptor __get__ implementation. A getattr on an event returns
13326
/// a "bound" event that keeps a reference to the object instance.
@@ -141,25 +34,17 @@ public static NewReference tp_descr_get(BorrowedReference ds, BorrowedReference
14134
return Exceptions.RaiseTypeError("invalid argument");
14235
}
14336

144-
// If the event is accessed through its type (rather than via
145-
// an instance) we return an 'unbound' EventBinding that will
146-
// be cached for future accesses through the type.
147-
14837
if (ob == null)
14938
{
150-
if (self.unbound == null)
151-
{
152-
self.unbound = new EventBinding(self, target: null).Alloc().MoveToPyObject();
153-
}
154-
return new NewReference(self.unbound);
39+
return new NewReference(ds);
15540
}
15641

15742
if (Runtime.PyObject_IsInstance(ob, tp) < 1)
15843
{
15944
return Exceptions.RaiseTypeError("invalid argument");
16045
}
16146

162-
return new EventBinding(self, new PyObject(ob)).Alloc();
47+
return new EventBinding(self.name, self.reg, new PyObject(ob)).Alloc();
16348
}
16449

16550

@@ -192,13 +77,6 @@ public static NewReference tp_repr(BorrowedReference ob)
19277
var self = (EventObject)GetManagedObject(ob)!;
19378
return Runtime.PyString_FromString($"<event '{self.name}'>");
19479
}
195-
196-
197-
protected override void Clear(BorrowedReference ob)
198-
{
199-
this.unbound = null!;
200-
base.Clear(ob);
201-
}
20280
}
20381

20482

tests/domain_tests/TestRunner.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ public class Cls
310310
public static event Action Before;
311311
public static void Call()
312312
{
313-
Before();
313+
if (Before != null) Before();
314314
}
315315
}
316316
}",
@@ -324,7 +324,7 @@ public class Cls
324324
public static event Action After;
325325
public static void Call()
326326
{
327-
After();
327+
if (After != null) After();
328328
}
329329
}
330330
}",
@@ -335,21 +335,29 @@ import sys
335335
from TestNamespace import Cls
336336
337337
called = False
338+
before_reload_called = False
339+
after_reload_called = False
338340
339341
def callback_function():
340342
global called
341343
called = True
342344
343345
def before_reload():
344-
global called
346+
global called, before_reload_called
345347
called = False
346348
Cls.Before += callback_function
347349
Cls.Call()
348350
assert called is True
351+
before_reload_called = True
349352
350353
def after_reload():
351-
global called
354+
global called, after_reload_called, before_reload_called
355+
356+
assert before_reload_called is True
357+
if not after_reload_called:
352358
assert called is True
359+
after_reload_called = True
360+
353361
called = False
354362
Cls.Call()
355363
assert called is False

0 commit comments

Comments
 (0)