From 4f0b2df49333c601a552cb7100bd2e388c6fbe4d Mon Sep 17 00:00:00 2001 From: Christopher Doris Date: Mon, 6 Nov 2023 18:06:01 +0000 Subject: [PATCH] refactor tests to match refactored modules --- test/{abstract.jl => Py.jl} | 390 ++++++++++++++++++++++++++++++ test/compat.jl | 297 ----------------------- test/concrete.jl | 346 -------------------------- test/{convert.jl => pyconvert.jl} | 0 test/pymacro.jl | 253 +++++++++++++++++++ 5 files changed, 643 insertions(+), 643 deletions(-) rename test/{abstract.jl => Py.jl} (51%) delete mode 100644 test/concrete.jl rename test/{convert.jl => pyconvert.jl} (100%) create mode 100644 test/pymacro.jl diff --git a/test/abstract.jl b/test/Py.jl similarity index 51% rename from test/abstract.jl rename to test/Py.jl index 1e36e6e7..9e06125c 100644 --- a/test/abstract.jl +++ b/test/Py.jl @@ -402,3 +402,393 @@ end @test pyeq(Bool, pybuiltins.eval(ans, pydict()), 7) end end + +@testitem "import" begin + sys = pyimport("sys") + os = pyimport("os") + @test pyeq(Bool, sys.__name__, "sys") + @test pyeq(Bool, os.__name__, "os") + sysos = pyimport("sys", "os") + @test sysos isa Tuple{Py, Py} + @test pyis(sysos[1], sys) + @test pyis(sysos[2], os) + ver = pyimport("sys" => "version") + @test pyis(ver, sys.version) + path = pyimport("sys" => "path") + @test pyis(path, sys.path) + verpath = pyimport("sys" => ("version", "path")) + @test verpath isa Tuple{Py, Py} + @test pyis(verpath[1], ver) + @test pyis(verpath[2], path) +end + +@testitem "consts" begin + @test pybuiltins.None isa Py + @test pystr(String, pybuiltins.None) == "None" +end + +@testitem "str" begin + @test pyisinstance(pystr("foo"), pybuiltins.str) + @test pyeq(Bool, pystr(pystr("foo")), pystr("foo")) + @test pyeq(Bool, pystr(SubString("foobarbaz", 4:6)), pystr("bar")) + @test pyeq(Bool, pystr('x'), pystr("x")) + @test pystr(String, pybuiltins.None) === "None" + @test pystr(String, pyint(123)) === "123" + @test pystr(String, pystr("foo")) === "foo" +end + +@testitem "bytes" begin + @test pyisinstance(pybytes(UInt8[1,2,3]), pybuiltins.bytes) + @test pyeq(Bool, pybytes(pylist([1,2,3])), pybytes(UInt8[1,2,3])) + @test pyeq(Bool, pybytes(b"foo"), pystr("foo").encode("ascii")) + @test pyeq(Bool, pybytes(codeunits(SubString("foobarbaz", 4:6))), pystr("bar").encode("ascii")) + @test pybytes(Vector, pylist([1,2,3])) == UInt8[1,2,3] + @test pybytes(Vector{UInt8}, pylist([1,2,3])) == UInt8[1,2,3] + @test pybytes(Base.CodeUnits, pystr("foo").encode("ascii")) == b"foo" + @test pybytes(Base.CodeUnits{UInt8,String}, pystr("bar").encode("ascii")) == b"bar" +end + +@testitem "tuple" begin + z = pytuple() + @test pyisinstance(z, pybuiltins.tuple) + @test pylen(z) == 0 + x = pytuple((1,2,3)) + @test pyisinstance(x, pybuiltins.tuple) + @test pylen(x) == 3 + @test pyeq(Bool, pygetitem(x, 0), 1) + @test pyeq(Bool, pygetitem(x, 1), 2) + @test pyeq(Bool, pygetitem(x, 2), 3) + @test pyeq(Bool, pytuple([1,2,3]), x) + @test pyeq(Bool, pytuple(i+1 for i in 0:10 if i<3), x) + @test pyeq(Bool, pytuple(pytuple((1,2,3))), x) + @test pyeq(Bool, pytuple(pylist([1,2,3])), x) +end + +@testitem "list" begin + z = pylist() + @test pyisinstance(z, pybuiltins.list) + @test pylen(z) == 0 + x = pylist((1,2,3)) + @test pyisinstance(x, pybuiltins.list) + @test pylen(x) == 3 + @test pyeq(Bool, pygetitem(x, 0), 1) + @test pyeq(Bool, pygetitem(x, 1), 2) + @test pyeq(Bool, pygetitem(x, 2), 3) + @test pyeq(Bool, pylist([1,2,3]), x) + @test pyeq(Bool, pylist(i+1 for i in 0:10 if i<3), x) + @test pyeq(Bool, pylist(pylist((1,2,3))), x) + @test pyeq(Bool, pylist(pytuple([1,2,3])), x) + @test pyeq(Bool, pycollist([1,2,3]), pylist([1,2,3])) + @test pyeq(Bool, pycollist([1 2; 3 4]), pylist((pylist([1,3]), pylist([2,4])))) + @test pyeq(Bool, pyrowlist([1,2,3]), pylist([1,2,3])) + @test pyeq(Bool, pyrowlist([1 2; 3 4]), pylist((pylist([1,2]), pylist([3,4])))) +end + +@testitem "dict" begin + z = pydict() + @test pyisinstance(z, pybuiltins.dict) + @test pylen(z) == 0 + x = pydict(foo=1, bar=2) + @test pyisinstance(x, pybuiltins.dict) + @test pylen(x) == 2 + @test pyeq(Bool, pygetitem(x, "foo"), 1) + @test pyeq(Bool, pygetitem(x, "bar"), 2) + @test pyeq(Bool, pydict(["foo"=>1, "bar"=>2]), x) + @test pyeq(Bool, pydict([("foo"=>1), ("bar"=>2)]), x) + @test pyeq(Bool, pydict(Dict("foo"=>1, "bar"=>2)), x) + @test pyeq(Bool, pydict((foo=1, bar=2)), x) + @test pyeq(Bool, pydict(x), x) +end + +@testitem "bool" begin + @test pyis(pybool(), pybuiltins.False) + @test pyis(pybool(false), pybuiltins.False) + @test pyis(pybool(true), pybuiltins.True) + @test pyis(pybool(0.0), pybuiltins.False) + @test pyis(pybool(-1.2), pybuiltins.True) + @test pyis(pybool(pybuiltins.None), pybuiltins.False) + @test pyis(pybool(pylist()), pybuiltins.False) + @test pyis(pybool(pylist([1,2,3])), pybuiltins.True) +end + +@testitem "int" begin + @test pyisinstance(pyint(), pybuiltins.int) + @test pystr(String, pyint()) == "0" + x = 123 + y = pyint(x) + @test pyisinstance(y, pybuiltins.int) + @test pystr(String, y) == string(x) + x = BigInt(123) << 200 + y = pyint(x) + @test pyisinstance(y, pybuiltins.int) + @test pystr(String, y) == string(x) + x = UInt(123) + y = pyint(x) + @test pyisinstance(y, pybuiltins.int) + @test pystr(String, y) == string(x) + x = UInt128(123) << 100 + y = pyint(x) + @test pyisinstance(y, pybuiltins.int) + @test pystr(String, y) == string(x) + @test pyeq(Bool, pyint(pyint(123)), pyint(123)) + @test pyeq(Bool, pyint(pyfloat(12.3)), pyint(12)) +end + +@testitem "float" begin + y = pyfloat() + @test pyisinstance(y, pybuiltins.float) + @test pyeq(Bool, y, pyint(0)) + x = 123 + y = pyfloat(x) + @test pyisinstance(y, pybuiltins.float) + @test pyeq(Bool, y, pyint(x)) + x = 0.25 + y = pyfloat(x) + @test pyisinstance(y, pybuiltins.float) + @test pyeq(Bool, y, pytruediv(1, 4)) + x = 1//4 + y = pyfloat(x) + @test pyisinstance(y, pybuiltins.float) + @test pyeq(Bool, y, pyfloat(float(x))) + @test pyeq(Bool, pyfloat(pyfloat(12.3)), pyfloat(12.3)) + @test pyeq(Bool, pyfloat(pyint(123)), pyfloat(123)) +end + +@testitem "complex" begin + y = pycomplex() + @test pyisinstance(y, pybuiltins.complex) + @test pyeq(Bool, y, pyint(0)) + x = 12.3 + y = pycomplex(x) + @test pyisinstance(y, pybuiltins.complex) + @test pyeq(Bool, y, pyfloat(x)) + xr, xi = 12, 34 + y = pycomplex(xr, xi) + @test pyisinstance(y, pybuiltins.complex) + @test pyeq(Bool, y.real, pyfloat(xr)) + @test pyeq(Bool, y.imag, pyfloat(xi)) + x = Complex(12, 34) + y = pycomplex(x) + @test pyisinstance(y, pybuiltins.complex) + @test pyeq(Bool, y.real, pyfloat(real(x))) + @test pyeq(Bool, y.imag, pyfloat(imag(x))) + @test pyeq(Bool, pycomplex(y), y) + @test pyeq(Bool, pycomplex(pyint(12), pyint(34)), y) +end + +@testitem "set" begin + y = pyset() + yf = pyfrozenset() + @test pyisinstance(y, pybuiltins.set) + @test pylen(y) == 0 + @test pyisinstance(yf, pybuiltins.frozenset) + @test pylen(yf) == 0 + @test pyeq(Bool, y, yf) + x = [1,2,3,2,1] + y = pyset(x) + yf = pyfrozenset(x) + @test pyisinstance(y, pybuiltins.set) + @test pylen(y) == 3 + @test pycontains(y, 1) + @test pycontains(y, 2) + @test pycontains(y, 3) + @test pyeq(Bool, pyset(y), y) + @test pyisinstance(yf, pybuiltins.frozenset) + @test pylen(yf) == 3 + @test pycontains(yf, 1) + @test pycontains(yf, 2) + @test pycontains(yf, 3) + @test pyeq(Bool, pyfrozenset(y), y) + @test pyeq(Bool, y, yf) +end + +@testitem "slice" begin + x = pyslice(12) + @test pyisinstance(x, pybuiltins.slice) + @test pyeq(Bool, x.start, pybuiltins.None) + @test pyeq(Bool, x.stop, 12) + @test pyeq(Bool, x.step, pybuiltins.None) + x = pyslice(12, 34) + @test pyisinstance(x, pybuiltins.slice) + @test pyeq(Bool, x.start, 12) + @test pyeq(Bool, x.stop, 34) + @test pyeq(Bool, x.step, pybuiltins.None) + x = pyslice(12, 34, 56) + @test pyisinstance(x, pybuiltins.slice) + @test pyeq(Bool, x.start, 12) + @test pyeq(Bool, x.stop, 34) + @test pyeq(Bool, x.step, 56) +end + +@testitem "range" begin + x = pyrange(123) + @test pyisinstance(x, pybuiltins.range) + @test pyeq(Bool, x.start, 0) + @test pyeq(Bool, x.stop, 123) + @test pyeq(Bool, x.step, 1) + x = pyrange(12, 123) + @test pyisinstance(x, pybuiltins.range) + @test pyeq(Bool, x.start, 12) + @test pyeq(Bool, x.stop, 123) + @test pyeq(Bool, x.step, 1) + x = pyrange(12, 123, 3) + @test pyisinstance(x, pybuiltins.range) + @test pyeq(Bool, x.start, 12) + @test pyeq(Bool, x.stop, 123) + @test pyeq(Bool, x.step, 3) +end + +@testitem "none" begin + # TODO +end + +@testitem "type" begin + x = pytype(pyint()) + @test pyisinstance(x, pybuiltins.type) + @test pyis(x, pybuiltins.int) + x = pytype(pybuiltins.type) + @test pyisinstance(x, pybuiltins.type) + @test pyis(x, pybuiltins.type) + x = pytype("Foo", (), ["foo"=>1, "bar"=>2]) + @test pyisinstance(x, pybuiltins.type) + @test pyeq(Bool, x.__name__, "Foo") + @test pyeq(Bool, x.foo, 1) + @test pyeq(Bool, x.bar, 2) +end + +@testitem "fraction" begin + # TODO +end + +@testitem "method" begin + # TODO +end + +@testitem "datetime" begin + using Dates + dt = pyimport("datetime") + x1 = pydate(2001, 2, 3) + @test pyisinstance(x1, dt.date) + @test pyeq(Bool, x1, dt.date(2001, 2, 3)) + x2 = pydate(Date(2002, 3, 4)) + @test pyisinstance(x2, dt.date) + @test pyeq(Bool, x2, dt.date(2002, 3, 4)) + x3 = pytime(12, 3, 4, 5) + @test pyisinstance(x3, dt.time) + @test pyeq(Bool, x3, dt.time(12, 3, 4, 5)) + x4 = pytime(Time(23, 4, 5, 0, 6)) + @test pyisinstance(x4, dt.time) + @test pyeq(Bool, x4, dt.time(23, 4, 5, 6)) + x5 = pydatetime(2001, 2, 3, 4, 5, 6, 7) + @test pyisinstance(x5, dt.datetime) + @test pyeq(Bool, x5, dt.datetime(2001, 2, 3, 4, 5, 6, 7)) + x6 = pydatetime(Date(2007, 8, 9)) + @test pyisinstance(x6, dt.datetime) + @test pyeq(Bool, x6, dt.datetime(2007, 8, 9)) + x7 = pydatetime(DateTime(2001, 2, 3, 4, 5, 6, 7)) + @test pyisinstance(x7, dt.datetime) + @test pyeq(Bool, x7, dt.datetime(2001, 2, 3, 4, 5, 6, 7000)) +end + +@testitem "code" begin + # check for ArgumentError when inputs are mixed up + @test_throws ArgumentError pyeval(Main, "1+1") + @test_throws ArgumentError pyeval(Main, Main) + @test_throws ArgumentError pyeval("1+1", "1+1") + @test_throws ArgumentError pyexec(Main, "1+1") + @test_throws ArgumentError pyexec(Main, Main) + @test_throws ArgumentError pyexec("1+1", "1+1") + # basic code execution + m = Module(:test) + g = pydict() + @test pyeq(Bool, pyeval("1+1", m), 2) + @test pyeq(Bool, pyeval("1+1", g), 2) + @test pyeq(Bool, pyeval(pystr("1+1"), g), 2) + @test pyexec("1+1", m) === nothing + @test pyexec("1+1", g) === nothing + @test pyexec(pystr("1+1"), g) === nothing + # check the globals are what we think they are + @test pyis(pyeval("globals()", g), g) + mg = pyeval("globals()", m) + @test pyisinstance(mg, pybuiltins.dict) + # these automatically gain 1 item, __builtins__ + @test length(g) == 1 + @test length(mg) == 1 + @test pycontains(g, "__builtins__") + @test pycontains(mg, "__builtins__") + # code should fail when x does not exist + @test_throws PyException pyeval("x+1", g) + @test_throws PyException pyeval("x+1", g) + # now set x and try again + g["x"] = 1 + @test pyeq(Bool, pyeval("x+1", g), 2) + # set x using pyexec this time + pyexec("x=2", g) + @test pyeq(Bool, g["x"], 2) + @test pyeq(Bool, pyeval("x+1", g), 3) + # now use locals + # check empty locals have no effect + l = pydict() + @test pyeq(Bool, pyeval("x+1", g, l), 3) + @test pyeq(Bool, pyeval("x+1", g, Dict()), 3) + # now set x locally + l["x"] = 3 + @test pyeq(Bool, pyeval("x+1", g, l), 4) + @test pyeq(Bool, pyeval("x+1", g, Dict()), 3) + @test pyeq(Bool, pyeval("x+1", g, Dict("x" => 0)), 1) + @test pyeq(Bool, pyeval("x+1", g, (x=1,)), 2) + # check pyexec runs in local scope + pyexec("x=4", g, l) + @test pyeq(Bool, g["x"], 2) + @test pyeq(Bool, l["x"], 4) + # check global code runs in global scope + pyexec("global y; y=x+1", g, l) + @test pyeq(Bool, g["y"], 5) + @test !pycontains(l, "y") + # check pyeval converts types correctly + @test pyeval(Int, "1+1", g) === 2 + @test pyeval(Nothing, "None", g) === nothing +end + +@testitem "@pyconst" begin + f() = @pyconst "hello" + g() = @pyconst "hello" + @test f() === f() + @test f() === f() + @test g() === g() + @test g() !== f() + @test f() isa Py + @test pyeq(Bool, f(), "hello") +end + +@testitem "Base.jl" begin + @testset "broadcast" begin + # Py always broadcasts as a scalar + x = [1 2; 3 4] .+ Py(1) + @test isequal(x, [Py(2) Py(3); Py(4) Py(5)]) + x = Py("foo") .* [1 2; 3 4] + @test isequal(x, [Py("foo") Py("foofoo"); Py("foofoofoo") Py("foofoofoofoo")]) + # this previously treated the list as a shape (2,) object + # but now tries to do `1 + [1, 2]` which properly fails + @test_throws PyException [1 2; 3 4] .+ pylist([1, 2]) + end +end + +@testitem "pywith" begin + @testset "no error" begin + tdir = pyimport("tempfile").TemporaryDirectory() + tname = pyconvert(String, tdir.name) + @test isdir(tname) + pywith(tdir) do name + @test pyconvert(String, name) == tname + end + @test !isdir(tname) + end + @testset "error" begin + tdir = pyimport("tempfile").TemporaryDirectory() + tname = pyconvert(String, tdir.name) + @test isdir(tname) + @test_throws PyException pywith(name -> name.invalid_attr, tdir) + @test !isdir(tname) + end +end diff --git a/test/compat.jl b/test/compat.jl index 4d5c1745..95f0a6d0 100644 --- a/test/compat.jl +++ b/test/compat.jl @@ -1,35 +1,3 @@ -@testitem "Base.jl" begin - @testset "broadcast" begin - # Py always broadcasts as a scalar - x = [1 2; 3 4] .+ Py(1) - @test isequal(x, [Py(2) Py(3); Py(4) Py(5)]) - x = Py("foo") .* [1 2; 3 4] - @test isequal(x, [Py("foo") Py("foofoo"); Py("foofoofoo") Py("foofoofoofoo")]) - # this previously treated the list as a shape (2,) object - # but now tries to do `1 + [1, 2]` which properly fails - @test_throws PyException [1 2; 3 4] .+ pylist([1, 2]) - end -end - -@testitem "pywith" begin - @testset "no error" begin - tdir = pyimport("tempfile").TemporaryDirectory() - tname = pyconvert(String, tdir.name) - @test isdir(tname) - pywith(tdir) do name - @test pyconvert(String, name) == tname - end - @test !isdir(tname) - end - @testset "error" begin - tdir = pyimport("tempfile").TemporaryDirectory() - tname = pyconvert(String, tdir.name) - @test isdir(tname) - @test_throws PyException pywith(name -> name.invalid_attr, tdir) - @test !isdir(tname) - end -end - @testitem "gui" begin @testset "fix_qt_plugin_path" begin @test PythonCall.fix_qt_plugin_path() isa Bool @@ -120,268 +88,3 @@ end @test pyeq(Bool, y, pylist([pydict(x=1, y="a"), pydict(x=2, y="b"), pydict(x=3, y="c")])) end end - -@testitem "@pyconst" begin - f() = @pyconst "hello" - g() = @pyconst "hello" - @test f() === f() - @test f() === f() - @test g() === g() - @test g() !== f() - @test f() isa Py - @test pyeq(Bool, f(), "hello") -end - -@testitem "@py" begin - @testset "literals" begin - # int - x = @py(123) - @test x isa Py - @test pyis(pytype(x), pybuiltins.int) - @test pyeq(Bool, x, 123) - # uint - x = @py(0x123) - @test x isa Py - @test pyis(pytype(x), pybuiltins.int) - @test pyeq(Bool, x, 0x123) - # TODO: these don't work on all platforms?? - # # int128 - # x = @py(12345678901234567890) - # @test x isa Py - # @test pyis(pytype(x), pybuiltins.int) - # @test pyeq(Bool, x, 12345678901234567890) - # # uint128 - # x = @py(0x12345678901234567890) - # @test x isa Py - # @test pyis(pytype(x), pybuiltins.int) - # @test pyeq(Bool, x, 0x12345678901234567890) - # # bigint - # x = @py(big"1234567890123456789012345678901234567890") - # @test x isa Py - # @test pyis(pytype(x), pybuiltins.int) - # @test pyeq(Bool, x, big"1234567890123456789012345678901234567890") - # x = @py(1234567890123456789012345678901234567890) - # @test x isa Py - # @test pyis(pytype(x), pybuiltins.int) - # @test pyeq(Bool, x, big"1234567890123456789012345678901234567890") - # None - x = @py(None) - @test pyis(x, pybuiltins.None) - # True - x = @py(True) - @test x isa Py - @test pyis(x, pybuiltins.True) - x = @py(true) - @test x isa Py - @test pyis(x, pybuiltins.True) - # False - x = @py(False) - @test x isa Py - @test pyis(x, pybuiltins.False) - x = @py(false) - @test x isa Py - @test pyis(x, pybuiltins.False) - # str - x = @py("hello") - @test x isa Py - @test pyis(pytype(x), pybuiltins.str) - @test pyeq(Bool, x, "hello") - # float - x = @py(1.23) - @test x isa Py - @test pyis(pytype(x), pybuiltins.float) - @test pyeq(Bool, x, 1.23) - # tuple - x = @py tuple() - @test x isa Py - @test pyis(pytype(x), pybuiltins.tuple) - @test pyeq(Bool, x, ()) - x = @py (1, 2, 3) - @test x isa Py - @test pyis(pytype(x), pybuiltins.tuple) - @test pyeq(Bool, x, (1, 2, 3)) - # list - x = @py list() - @test x isa Py - @test pyis(pytype(x), pybuiltins.list) - @test pyeq(Bool, x, pylist()) - x = @py [1, 2, 3] - @test x isa Py - @test pyis(pytype(x), pybuiltins.list) - @test pyeq(Bool, x, pylist([1, 2, 3])) - # dict - x = @py dict() - @test x isa Py - @test pyis(pytype(x), pybuiltins.dict) - @test pyeq(Bool, x, pydict()) - x = @py {} - @test x isa Py - @test pyis(pytype(x), pybuiltins.dict) - @test pyeq(Bool, x, pydict()) - x = @py {x=1, y=2} - @test x isa Py - @test pyis(pytype(x), pybuiltins.dict) - @test pyeq(Bool, x, pydict(x=1, y=2)) - x = @py {"x": 1, "y": 2} - @test x isa Py - @test pyis(pytype(x), pybuiltins.dict) - @test pyeq(Bool, x, pydict(x=1, y=2)) - # set - x = @py set() - @test x isa Py - @test pyis(pytype(x), pybuiltins.set) - @test pyeq(Bool, x, pyset()) - x = @py {1, 2, 3} - @test x isa Py - @test pyis(pytype(x), pybuiltins.set) - @test pyeq(Bool, x, pyset([1, 2, 3])) - end - @testset "__file__" begin - x = @py __file__ - @test x isa Py - @test pyis(pytype(x), pybuiltins.str) - @test pyeq(Bool, x, @__FILE__) - end - @testset "__line__" begin - x = @py __line__ - @test x isa Py - @test pyis(pytype(x), pybuiltins.int) - @test pyeq(Bool, x, @__LINE__() - 3) - end - @testset "builtins" begin - x = @py int - @test pyis(x, pybuiltins.int) - x = @py float - @test pyis(x, pybuiltins.float) - x = @py ValueError - @test pyis(x, pybuiltins.ValueError) - end - @testset "variables" begin - x = 123 - y = @py x - @test y === x - end - @testset "arithmetic" begin - x = @py 1 + 2 + 3 - @test pyeq(Bool, x, 6) - x = @py "foo" + "bar" - @test pyeq(Bool, x, "foobar") - x = @py 9 - 2 - @test pyeq(Bool, x, 7) - x = @py 2 * 3 * 4 - @test pyeq(Bool, x, 24) - x = @py "foo" * 3 - @test pyeq(Bool, x, "foofoofoo") - x = @py 10 / 4 - @test pyeq(Bool, x, 2.5) - x = @py 1 << 10 - @test pyeq(Bool, x, 1024) - x = @py -(10) - @test pyeq(Bool, x, -10) - end - @testset "attrs" begin - t = pytype("Test", (pybuiltins.object,), []) - x = t() - @py x.foo = "foo" - @test pyeq(Bool, x.foo, "foo") - end - @testset "items" begin - x = pylist([1, 2, 3]) - @test pyeq(Bool, @py(x[0]), 1) - @test pyeq(Bool, @py(x[-1]), 3) - @test pyeq(Bool, @py(x[0:2]), pylist([1, 2])) - @py x[1] = 0 - @test pyeq(Bool, x, pylist([1, 0, 3])) - end - @testset "assign" begin - @py x = 12 - @test x isa Py - @test pyis(pytype(x), pybuiltins.int) - @test pyeq(Bool, x, 12) - end - @testset "@jl" begin - x = @py @jl "foo"^3 - @test x == "foofoofoo" - end - @testset "begin" begin - z = @py begin - x = "foo" - y = 4 - x * y - end - @test x isa Py - @test pyeq(Bool, x, "foo") - @test y isa Py - @test pyeq(Bool, y, 4) - @test z isa Py - @test pyeq(Bool, z, "foofoofoofoo") - end - @testset "import" begin - @py import sys - @test pyis(sys, pyimport("sys")) - @py import sys as _sys - @test pyis(_sys, sys) - @py import sys as _sys2, sys as _sys3 - @test pyis(_sys2, sys) - @test pyis(_sys3, sys) - @py import sys: version_info - @test pyis(version_info, sys.version_info) - @py import sys: modules as _mods, version_info as _ver - @test pyis(_mods, sys.modules) - @test pyis(_ver, sys.version_info) - end - @testset "short-circuit" begin - x = @py 3 && pylist([1,2]) - @test pyeq(Bool, x, pylist([1, 2])) - x = @py None && True - @test pyis(x, pybuiltins.None) - x = @py None || 0 || pyset() - @test pyeq(Bool, x, pyset()) - x = @py pydict() || 8 || "" - @test pyeq(Bool, x, 8) - end - @testset "if" begin - x = @py if 1 == 2; "a"; end - @test x isa Py - @test pyis(x, pybuiltins.None) - x = @py if 1 < 2; "a"; end - @test x isa Py - @test pyeq(Bool, x, "a") - x = @py if 1 == 2; "a"; else; "b"; end - @test x isa Py - @test pyeq(Bool, x, "b") - x = @py if 1 < 2; "a"; else; "b"; end - @test x isa Py - @test pyeq(Bool, x, "a") - x = @py if 1 == 2; "a"; elseif 1 < 2; "b"; end - @test x isa Py - @test pyeq(Bool, x, "b") - x = @py if 1 < 2; "a"; elseif 2 < 3; "b"; end - @test x isa Py - @test pyeq(Bool, x, "a") - x = @py if 1 == 2; "a"; elseif 2 == 3; "b"; end - @test x isa Py - @test pyis(x, pybuiltins.None) - end - @testset "for" begin - x = pydict(x=1, y=2) - y = pylist() - @py for k in x - y.append(k) - end - @test pyeq(Bool, y, pylist(["x", "y"])) - end - @testset "while" begin - x = pylist([1, 2, 3, 4]) - y = pylist() - @py while len(x) > 2 - y.append(x.pop()) - end - @test pyeq(Bool, x, pylist([1, 2])) - @test pyeq(Bool, y, pylist([4, 3])) - end - @testset "string interpolation" begin - x = @py """$(None)$(True)$("foo"*2)""" - @test pyeq(Bool, x, "NoneTruefoofoo") - end -end diff --git a/test/concrete.jl b/test/concrete.jl deleted file mode 100644 index a27d44f3..00000000 --- a/test/concrete.jl +++ /dev/null @@ -1,346 +0,0 @@ -@testitem "import" begin - sys = pyimport("sys") - os = pyimport("os") - @test pyeq(Bool, sys.__name__, "sys") - @test pyeq(Bool, os.__name__, "os") - sysos = pyimport("sys", "os") - @test sysos isa Tuple{Py, Py} - @test pyis(sysos[1], sys) - @test pyis(sysos[2], os) - ver = pyimport("sys" => "version") - @test pyis(ver, sys.version) - path = pyimport("sys" => "path") - @test pyis(path, sys.path) - verpath = pyimport("sys" => ("version", "path")) - @test verpath isa Tuple{Py, Py} - @test pyis(verpath[1], ver) - @test pyis(verpath[2], path) -end - -@testitem "consts" begin - @test pybuiltins.None isa Py - @test pystr(String, pybuiltins.None) == "None" -end - -@testitem "str" begin - @test pyisinstance(pystr("foo"), pybuiltins.str) - @test pyeq(Bool, pystr(pystr("foo")), pystr("foo")) - @test pyeq(Bool, pystr(SubString("foobarbaz", 4:6)), pystr("bar")) - @test pyeq(Bool, pystr('x'), pystr("x")) - @test pystr(String, pybuiltins.None) === "None" - @test pystr(String, pyint(123)) === "123" - @test pystr(String, pystr("foo")) === "foo" -end - -@testitem "bytes" begin - @test pyisinstance(pybytes(UInt8[1,2,3]), pybuiltins.bytes) - @test pyeq(Bool, pybytes(pylist([1,2,3])), pybytes(UInt8[1,2,3])) - @test pyeq(Bool, pybytes(b"foo"), pystr("foo").encode("ascii")) - @test pyeq(Bool, pybytes(codeunits(SubString("foobarbaz", 4:6))), pystr("bar").encode("ascii")) - @test pybytes(Vector, pylist([1,2,3])) == UInt8[1,2,3] - @test pybytes(Vector{UInt8}, pylist([1,2,3])) == UInt8[1,2,3] - @test pybytes(Base.CodeUnits, pystr("foo").encode("ascii")) == b"foo" - @test pybytes(Base.CodeUnits{UInt8,String}, pystr("bar").encode("ascii")) == b"bar" -end - -@testitem "tuple" begin - z = pytuple() - @test pyisinstance(z, pybuiltins.tuple) - @test pylen(z) == 0 - x = pytuple((1,2,3)) - @test pyisinstance(x, pybuiltins.tuple) - @test pylen(x) == 3 - @test pyeq(Bool, pygetitem(x, 0), 1) - @test pyeq(Bool, pygetitem(x, 1), 2) - @test pyeq(Bool, pygetitem(x, 2), 3) - @test pyeq(Bool, pytuple([1,2,3]), x) - @test pyeq(Bool, pytuple(i+1 for i in 0:10 if i<3), x) - @test pyeq(Bool, pytuple(pytuple((1,2,3))), x) - @test pyeq(Bool, pytuple(pylist([1,2,3])), x) -end - -@testitem "list" begin - z = pylist() - @test pyisinstance(z, pybuiltins.list) - @test pylen(z) == 0 - x = pylist((1,2,3)) - @test pyisinstance(x, pybuiltins.list) - @test pylen(x) == 3 - @test pyeq(Bool, pygetitem(x, 0), 1) - @test pyeq(Bool, pygetitem(x, 1), 2) - @test pyeq(Bool, pygetitem(x, 2), 3) - @test pyeq(Bool, pylist([1,2,3]), x) - @test pyeq(Bool, pylist(i+1 for i in 0:10 if i<3), x) - @test pyeq(Bool, pylist(pylist((1,2,3))), x) - @test pyeq(Bool, pylist(pytuple([1,2,3])), x) - @test pyeq(Bool, pycollist([1,2,3]), pylist([1,2,3])) - @test pyeq(Bool, pycollist([1 2; 3 4]), pylist((pylist([1,3]), pylist([2,4])))) - @test pyeq(Bool, pyrowlist([1,2,3]), pylist([1,2,3])) - @test pyeq(Bool, pyrowlist([1 2; 3 4]), pylist((pylist([1,2]), pylist([3,4])))) -end - -@testitem "dict" begin - z = pydict() - @test pyisinstance(z, pybuiltins.dict) - @test pylen(z) == 0 - x = pydict(foo=1, bar=2) - @test pyisinstance(x, pybuiltins.dict) - @test pylen(x) == 2 - @test pyeq(Bool, pygetitem(x, "foo"), 1) - @test pyeq(Bool, pygetitem(x, "bar"), 2) - @test pyeq(Bool, pydict(["foo"=>1, "bar"=>2]), x) - @test pyeq(Bool, pydict([("foo"=>1), ("bar"=>2)]), x) - @test pyeq(Bool, pydict(Dict("foo"=>1, "bar"=>2)), x) - @test pyeq(Bool, pydict((foo=1, bar=2)), x) - @test pyeq(Bool, pydict(x), x) -end - -@testitem "bool" begin - @test pyis(pybool(), pybuiltins.False) - @test pyis(pybool(false), pybuiltins.False) - @test pyis(pybool(true), pybuiltins.True) - @test pyis(pybool(0.0), pybuiltins.False) - @test pyis(pybool(-1.2), pybuiltins.True) - @test pyis(pybool(pybuiltins.None), pybuiltins.False) - @test pyis(pybool(pylist()), pybuiltins.False) - @test pyis(pybool(pylist([1,2,3])), pybuiltins.True) -end - -@testitem "int" begin - @test pyisinstance(pyint(), pybuiltins.int) - @test pystr(String, pyint()) == "0" - x = 123 - y = pyint(x) - @test pyisinstance(y, pybuiltins.int) - @test pystr(String, y) == string(x) - x = BigInt(123) << 200 - y = pyint(x) - @test pyisinstance(y, pybuiltins.int) - @test pystr(String, y) == string(x) - x = UInt(123) - y = pyint(x) - @test pyisinstance(y, pybuiltins.int) - @test pystr(String, y) == string(x) - x = UInt128(123) << 100 - y = pyint(x) - @test pyisinstance(y, pybuiltins.int) - @test pystr(String, y) == string(x) - @test pyeq(Bool, pyint(pyint(123)), pyint(123)) - @test pyeq(Bool, pyint(pyfloat(12.3)), pyint(12)) -end - -@testitem "float" begin - y = pyfloat() - @test pyisinstance(y, pybuiltins.float) - @test pyeq(Bool, y, pyint(0)) - x = 123 - y = pyfloat(x) - @test pyisinstance(y, pybuiltins.float) - @test pyeq(Bool, y, pyint(x)) - x = 0.25 - y = pyfloat(x) - @test pyisinstance(y, pybuiltins.float) - @test pyeq(Bool, y, pytruediv(1, 4)) - x = 1//4 - y = pyfloat(x) - @test pyisinstance(y, pybuiltins.float) - @test pyeq(Bool, y, pyfloat(float(x))) - @test pyeq(Bool, pyfloat(pyfloat(12.3)), pyfloat(12.3)) - @test pyeq(Bool, pyfloat(pyint(123)), pyfloat(123)) -end - -@testitem "complex" begin - y = pycomplex() - @test pyisinstance(y, pybuiltins.complex) - @test pyeq(Bool, y, pyint(0)) - x = 12.3 - y = pycomplex(x) - @test pyisinstance(y, pybuiltins.complex) - @test pyeq(Bool, y, pyfloat(x)) - xr, xi = 12, 34 - y = pycomplex(xr, xi) - @test pyisinstance(y, pybuiltins.complex) - @test pyeq(Bool, y.real, pyfloat(xr)) - @test pyeq(Bool, y.imag, pyfloat(xi)) - x = Complex(12, 34) - y = pycomplex(x) - @test pyisinstance(y, pybuiltins.complex) - @test pyeq(Bool, y.real, pyfloat(real(x))) - @test pyeq(Bool, y.imag, pyfloat(imag(x))) - @test pyeq(Bool, pycomplex(y), y) - @test pyeq(Bool, pycomplex(pyint(12), pyint(34)), y) -end - -@testitem "set" begin - y = pyset() - yf = pyfrozenset() - @test pyisinstance(y, pybuiltins.set) - @test pylen(y) == 0 - @test pyisinstance(yf, pybuiltins.frozenset) - @test pylen(yf) == 0 - @test pyeq(Bool, y, yf) - x = [1,2,3,2,1] - y = pyset(x) - yf = pyfrozenset(x) - @test pyisinstance(y, pybuiltins.set) - @test pylen(y) == 3 - @test pycontains(y, 1) - @test pycontains(y, 2) - @test pycontains(y, 3) - @test pyeq(Bool, pyset(y), y) - @test pyisinstance(yf, pybuiltins.frozenset) - @test pylen(yf) == 3 - @test pycontains(yf, 1) - @test pycontains(yf, 2) - @test pycontains(yf, 3) - @test pyeq(Bool, pyfrozenset(y), y) - @test pyeq(Bool, y, yf) -end - -@testitem "slice" begin - x = pyslice(12) - @test pyisinstance(x, pybuiltins.slice) - @test pyeq(Bool, x.start, pybuiltins.None) - @test pyeq(Bool, x.stop, 12) - @test pyeq(Bool, x.step, pybuiltins.None) - x = pyslice(12, 34) - @test pyisinstance(x, pybuiltins.slice) - @test pyeq(Bool, x.start, 12) - @test pyeq(Bool, x.stop, 34) - @test pyeq(Bool, x.step, pybuiltins.None) - x = pyslice(12, 34, 56) - @test pyisinstance(x, pybuiltins.slice) - @test pyeq(Bool, x.start, 12) - @test pyeq(Bool, x.stop, 34) - @test pyeq(Bool, x.step, 56) -end - -@testitem "range" begin - x = pyrange(123) - @test pyisinstance(x, pybuiltins.range) - @test pyeq(Bool, x.start, 0) - @test pyeq(Bool, x.stop, 123) - @test pyeq(Bool, x.step, 1) - x = pyrange(12, 123) - @test pyisinstance(x, pybuiltins.range) - @test pyeq(Bool, x.start, 12) - @test pyeq(Bool, x.stop, 123) - @test pyeq(Bool, x.step, 1) - x = pyrange(12, 123, 3) - @test pyisinstance(x, pybuiltins.range) - @test pyeq(Bool, x.start, 12) - @test pyeq(Bool, x.stop, 123) - @test pyeq(Bool, x.step, 3) -end - -@testitem "none" begin - # TODO -end - -@testitem "type" begin - x = pytype(pyint()) - @test pyisinstance(x, pybuiltins.type) - @test pyis(x, pybuiltins.int) - x = pytype(pybuiltins.type) - @test pyisinstance(x, pybuiltins.type) - @test pyis(x, pybuiltins.type) - x = pytype("Foo", (), ["foo"=>1, "bar"=>2]) - @test pyisinstance(x, pybuiltins.type) - @test pyeq(Bool, x.__name__, "Foo") - @test pyeq(Bool, x.foo, 1) - @test pyeq(Bool, x.bar, 2) -end - -@testitem "fraction" begin - # TODO -end - -@testitem "method" begin - # TODO -end - -@testitem "datetime" begin - using Dates - dt = pyimport("datetime") - x1 = pydate(2001, 2, 3) - @test pyisinstance(x1, dt.date) - @test pyeq(Bool, x1, dt.date(2001, 2, 3)) - x2 = pydate(Date(2002, 3, 4)) - @test pyisinstance(x2, dt.date) - @test pyeq(Bool, x2, dt.date(2002, 3, 4)) - x3 = pytime(12, 3, 4, 5) - @test pyisinstance(x3, dt.time) - @test pyeq(Bool, x3, dt.time(12, 3, 4, 5)) - x4 = pytime(Time(23, 4, 5, 0, 6)) - @test pyisinstance(x4, dt.time) - @test pyeq(Bool, x4, dt.time(23, 4, 5, 6)) - x5 = pydatetime(2001, 2, 3, 4, 5, 6, 7) - @test pyisinstance(x5, dt.datetime) - @test pyeq(Bool, x5, dt.datetime(2001, 2, 3, 4, 5, 6, 7)) - x6 = pydatetime(Date(2007, 8, 9)) - @test pyisinstance(x6, dt.datetime) - @test pyeq(Bool, x6, dt.datetime(2007, 8, 9)) - x7 = pydatetime(DateTime(2001, 2, 3, 4, 5, 6, 7)) - @test pyisinstance(x7, dt.datetime) - @test pyeq(Bool, x7, dt.datetime(2001, 2, 3, 4, 5, 6, 7000)) -end - -@testitem "code" begin - # check for ArgumentError when inputs are mixed up - @test_throws ArgumentError pyeval(Main, "1+1") - @test_throws ArgumentError pyeval(Main, Main) - @test_throws ArgumentError pyeval("1+1", "1+1") - @test_throws ArgumentError pyexec(Main, "1+1") - @test_throws ArgumentError pyexec(Main, Main) - @test_throws ArgumentError pyexec("1+1", "1+1") - # basic code execution - m = Module(:test) - g = pydict() - @test pyeq(Bool, pyeval("1+1", m), 2) - @test pyeq(Bool, pyeval("1+1", g), 2) - @test pyeq(Bool, pyeval(pystr("1+1"), g), 2) - @test pyexec("1+1", m) === nothing - @test pyexec("1+1", g) === nothing - @test pyexec(pystr("1+1"), g) === nothing - # check the globals are what we think they are - @test pyis(pyeval("globals()", g), g) - mg = pyeval("globals()", m) - @test pyisinstance(mg, pybuiltins.dict) - # these automatically gain 1 item, __builtins__ - @test length(g) == 1 - @test length(mg) == 1 - @test pycontains(g, "__builtins__") - @test pycontains(mg, "__builtins__") - # code should fail when x does not exist - @test_throws PyException pyeval("x+1", g) - @test_throws PyException pyeval("x+1", g) - # now set x and try again - g["x"] = 1 - @test pyeq(Bool, pyeval("x+1", g), 2) - # set x using pyexec this time - pyexec("x=2", g) - @test pyeq(Bool, g["x"], 2) - @test pyeq(Bool, pyeval("x+1", g), 3) - # now use locals - # check empty locals have no effect - l = pydict() - @test pyeq(Bool, pyeval("x+1", g, l), 3) - @test pyeq(Bool, pyeval("x+1", g, Dict()), 3) - # now set x locally - l["x"] = 3 - @test pyeq(Bool, pyeval("x+1", g, l), 4) - @test pyeq(Bool, pyeval("x+1", g, Dict()), 3) - @test pyeq(Bool, pyeval("x+1", g, Dict("x" => 0)), 1) - @test pyeq(Bool, pyeval("x+1", g, (x=1,)), 2) - # check pyexec runs in local scope - pyexec("x=4", g, l) - @test pyeq(Bool, g["x"], 2) - @test pyeq(Bool, l["x"], 4) - # check global code runs in global scope - pyexec("global y; y=x+1", g, l) - @test pyeq(Bool, g["y"], 5) - @test !pycontains(l, "y") - # check pyeval converts types correctly - @test pyeval(Int, "1+1", g) === 2 - @test pyeval(Nothing, "None", g) === nothing -end diff --git a/test/convert.jl b/test/pyconvert.jl similarity index 100% rename from test/convert.jl rename to test/pyconvert.jl diff --git a/test/pymacro.jl b/test/pymacro.jl new file mode 100644 index 00000000..d3a1b1b0 --- /dev/null +++ b/test/pymacro.jl @@ -0,0 +1,253 @@ +@testitem "@py" begin + @testset "literals" begin + # int + x = @py(123) + @test x isa Py + @test pyis(pytype(x), pybuiltins.int) + @test pyeq(Bool, x, 123) + # uint + x = @py(0x123) + @test x isa Py + @test pyis(pytype(x), pybuiltins.int) + @test pyeq(Bool, x, 0x123) + # TODO: these don't work on all platforms?? + # # int128 + # x = @py(12345678901234567890) + # @test x isa Py + # @test pyis(pytype(x), pybuiltins.int) + # @test pyeq(Bool, x, 12345678901234567890) + # # uint128 + # x = @py(0x12345678901234567890) + # @test x isa Py + # @test pyis(pytype(x), pybuiltins.int) + # @test pyeq(Bool, x, 0x12345678901234567890) + # # bigint + # x = @py(big"1234567890123456789012345678901234567890") + # @test x isa Py + # @test pyis(pytype(x), pybuiltins.int) + # @test pyeq(Bool, x, big"1234567890123456789012345678901234567890") + # x = @py(1234567890123456789012345678901234567890) + # @test x isa Py + # @test pyis(pytype(x), pybuiltins.int) + # @test pyeq(Bool, x, big"1234567890123456789012345678901234567890") + # None + x = @py(None) + @test pyis(x, pybuiltins.None) + # True + x = @py(True) + @test x isa Py + @test pyis(x, pybuiltins.True) + x = @py(true) + @test x isa Py + @test pyis(x, pybuiltins.True) + # False + x = @py(False) + @test x isa Py + @test pyis(x, pybuiltins.False) + x = @py(false) + @test x isa Py + @test pyis(x, pybuiltins.False) + # str + x = @py("hello") + @test x isa Py + @test pyis(pytype(x), pybuiltins.str) + @test pyeq(Bool, x, "hello") + # float + x = @py(1.23) + @test x isa Py + @test pyis(pytype(x), pybuiltins.float) + @test pyeq(Bool, x, 1.23) + # tuple + x = @py tuple() + @test x isa Py + @test pyis(pytype(x), pybuiltins.tuple) + @test pyeq(Bool, x, ()) + x = @py (1, 2, 3) + @test x isa Py + @test pyis(pytype(x), pybuiltins.tuple) + @test pyeq(Bool, x, (1, 2, 3)) + # list + x = @py list() + @test x isa Py + @test pyis(pytype(x), pybuiltins.list) + @test pyeq(Bool, x, pylist()) + x = @py [1, 2, 3] + @test x isa Py + @test pyis(pytype(x), pybuiltins.list) + @test pyeq(Bool, x, pylist([1, 2, 3])) + # dict + x = @py dict() + @test x isa Py + @test pyis(pytype(x), pybuiltins.dict) + @test pyeq(Bool, x, pydict()) + x = @py {} + @test x isa Py + @test pyis(pytype(x), pybuiltins.dict) + @test pyeq(Bool, x, pydict()) + x = @py {x=1, y=2} + @test x isa Py + @test pyis(pytype(x), pybuiltins.dict) + @test pyeq(Bool, x, pydict(x=1, y=2)) + x = @py {"x": 1, "y": 2} + @test x isa Py + @test pyis(pytype(x), pybuiltins.dict) + @test pyeq(Bool, x, pydict(x=1, y=2)) + # set + x = @py set() + @test x isa Py + @test pyis(pytype(x), pybuiltins.set) + @test pyeq(Bool, x, pyset()) + x = @py {1, 2, 3} + @test x isa Py + @test pyis(pytype(x), pybuiltins.set) + @test pyeq(Bool, x, pyset([1, 2, 3])) + end + @testset "__file__" begin + x = @py __file__ + @test x isa Py + @test pyis(pytype(x), pybuiltins.str) + @test pyeq(Bool, x, @__FILE__) + end + @testset "__line__" begin + x = @py __line__ + @test x isa Py + @test pyis(pytype(x), pybuiltins.int) + @test pyeq(Bool, x, @__LINE__() - 3) + end + @testset "builtins" begin + x = @py int + @test pyis(x, pybuiltins.int) + x = @py float + @test pyis(x, pybuiltins.float) + x = @py ValueError + @test pyis(x, pybuiltins.ValueError) + end + @testset "variables" begin + x = 123 + y = @py x + @test y === x + end + @testset "arithmetic" begin + x = @py 1 + 2 + 3 + @test pyeq(Bool, x, 6) + x = @py "foo" + "bar" + @test pyeq(Bool, x, "foobar") + x = @py 9 - 2 + @test pyeq(Bool, x, 7) + x = @py 2 * 3 * 4 + @test pyeq(Bool, x, 24) + x = @py "foo" * 3 + @test pyeq(Bool, x, "foofoofoo") + x = @py 10 / 4 + @test pyeq(Bool, x, 2.5) + x = @py 1 << 10 + @test pyeq(Bool, x, 1024) + x = @py -(10) + @test pyeq(Bool, x, -10) + end + @testset "attrs" begin + t = pytype("Test", (pybuiltins.object,), []) + x = t() + @py x.foo = "foo" + @test pyeq(Bool, x.foo, "foo") + end + @testset "items" begin + x = pylist([1, 2, 3]) + @test pyeq(Bool, @py(x[0]), 1) + @test pyeq(Bool, @py(x[-1]), 3) + @test pyeq(Bool, @py(x[0:2]), pylist([1, 2])) + @py x[1] = 0 + @test pyeq(Bool, x, pylist([1, 0, 3])) + end + @testset "assign" begin + @py x = 12 + @test x isa Py + @test pyis(pytype(x), pybuiltins.int) + @test pyeq(Bool, x, 12) + end + @testset "@jl" begin + x = @py @jl "foo"^3 + @test x == "foofoofoo" + end + @testset "begin" begin + z = @py begin + x = "foo" + y = 4 + x * y + end + @test x isa Py + @test pyeq(Bool, x, "foo") + @test y isa Py + @test pyeq(Bool, y, 4) + @test z isa Py + @test pyeq(Bool, z, "foofoofoofoo") + end + @testset "import" begin + @py import sys + @test pyis(sys, pyimport("sys")) + @py import sys as _sys + @test pyis(_sys, sys) + @py import sys as _sys2, sys as _sys3 + @test pyis(_sys2, sys) + @test pyis(_sys3, sys) + @py import sys: version_info + @test pyis(version_info, sys.version_info) + @py import sys: modules as _mods, version_info as _ver + @test pyis(_mods, sys.modules) + @test pyis(_ver, sys.version_info) + end + @testset "short-circuit" begin + x = @py 3 && pylist([1,2]) + @test pyeq(Bool, x, pylist([1, 2])) + x = @py None && True + @test pyis(x, pybuiltins.None) + x = @py None || 0 || pyset() + @test pyeq(Bool, x, pyset()) + x = @py pydict() || 8 || "" + @test pyeq(Bool, x, 8) + end + @testset "if" begin + x = @py if 1 == 2; "a"; end + @test x isa Py + @test pyis(x, pybuiltins.None) + x = @py if 1 < 2; "a"; end + @test x isa Py + @test pyeq(Bool, x, "a") + x = @py if 1 == 2; "a"; else; "b"; end + @test x isa Py + @test pyeq(Bool, x, "b") + x = @py if 1 < 2; "a"; else; "b"; end + @test x isa Py + @test pyeq(Bool, x, "a") + x = @py if 1 == 2; "a"; elseif 1 < 2; "b"; end + @test x isa Py + @test pyeq(Bool, x, "b") + x = @py if 1 < 2; "a"; elseif 2 < 3; "b"; end + @test x isa Py + @test pyeq(Bool, x, "a") + x = @py if 1 == 2; "a"; elseif 2 == 3; "b"; end + @test x isa Py + @test pyis(x, pybuiltins.None) + end + @testset "for" begin + x = pydict(x=1, y=2) + y = pylist() + @py for k in x + y.append(k) + end + @test pyeq(Bool, y, pylist(["x", "y"])) + end + @testset "while" begin + x = pylist([1, 2, 3, 4]) + y = pylist() + @py while len(x) > 2 + y.append(x.pop()) + end + @test pyeq(Bool, x, pylist([1, 2])) + @test pyeq(Bool, y, pylist([4, 3])) + end + @testset "string interpolation" begin + x = @py """$(None)$(True)$("foo"*2)""" + @test pyeq(Bool, x, "NoneTruefoofoo") + end +end