Skip to content

Commit c130bb2

Browse files
Dispose of python algorithm instance (#8695)
1 parent a1302d2 commit c130bb2

4 files changed

Lines changed: 71 additions & 28 deletions

File tree

AlgorithmFactory/Python/Wrappers/AlgorithmPythonWrapper.cs

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ namespace QuantConnect.AlgorithmFactory.Python.Wrappers
4747
/// </summary>
4848
public class AlgorithmPythonWrapper : BasePythonWrapper<IAlgorithm>, IAlgorithm
4949
{
50-
private readonly PyObject _algorithm;
5150
private readonly dynamic _onData;
5251
private readonly dynamic _onMarginCall;
5352
private readonly IAlgorithm _baseAlgorithm;
@@ -110,9 +109,8 @@ public AlgorithmPythonWrapper(string moduleName)
110109
{
111110
Logging.Log.Trace("AlgorithmPythonWrapper(): Creating IAlgorithm instance.");
112111

113-
_algorithm = attr.Invoke();
114-
SetPythonInstance(_algorithm);
115-
var dynAlgorithm = _algorithm as dynamic;
112+
SetPythonInstance(attr.Invoke());
113+
var dynAlgorithm = Instance as dynamic;
116114

117115
// Set pandas
118116
dynAlgorithm.SetPandasConverter();
@@ -122,11 +120,11 @@ public AlgorithmPythonWrapper(string moduleName)
122120

123121
// determines whether OnData method was defined or inherits from QCAlgorithm
124122
// If it is not, OnData from the base class will not be called
125-
_onData = _algorithm.GetPythonMethod("OnData");
123+
_onData = Instance.GetPythonMethod("OnData");
126124

127-
_onMarginCall = _algorithm.GetPythonMethod("OnMarginCall");
125+
_onMarginCall = Instance.GetPythonMethod("OnMarginCall");
128126

129-
PyObject endOfDayMethod = _algorithm.GetPythonMethod("OnEndOfDay");
127+
using PyObject endOfDayMethod = Instance.GetPythonMethod("OnEndOfDay");
130128
if (endOfDayMethod != null)
131129
{
132130
// Since we have a EOD method implemented
@@ -148,27 +146,27 @@ public AlgorithmPythonWrapper(string moduleName)
148146
}
149147

150148
// Initialize the python methods
151-
_onBrokerageDisconnect = _algorithm.GetMethod("OnBrokerageDisconnect");
152-
_onBrokerageMessage = _algorithm.GetMethod("OnBrokerageMessage");
153-
_onBrokerageReconnect = _algorithm.GetMethod("OnBrokerageReconnect");
154-
_onSplits = _algorithm.GetMethod("OnSplits");
155-
_onDividends = _algorithm.GetMethod("OnDividends");
156-
_onDelistings = _algorithm.GetMethod("OnDelistings");
157-
_onSymbolChangedEvents = _algorithm.GetMethod("OnSymbolChangedEvents");
158-
_onEndOfDay = _algorithm.GetMethod("OnEndOfDay");
159-
_onCommand = _algorithm.GetMethod("OnCommand");
160-
_onMarginCallWarning = _algorithm.GetMethod("OnMarginCallWarning");
161-
_onOrderEvent = _algorithm.GetMethod("OnOrderEvent");
162-
_onAssignmentOrderEvent = _algorithm.GetMethod("OnAssignmentOrderEvent");
163-
_onSecuritiesChanged = _algorithm.GetMethod("OnSecuritiesChanged");
164-
_onFrameworkSecuritiesChanged = _algorithm.GetMethod("OnFrameworkSecuritiesChanged");
149+
_onBrokerageDisconnect = Instance.GetMethod("OnBrokerageDisconnect");
150+
_onBrokerageMessage = Instance.GetMethod("OnBrokerageMessage");
151+
_onBrokerageReconnect = Instance.GetMethod("OnBrokerageReconnect");
152+
_onSplits = Instance.GetMethod("OnSplits");
153+
_onDividends = Instance.GetMethod("OnDividends");
154+
_onDelistings = Instance.GetMethod("OnDelistings");
155+
_onSymbolChangedEvents = Instance.GetMethod("OnSymbolChangedEvents");
156+
_onEndOfDay = Instance.GetMethod("OnEndOfDay");
157+
_onCommand = Instance.GetMethod("OnCommand");
158+
_onMarginCallWarning = Instance.GetMethod("OnMarginCallWarning");
159+
_onOrderEvent = Instance.GetMethod("OnOrderEvent");
160+
_onAssignmentOrderEvent = Instance.GetMethod("OnAssignmentOrderEvent");
161+
_onSecuritiesChanged = Instance.GetMethod("OnSecuritiesChanged");
162+
_onFrameworkSecuritiesChanged = Instance.GetMethod("OnFrameworkSecuritiesChanged");
165163
}
166164
attr.Dispose();
167165
}
168166
module.Dispose();
169167
pyList.Dispose();
170168
// If _algorithm could not be set, throw exception
171-
if (_algorithm == null)
169+
if (Instance == null)
172170
{
173171
throw new Exception("Please ensure that one class inherits from QCAlgorithm.");
174172
}
@@ -1163,13 +1161,13 @@ private bool TryConvert<T>(PyObject pyObject, out T result)
11631161
/// <returns></returns>
11641162
public override string ToString()
11651163
{
1166-
if (_algorithm == null)
1164+
if (Instance == null)
11671165
{
11681166
return base.ToString();
11691167
}
11701168
using (Py.GIL())
11711169
{
1172-
return _algorithm.Repr();
1170+
return Instance.Repr();
11731171
}
11741172
}
11751173

@@ -1270,5 +1268,30 @@ public void SetTags(HashSet<string> tags)
12701268
/// <returns>The command result</returns>
12711269
public CommandResultPacket RunCommand(CallbackCommand command) => _baseAlgorithm.RunCommand(command);
12721270

1271+
/// <summary>
1272+
/// Dispose of this instance
1273+
/// </summary>
1274+
public override void Dispose()
1275+
{
1276+
using var _ = Py.GIL();
1277+
_onBrokerageDisconnect?.Dispose();
1278+
_onBrokerageMessage?.Dispose();
1279+
_onBrokerageReconnect?.Dispose();
1280+
_onSplits?.Dispose();
1281+
_onDividends?.Dispose();
1282+
_onDelistings?.Dispose();
1283+
_onSymbolChangedEvents?.Dispose();
1284+
_onEndOfDay?.Dispose();
1285+
_onMarginCallWarning?.Dispose();
1286+
_onOrderEvent?.Dispose();
1287+
_onCommand?.Dispose();
1288+
_onAssignmentOrderEvent?.Dispose();
1289+
_onSecuritiesChanged?.Dispose();
1290+
_onFrameworkSecuritiesChanged?.Dispose();
1291+
1292+
_onData?.Dispose();
1293+
_onMarginCall?.Dispose();
1294+
base.Dispose();
1295+
}
12731296
}
12741297
}

Common/Extensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,14 +637,14 @@ public static dynamic GetPythonMethod(this PyObject instance, string name)
637637
}
638638

639639
method = instance.GetAttr(name);
640-
var pythonType = method.GetPythonType();
640+
using var pythonType = method.GetPythonType();
641641
var isPythonDefined = pythonType.Repr().Equals("<class \'method\'>", StringComparison.Ordinal);
642642

643643
if (isPythonDefined)
644644
{
645645
return method;
646646
}
647-
647+
method.Dispose();
648648
return null;
649649
}
650650
}

Common/Python/BasePythonWrapper.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,17 @@
1313
* limitations under the License.
1414
*/
1515

16-
using Python.Runtime;
1716
using System;
17+
using Python.Runtime;
18+
using QuantConnect.Util;
1819
using System.Collections.Generic;
1920

2021
namespace QuantConnect.Python
2122
{
2223
/// <summary>
2324
/// Base class for Python wrapper classes
2425
/// </summary>
25-
public class BasePythonWrapper<TInterface> : IEquatable<BasePythonWrapper<TInterface>>
26+
public class BasePythonWrapper<TInterface> : IEquatable<BasePythonWrapper<TInterface>>, IDisposable
2627
{
2728
private PyObject _instance;
2829
private object _underlyingClrObject;
@@ -330,6 +331,23 @@ private bool Equals(PyObject other)
330331
return PythonReferenceComparer.Instance.Equals(_instance, other);
331332
}
332333

334+
/// <summary>
335+
/// Dispose of this instance
336+
/// </summary>
337+
public virtual void Dispose()
338+
{
339+
using var _ = Py.GIL();
340+
if (_pythonMethods != null)
341+
{
342+
foreach (var methods in _pythonMethods.Values)
343+
{
344+
methods.Dispose();
345+
}
346+
_pythonMethods.Clear();
347+
}
348+
_instance?.Dispose();
349+
}
350+
333351
/// <summary>
334352
/// Set of helper methods to invoke Python methods with runtime checks for return values and out parameter's conversions.
335353
/// </summary>

Engine/Engine.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using System.Runtime.CompilerServices;
2121
using System.Threading;
2222
using System.Threading.Tasks;
23+
using QuantConnect.AlgorithmFactory.Python.Wrappers;
2324
using QuantConnect.Brokerages;
2425
using QuantConnect.Configuration;
2526
using QuantConnect.Data;
@@ -450,6 +451,7 @@ public void Run(AlgorithmNodePacket job, AlgorithmManager manager, string assemb
450451
AlgorithmHandlers.Transactions.Exit();
451452
AlgorithmHandlers.RealTime.Exit();
452453
AlgorithmHandlers.DataMonitor.Exit();
454+
(algorithm as AlgorithmPythonWrapper)?.DisposeSafely();
453455
}
454456
}
455457

0 commit comments

Comments
 (0)