Lean  $LEAN_TAG$
CommandPythonWrapper.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using Python.Runtime;
17 using Newtonsoft.Json;
18 using Newtonsoft.Json.Linq;
21 using System.Collections.Generic;
22 
23 namespace QuantConnect.Python
24 {
25  /// <summary>
26  /// Python wrapper for a python defined command type
27  /// </summary>
28  public class CommandPythonWrapper : BasePythonWrapper<Command>
29  {
30  private static PyObject _linkSerializationMethod;
31 
32  /// <summary>
33  /// Constructor for initialising the <see cref="CommandPythonWrapper"/> class with wrapped <see cref="PyObject"/> object
34  /// </summary>
35  /// <param name="type">Python command type</param>
36  /// <param name="data">Command data</param>
37  public CommandPythonWrapper(PyObject type, string data = null)
38  : base()
39  {
40  using var _ = Py.GIL();
41 
42  var instance = type.Invoke();
43 
44  SetPythonInstance(instance);
45  if (!string.IsNullOrEmpty(data))
46  {
47  if (HasAttr("PayloadData"))
48  {
49  SetProperty("PayloadData", data);
50  }
51 
52  foreach (var kvp in JsonConvert.DeserializeObject<Dictionary<string, object>>(data))
53  {
54  if (kvp.Value is JArray jArray)
55  {
56  SetProperty(kvp.Key, jArray.ToObject<List<object>>());
57  }
58  else if (kvp.Value is JObject jobject)
59  {
60  SetProperty(kvp.Key, jobject.ToObject<Dictionary<string, object>>());
61  }
62  else
63  {
64  SetProperty(kvp.Key, kvp.Value);
65  }
66  }
67  }
68  }
69 
70  /// <summary>
71  /// Run this command using the target algorithm
72  /// </summary>
73  /// <param name="algorithm">The algorithm instance</param>
74  /// <returns>True if success, false otherwise. Returning null will disable command feedback</returns>
75  public bool? Run(IAlgorithm algorithm)
76  {
77  using var _ = Py.GIL();
78  var result = InvokeMethod(nameof(Run), algorithm);
79  return result.GetAndDispose<bool?>();
80  }
81 
82  /// <summary>
83  /// Helper method to serialize a command instance
84  /// </summary>
85  public static string Serialize(PyObject command)
86  {
87  if (command == null)
88  {
89  return string.Empty;
90  }
91 
92  using var _ = Py.GIL();
93  if (_linkSerializationMethod == null)
94  {
95  var module = PyModule.FromString("python_serialization", @"from json import dumps
96 from inspect import getmembers
97 
98 def serialize(target):
99  if isinstance(target, dict):
100  # dictionary
101  return dumps(target)
102  if not hasattr(target, '__dict__') or not target.__dict__:
103  # python command inheriting base Command
104  members = getmembers(target)
105  result = {}
106  for name, value in members:
107  if value and not name.startswith('__'):
108  potential_entry = str(value)
109  if not potential_entry.startswith('<bound '):
110  result[name] = value
111  return dumps(result)
112  # pure python command object
113  return dumps(target.__dict__)
114 ");
115  _linkSerializationMethod = module.GetAttr("serialize");
116  }
117  using var strResult = _linkSerializationMethod.Invoke(command);
118 
119  return strResult.As<string>();
120  }
121  }
122 }