@@ -79,12 +79,13 @@ public static void DomainReloadAndGC()
79
79
80
80
#region CrossDomainObject
81
81
82
- class CrossDomianObjectStep1 : CrossCaller
82
+ class CrossDomainObjectStep1 : CrossCaller
83
83
{
84
84
public override ValueType Execute ( ValueType arg )
85
85
{
86
86
try
87
87
{
88
+ // Create a C# user-defined object in Python. Asssing some values.
88
89
Type type = typeof ( Python . EmbeddingTest . Domain . MyClass ) ;
89
90
string code = string . Format ( @"
90
91
import clr
@@ -133,6 +134,7 @@ public override ValueType Execute(ValueType arg)
133
134
{
134
135
IntPtr tp = Runtime . Runtime . PyObject_TYPE ( handle ) ;
135
136
IntPtr tp_clear = Marshal . ReadIntPtr ( tp , TypeOffset . tp_clear ) ;
137
+ Assert . That ( tp_clear , Is . Not . Null ) ;
136
138
137
139
using ( PyObject obj = new PyObject ( handle ) )
138
140
{
@@ -164,10 +166,15 @@ public override ValueType Execute(ValueType arg)
164
166
}
165
167
}
166
168
169
+ /// <summary>
170
+ /// Create a C# custom object in a domain, in python code.
171
+ /// Unload the domain, create a new domain.
172
+ /// Make sure the C# custom object created in the previous domain has been re-created
173
+ /// </summary>
167
174
[ Test ]
168
175
public static void CrossDomainObject ( )
169
176
{
170
- RunDomainReloadSteps < CrossDomianObjectStep1 , CrossDomainObjectStep2 > ( ) ;
177
+ RunDomainReloadSteps < CrossDomainObjectStep1 , CrossDomainObjectStep2 > ( ) ;
171
178
}
172
179
173
180
#endregion
@@ -193,19 +200,26 @@ def test_obj_call():
193
200
const string name = "test_domain_reload_mod" ;
194
201
using ( Py . GIL ( ) )
195
202
{
203
+ // Create a new module
196
204
IntPtr module = PyRuntime . PyModule_New ( name ) ;
197
205
Assert . That ( module != IntPtr . Zero ) ;
198
206
IntPtr globals = PyRuntime . PyObject_GetAttrString ( module , "__dict__" ) ;
199
207
Assert . That ( globals != IntPtr . Zero ) ;
200
208
try
201
209
{
210
+ // import builtins
211
+ // module.__dict__[__builtins__] = builtins
202
212
int res = PyRuntime . PyDict_SetItemString ( globals , "__builtins__" ,
203
213
PyRuntime . PyEval_GetBuiltins ( ) ) ;
204
214
PythonException . ThrowIfIsNotZero ( res ) ;
205
215
216
+ // Execute the code in the module's scope
206
217
PythonEngine . Exec ( code , globals ) ;
218
+ // import sys
219
+ // modules = sys.modules
207
220
IntPtr modules = PyRuntime . PyImport_GetModuleDict ( ) ;
208
- res = PyRuntime . PyDict_SetItemString ( modules , name , modules ) ;
221
+ // modules[name] = module
222
+ res = PyRuntime . PyDict_SetItemString ( modules , name , module ) ;
209
223
PythonException . ThrowIfIsNotZero ( res ) ;
210
224
}
211
225
catch
@@ -244,6 +258,11 @@ public override ValueType Execute(ValueType arg)
244
258
245
259
246
260
[ Test ]
261
+ /// <summary>
262
+ /// Create a new Python module, define a function in it.
263
+ /// Unload the domain, load a new one.
264
+ /// Make sure the function (and module) still exists.
265
+ /// </summary>
247
266
public void TestClassReference ( )
248
267
{
249
268
RunDomainReloadSteps < ReloadClassRefStep1 , ReloadClassRefStep2 > ( ) ;
@@ -261,7 +280,12 @@ void ExecTest()
261
280
{
262
281
try
263
282
{
283
+ PythonEngine . Initialize ( ) ;
264
284
var numRef = CreateNumReference ( ) ;
285
+ Assert . True ( numRef . IsAlive ) ;
286
+ PythonEngine . Shutdown ( ) ; // <- "run" 1 ends
287
+ PythonEngine . Initialize ( ) ; // <- "run" 2 starts
288
+
265
289
GC . Collect ( ) ;
266
290
GC . WaitForPendingFinalizers ( ) ; // <- this will put former `num` into Finalizer queue
267
291
Finalizer . Instance . Collect ( forceDispose : true ) ;
@@ -302,7 +326,11 @@ void ExecTest()
302
326
{
303
327
try
304
328
{
329
+ PythonEngine . Initialize ( ) ;
305
330
var objRef = CreateConcreateObject ( ) ;
331
+ Assert . True ( objRef . IsAlive ) ;
332
+ PythonEngine . Shutdown ( ) ; // <- "run" 1 ends
333
+ PythonEngine . Initialize ( ) ; // <- "run" 2 starts
306
334
GC . Collect ( ) ;
307
335
GC . WaitForPendingFinalizers ( ) ;
308
336
Finalizer . Instance . Collect ( forceDispose : true ) ;
@@ -336,25 +364,17 @@ void ErrorHandler(object sender, Finalizer.ErrorArgs e)
336
364
337
365
private static WeakReference CreateNumReference ( )
338
366
{
339
- PythonEngine . Initialize ( ) ;
340
367
var num = 3216757418 . ToPython ( ) ;
341
368
Assert . AreEqual ( num . Refcount , 1 ) ;
342
369
WeakReference numRef = new WeakReference ( num , false ) ;
343
- PythonEngine . Shutdown ( ) ; // <- "run" 1 ends
344
- PythonEngine . Initialize ( ) ; // <- "run" 2 starts
345
- num = null ;
346
370
return numRef ;
347
371
}
348
372
349
373
private static WeakReference CreateConcreateObject ( )
350
374
{
351
- PythonEngine . Initialize ( ) ;
352
375
var obj = new Domain . MyClass ( ) . ToPython ( ) ;
353
376
Assert . AreEqual ( obj . Refcount , 1 ) ;
354
377
WeakReference numRef = new WeakReference ( obj , false ) ;
355
- PythonEngine . Shutdown ( ) ;
356
- PythonEngine . Initialize ( ) ;
357
- obj = null ;
358
378
return numRef ;
359
379
}
360
380
@@ -380,6 +400,15 @@ public object Call(string methodName, params object[] args)
380
400
return method . Invoke ( null , args ) ;
381
401
}
382
402
}
403
+
404
+ static T CreateInstanceInstanceAndUnwrap < T > ( AppDomain domain )
405
+ {
406
+ Type type = typeof ( T ) ;
407
+ var theProxy = ( T ) domain . CreateInstanceAndUnwrap (
408
+ type . Assembly . FullName ,
409
+ type . FullName ) ;
410
+ return theProxy ;
411
+ }
383
412
384
413
/// <summary>
385
414
/// Create a domain, run the assembly in it (the RunPython function),
@@ -392,14 +421,13 @@ static void RunAssemblyAndUnload(string domainName)
392
421
AppDomain domain = CreateDomain ( domainName ) ;
393
422
// Create a Proxy object in the new domain, where we want the
394
423
// assembly (and Python .NET) to reside
395
- Type type = typeof ( Proxy ) ;
396
- var theProxy = ( Proxy ) domain . CreateInstanceAndUnwrap (
397
- type . Assembly . FullName ,
398
- type . FullName ) ;
424
+ var theProxy = CreateInstanceInstanceAndUnwrap < Proxy > ( domain ) ;
399
425
426
+ theProxy . Call ( "InitPython" , ShutdownMode . Soft ) ;
400
427
// From now on use the Proxy to call into the new assembly
401
428
theProxy . RunPython ( ) ;
402
429
430
+ theProxy . Call ( "ShutdownPython" ) ;
403
431
Console . WriteLine ( $ "[Program.Main] Before Domain Unload on { domainName } ") ;
404
432
AppDomain . Unload ( domain ) ;
405
433
Console . WriteLine ( $ "[Program.Main] After Domain Unload on { domainName } ") ;
@@ -459,17 +487,13 @@ static void RunDomainReloadSteps<T1, T2>() where T1 : CrossCaller where T2 : Cro
459
487
ValueType arg = null ;
460
488
Type type = typeof ( Proxy ) ;
461
489
{
462
- AppDomain domain = CreateDomain ( "test_domain_reload " ) ;
490
+ AppDomain domain = CreateDomain ( "test_domain_reload_1 " ) ;
463
491
try
464
492
{
465
- var theProxy = ( Proxy ) domain . CreateInstanceAndUnwrap (
466
- type . Assembly . FullName ,
467
- type . FullName ) ;
493
+ var theProxy = CreateInstanceInstanceAndUnwrap < Proxy > ( domain ) ;
468
494
theProxy . Call ( "InitPython" , ShutdownMode . Reload ) ;
469
495
470
- var caller = ( T1 ) domain . CreateInstanceAndUnwrap (
471
- typeof ( T1 ) . Assembly . FullName ,
472
- typeof ( T1 ) . FullName ) ;
496
+ var caller = CreateInstanceInstanceAndUnwrap < T1 > ( domain ) ;
473
497
arg = caller . Execute ( arg ) ;
474
498
475
499
theProxy . Call ( "ShutdownPython" ) ;
@@ -481,17 +505,13 @@ static void RunDomainReloadSteps<T1, T2>() where T1 : CrossCaller where T2 : Cro
481
505
}
482
506
483
507
{
484
- AppDomain domain = CreateDomain ( "test_domain_reload " ) ;
508
+ AppDomain domain = CreateDomain ( "test_domain_reload_2 " ) ;
485
509
try
486
510
{
487
- var theProxy = ( Proxy ) domain . CreateInstanceAndUnwrap (
488
- type . Assembly . FullName ,
489
- type . FullName ) ;
511
+ var theProxy = CreateInstanceInstanceAndUnwrap < Proxy > ( domain ) ;
490
512
theProxy . Call ( "InitPython" , ShutdownMode . Reload ) ;
491
513
492
- var caller = ( T2 ) domain . CreateInstanceAndUnwrap (
493
- typeof ( T2 ) . Assembly . FullName ,
494
- typeof ( T2 ) . FullName ) ;
514
+ var caller = CreateInstanceInstanceAndUnwrap < T2 > ( domain ) ;
495
515
caller . Execute ( arg ) ;
496
516
theProxy . Call ( "ShutdownPythonCompletely" ) ;
497
517
}
@@ -523,39 +543,26 @@ public static void RunPython()
523
543
{
524
544
AppDomain . CurrentDomain . DomainUnload += OnDomainUnload ;
525
545
string name = AppDomain . CurrentDomain . FriendlyName ;
526
- Console . WriteLine ( string . Format ( "[{0} in .NET] In PythonRunner.RunPython" , name ) ) ;
527
- var mode = PythonEngine . DefaultShutdownMode ;
528
- if ( mode == ShutdownMode . Normal )
529
- {
530
- mode = ShutdownMode . Soft ;
531
- }
532
- PythonEngine . Initialize ( mode : mode ) ;
533
- try
546
+ Console . WriteLine ( "[{0} in .NET] In PythonRunner.RunPython" , name ) ;
547
+ using ( Py . GIL ( ) )
534
548
{
535
- using ( Py . GIL ( ) )
549
+ try
536
550
{
537
- try
538
- {
539
- var pyScript = string . Format ( "import clr\n "
540
- + "print('[{0} in python] imported clr')\n "
541
- + "clr.AddReference('System')\n "
542
- + "print('[{0} in python] allocated a clr object')\n "
543
- + "import gc\n "
544
- + "gc.collect()\n "
545
- + "print('[{0} in python] collected garbage')\n " ,
546
- name ) ;
547
- PythonEngine . Exec ( pyScript ) ;
548
- }
549
- catch ( Exception e )
550
- {
551
- Console . WriteLine ( string . Format ( "[{0} in .NET] Caught exception: {1}" , name , e ) ) ;
552
- throw ;
553
- }
551
+ var pyScript = string . Format ( "import clr\n "
552
+ + "print('[{0} in python] imported clr')\n "
553
+ + "clr.AddReference('System')\n "
554
+ + "print('[{0} in python] allocated a clr object')\n "
555
+ + "import gc\n "
556
+ + "gc.collect()\n "
557
+ + "print('[{0} in python] collected garbage')\n " ,
558
+ name ) ;
559
+ PythonEngine . Exec ( pyScript ) ;
560
+ }
561
+ catch ( Exception e )
562
+ {
563
+ Console . WriteLine ( string . Format ( "[{0} in .NET] Caught exception: {1}" , name , e ) ) ;
564
+ throw ;
554
565
}
555
- }
556
- finally
557
- {
558
- PythonEngine . BeginAllowThreads ( ) ;
559
566
}
560
567
}
561
568
0 commit comments