Provide hook to implement __repr__ (#808)

Provide hook to implement __repr__
This commit is contained in:
Mohamed Koubaa 2019-10-18 03:07:57 -05:00 коммит произвёл Benedikt Reinartz
Родитель 60e6045f68
Коммит 4a9457fed5
9 изменённых файлов: 244 добавлений и 6 удалений

Просмотреть файл

@ -41,6 +41,7 @@
- Luke Stratman ([@lstratman](https://github.com/lstratman))
- Konstantin Posudevskiy ([@konstantin-posudevskiy](https://github.com/konstantin-posudevskiy))
- Matthias Dittrich ([@matthid](https://github.com/matthid))
- Mohamed Koubaa ([@koubaa](https://github.com/koubaa))
- Patrick Stewart ([@patstew](https://github.com/patstew))
- Raphael Nestler ([@rnestler](https://github.com/rnestler))
- Rickard Holmberg ([@rickardraysearch](https://github.com/rickardraysearch))

Просмотреть файл

@ -46,6 +46,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][].
- Added PyObject finalizer support, Python objects referred by C# can be auto collect now ([#692][p692]).
- Added detailed comments about aproaches and dangers to handle multi-app-domains ([#625][p625])
- Python 3.7 support, builds and testing added. Defaults changed from Python 3.6 to 3.7 ([#698][p698])
- Added support for C# types to provide `__repr__` ([#680][p680])
### Changed

Просмотреть файл

@ -246,6 +246,44 @@ namespace Python.Runtime
}
}
public static IntPtr tp_repr(IntPtr ob)
{
var co = GetManagedObject(ob) as CLRObject;
if (co == null)
{
return Exceptions.RaiseTypeError("invalid object");
}
try
{
//if __repr__ is defined, use it
var instType = co.inst.GetType();
System.Reflection.MethodInfo methodInfo = instType.GetMethod("__repr__");
if (methodInfo != null && methodInfo.IsPublic)
{
var reprString = methodInfo.Invoke(co.inst, null) as string;
return Runtime.PyString_FromString(reprString);
}
//otherwise use the standard object.__repr__(inst)
IntPtr args = Runtime.PyTuple_New(1);
Runtime.PyTuple_SetItem(args, 0, ob);
IntPtr reprFunc = Runtime.PyObject_GetAttrString(Runtime.PyBaseObjectType, "__repr__");
var output = Runtime.PyObject_Call(reprFunc, args, IntPtr.Zero);
Runtime.XDecref(args);
Runtime.XDecref(reprFunc);
return output;
}
catch (Exception e)
{
if (e.InnerException != null)
{
e = e.InnerException;
}
Exceptions.SetError(e);
return IntPtr.Zero;
}
}
/// <summary>
/// Standard dealloc implementation for instances of reflected types.

Просмотреть файл

@ -36,6 +36,29 @@ namespace Python.Runtime
return e;
}
/// <summary>
/// Exception __repr__ implementation
/// </summary>
public new static IntPtr tp_repr(IntPtr ob)
{
Exception e = ToException(ob);
if (e == null)
{
return Exceptions.RaiseTypeError("invalid object");
}
string name = e.GetType().Name;
string message;
if (e.Message != String.Empty)
{
message = String.Format("{0}('{1}')", name, e.Message);
}
else
{
message = String.Format("{0}()", name);
}
return Runtime.PyUnicode_FromString(message);
}
/// <summary>
/// Exception __str__ implementation
/// </summary>

Просмотреть файл

@ -91,6 +91,7 @@
<Compile Include="threadtest.cs" />
<Compile Include="doctest.cs" />
<Compile Include="subclasstest.cs" />
<Compile Include="ReprTest.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
@ -111,4 +112,4 @@
<Copy SourceFiles="$(TargetAssembly)" DestinationFolder="$(SolutionDir)\src\tests\fixtures" />
<!--Copy SourceFiles="$(TargetAssemblyPdb)" Condition="Exists('$(TargetAssemblyPdb)')" DestinationFolder="$(PythonBuildDir)" /-->
</Target>
</Project>
</Project>

108
src/testing/ReprTest.cs Normal file
Просмотреть файл

@ -0,0 +1,108 @@
using System;
using System.Text;
namespace Python.Test
{
/// <summary>
/// Supports repr unit tests.
/// </summary>
public class ReprTest
{
public class Point
{
public Point(double x, double y)
{
X = x;
Y = y;
}
public double X { get; set; }
public double Y { get; set; }
public override string ToString()
{
return base.ToString() + ": X=" + X.ToString() + ", Y=" + Y.ToString();
}
public string __repr__()
{
return "Point(" + X.ToString() + "," + Y.ToString() + ")";
}
}
public class Foo
{
public string __repr__()
{
return "I implement __repr__() but not ToString()!";
}
}
public class Bar
{
public override string ToString()
{
return "I implement ToString() but not __repr__()!";
}
}
public class BazBase
{
public override string ToString()
{
return "Base class implementing ToString()!";
}
}
public class BazMiddle : BazBase
{
public override string ToString()
{
return "Middle class implementing ToString()!";
}
}
//implements ToString via BazMiddle
public class Baz : BazMiddle
{
}
public class Quux
{
public string ToString(string format)
{
return "I implement ToString() with an argument!";
}
}
public class QuuzBase
{
protected string __repr__()
{
return "I implement __repr__ but it isn't public!";
}
}
public class Quuz : QuuzBase
{
}
public class Corge
{
public string __repr__(int i)
{
return "__repr__ implemention with input parameter!";
}
}
public class Grault
{
public int __repr__()
{
return "__repr__ implemention with wrong return type!".Length;
}
}
}
}

Просмотреть файл

@ -282,11 +282,8 @@ def test_python_compat_of_managed_exceptions():
assert e.args == (msg,)
assert isinstance(e.args, tuple)
if PY3:
strexp = "OverflowException('Simple message"
assert repr(e)[:len(strexp)] == strexp
elif PY2:
assert repr(e) == "OverflowException(u'Simple message',)"
strexp = "OverflowException('Simple message"
assert repr(e)[:len(strexp)] == strexp
def test_exception_is_instance_of_system_object():

68
src/tests/test_repr.py Normal file
Просмотреть файл

@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
"""Test __repr__ output"""
import System
import pytest
from Python.Test import ReprTest
def test_basic():
"""Test Point class which implements both ToString and __repr__ without inheritance"""
ob = ReprTest.Point(1,2)
# point implements ToString() and __repr__()
assert ob.__repr__() == "Point(1,2)"
assert str(ob) == "Python.Test.ReprTest+Point: X=1, Y=2"
def test_system_string():
"""Test system string"""
ob = System.String("hello")
assert str(ob) == "hello"
assert "<System.String object at " in ob.__repr__()
def test_str_only():
"""Test class implementing ToString() but not __repr__()"""
ob = ReprTest.Bar()
assert str(ob) == "I implement ToString() but not __repr__()!"
assert "<Python.Test.Bar object at " in ob.__repr__()
def test_hierarchy1():
"""Test inheritance heirarchy with base & middle class implementing ToString"""
ob1 = ReprTest.BazBase()
assert str(ob1) == "Base class implementing ToString()!"
assert "<Python.Test.BazBase object at " in ob1.__repr__()
ob2 = ReprTest.BazMiddle()
assert str(ob2) == "Middle class implementing ToString()!"
assert "<Python.Test.BazMiddle object at " in ob2.__repr__()
ob3 = ReprTest.Baz()
assert str(ob3) == "Middle class implementing ToString()!"
assert "<Python.Test.Baz object at " in ob3.__repr__()
def bad_tostring():
"""Test ToString that can't be used by str()"""
ob = ReprTest.Quux()
assert str(ob) == "Python.Test.ReprTest+Quux"
assert "<Python.Test.Quux object at " in ob.__repr__()
def bad_repr():
"""Test incorrect implementation of repr"""
ob1 = ReprTest.QuuzBase()
assert str(ob1) == "Python.Test.ReprTest+QuuzBase"
assert "<Python.Test.QuuzBase object at " in ob.__repr__()
ob2 = ReprTest.Quuz()
assert str(ob2) == "Python.Test.ReprTest+Quuz"
assert "<Python.Test.Quuz object at " in ob.__repr__()
ob3 = ReprTest.Corge()
with pytest.raises(Exception):
str(ob3)
with pytest.raises(Exception):
ob3.__repr__()
ob4 = ReprTest.Grault()
with pytest.raises(Exception):
str(ob4)
with pytest.raises(Exception):
ob4.__repr__()

Просмотреть файл

@ -59,6 +59,7 @@
<Compile Include="test_recursive_types.py" />
<Compile Include="test_subclass.py" />
<Compile Include="test_thread.py" />
<Compile Include="test_repr.py" />
<Compile Include="utils.py" />
<Compile Include="fixtures\argv-fixture.py" />
</ItemGroup>