Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit8f6bdce

Browse files
committed
Provide hook to implement __repr__
Issue: 680
1 parentd3ca2e8 commit8f6bdce

File tree

8 files changed

+277
-0
lines changed

8 files changed

+277
-0
lines changed

‎AUTHORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
- Luke Stratman ([@lstratman](https://github.com/lstratman))
3737
- Konstantin Posudevskiy ([@konstantin-posudevskiy](https://github.com/konstantin-posudevskiy))
3838
- Matthias Dittrich ([@matthid](https://github.com/matthid))
39+
- Mohamed Koubaa ([@koubaa](https://github.com/koubaa))
3940
- Patrick Stewart ([@patstew](https://github.com/patstew))
4041
- Raphael Nestler ([@rnestler](https://github.com/rnestler))
4142
- Rickard Holmberg ([@rickardraysearch](https://github.com/rickardraysearch))

‎CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
2323
- Incorporated reference-style links to issues and pull requests in the CHANGELOG ([#608][i608])
2424
- Added detailed comments about aproaches and dangers to handle multi-app-domains ([#625][p625])
2525
- Python 3.7 support, builds and testing added. Defaults changed from Python 3.6 to 3.7 ([#698][p698])
26+
- Added support for C# types to provide`__repr__` ([#680][p680])
2627

2728
###Changed
2829

‎src/runtime/classbase.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,75 @@ public static IntPtr tp_str(IntPtr ob)
233233
}
234234
try
235235
{
236+
//As per python doc:
237+
//The return value must be a string object. If a class defines __repr__() but not __str__(),
238+
//then __repr__() is also used when an “informal” string representation of instances of that
239+
//class is required.
240+
//In C#, everything provides ToString(), so the check here will be whether the type explicitly
241+
//provides ToString() or if it is language provided (i.e. the fully qualified type name as a string)
242+
243+
//First check which type in the object hierarchy provides ToString()
244+
//ToString has two "official" overloads so loop over GetMethods to get the one without parameters
245+
varinstType=co.inst.GetType();
246+
foreach(varmethodininstType.GetMethods())
247+
{
248+
249+
//TODO this could probably be done more cleanly with Linq
250+
if(!method.IsPublic)continue;//skip private/protected methods
251+
if(method.Name!="ToString")continue;//only look for ToString
252+
if(method.DeclaringType==typeof(object))continue;//ignore default from object
253+
if(method.GetParameters().Length!=0)continue;//ignore Formatter overload of ToString
254+
255+
//match! something other than object provides a parameter-less overload of ToString
256+
returnRuntime.PyString_FromString(co.inst.ToString());
257+
}
258+
259+
//If the object defines __repr__, call it.
260+
System.Reflection.MethodInforeprMethodInfo=instType.GetMethod("__repr__");
261+
if(reprMethodInfo!=null&&reprMethodInfo.IsPublic)
262+
{
263+
varreprString=reprMethodInfo.Invoke(co.inst,null)asstring;
264+
returnRuntime.PyString_FromString(reprString);
265+
}
266+
267+
//otherwise fallback to object's ToString() implementation
236268
returnRuntime.PyString_FromString(co.inst.ToString());
269+
270+
}
271+
catch(Exceptione)
272+
{
273+
if(e.InnerException!=null)
274+
{
275+
e=e.InnerException;
276+
}
277+
Exceptions.SetError(e);
278+
returnIntPtr.Zero;
279+
}
280+
}
281+
282+
publicstaticIntPtrtp_repr(IntPtrob)
283+
{
284+
varco=GetManagedObject(ob)asCLRObject;
285+
if(co==null)
286+
{
287+
returnExceptions.RaiseTypeError("invalid object");
288+
}
289+
try
290+
{
291+
//if __repr__ is defined, use it
292+
varinstType=co.inst.GetType();
293+
System.Reflection.MethodInfomethodInfo=instType.GetMethod("__repr__");
294+
if(methodInfo!=null&&methodInfo.IsPublic)
295+
{
296+
varreprString=methodInfo.Invoke(co.inst,null)asstring;
297+
returnRuntime.PyString_FromString(reprString);
298+
}
299+
300+
//otherwise use the standard object.__repr__(inst)
301+
IntPtrargs=Runtime.PyTuple_New(1);
302+
Runtime.PyTuple_SetItem(args,0,ob);
303+
IntPtrreprFunc=Runtime.PyObject_GetAttrString(Runtime.PyBaseObjectType,"__repr__");
304+
returnRuntime.PyObject_Call(reprFunc,args,IntPtr.Zero);
237305
}
238306
catch(Exceptione)
239307
{

‎src/runtime/exceptions.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,29 @@ internal static Exception ToException(IntPtr ob)
3636
returne;
3737
}
3838

39+
/// <summary>
40+
/// Exception __repr__ implementation
41+
/// </summary>
42+
publicstaticIntPtrtp_repr(IntPtrob)
43+
{
44+
Exceptione=ToException(ob);
45+
if(e==null)
46+
{
47+
returnExceptions.RaiseTypeError("invalid object");
48+
}
49+
stringname=e.GetType().Name;
50+
stringmessage;
51+
if(e.Message!=String.Empty)
52+
{
53+
message=String.Format("{0}('{1}')",name,e.Message);
54+
}
55+
else
56+
{
57+
message=String.Format("{0}()",name);
58+
}
59+
returnRuntime.PyUnicode_FromString(message);
60+
}
61+
3962
/// <summary>
4063
/// Exception __str__ implementation
4164
/// </summary>

‎src/testing/Python.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
<CompileInclude="threadtest.cs" />
9292
<CompileInclude="doctest.cs" />
9393
<CompileInclude="subclasstest.cs" />
94+
<CompileInclude="reprtest.cs" />
9495
</ItemGroup>
9596
<ItemGroup>
9697
<ReferenceInclude="Microsoft.CSharp" />

‎src/testing/ReprTest.cs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
usingSystem;
2+
usingSystem.Text;
3+
4+
namespacePython.Test
5+
{
6+
/// <summary>
7+
/// Supports repr unit tests.
8+
/// </summary>
9+
publicclassReprTest
10+
{
11+
publicclassPoint
12+
{
13+
publicPoint(doublex,doubley)
14+
{
15+
X=x;
16+
Y=y;
17+
}
18+
19+
publicdoubleX{get;set;}
20+
publicdoubleY{get;set;}
21+
22+
publicoverridestringToString()
23+
{
24+
returnbase.ToString()+": X="+X.ToString()+", Y="+Y.ToString();
25+
}
26+
27+
publicstring__repr__()
28+
{
29+
return"Point("+X.ToString()+","+Y.ToString()+")";
30+
}
31+
}
32+
33+
publicclassFoo
34+
{
35+
publicstring__repr__()
36+
{
37+
return"I implement __repr__() but not ToString()!";
38+
}
39+
}
40+
41+
publicclassBar
42+
{
43+
publicoverridestringToString()
44+
{
45+
return"I implement ToString() but not __repr__()!";
46+
}
47+
}
48+
49+
publicclassBazBase
50+
{
51+
publicoverridestringToString()
52+
{
53+
return"Base class implementing ToString()!";
54+
}
55+
}
56+
57+
publicclassBazMiddle:BazBase
58+
{
59+
publicoverridestringToString()
60+
{
61+
return"Middle class implementing ToString()!";
62+
}
63+
}
64+
65+
//implements ToString via BazMiddle
66+
publicclassBaz:BazMiddle
67+
{
68+
69+
}
70+
71+
publicclassQuux
72+
{
73+
publicstringToString(stringformat)
74+
{
75+
return"I implement ToString() with an argument!";
76+
}
77+
}
78+
79+
publicclassQuuzBase
80+
{
81+
protectedstring__repr__()
82+
{
83+
return"I implement __repr__ but it isn't public!";
84+
}
85+
}
86+
87+
publicclassQuuz:QuuzBase
88+
{
89+
90+
}
91+
92+
publicclassCorge
93+
{
94+
publicstring__repr__(inti)
95+
{
96+
return"__repr__ implemention with input parameter!";
97+
}
98+
}
99+
100+
publicclassGrault
101+
{
102+
publicint__repr__()
103+
{
104+
return"__repr__ implemention with wrong return type!".Length;
105+
}
106+
}
107+
}
108+
}

‎src/tests/test_repr.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""Test __repr__ output"""
4+
5+
importSystem
6+
importpytest
7+
fromPython.TestimportReprTest
8+
9+
deftest_basic():
10+
"""Test Point class which implements both ToString and __repr__ without inheritance"""
11+
ob=ReprTest.Point(1,2)
12+
# point implements ToString() and __repr__()
13+
assertob.__repr__()=="Point(1,2)"
14+
assertstr(ob)=="Python.Test.ReprTest+Point: X=1, Y=2"
15+
16+
deftest_system_string():
17+
"""Test system string"""
18+
ob=System.String("hello")
19+
assertstr(ob)=="hello"
20+
assert"<System.String object at "inob.__repr__()
21+
22+
deftest_repr_only():
23+
"""Test class implementing __repr__() but not ToString()"""
24+
ob=ReprTest.Foo()
25+
assertstr(ob)=="I implement __repr__() but not ToString()!"
26+
assertob.__repr__()=="I implement __repr__() but not ToString()!"
27+
28+
deftest_str_only():
29+
"""Test class implementing ToString() but not __repr__()"""
30+
ob=ReprTest.Bar()
31+
assertstr(ob)=="I implement ToString() but not __repr__()!"
32+
assert"<Python.Test.Bar object at "inob.__repr__()
33+
34+
deftest_hierarchy1():
35+
"""Test inheritance heirarchy with base & middle class implementing ToString"""
36+
ob1=ReprTest.BazBase()
37+
assertstr(ob1)=="Base class implementing ToString()!"
38+
assert"<Python.Test.BazBase object at "inob1.__repr__()
39+
40+
ob2=ReprTest.BazMiddle()
41+
assertstr(ob2)=="Middle class implementing ToString()!"
42+
assert"<Python.Test.BazMiddle object at "inob2.__repr__()
43+
44+
ob3=ReprTest.Baz()
45+
assertstr(ob3)=="Middle class implementing ToString()!"
46+
assert"<Python.Test.Baz object at "inob3.__repr__()
47+
48+
defbad_tostring():
49+
"""Test ToString that can't be used by str()"""
50+
ob=ReprTest.Quux()
51+
assertstr(ob)=="Python.Test.ReprTest+Quux"
52+
assert"<Python.Test.Quux object at "inob.__repr__()
53+
54+
defbad_repr():
55+
"""Test incorrect implementation of repr"""
56+
ob1=ReprTest.QuuzBase()
57+
assertstr(ob1)=="Python.Test.ReprTest+QuuzBase"
58+
assert"<Python.Test.QuuzBase object at "inob.__repr__()
59+
60+
ob2=ReprTest.Quuz()
61+
assertstr(ob2)=="Python.Test.ReprTest+Quuz"
62+
assert"<Python.Test.Quuz object at "inob.__repr__()
63+
64+
ob3=ReprTest.Corge()
65+
withpytest.raises(Exception):
66+
str(ob3)
67+
withpytest.raises(Exception):
68+
ob3.__repr__()
69+
70+
ob4=ReprTest.Grault()
71+
withpytest.raises(Exception):
72+
str(ob4)
73+
withpytest.raises(Exception):
74+
ob4.__repr__()

‎src/tests/tests.pyproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
<CompileInclude="test_recursive_types.py" />
6060
<CompileInclude="test_subclass.py" />
6161
<CompileInclude="test_thread.py" />
62+
<CompileInclude="test_repr.py" />
6263
<CompileInclude="utils.py" />
6364
<CompileInclude="fixtures\argv-fixture.py" />
6465
</ItemGroup>

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp