Provide hook to implement __repr__ (#808)
Provide hook to implement __repr__
This commit is contained in:
Родитель
60e6045f68
Коммит
4a9457fed5
|
@ -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>
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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>
|
||||
|
|
Загрузка…
Ссылка в новой задаче