[Python.NET] Wrap in interface (#1240)

2020-11-02 Thread Mohamed Koubaa
Hello,

This PR changed behavior of 
pythonnet to use stricter typing when a method returns an interface.  I'd like 
a way to opt-out of this behavior, and I'll share a representative use-case 
below.  The tl;dr is that python is a dynamic language and I expect the C# 
objects which pythonnet wraps to behave more like "dynamic" and less like the 
static types on the interface.

 C# code 
namespace Example1
{
public enum ShapeType
{
Square,
Circle
}
public interface IShape
{
void Draw();

double Area { get; }

ShapeType Type { get; }
}

public class Square : IShape
{
public double Length { get; }
public Square(double length)
{
Length = length;
}

public void Draw() {}
public double Area { get { return Length * Length; } }
public ShapeType Type { get { return ShapeType.Square; } }
}

public class Circle : IShape
{
public double Radius { get; }
public Circle(double radius)
{
Radius = radius;
}

public void Draw() {}
public double Area { get { return Math.PI * Radius * Radius; } }
public ShapeType Type { get { return ShapeType.Circle; } }
}

public class ShapeDataModel
{
private Dictionary _shapes = new Dictionary();
private int _nextId = 1;

public int AddShape(IShape shape)
{
int id = _nextId;
_shapes[id] = shape;
_nextId++;
return id;
}

public IShape GetShape(int id)
{
return _shapes[id];
}
}
}


 Python code 
>>> import clr
>>> clr.AddReference("Example1")
>>> import Example1
>>> sq1 = Example1.Square(2.)
>>> ci1 = Example1.Circle(1.3)
>>> sq2 = Example1.Square(2.5)
>>> m = Example1.ShapeDataModel()
>>> m.AddShape(sq1)
1
>>> m.AddShape(sq2)
2
>>> m.AddShape(ci1)
3
>>> m.GetShape(2)

>>> m.GetShape(2).Area
6.25
>>> m.GetShape(2).Length
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'IShape' object has no attribute 'Length'

 summary 
This factory and/or datamodel pattern is very common in my codebase and 
probably also lots of object oriented systems.  The mentioned PR breaks this 
common design pattern in C# objects wrapped by pythonnet, and I would like a 
way to opt-out of it.  Maybe an attribute on the method which returns an 
interface can be used, or maybe something global to switch this behavior.  I 
think perhaps the original bug might also have been fixed in a different way.

Thanks!
Mohamed
___
PythonNet mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/pythonnet.python.org/
Member address: [email protected]


[Python.NET] Re: Wrap in interface (#1240)

2020-11-02 Thread Victor “LOST” Milovanov
The original bug report described a similar problem, except the behavior without defaulting to stricter interface types is very unintuitive. Imagine you have a well documented library, that exposes property Prop of type IInterface. When specific class implements some Method  in IInterface explicitly (which arguably is a very minor implementation detail), and you call Prop.Method() in Python, it can fail with AttributeError despite the documentation claiming this method exists on the interface. This is very confusing. IMHO, more confusing, than inability to get Length of an element of IShape[]. We could try to improve the situation for Python folks by allowing interface instance wrappers to fall back to the original class instance in case the attribute is not found in the interface itself. I think this might still be problematic, as it would make the behavior of hasattr and other similar functions a bit surprising. Regardless, there’s a workaround for the scenario you described which is just doing m.GetShape(1).__raw_implementation__.Length or m.GetShape(1).__implementation__.Length depending if you need an encoded instance. Regards,Victor Milovanov From: Mohamed KoubaaSent: Monday, November 2, 2020 6:15 PMTo: [email protected]: [Python.NET] Wrap in interface (#1240) Hello, This PR changed behavior of pythonnet to use stricter typing when a method returns an interface.  I'd like a way to opt-out of this behavior, and I'll share a representative use-case below.  The tl;dr is that python is a dynamic language and I expect the C# objects which pythonnet wraps to behave more like "dynamic" and less like the static types on the interface.  C# code namespace Example1 {    public enum ShapeType    {        Square,        Circle    }    public interface IShape    {        void Draw();         double Area { get; }         ShapeType Type { get; }    }     public class Square : IShape    {        public double Length { get; }        public Square(double length)        {            Length = length;        }         public void Draw() {}        public double Area { get { return Length * Length; } }        public ShapeType Type { get { return ShapeType.Square; } }    }     public class Circle : IShape    {        public double Radius { get; }        public Circle(double radius)        {            Radius = radius;        }         public void Draw() {}        public double Area { get { return Math.PI * Radius * Radius; } }        public ShapeType Type { get { return ShapeType.Circle; } }    }     public class ShapeDataModel    {        private Dictionary _shapes = new Dictionary();        private int _nextId = 1;         public int AddShape(IShape shape)        {            int id = _nextId;            _shapes[id] = shape;            _nextId++;            return id;        }         public IShape GetShape(int id)        {            return _shapes[id];        }    }}   Python code >>> import clr>>> clr.AddReference("Example1")>>> import Example1>>> sq1 = Example1.Square(2.)>>> ci1 = Example1.Circle(1.3)>>> sq2 = Example1.Square(2.5)>>> m = Example1.ShapeDataModel()>>> m.AddShape(sq1)1>>> m.AddShape(sq2)2>>> m.AddShape(ci1)3>>> m.GetShape(2)>>> m.GetShape(2).Area6.25>>> m.GetShape(2).LengthTraceback (most recent call last):  File "", line 1, in AttributeError: 'IShape' object has no attribute 'Length' summary This factory and/or datamodel pattern is very common in my codebase and probably also lots of object oriented systems.  The mentioned PR breaks this common design pattern in C# objects wrapped by pythonnet, and I would like a way to opt-out of it.  Maybe an attribute on the method which returns an interface can be used, or maybe something global to switch this behavior.  I think perhaps the original bug might also have been fixed in a different way. Thanks!Mohamed ___
PythonNet mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/pythonnet.python.org/
Member address: [email protected]