18 using System.Collections.Generic;
27 private PyObject _instance;
28 private object _underlyingClrObject;
29 private Dictionary<string, PyObject> _pythonMethods;
30 private Dictionary<string, string> _pythonPropertyNames;
32 private readonly
bool _validateInterface;
45 _pythonMethods =
new();
46 _pythonPropertyNames =
new();
47 _validateInterface = validateInterface;
56 : this(validateInterface)
67 if (_instance !=
null)
69 _pythonMethods.Clear();
70 _pythonPropertyNames.Clear();
73 _instance = _validateInterface ? instance.ValidateImplementationOf<TInterface>() : instance;
74 _instance.TryConvert(out _underlyingClrObject);
83 using var _ = Py.GIL();
84 return PythonRuntimeChecker.ConvertAndDispose<T>(
GetProperty(propertyName), propertyName, isMethod:
false);
93 using var _ = Py.GIL();
94 return _instance.GetAttr(GetPropertyName(propertyName));
104 using var _ = Py.GIL();
105 _instance.SetAttr(GetPropertyName(propertyName), value.ToPython());
114 using var _ = Py.GIL();
115 return _instance.GetAttr(GetPropertyName(name,
true));
125 using var _ = Py.GIL();
126 return _instance.HasAttr(name) || _instance.HasAttr(name.ToSnakeCase());
136 if (!_pythonMethods.TryGetValue(methodName, out var method))
138 method = _instance.GetMethod(methodName);
139 _pythonMethods = AddToDictionary(_pythonMethods, methodName, method);
154 return PythonRuntimeChecker.InvokeMethod<T>(method, methodName, args);
164 using var _ = Py.GIL();
166 return method.Invoke(args);
188 return PythonRuntimeChecker.InvokeMethodAndEnumerate<T>(method, methodName, args);
200 return PythonRuntimeChecker.InvokeMethodAndGetDictionary<TKey, TValue>(method, methodName, args);
214 return PythonRuntimeChecker.InvokeMethodAndGetOutParameters<T>(method, methodName, outParametersTypes, out outParameters, args);
228 return PythonRuntimeChecker.InvokeMethodAndWrapResult(method, methodName, wrapResult, args);
231 private string GetPropertyName(
string propertyName,
bool isEvent =
false)
233 if (!_pythonPropertyNames.TryGetValue(propertyName, out var pythonPropertyName))
235 var snakeCasedPropertyName = propertyName.ToSnakeCase();
239 if (!isEvent && _underlyingClrObject !=
null)
241 var underlyingClrObjectType = _underlyingClrObject.GetType();
242 var
property = underlyingClrObjectType.GetProperty(propertyName);
243 if (property !=
null)
245 var clrPropertyValue =
property.GetValue(_underlyingClrObject);
246 var pyObjectSnakeCasePropertyValue = _instance.GetAttr(snakeCasedPropertyName);
248 if (!pyObjectSnakeCasePropertyValue.TryConvert(out
object pyObjectSnakeCasePropertyClrValue,
true) ||
249 !ReferenceEquals(clrPropertyValue, pyObjectSnakeCasePropertyClrValue))
251 pythonPropertyName = snakeCasedPropertyName;
255 pythonPropertyName = propertyName;
260 if (pythonPropertyName ==
null)
262 pythonPropertyName = snakeCasedPropertyName;
263 if (!_instance.HasAttr(pythonPropertyName))
265 pythonPropertyName = propertyName;
269 _pythonPropertyNames = AddToDictionary(_pythonPropertyNames, propertyName, pythonPropertyName);
272 return pythonPropertyName;
280 private static Dictionary<string, T> AddToDictionary<T>(Dictionary<string, T> dictionary,
string key, T value)
282 return new Dictionary<string, T>(dictionary)
296 return other is not
null && (ReferenceEquals(
this, other) ||
Equals(other._instance));
316 using var _ = Py.GIL();
317 return PythonReferenceComparer.Instance.GetHashCode(_instance);
323 private bool Equals(PyObject other)
325 if (other is
null)
return false;
326 if (ReferenceEquals(_instance, other))
return true;
328 using var _ = Py.GIL();
330 return PythonReferenceComparer.Instance.Equals(_instance, other);
343 using var _ = Py.GIL();
344 using var result = method.Invoke(args);
346 return Convert<TResult>(result, pythonMethodName);
355 using var _ = Py.GIL();
356 var result = method.Invoke(args);
358 foreach (var item
in EnumerateAndDisposeItems<TItem>(result, pythonMethodName))
372 using var _ = Py.GIL();
373 using var result = method.Invoke(args);
375 Dictionary<TKey, TValue> dict;
376 if (result.TryConvert(out dict))
384 Func<PyObject, string> keyErrorMessageFunc =
386 foreach (var (managedKey, pyKey) in Enumerate<TKey>(result, pythonMethodName, keyErrorMessageFunc))
388 var pyValue = result.GetItem(pyKey);
391 dict[managedKey] = pyValue.GetAndDispose<TValue>();
393 catch (InvalidCastException ex)
395 throw new InvalidCastException(
410 params
object[] args)
412 using var _ = Py.GIL();
413 var result = method.Invoke(args);
415 if (!result.TryConvert<TResult>(out var managedResult))
417 return wrapResult(result);
421 return managedResult;
430 out
object[] outParameters, params
object[] args)
432 using var _ = Py.GIL();
433 using var result = method.Invoke(args);
437 if (!PyTuple.IsTupleType(result))
439 throw new ArgumentException(
443 if (result.Length() < outParametersTypes.Length + 1)
446 pythonMethodName, outParametersTypes.Length + 1, result.Length()));
449 var managedResult = Convert<TResult>(result[0], pythonMethodName);
451 outParameters =
new object[outParametersTypes.Length];
455 for (; i < outParametersTypes.Length; i++)
457 outParameters[i] = result[i + 1].AsManagedObject(outParametersTypes[i]);
460 catch (InvalidCastException exception)
462 throw new InvalidCastException(
463 Messages.
BasePythonWrapper.InvalidOutParameterType(pythonMethodName, i, outParametersTypes[i], result[i + 1].GetPythonType()),
467 return managedResult;
474 public static T
Convert<T>(PyObject pyObject,
string pythonName,
bool isMethod =
true)
476 var type = typeof(T);
479 if (type == typeof(
void))
484 if (type == typeof(PyObject))
486 return (T)(object)pyObject;
489 return (T)pyObject.AsManagedObject(type);
491 catch (InvalidCastException e)
493 throw new InvalidCastException(
Messages.
BasePythonWrapper.InvalidReturnType(pythonName, type, pyObject.GetPythonType(), isMethod), e);
506 return Convert<T>(pyObject, pythonName, isMethod);
518 private static IEnumerable<(TItem, PyObject)> Enumerate<TItem>(PyObject result,
string pythonMethodName,
519 Func<PyObject, string> getInvalidCastExceptionMessage =
null)
521 if (!result.IsIterable())
523 throw new InvalidCastException(
Messages.
BasePythonWrapper.InvalidIterable(pythonMethodName, typeof(TItem), result.GetPythonType()));
526 using var iterator = result.GetIterator();
527 foreach (PyObject item
in iterator)
533 managedItem = item.As<TItem>();
535 catch (InvalidCastException ex)
537 var message = getInvalidCastExceptionMessage?.Invoke(item) ??
539 throw new InvalidCastException(message, ex);
542 yield
return (managedItem, item);
549 private static IEnumerable<TItem> EnumerateAndDisposeItems<TItem>(PyObject result,
string pythonMethodName)
551 foreach (var (managedItem, pyItem) in Enumerate<TItem>(result, pythonMethodName))
554 yield
return managedItem;