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

Mohamed Koubaa <[email protected]> Mon, 8 Feb 2021 19:18:42 +0000
Newsgroups gmane.comp.python.dotnet
Message-ID <SN6PR01MB497662EB218106862672133C998F9@SN6PR01MB4976.prod.exchangelabs.com>
--===============5566050003740487892==
Content-Language: en-US
Content-Type: multipart/related;
	boundary="_004_SN6PR01MB497662EB218106862672133C998F9SN6PR01MB4976prod_";
	type="multipart/alternative"

--_004_SN6PR01MB497662EB218106862672133C998F9SN6PR01MB4976prod_
Content-Type: multipart/alternative;
	boundary="_000_SN6PR01MB497662EB218106862672133C998F9SN6PR01MB4976prod_"

--_000_SN6PR01MB497662EB218106862672133C998F9SN6PR01MB4976prod_
Content-Type: text/plain; charset="Windows-1252"
Content-Transfer-Encoding: quoted-printable

Victor,

Our API is C# and some of our users actually directly use C# to access them=
 and don't use python.  We don't intend to change the API.  Of course, user=
s may have to change their scripts to be python3 compatible when they opt i=
nto using our API from CPython, but we expect most anything unrelated to 2t=
o3 to continue to work.

My expectation is that if a method/property returns an object using the int=
erface but the concrete class which it returns implements a given method, t=
hen hasattr should find it.  I think options 4&5 do what I need (but isn't =
option 4 what we had prior to #1240?)

Thanks,
Mohamed
________________________________
From: Victor =93LOST=94 Milovanov <[email protected]>
Sent: Monday, February 8, 2021 12:56 PM
To: A list for users and developers of Python.NET <[email protected]>
Subject: [Python.NET] Re: Wrap in interface (#1240)


[External Sender]

A dump of thoughts on the matter:



  1.  IronPython is Python 2 only, Python.NET 3.0+ is Python 3 only. Why no=
t use 2.5.x until you are comfortable to change API?
  2.  Flag is possible, but I would not recommend that approach, because it=
 makes combining multiple libraries that use different global flags problem=
atic.
  3.  Perhaps we could allow codecs to handle instances of wrapped interfac=
es (actually, isn=92t that currently allowed?)
  4.  IronPython apparently allows explicit interface implementations to be=
 called (instead of returning interface-wrapped instances), if there=92s no=
 conflict with regular methods. Perhaps we should consider this as an optio=
n.
  5.  Alternatively, we can keep wrapped instances, but allow to resolve pu=
blic members too if the method is not found in the interface. I like this o=
ption better than 4, because actual type of an element in ISomething[] won=
=92t change behavior of invoking interface member. It might still affect `h=
asattr`, but this is somewhat expected.



Regards,

Victor



From: Mohamed Koubaa<mailto:[email protected]>
Sent: Monday, February 8, 2021 8:34 AM
To: A list for users and developers of Python.NET<mailto:[email protected]=
rg>
Subject: [Python.NET] Re: Wrap in interface (#1240)



Victor,



Sorry for the late response.  I think what I would need is some kind of fla=
g in the python runtime to choose the old behavior (without having to have =
my own branch).  Of course there is a workaround but we've invested a lot o=
f time in designing the API for both CPython & IronPython and it would be i=
deal if they did not differ in this way.



This could be a global flag on the runtime and/or an attribute on a class/i=
nterface to decide on a granular level whether the specific class/interface=
 will use interface method binding or instance method binding.



Thoughts?



Thanks!

Mohamed



From: Victor =93LOST=94 Milovanov <[email protected]>
Sent: Monday, November 2, 2020 8:35 PM
To: A list for users and developers of Python.NET <[email protected]>
Subject: [Python.NET] Re: Wrap in interface (#1240)



[External Sender]

The original bug report described a similar problem, except the behavior wi=
thout defaulting to stricter interface types is very unintuitive. Imagine y=
ou have a well documented library, that exposes property Prop of type IInte=
rface. 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 documentati=
on claiming this method exists on the interface. This is very confusing. IM=
HO, more confusing, than inability to get Length of an element of IShape[].



We could try to improve the situation for Python folks by allowing interfac=
e 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 b=
e problematic, as it would make the behavior of hasattr and other similar f=
unctions a bit surprising.



Regardless, there=92s 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 in=
stance.



Regards,

Victor Milovanov



From: Mohamed Koubaa<mailto:[email protected]>
Sent: Monday, November 2, 2020 6:15 PM
To: [email protected]<mailto:[email protected]>
Subject: [Python.NET] Wrap in interface (#1240)



Hello,



This PR<https://nam10.safelinks.protection.outlook.com/?url=3Dhttps%3A%2F%2=
Fgithub.com%2Fpythonnet%2Fpythonnet%2Fpull%2F1240&data=3D04%7C01%7Cmohamed.=
koubaa%40ansys.com%7C4b457216f75446301a4508d8cc634c3b%7C34c6ce6715b84eff80e=
952da8be89706%7C0%7C0%7C637484074196952423%7CUnknown%7CTWFpbGZsb3d8eyJWIjoi=
MC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=3Dc=
AFUSSzeNl11sru%2B1ZNS8EjdoqhST%2FAGvY73dUQgV9U%3D&reserved=3D0> changed beh=
avior of pythonnet to use stricter typing when a method returns an interfac=
e.  I'd like a way to opt-out of this behavior, and I'll share a representa=
tive 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" a=
nd 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 =3D 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 =3D 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<int, IShape> _shapes =3D new Dictionary<int, ISh=
ape>();

        private int _nextId =3D 1;



        public int AddShape(IShape shape)

        {

            int id =3D _nextId;

            _shapes[id] =3D shape;

            _nextId++;

            return id;

        }



        public IShape GetShape(int id)

        {

            return _shapes[id];

        }

    }

}





**** Python code ****

>>> import clr

>>> clr.AddReference("Example1")

>>> import Example1

>>> sq1 =3D Example1.Square(2.)

>>> ci1 =3D Example1.Circle(1.3)

>>> sq2 =3D Example1.Square(2.5)

>>> m =3D Example1.ShapeDataModel()

>>> m.AddShape(sq1)

1

>>> m.AddShape(sq2)

2

>>> m.AddShape(ci1)

3

>>> m.GetShape(2)

<Example1.IShape object at 0x000002A3448A09A0>

>>> m.GetShape(2).Area

6.25

>>> m.GetShape(2).Length

Traceback (most recent call last):

  File "<stdin>", line 1, in <module>

AttributeError: 'IShape' object has no attribute 'Length'



**** summary ****

This factory and/or datamodel pattern is very common in my codebase and pro=
bably also lots of object oriented systems.  The mentioned PR breaks this c=
ommon 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 i=
nterface 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 w=
ay.



Thanks!
Mohamed





--_000_SN6PR01MB497662EB218106862672133C998F9SN6PR01MB4976prod_
Content-Type: text/html; charset="Windows-1252"
Content-Transfer-Encoding: quoted-printable

<html>
<head>
<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3DWindows-1=
252">
<style type=3D"text/css" style=3D"display:none;"> P {margin-top:0;margin-bo=
ttom:0;} </style>
</head>
<body dir=3D"ltr">
<div style=3D"font-family: Calibri, Arial, Helvetica, sans-serif; font-size=
: 12pt; color: rgb(0, 0, 0);">
Victor,</div>
<div style=3D"font-family: Calibri, Arial, Helvetica, sans-serif; font-size=
: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div style=3D"font-family: Calibri, Arial, Helvetica, sans-serif; font-size=
: 12pt; color: rgb(0, 0, 0);">
<span style=3D"background-color:rgb(255, 255, 255);display:inline !importan=
t">Our API is C# and some of our users actually directly use C# to access t=
hem and don't use python.<span>&nbsp; We don't intend to change the API</sp=
an></span>.&nbsp; Of course, users may have
 to change their scripts to be python3 compatible when they opt into using =
our API from CPython, but we expect most anything unrelated to 2to3 to cont=
inue to work.</div>
<div style=3D"font-family: Calibri, Arial, Helvetica, sans-serif; font-size=
: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div style=3D"font-family: Calibri, Arial, Helvetica, sans-serif; font-size=
: 12pt; color: rgb(0, 0, 0);">
My expectation is that if a method/property returns an object using the int=
erface but the concrete class which it returns implements a given method, t=
hen hasattr should find it.&nbsp; I think options 4&amp;5 do what I need (b=
ut isn't option 4 what we had prior to #1240?)</div>
<div style=3D"font-family: Calibri, Arial, Helvetica, sans-serif; font-size=
: 12pt; color: rgb(0, 0, 0);">
<br>
</div>
<div style=3D"font-family: Calibri, Arial, Helvetica, sans-serif; font-size=
: 12pt; color: rgb(0, 0, 0);">
Thanks,</div>
<div style=3D"font-family: Calibri, Arial, Helvetica, sans-serif; font-size=
: 12pt; color: rgb(0, 0, 0);">
Mohamed</div>
<div id=3D"appendonsend"></div>
<hr style=3D"display:inline-block;width:98%" tabindex=3D"-1">
<div id=3D"divRplyFwdMsg" dir=3D"ltr"><font face=3D"Calibri, sans-serif" st=
yle=3D"font-size:11pt" color=3D"#000000"><b>From:</b> Victor =93LOST=94 Mil=
ovanov &lt;[email protected]&gt;<br>
<b>Sent:</b> Monday, February 8, 2021 12:56 PM<br>
<b>To:</b> A list for users and developers of Python.NET &lt;pythonnet@pyth=
on.org&gt;<br>
<b>Subject:</b> [Python.NET] Re: Wrap in interface (#1240)</font>
<div>&nbsp;</div>
</div>
<style>
<!--
@font-face
	{font-family:"Cambria Math"}
@font-face
	{font-family:Calibri}
p.x_MsoNormal, li.x_MsoNormal, div.x_MsoNormal
	{margin:0in;
	font-size:11.0pt;
	font-family:"Calibri",sans-serif}
a:link, span.x_MsoHyperlink
	{color:blue;
	text-decoration:underline}
p.x_MsoListParagraph, li.x_MsoListParagraph, div.x_MsoListParagraph
	{margin-top:0in;
	margin-right:0in;
	margin-bottom:0in;
	margin-left:.5in;
	font-size:11.0pt;
	font-family:"Calibri",sans-serif}
p.x_xmsonormal, li.x_xmsonormal, div.x_xmsonormal
	{margin:0in;
	font-size:11.0pt;
	font-family:"Calibri",sans-serif}
.x_MsoChpDefault
	{}
@page WordSection1
	{margin:1.0in 1.0in 1.0in 1.0in}
div.x_WordSection1
	{}
ol
	{margin-bottom:0in}
ul
	{margin-bottom:0in}
-->
</style>
<div lang=3D"EN-US" link=3D"blue" vlink=3D"#954F72" style=3D"word-wrap:brea=
k-word">
<div style=3D"border:solid #9C6500 1.0pt; padding:2.0pt 2.0pt 2.0pt 2.0pt">
<p style=3D"line-height:12.0pt; background:#FFEB9C"><b><span lang=3D"EN-US"=
 style=3D"font-size:10.0pt; color:red">[External Sender]</span></b></p>
</div>
<div>
<div class=3D"x_WordSection1">
<p class=3D"x_MsoNormal">A dump of thoughts on the matter:</p>
<p class=3D"x_MsoNormal">&nbsp;</p>
<ol start=3D"1" type=3D"1" style=3D"margin-top:0in">
<li class=3D"x_MsoListParagraph" style=3D"margin-left:0in">IronPython is Py=
thon 2 only, Python.NET 3.0+ is Python 3 only. Why not use 2.5.x until you =
are comfortable to change API?</li><li class=3D"x_MsoListParagraph" style=
=3D"margin-left:0in">Flag is possible, but I would not recommend that appro=
ach, because it makes combining multiple libraries that use different globa=
l flags problematic.</li><li class=3D"x_MsoListParagraph" style=3D"margin-l=
eft:0in">Perhaps we could allow codecs to handle instances of wrapped inter=
faces (actually, isn=92t that currently allowed?)</li><li class=3D"x_MsoLis=
tParagraph" style=3D"margin-left:0in">IronPython apparently allows explicit=
 interface implementations to be called (instead of returning interface-wra=
pped instances), if there=92s no conflict with regular methods. Perhaps we =
should consider this
 as an option.</li><li class=3D"x_MsoListParagraph" style=3D"margin-left:0i=
n">Alternatively, we can keep wrapped instances, but allow to resolve publi=
c members too if the method is not found in the interface. I like this opti=
on better than 4, because actual type of an element in
 ISomething[] won=92t change behavior of invoking interface member. It migh=
t still affect `hasattr`, but this is somewhat expected.</li></ol>
<p class=3D"x_MsoNormal">&nbsp;</p>
<p class=3D"x_MsoNormal">Regards,</p>
<p class=3D"x_MsoNormal">Victor</p>
<p class=3D"x_MsoNormal">&nbsp;</p>
<div style=3D"border:none; border-top:solid #E1E1E1 1.0pt; padding:3.0pt 0i=
n 0in 0in">
<p class=3D"x_MsoNormal" style=3D"border:none; padding:0in"><b>From: </b><a=
 href=3D"mailto:[email protected]">Mohamed Koubaa</a><br>
<b>Sent: </b>Monday, February 8, 2021 8:34 AM<br>
<b>To: </b><a href=3D"mailto:[email protected]">A list for users and dev=
elopers of Python.NET</a><br>
<b>Subject: </b>[Python.NET] Re: Wrap in interface (#1240)</p>
</div>
<p class=3D"x_MsoNormal">&nbsp;</p>
<div>
<p class=3D"x_MsoNormal"><span style=3D"font-size:12.0pt; color:black">Vict=
or,</span></p>
</div>
<div>
<p class=3D"x_MsoNormal"><span style=3D"font-size:12.0pt; color:black">&nbs=
p;</span></p>
</div>
<div>
<p class=3D"x_MsoNormal"><span style=3D"font-size:12.0pt; color:black">Sorr=
y for the late response.&nbsp; I think what I would need is some kind of fl=
ag in the python runtime to choose the old behavior (without having to have=
 my own branch).&nbsp; Of course there is a workaround
 but we've invested a lot of time in designing the API for both CPython &am=
p; IronPython and it would be ideal if they did not differ in this way.&nbs=
p;&nbsp;</span></p>
</div>
<div>
<p class=3D"x_MsoNormal"><span style=3D"font-size:12.0pt; color:black">&nbs=
p;</span></p>
</div>
<div>
<p class=3D"x_MsoNormal"><span style=3D"font-size:12.0pt; color:black">This=
 could be a global flag on the runtime and/or an attribute on a class/inter=
face to decide on a granular level whether the specific class/interface wil=
l use interface method binding or instance
 method binding.</span></p>
</div>
<div>
<p class=3D"x_MsoNormal"><span style=3D"font-size:12.0pt; color:black">&nbs=
p;</span></p>
</div>
<div>
<p class=3D"x_MsoNormal"><span style=3D"font-size:12.0pt; color:black">Thou=
ghts?</span></p>
</div>
<div>
<p class=3D"x_MsoNormal"><span style=3D"font-size:12.0pt; color:black">&nbs=
p;</span></p>
</div>
<div>
<p class=3D"x_MsoNormal"><span style=3D"font-size:12.0pt; color:black">Than=
ks!</span></p>
</div>
<div>
<p class=3D"x_MsoNormal"><span style=3D"font-size:12.0pt; color:black">Moha=
med</span></p>
</div>
<p class=3D"x_MsoNormal"><img border=3D"0" width=3D"694" height=3D"2" id=3D=
"x_Horizontal_x0020_Line_x0020_1" style=3D"width:7.2291in; height:.0208in" =
data-outlook-trace=3D"F:1|T:1" src=3D"cid:image001.png-CFHuyTl/[email protected]"><=
/p>
<div id=3D"x_divRplyFwdMsg">
<p class=3D"x_MsoNormal"><b><span style=3D"color:black">From:</span></b><sp=
an style=3D"color:black"> Victor =93LOST=94 Milovanov &lt;lostfreeman@gmail=
.com&gt;<br>
<b>Sent:</b> Monday, November 2, 2020 8:35 PM<br>
<b>To:</b> A list for users and developers of Python.NET &lt;pythonnet@pyth=
on.org&gt;<br>
<b>Subject:</b> [Python.NET] Re: Wrap in interface (#1240)</span> </p>
<div>
<p class=3D"x_MsoNormal">&nbsp;</p>
</div>
</div>
<div>
<div style=3D"border:solid #9C6500 1.0pt; padding:2.0pt 2.0pt 2.0pt 2.0pt">
<p style=3D"line-height:12.0pt; background:#FFEB9C"><b><span style=3D"font-=
size:10.0pt; color:red">[External Sender]</span></b></p>
</div>
<div>
<div>
<p class=3D"x_xmsonormal">The original bug report described a similar probl=
em, except the behavior without defaulting to stricter interface types is v=
ery unintuitive. Imagine you have a well documented library, that exposes p=
roperty
<b>Prop</b> of type <b>IInterface</b>. When specific class implements some =
<b>Method</b> &nbsp;in
<b>IInterface</b> explicitly (which arguably is a very minor implementation=
 detail), and you call
<b>Prop.Method()</b> in Python, it can fail with AttributeError despite the=
 documentation claiming this method exists on the interface. This is very c=
onfusing. IMHO, more confusing, than inability to get Length of an element =
of
<b>IShape[]</b>.</p>
<p class=3D"x_xmsonormal">&nbsp;</p>
<p class=3D"x_xmsonormal">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.</p>
<p class=3D"x_xmsonormal">&nbsp;</p>
<p class=3D"x_xmsonormal">Regardless, there=92s a workaround for the scenar=
io you described which is just doing
<b>m.GetShape(1).__raw_implementation__.Length </b>or </p>
<p class=3D"x_xmsonormal"><b>m.GetShape(1).__implementation__.Length </b>de=
pending if you need an encoded instance.</p>
<p class=3D"x_xmsonormal">&nbsp;</p>
<p class=3D"x_xmsonormal">Regards,</p>
<p class=3D"x_xmsonormal">Victor Milovanov</p>
<p class=3D"x_xmsonormal">&nbsp;</p>
<div style=3D"border:none; border-top:solid #E1E1E1 1.0pt; padding:3.0pt 0i=
n 0in 0in">
<p class=3D"x_xmsonormal"><b>From: </b><a href=3D"mailto:Mohamed.Koubaa@ans=
ys.com">Mohamed Koubaa</a><br>
<b>Sent: </b>Monday, November 2, 2020 6:15 PM<br>
<b>To: </b><a href=3D"mailto:[email protected]">[email protected]</a>=
<br>
<b>Subject: </b>[Python.NET] Wrap in interface (#1240)</p>
</div>
<p class=3D"x_xmsonormal">&nbsp;</p>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">Hel=
lo,</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">&nb=
sp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">Thi=
s <a href=3D"https://nam10.safelinks.protection.outlook.com/?url=3Dhttps%3A=
%2F%2Fgithub.com%2Fpythonnet%2Fpythonnet%2Fpull%2F1240&amp;data=3D04%7C01%7=
Cmohamed.koubaa%40ansys.com%7C4b457216f75446301a4508d8cc634c3b%7C34c6ce6715=
b84eff80e952da8be89706%7C0%7C0%7C637484074196952423%7CUnknown%7CTWFpbGZsb3d=
8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&=
amp;sdata=3DcAFUSSzeNl11sru%2B1ZNS8EjdoqhST%2FAGvY73dUQgV9U%3D&amp;reserved=
=3D0" originalsrc=3D"https://github.com/pythonnet/pythonnet/pull/1240" shas=
h=3D"g+OMDHa6fQBKO3G0Kt50iK94uCcZcWmnrUSPWrQKXrWRegJgPJMRnpMcVcJFhdaSG7IVD+=
vkH7fsWmy9JR6otCRCiVmQ/BBPhzAt4OComDtsJSFrf9k3Uf3K9bdVrixnu1KdLewjccx+0mEWx=
2ObgqBJYCcOhW3UB1Cb4YnEtz4=3D" title=3D"https://github.com/pythonnet/python=
net/pull/1240">
PR</a>&nbsp;changed behavior of pythonnet to use stricter typing when a met=
hod returns an interface.&nbsp; I'd like a way to opt-out of this behavior,=
 and I'll share a representative use-case below.&nbsp; The tl;dr is that py=
thon is a dynamic language and I expect the C#
 objects which pythonnet wraps to behave more like &quot;dynamic&quot; and =
less like the static types on the interface.</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">&nb=
sp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">***=
* C# code ****</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">namespace Example1</span><span style=3D"f=
ont-size:12.0pt; color:black">
</span></p>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">{</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; public enum ShapeType</span=
></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; {</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; Square,</span=
></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; Circle</span>=
</p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; public interface IShape</sp=
an></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; {</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; void Draw();<=
/span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">&nb=
sp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; double Area {=
 get; }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">&nb=
sp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; ShapeType Typ=
e { get; }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">&nb=
sp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; public class Square : IShap=
e</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; {</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; public double=
 Length { get; }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; public Square=
(double length)</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; {</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;=
 Length =3D length;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">&nb=
sp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; public void D=
raw() {}</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; public double=
 Area { get { return Length * Length; } }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; public ShapeT=
ype Type { get { return ShapeType.Square; } }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">&nb=
sp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; public class Circle : IShap=
e</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; {</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; public double=
 Radius { get; }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; public Circle=
(double radius)</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; {</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;=
 Radius =3D radius;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">&nb=
sp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; public void D=
raw() {}</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; public double=
 Area { get { return Math.PI * Radius * Radius; } }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; public ShapeT=
ype Type { get { return ShapeType.Circle; } }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">&nb=
sp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; public class ShapeDataModel=
</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; {</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; private Dicti=
onary&lt;int, IShape&gt; _shapes =3D new Dictionary&lt;int, IShape&gt;();</=
span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; private int _=
nextId =3D 1;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">&nb=
sp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; public int Ad=
dShape(IShape shape)</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; {</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;=
 int id =3D _nextId;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;=
 _shapes[id] =3D shape;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;=
 _nextId++;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;=
 return id;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">&nb=
sp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; public IShape=
 GetShape(int id)</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; {</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;=
 return _shapes[id];</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; &nbsp; &nbsp; }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">&nbsp; &nbsp; }</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black">}</span></p>
</div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">&nb=
sp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black">&nb=
sp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black; bac=
kground:#181A1B">**** Python code ****</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&gt;&gt;&gt; import c=
lr</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&gt;&gt;&gt; clr.AddR=
eference(&quot;Example1&quot;)</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&gt;&gt;&gt; import E=
xample1</span></p>
</div>
<div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&gt;&gt;&gt; sq1 =3D =
Example1.Square(2.)</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&gt;&gt;&gt; ci1 =3D =
Example1.Circle(1.3)</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&gt;&gt;&gt; sq2 =3D =
Example1.Square(2.5)</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&gt;&gt;&gt; m =3D Ex=
ample1.ShapeDataModel()</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&gt;&gt;&gt; m.AddSha=
pe(sq1)</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">1</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&gt;&gt;&gt; m.AddSha=
pe(sq2)</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">2</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&gt;&gt;&gt; m.AddSha=
pe(ci1)</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">3</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&gt;&gt;&gt; m.GetSha=
pe(2)</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&lt;Example1.IShape o=
bject at 0x000002A3448A09A0&gt;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&gt;&gt;&gt; m.GetSha=
pe(2).Area</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">6.25</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">&gt;&gt;&gt; m.GetSha=
pe(2).Length</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:black; background:#131516">Traceback (most recen=
t call last):</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:#ED5C57; background:#131516">&nbsp; File &quot;&=
lt;stdin&gt;&quot;, line 1, in &lt;module&gt;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; font-family:&quo=
t;Courier New&quot;; color:#ED5C57; background:#131516">AttributeError: 'IS=
hape' object has no attribute 'Length'</span></p>
</div>
</div>
<div>
<p class=3D"x_xmsonormal" style=3D"margin-bottom:12.0pt">&nbsp;</p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black; bac=
kground:#131516">**** summary ****</span></p>
</div>
<div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black; bac=
kground:#131516">This factory and/or datamodel pattern is very common in my=
 codebase and probably also lots of object oriented systems.&nbsp; The ment=
ioned PR breaks this common design pattern
 in C# objects wrapped by pythonnet, and I would like a way to opt-out of i=
t.&nbsp; Maybe an attribute on the method which returns an interface can be=
 used, or maybe something global to switch this behavior.&nbsp; I think per=
haps the original bug might also have been
 fixed in a different way.&nbsp;</span></p>
</div>
<div>
<p class=3D"x_xmsonormal" style=3D"margin-bottom:12.0pt">&nbsp;</p>
</div>
<p class=3D"x_xmsonormal"><span style=3D"font-size:12.0pt; color:black; bac=
kground:#131516">Thanks!<br>
Mohamed</span></p>
</div>
</div>
</div>
<p class=3D"x_xmsonormal">&nbsp;</p>
<p class=3D"x_MsoNormal">&nbsp;</p>
</div>
</div>
</div>
</body>
</html>

--_000_SN6PR01MB497662EB218106862672133C998F9SN6PR01MB4976prod_--

--_004_SN6PR01MB497662EB218106862672133C998F9SN6PR01MB4976prod_
Content-Type: image/png; name="C081996CDAA945DBACD618306EE5771E.png"
Content-Description: C081996CDAA945DBACD618306EE5771E.png
Content-Disposition: inline; filename="C081996CDAA945DBACD618306EE5771E.png";
	size=144; creation-date="Mon, 08 Feb 2021 18:56:59 GMT";
	modification-date="Mon, 08 Feb 2021 18:56:59 GMT"
Content-ID: <image001.png-CFHuyTl/[email protected]>
Content-Transfer-Encoding: base64

iVBORw0KGgoAAAANSUhEUgAAArYAAAACCAYAAACtx5+mAAAAAXNSR0IArs4c6QAAAARnQU1BAACx
jwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAlSURBVFhH7cMBDQAABAAw/UtJIIEw5GD/9gAA
gBeyelRV9faeBRsQNwwY9nKzAAAAAElFTkSuQmCC

--_004_SN6PR01MB497662EB218106862672133C998F9SN6PR01MB4976prod_--

--===============5566050003740487892==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
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]

--===============5566050003740487892==--