1 module pynih.python.conv; 2 3 4 import unit_threaded; 5 import python.conv; 6 import std.datetime: Date, DateTime; 7 import std.typecons: tuple; 8 9 10 struct IntString { 11 int i; 12 string s; 13 } 14 15 16 // helper function to test that converting to python and back yields the same value 17 private void backAndForth(T) 18 (T value, in string file = __FILE__, in size_t line = __LINE__) 19 { 20 value.toPython.to!(typeof(value)).shouldEqual(value, file, line); 21 } 22 23 24 @Types!( 25 bool, 26 byte, ubyte, short, ushort, int, uint, long, ulong, // integral 27 float, double, 28 ) 29 void testBackAndForthNoGc(T)() 30 { 31 check!((T d) @nogc => d.toPython.to!T == d); 32 } 33 34 35 @Types!( 36 int[], double[], 37 ) 38 void testBackAndForthGc(T)() 39 { 40 check!((T d) => d.toPython.to!T == d); 41 } 42 43 44 @("DateTime") 45 unittest { 46 backAndForth(DateTime(2018, 1, 2, 3, 4, 5)); 47 } 48 49 50 @("Date") 51 unittest { 52 backAndForth(Date(2018, 1, 2)); 53 } 54 55 56 @Values("foobar", "quux") 57 @("string.ascii") 58 unittest { 59 const value = getValue!string; 60 backAndForth(value); 61 } 62 63 64 @ShouldFail 65 @Values("café", "açaí") 66 @("string.unicode") 67 unittest { 68 const value = getValue!string; 69 backAndForth(value); 70 } 71 72 @("stringz") 73 unittest { 74 import std.string: toStringz, fromStringz; 75 const value = "foobar".toStringz; 76 value.toPython.to!(typeof(value)).fromStringz.should == "foobar"; 77 } 78 79 @("array.static.int") 80 unittest { 81 int[2] value = [1, 2]; 82 backAndForth(value); 83 } 84 85 86 @("array.static.double") 87 unittest { 88 double[3] value = [11.1, 22.2, 33.3]; 89 backAndForth(value); 90 } 91 92 93 @("array.static.immutable(int)") 94 unittest { 95 immutable(int)[3] ints = [1, 2, 3]; 96 backAndForth(ints); 97 } 98 99 100 @("array.slice.char") 101 unittest { 102 auto chars = ['a', 'b', 'c']; 103 backAndForth(chars); 104 } 105 106 107 @("array.slice.immutable(int)") 108 unittest { 109 immutable(int)[] ints = [1, 2, 3]; 110 backAndForth(ints); 111 } 112 113 @("aa.int.string") 114 unittest { 115 backAndForth([1: "foo", 2: "bar"]); 116 } 117 118 119 @("aa.string.int") 120 unittest { 121 backAndForth(["foo": 1, "bar": 2]); 122 } 123 124 125 @("tuple.int.int.int") 126 unittest { 127 backAndForth(tuple(3, 5, 7)); 128 } 129 130 131 @("tuple.int.string") 132 unittest { 133 backAndForth(tuple(42, "foo")); 134 } 135 136 137 @("udt.intstring.val") 138 unittest { 139 backAndForth(IntString(42, "quux")); 140 } 141 142 @("udt.intstring.ptr") 143 unittest { 144 const value = new IntString(42, "quux"); 145 const back = value.toPython.to!(typeof(value)); 146 (*back).should == *value; 147 } 148 149 150 @("udt.class") 151 unittest { 152 153 static class Class { 154 int i; 155 this(int i) { this.i = i; } 156 int twice() @safe @nogc pure nothrow const { return i * 2; } 157 override string toString() @safe pure const { 158 import std.conv: text; 159 return text("Class(", i, ")"); 160 } 161 } 162 163 backAndForth(new Class(42)); 164 } 165 166 167 @("udt.class.baseref") 168 unittest { 169 import std.conv: text; 170 import std.string: fromStringz; 171 172 static class Base { 173 int i; 174 this() {} 175 this(int i) { this.i = i; } 176 int twice() @safe @nogc pure nothrow const { return i * 2; } 177 override string toString() @safe pure const { 178 import std.conv: text; 179 return text("Base(", i, ")"); 180 } 181 } 182 183 static class Child: Base { 184 this() {} 185 this(int i) { super(i); } 186 override string toString() @safe pure const { 187 import std.conv: text; 188 return text("Child(", i, ")"); 189 } 190 } 191 192 193 auto child = new Child(42); 194 child.toString.should == "Child(42)"; 195 auto python = child.toPython; 196 auto back = python.to!Base; 197 assert(typeid(back) == typeid(Child), text("Expected Child but got ", typeid(back))); 198 back.toString.should == "Child(42)"; 199 } 200 201 202 @("udt.struct.ptr.uncopiable") 203 unittest { 204 static struct Uncopiable { 205 @disable this(this); 206 int i; 207 } 208 const value = new Uncopiable(42); 209 const back = value.toPython.to!(typeof(value)); 210 } 211 212 213 @("udt.struct.char") 214 unittest { 215 static struct Char { 216 char c; 217 } 218 backAndForth(Char('a')); 219 } 220 221 222 @("udt.struct.charptr") 223 unittest { 224 static struct CharPtr { 225 char* ptr; 226 bool opEquals(in CharPtr other) @safe @nogc pure nothrow const { 227 if(ptr is null && other.ptr is null) return true; 228 if(ptr is null && other.ptr !is null) return false; 229 if(ptr !is null && other.ptr is null) return false; 230 return *ptr == *other.ptr; 231 } 232 } 233 backAndForth(CharPtr(new char('a'))); 234 } 235 236 237 // Similar to issue with std.bitmanip.BitArray. 238 // The presence of `opCast` makes the conversion to `const(T)` fail 239 // since there's no available cast to `const(T)`. 240 @("udt.struct.opCast") 241 unittest { 242 static struct Struct { 243 244 void[] opCast(T: void[])() @safe @nogc pure nothrow { 245 return null; 246 } 247 248 size_t[] opCast(T: size_t[])() @safe @nogc pure nothrow { 249 return null; 250 } 251 } 252 253 const s = Struct(); 254 backAndForth(s); 255 } 256 257 258 @ShouldFail("D function pointers not yet supported") 259 @("function") 260 unittest { 261 static string fun(int i, double d) { 262 import std.conv: text; 263 return text("i: ", i, " d: ", d); 264 } 265 const back = (&fun).toPython.to!(typeof(&fun)); 266 } 267 268 269 @("Duration") 270 unittest { 271 import core.time: seconds; 272 backAndForth(1.seconds); 273 } 274 275 276 @("enum.int") 277 unittest { 278 static enum Enum { 279 foo, 280 bar, 281 baz, 282 } 283 284 backAndForth(Enum.bar); 285 } 286 287 288 @("enum.char") 289 unittest { 290 static enum Char: char { 291 a = 'a', 292 b = 'b', 293 } 294 295 backAndForth(Char.b); 296 } 297 298 299 @("dchar") 300 unittest { 301 const str = "foobar"d; 302 backAndForth(str[0]); 303 } 304 305 306 @("range.infinite") 307 unittest { 308 309 import std.range.primitives: isForwardRange; 310 311 // infinite range 312 static struct FortyTwos { 313 int i; 314 enum empty = false; 315 int front() { return 42; } 316 void popFront() {} 317 auto save() { return this; } 318 string toString() { 319 import std.conv: text; 320 return i.text; 321 } 322 } 323 324 static assert(isForwardRange!FortyTwos); 325 auto py = FortyTwos(77).toPython; 326 auto d = py.to!FortyTwos; 327 d.i.should == 77; 328 } 329 330 331 @("exception.msg") 332 unittest { 333 static class MyException: Exception { 334 // These members make no sense, but that's what 335 // std.xml.CheckException does 336 this(string msg) { super(msg); } 337 string msg; 338 size_t line; 339 } 340 341 // FIXME - cannot deal with repeated field names 342 // auto py = (new MyException("oops")).toPython; 343 }