Environment
- Pythonnet version: 3.0.3
- Python version: 3.8.1+
- Operating System: Windows 10
- .NET Runtime: .NET 6
Details
internal class DecimalCodec : IPyObjectDecoder, IPyObjectEncoder
{
public bool CanDecode(PyType objectType, Type targetType)
{
return targetType == typeof(decimal);
}
public bool CanEncode(Type type)
{
return type == typeof(decimal)
|| type == typeof(object); // this is necessary to get support for boxed decimals
}
public bool TryDecode<T>(PyObject pyObj, out T? value)
{
if (decimal.TryParse(pyObj.ToString(CultureInfo.InvariantCulture), out decimal val))
{
value = (T)(object)val;
return true;
}
else
{
value = default;
return false;
}
}
public PyObject? TryEncode(object value)
{
if (value is not decimal d)
{
return null;
}
return new PyFloat(d.ToString(CultureInfo.InvariantCulture));
}
}
Basically I am trying to do things like the following:
num: int or float
is_equal_to_num = someObject.DecimalFieldOrProperty == num # works with my codec
s_equal_to_num = someObject.BoxedDecimalFieldOrProperty == num # works with my codec
SomeObject.SomeMethodAcceptingDecimal(num) # does not work, because Python class 'int' is not convertible to System.Decimal
>>> SomeObject.SomeMethodAcceptingDecimal(num)
Python.Runtime.PythonException: 'int' value cannot be converted to System.Decimal
The above exception was the direct cause of the following exception:
System.ArgumentException: 'int' value cannot be converted to System.Decimal in method Void SomeMethodAcceptingDecimal(System.Decimal)
---> Python.Runtime.PythonException: 'int' value cannot be converted to System.Decimal
--- End of inner exception stack trace ---
The above exception was the direct cause of the following exception:
System.AggregateException: One or more errors occurred. ('int' value cannot be converted to System.Decimal in method Void SomeMethodAcceptingDecimal(System.Decimal))
---> System.ArgumentException: 'int' value cannot be converted to System.Decimal in method Void SomeMethodAcceptingDecimal(System.Decimal)
---> Python.Runtime.PythonException: 'int' value cannot be converted to System.Decimal
--- End of inner exception stack trace ---
--- End of inner exception stack trace ---
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: No method matches given arguments for SomeObject.SomeMethodAcceptingDecimal: (<class 'int'>)
I see a few issues with this approach:
- It's not working (well) with overload resolution
- It relies on strings to do the conversion, which is expensive. There must be a way to do it faster by accessing raw bits and python internals.
- Support for boxed decimals is hacky, because the Codec gets called for every object. This could be solved by adding a
object value parameter to CanEncode so it has access to the same information as Python.Runtime.Converter.ToPython, which uses the actual TypeCode of the value to handle boxed primitive types.
Note: I am willing to contribute and work with the pythonnet team to get this feature added to the pythonnet runtime.
Environment
Details
Describe what you were trying to get done.
I implemented a pythonnet codec for System.Decimal:
Basically I am trying to do things like the following:
I see a few issues with this approach:
object valueparameter toCanEncodeso it has access to the same information asPython.Runtime.Converter.ToPython, which uses the actualTypeCodeof the value to handle boxed primitive types.Note: I am willing to contribute and work with the pythonnet team to get this feature added to the pythonnet runtime.