| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 60e6045 commit 4a9457f
9 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -41,6 +41,7 @@ | |||
| 41 | 41 | - Luke Stratman ([@lstratman](https://github.com/lstratman)) | |
| 42 | 42 | - Konstantin Posudevskiy ([@konstantin-posudevskiy](https://github.com/konstantin-posudevskiy)) | |
| 43 | 43 | - Matthias Dittrich ([@matthid](https://github.com/matthid)) | |
| 44 | + - Mohamed Koubaa ([@koubaa](https://github.com/koubaa)) | ||
| 44 | 45 | - Patrick Stewart ([@patstew](https://github.com/patstew)) | |
| 45 | 46 | - Raphael Nestler ([@rnestler](https://github.com/rnestler)) | |
| 46 | 47 | - Rickard Holmberg ([@rickardraysearch](https://github.com/rickardraysearch)) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -46,6 +46,7 @@ This document follows the conventions laid out in [Keep a CHANGELOG][]. | |||
| 46 | 46 | - Added PyObject finalizer support, Python objects referred by C# can be auto collect now ([#692][p692]). | |
| 47 | 47 | - Added detailed comments about aproaches and dangers to handle multi-app-domains ([#625][p625]) | |
| 48 | 48 | - Python 3.7 support, builds and testing added. Defaults changed from Python 3.6 to 3.7 ([#698][p698]) | |
| 49 | + - Added support for C# types to provide `__repr__` ([#680][p680]) | ||
| 49 | 50 | ||
| 50 | 51 | ### Changed | |
| 51 | 52 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -246,6 +246,44 @@ public static IntPtr tp_str(IntPtr ob) | |||
| 246 | 246 | } | |
| 247 | 247 | } | |
| 248 | 248 | ||
| 249 | + public static IntPtr tp_repr(IntPtr ob) | ||
| 250 | + { | ||
| 251 | + var co = GetManagedObject(ob) as CLRObject; | ||
| 252 | + if (co == null) | ||
| 253 | + { | ||
| 254 | + return Exceptions.RaiseTypeError("invalid object"); | ||
| 255 | + } | ||
| 256 | + try | ||
| 257 | + { | ||
| 258 | + //if __repr__ is defined, use it | ||
| 259 | + var instType = co.inst.GetType(); | ||
| 260 | + System.Reflection.MethodInfo methodInfo = instType.GetMethod("__repr__"); | ||
| 261 | + if (methodInfo != null && methodInfo.IsPublic) | ||
| 262 | + { | ||
| 263 | + var reprString = methodInfo.Invoke(co.inst, null) as string; | ||
| 264 | + return Runtime.PyString_FromString(reprString); | ||
| 265 | + } | ||
| 266 | + | ||
| 267 | + //otherwise use the standard object.__repr__(inst) | ||
| 268 | + IntPtr args = Runtime.PyTuple_New(1); | ||
| 269 | + Runtime.PyTuple_SetItem(args, 0, ob); | ||
| 270 | + IntPtr reprFunc = Runtime.PyObject_GetAttrString(Runtime.PyBaseObjectType, "__repr__"); | ||
| 271 | + var output = Runtime.PyObject_Call(reprFunc, args, IntPtr.Zero); | ||
| 272 | + Runtime.XDecref(args); | ||
| 273 | + Runtime.XDecref(reprFunc); | ||
| 274 | + return output; | ||
| 275 | + } | ||
| 276 | + catch (Exception e) | ||
| 277 | + { | ||
| 278 | + if (e.InnerException != null) | ||
| 279 | + { | ||
| 280 | + e = e.InnerException; | ||
| 281 | + } | ||
| 282 | + Exceptions.SetError(e); | ||
| 283 | + return IntPtr.Zero; | ||
| 284 | + } | ||
| 285 | + } | ||
| 286 | + | ||
| 249 | 287 | ||
| 250 | 288 | /// <summary> | |
| 251 | 289 | /// Standard dealloc implementation for instances of reflected types. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -36,6 +36,29 @@ internal static Exception ToException(IntPtr ob) | |||
| 36 | 36 | return e; | |
| 37 | 37 | } | |
| 38 | 38 | ||
| 39 | + /// <summary> | ||
| 40 | + /// Exception __repr__ implementation | ||
| 41 | + /// </summary> | ||
| 42 | + public new static IntPtr tp_repr(IntPtr ob) | ||
| 43 | + { | ||
| 44 | + Exception e = ToException(ob); | ||
| 45 | + if (e == null) | ||
| 46 | + { | ||
| 47 | + return Exceptions.RaiseTypeError("invalid object"); | ||
| 48 | + } | ||
| 49 | + string name = e.GetType().Name; | ||
| 50 | + string message; | ||
| 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 | + return Runtime.PyUnicode_FromString(message); | ||
| 60 | + } | ||
| 61 | + | ||
| 39 | 62 | /// <summary> | |
| 40 | 63 | /// Exception __str__ implementation | |
| 41 | 64 | /// </summary> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -91,6 +91,7 @@ | |||
| 91 | 91 | <Compile Include="threadtest.cs" /> | |
| 92 | 92 | <Compile Include="doctest.cs" /> | |
| 93 | 93 | <Compile Include="subclasstest.cs" /> | |
| 94 | + <Compile Include="ReprTest.cs" /> | ||
| 94 | 95 | </ItemGroup> | |
| 95 | 96 | <ItemGroup> | |
| 96 | 97 | <Reference Include="Microsoft.CSharp" /> | |
@@ -111,4 +112,4 @@ | |||
| 111 | 112 | <Copy SourceFiles="$(TargetAssembly)" DestinationFolder="$(SolutionDir)\src\tests\fixtures" /> | |
| 112 | 113 | <!--Copy SourceFiles="$(TargetAssemblyPdb)" Condition="Exists('$(TargetAssemblyPdb)')" DestinationFolder="$(PythonBuildDir)" /--> | |
| 113 | 114 | </Target> | |
| 114 | - </Project> | ||
| 115 | + </Project> | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,108 @@ | |||
| 1 | + using System; | ||
| 2 | + using System.Text; | ||
| 3 | + | ||
| 4 | + namespace Python.Test | ||
| 5 | + { | ||
| 6 | + /// <summary> | ||
| 7 | + /// Supports repr unit tests. | ||
| 8 | + /// </summary> | ||
| 9 | + public class ReprTest | ||
| 10 | + { | ||
| 11 | + public class Point | ||
| 12 | + { | ||
| 13 | + public Point(double x, double y) | ||
| 14 | + { | ||
| 15 | + X = x; | ||
| 16 | + Y = y; | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + public double X { get; set; } | ||
| 20 | + public double Y { get; set; } | ||
| 21 | + | ||
| 22 | + public override string ToString() | ||
| 23 | + { | ||
| 24 | + return base.ToString() + ": X=" + X.ToString() + ", Y=" + Y.ToString(); | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + public string __repr__() | ||
| 28 | + { | ||
| 29 | + return "Point(" + X.ToString() + "," + Y.ToString() + ")"; | ||
| 30 | + } | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + public class Foo | ||
| 34 | + { | ||
| 35 | + public string __repr__() | ||
| 36 | + { | ||
| 37 | + return "I implement __repr__() but not ToString()!"; | ||
| 38 | + } | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + public class Bar | ||
| 42 | + { | ||
| 43 | + public override string ToString() | ||
| 44 | + { | ||
| 45 | + return "I implement ToString() but not __repr__()!"; | ||
| 46 | + } | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + public class BazBase | ||
| 50 | + { | ||
| 51 | + public override string ToString() | ||
| 52 | + { | ||
| 53 | + return "Base class implementing ToString()!"; | ||
| 54 | + } | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + public class BazMiddle : BazBase | ||
| 58 | + { | ||
| 59 | + public override string ToString() | ||
| 60 | + { | ||
| 61 | + return "Middle class implementing ToString()!"; | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + //implements ToString via BazMiddle | ||
| 66 | + public class Baz : BazMiddle | ||
| 67 | + { | ||
| 68 | + | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + public class Quux | ||
| 72 | + { | ||
| 73 | + public string ToString(string format) | ||
| 74 | + { | ||
| 75 | + return "I implement ToString() with an argument!"; | ||
| 76 | + } | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + public class QuuzBase | ||
| 80 | + { | ||
| 81 | + protected string __repr__() | ||
| 82 | + { | ||
| 83 | + return "I implement __repr__ but it isn't public!"; | ||
| 84 | + } | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + public class Quuz : QuuzBase | ||
| 88 | + { | ||
| 89 | + | ||
| 90 | + } | ||
| 91 | + | ||
| 92 | + public class Corge | ||
| 93 | + { | ||
| 94 | + public string __repr__(int i) | ||
| 95 | + { | ||
| 96 | + return "__repr__ implemention with input parameter!"; | ||
| 97 | + } | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + public class Grault | ||
| 101 | + { | ||
| 102 | + public int __repr__() | ||
| 103 | + { | ||
| 104 | + return "__repr__ implemention with wrong return type!".Length; | ||
| 105 | + } | ||
| 106 | + } | ||
| 107 | + } | ||
| 108 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -282,11 +282,8 @@ def test_python_compat_of_managed_exceptions(): | |||
| 282 | 282 | ||
| 283 | 283 | assert e.args == (msg,) | |
| 284 | 284 | assert isinstance(e.args, tuple) | |
| 285 | - if PY3: | ||
| 286 | - strexp = "OverflowException('Simple message" | ||
| 287 | - assert repr(e)[:len(strexp)] == strexp | ||
| 288 | - elif PY2: | ||
| 289 | - assert repr(e) == "OverflowException(u'Simple message',)" | ||
| 285 | + strexp = "OverflowException('Simple message" | ||
| 286 | + assert repr(e)[:len(strexp)] == strexp | ||
| 290 | 287 | ||
| 291 | 288 | ||
| 292 | 289 | def test_exception_is_instance_of_system_object(): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,68 @@ | |||
| 1 | + # -*- coding: utf-8 -*- | ||
| 2 | + | ||
| 3 | + """Test __repr__ output""" | ||
| 4 | + | ||
| 5 | + import System | ||
| 6 | + import pytest | ||
| 7 | + from Python.Test import ReprTest | ||
| 8 | + | ||
| 9 | + def test_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 | + assert ob.__repr__() == "Point(1,2)" | ||
| 14 | + assert str(ob) == "Python.Test.ReprTest+Point: X=1, Y=2" | ||
| 15 | + | ||
| 16 | + def test_system_string(): | ||
| 17 | + """Test system string""" | ||
| 18 | + ob = System.String("hello") | ||
| 19 | + assert str(ob) == "hello" | ||
| 20 | + assert "<System.String object at " in ob.__repr__() | ||
| 21 | + | ||
| 22 | + def test_str_only(): | ||
| 23 | + """Test class implementing ToString() but not __repr__()""" | ||
| 24 | + ob = ReprTest.Bar() | ||
| 25 | + assert str(ob) == "I implement ToString() but not __repr__()!" | ||
| 26 | + assert "<Python.Test.Bar object at " in ob.__repr__() | ||
| 27 | + | ||
| 28 | + def test_hierarchy1(): | ||
| 29 | + """Test inheritance heirarchy with base & middle class implementing ToString""" | ||
| 30 | + ob1 = ReprTest.BazBase() | ||
| 31 | + assert str(ob1) == "Base class implementing ToString()!" | ||
| 32 | + assert "<Python.Test.BazBase object at " in ob1.__repr__() | ||
| 33 | + | ||
| 34 | + ob2 = ReprTest.BazMiddle() | ||
| 35 | + assert str(ob2) == "Middle class implementing ToString()!" | ||
| 36 | + assert "<Python.Test.BazMiddle object at " in ob2.__repr__() | ||
| 37 | + | ||
| 38 | + ob3 = ReprTest.Baz() | ||
| 39 | + assert str(ob3) == "Middle class implementing ToString()!" | ||
| 40 | + assert "<Python.Test.Baz object at " in ob3.__repr__() | ||
| 41 | + | ||
| 42 | + def bad_tostring(): | ||
| 43 | + """Test ToString that can't be used by str()""" | ||
| 44 | + ob = ReprTest.Quux() | ||
| 45 | + assert str(ob) == "Python.Test.ReprTest+Quux" | ||
| 46 | + assert "<Python.Test.Quux object at " in ob.__repr__() | ||
| 47 | + | ||
| 48 | + def bad_repr(): | ||
| 49 | + """Test incorrect implementation of repr""" | ||
| 50 | + ob1 = ReprTest.QuuzBase() | ||
| 51 | + assert str(ob1) == "Python.Test.ReprTest+QuuzBase" | ||
| 52 | + assert "<Python.Test.QuuzBase object at " in ob.__repr__() | ||
| 53 | + | ||
| 54 | + ob2 = ReprTest.Quuz() | ||
| 55 | + assert str(ob2) == "Python.Test.ReprTest+Quuz" | ||
| 56 | + assert "<Python.Test.Quuz object at " in ob.__repr__() | ||
| 57 | + | ||
| 58 | + ob3 = ReprTest.Corge() | ||
| 59 | + with pytest.raises(Exception): | ||
| 60 | + str(ob3) | ||
| 61 | + with pytest.raises(Exception): | ||
| 62 | + ob3.__repr__() | ||
| 63 | + | ||
| 64 | + ob4 = ReprTest.Grault() | ||
| 65 | + with pytest.raises(Exception): | ||
| 66 | + str(ob4) | ||
| 67 | + with pytest.raises(Exception): | ||
| 68 | + ob4.__repr__() | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -59,6 +59,7 @@ | |||
| 59 | 59 | <Compile Include="test_recursive_types.py" /> | |
| 60 | 60 | <Compile Include="test_subclass.py" /> | |
| 61 | 61 | <Compile Include="test_thread.py" /> | |
| 62 | + <Compile Include="test_repr.py" /> | ||
| 62 | 63 | <Compile Include="utils.py" /> | |
| 63 | 64 | <Compile Include="fixtures\argv-fixture.py" /> | |
| 64 | 65 | </ItemGroup> | |
| Back | FazBrowse Home | New Git URL |
0 commit comments