@@ -402,3 +402,393 @@ end
402402 @test pyeq (Bool, pybuiltins. eval (ans, pydict ()), 7 )
403403 end
404404end
405+
406+ @testitem " import" begin
407+ sys = pyimport (" sys" )
408+ os = pyimport (" os" )
409+ @test pyeq (Bool, sys. __name__, " sys" )
410+ @test pyeq (Bool, os. __name__, " os" )
411+ sysos = pyimport (" sys" , " os" )
412+ @test sysos isa Tuple{Py, Py}
413+ @test pyis (sysos[1 ], sys)
414+ @test pyis (sysos[2 ], os)
415+ ver = pyimport (" sys" => " version" )
416+ @test pyis (ver, sys. version)
417+ path = pyimport (" sys" => " path" )
418+ @test pyis (path, sys. path)
419+ verpath = pyimport (" sys" => (" version" , " path" ))
420+ @test verpath isa Tuple{Py, Py}
421+ @test pyis (verpath[1 ], ver)
422+ @test pyis (verpath[2 ], path)
423+ end
424+
425+ @testitem " consts" begin
426+ @test pybuiltins. None isa Py
427+ @test pystr (String, pybuiltins. None) == " None"
428+ end
429+
430+ @testitem " str" begin
431+ @test pyisinstance (pystr (" foo" ), pybuiltins. str)
432+ @test pyeq (Bool, pystr (pystr (" foo" )), pystr (" foo" ))
433+ @test pyeq (Bool, pystr (SubString (" foobarbaz" , 4 : 6 )), pystr (" bar" ))
434+ @test pyeq (Bool, pystr (' x' ), pystr (" x" ))
435+ @test pystr (String, pybuiltins. None) === " None"
436+ @test pystr (String, pyint (123 )) === " 123"
437+ @test pystr (String, pystr (" foo" )) === " foo"
438+ end
439+
440+ @testitem " bytes" begin
441+ @test pyisinstance (pybytes (UInt8[1 ,2 ,3 ]), pybuiltins. bytes)
442+ @test pyeq (Bool, pybytes (pylist ([1 ,2 ,3 ])), pybytes (UInt8[1 ,2 ,3 ]))
443+ @test pyeq (Bool, pybytes (b " foo" ), pystr (" foo" ). encode (" ascii" ))
444+ @test pyeq (Bool, pybytes (codeunits (SubString (" foobarbaz" , 4 : 6 ))), pystr (" bar" ). encode (" ascii" ))
445+ @test pybytes (Vector, pylist ([1 ,2 ,3 ])) == UInt8[1 ,2 ,3 ]
446+ @test pybytes (Vector{UInt8}, pylist ([1 ,2 ,3 ])) == UInt8[1 ,2 ,3 ]
447+ @test pybytes (Base. CodeUnits, pystr (" foo" ). encode (" ascii" )) == b " foo"
448+ @test pybytes (Base. CodeUnits{UInt8,String}, pystr (" bar" ). encode (" ascii" )) == b " bar"
449+ end
450+
451+ @testitem " tuple" begin
452+ z = pytuple ()
453+ @test pyisinstance (z, pybuiltins. tuple)
454+ @test pylen (z) == 0
455+ x = pytuple ((1 ,2 ,3 ))
456+ @test pyisinstance (x, pybuiltins. tuple)
457+ @test pylen (x) == 3
458+ @test pyeq (Bool, pygetitem (x, 0 ), 1 )
459+ @test pyeq (Bool, pygetitem (x, 1 ), 2 )
460+ @test pyeq (Bool, pygetitem (x, 2 ), 3 )
461+ @test pyeq (Bool, pytuple ([1 ,2 ,3 ]), x)
462+ @test pyeq (Bool, pytuple (i+ 1 for i in 0 : 10 if i< 3 ), x)
463+ @test pyeq (Bool, pytuple (pytuple ((1 ,2 ,3 ))), x)
464+ @test pyeq (Bool, pytuple (pylist ([1 ,2 ,3 ])), x)
465+ end
466+
467+ @testitem " list" begin
468+ z = pylist ()
469+ @test pyisinstance (z, pybuiltins. list)
470+ @test pylen (z) == 0
471+ x = pylist ((1 ,2 ,3 ))
472+ @test pyisinstance (x, pybuiltins. list)
473+ @test pylen (x) == 3
474+ @test pyeq (Bool, pygetitem (x, 0 ), 1 )
475+ @test pyeq (Bool, pygetitem (x, 1 ), 2 )
476+ @test pyeq (Bool, pygetitem (x, 2 ), 3 )
477+ @test pyeq (Bool, pylist ([1 ,2 ,3 ]), x)
478+ @test pyeq (Bool, pylist (i+ 1 for i in 0 : 10 if i< 3 ), x)
479+ @test pyeq (Bool, pylist (pylist ((1 ,2 ,3 ))), x)
480+ @test pyeq (Bool, pylist (pytuple ([1 ,2 ,3 ])), x)
481+ @test pyeq (Bool, pycollist ([1 ,2 ,3 ]), pylist ([1 ,2 ,3 ]))
482+ @test pyeq (Bool, pycollist ([1 2 ; 3 4 ]), pylist ((pylist ([1 ,3 ]), pylist ([2 ,4 ]))))
483+ @test pyeq (Bool, pyrowlist ([1 ,2 ,3 ]), pylist ([1 ,2 ,3 ]))
484+ @test pyeq (Bool, pyrowlist ([1 2 ; 3 4 ]), pylist ((pylist ([1 ,2 ]), pylist ([3 ,4 ]))))
485+ end
486+
487+ @testitem " dict" begin
488+ z = pydict ()
489+ @test pyisinstance (z, pybuiltins. dict)
490+ @test pylen (z) == 0
491+ x = pydict (foo= 1 , bar= 2 )
492+ @test pyisinstance (x, pybuiltins. dict)
493+ @test pylen (x) == 2
494+ @test pyeq (Bool, pygetitem (x, " foo" ), 1 )
495+ @test pyeq (Bool, pygetitem (x, " bar" ), 2 )
496+ @test pyeq (Bool, pydict ([" foo" => 1 , " bar" => 2 ]), x)
497+ @test pyeq (Bool, pydict ([(" foo" => 1 ), (" bar" => 2 )]), x)
498+ @test pyeq (Bool, pydict (Dict (" foo" => 1 , " bar" => 2 )), x)
499+ @test pyeq (Bool, pydict ((foo= 1 , bar= 2 )), x)
500+ @test pyeq (Bool, pydict (x), x)
501+ end
502+
503+ @testitem " bool" begin
504+ @test pyis (pybool (), pybuiltins. False)
505+ @test pyis (pybool (false ), pybuiltins. False)
506+ @test pyis (pybool (true ), pybuiltins. True)
507+ @test pyis (pybool (0.0 ), pybuiltins. False)
508+ @test pyis (pybool (- 1.2 ), pybuiltins. True)
509+ @test pyis (pybool (pybuiltins. None), pybuiltins. False)
510+ @test pyis (pybool (pylist ()), pybuiltins. False)
511+ @test pyis (pybool (pylist ([1 ,2 ,3 ])), pybuiltins. True)
512+ end
513+
514+ @testitem " int" begin
515+ @test pyisinstance (pyint (), pybuiltins. int)
516+ @test pystr (String, pyint ()) == " 0"
517+ x = 123
518+ y = pyint (x)
519+ @test pyisinstance (y, pybuiltins. int)
520+ @test pystr (String, y) == string (x)
521+ x = BigInt (123 ) << 200
522+ y = pyint (x)
523+ @test pyisinstance (y, pybuiltins. int)
524+ @test pystr (String, y) == string (x)
525+ x = UInt (123 )
526+ y = pyint (x)
527+ @test pyisinstance (y, pybuiltins. int)
528+ @test pystr (String, y) == string (x)
529+ x = UInt128 (123 ) << 100
530+ y = pyint (x)
531+ @test pyisinstance (y, pybuiltins. int)
532+ @test pystr (String, y) == string (x)
533+ @test pyeq (Bool, pyint (pyint (123 )), pyint (123 ))
534+ @test pyeq (Bool, pyint (pyfloat (12.3 )), pyint (12 ))
535+ end
536+
537+ @testitem " float" begin
538+ y = pyfloat ()
539+ @test pyisinstance (y, pybuiltins. float)
540+ @test pyeq (Bool, y, pyint (0 ))
541+ x = 123
542+ y = pyfloat (x)
543+ @test pyisinstance (y, pybuiltins. float)
544+ @test pyeq (Bool, y, pyint (x))
545+ x = 0.25
546+ y = pyfloat (x)
547+ @test pyisinstance (y, pybuiltins. float)
548+ @test pyeq (Bool, y, pytruediv (1 , 4 ))
549+ x = 1 // 4
550+ y = pyfloat (x)
551+ @test pyisinstance (y, pybuiltins. float)
552+ @test pyeq (Bool, y, pyfloat (float (x)))
553+ @test pyeq (Bool, pyfloat (pyfloat (12.3 )), pyfloat (12.3 ))
554+ @test pyeq (Bool, pyfloat (pyint (123 )), pyfloat (123 ))
555+ end
556+
557+ @testitem " complex" begin
558+ y = pycomplex ()
559+ @test pyisinstance (y, pybuiltins. complex)
560+ @test pyeq (Bool, y, pyint (0 ))
561+ x = 12.3
562+ y = pycomplex (x)
563+ @test pyisinstance (y, pybuiltins. complex)
564+ @test pyeq (Bool, y, pyfloat (x))
565+ xr, xi = 12 , 34
566+ y = pycomplex (xr, xi)
567+ @test pyisinstance (y, pybuiltins. complex)
568+ @test pyeq (Bool, y. real, pyfloat (xr))
569+ @test pyeq (Bool, y. imag, pyfloat (xi))
570+ x = Complex (12 , 34 )
571+ y = pycomplex (x)
572+ @test pyisinstance (y, pybuiltins. complex)
573+ @test pyeq (Bool, y. real, pyfloat (real (x)))
574+ @test pyeq (Bool, y. imag, pyfloat (imag (x)))
575+ @test pyeq (Bool, pycomplex (y), y)
576+ @test pyeq (Bool, pycomplex (pyint (12 ), pyint (34 )), y)
577+ end
578+
579+ @testitem " set" begin
580+ y = pyset ()
581+ yf = pyfrozenset ()
582+ @test pyisinstance (y, pybuiltins. set)
583+ @test pylen (y) == 0
584+ @test pyisinstance (yf, pybuiltins. frozenset)
585+ @test pylen (yf) == 0
586+ @test pyeq (Bool, y, yf)
587+ x = [1 ,2 ,3 ,2 ,1 ]
588+ y = pyset (x)
589+ yf = pyfrozenset (x)
590+ @test pyisinstance (y, pybuiltins. set)
591+ @test pylen (y) == 3
592+ @test pycontains (y, 1 )
593+ @test pycontains (y, 2 )
594+ @test pycontains (y, 3 )
595+ @test pyeq (Bool, pyset (y), y)
596+ @test pyisinstance (yf, pybuiltins. frozenset)
597+ @test pylen (yf) == 3
598+ @test pycontains (yf, 1 )
599+ @test pycontains (yf, 2 )
600+ @test pycontains (yf, 3 )
601+ @test pyeq (Bool, pyfrozenset (y), y)
602+ @test pyeq (Bool, y, yf)
603+ end
604+
605+ @testitem " slice" begin
606+ x = pyslice (12 )
607+ @test pyisinstance (x, pybuiltins. slice)
608+ @test pyeq (Bool, x. start, pybuiltins. None)
609+ @test pyeq (Bool, x. stop, 12 )
610+ @test pyeq (Bool, x. step, pybuiltins. None)
611+ x = pyslice (12 , 34 )
612+ @test pyisinstance (x, pybuiltins. slice)
613+ @test pyeq (Bool, x. start, 12 )
614+ @test pyeq (Bool, x. stop, 34 )
615+ @test pyeq (Bool, x. step, pybuiltins. None)
616+ x = pyslice (12 , 34 , 56 )
617+ @test pyisinstance (x, pybuiltins. slice)
618+ @test pyeq (Bool, x. start, 12 )
619+ @test pyeq (Bool, x. stop, 34 )
620+ @test pyeq (Bool, x. step, 56 )
621+ end
622+
623+ @testitem " range" begin
624+ x = pyrange (123 )
625+ @test pyisinstance (x, pybuiltins. range)
626+ @test pyeq (Bool, x. start, 0 )
627+ @test pyeq (Bool, x. stop, 123 )
628+ @test pyeq (Bool, x. step, 1 )
629+ x = pyrange (12 , 123 )
630+ @test pyisinstance (x, pybuiltins. range)
631+ @test pyeq (Bool, x. start, 12 )
632+ @test pyeq (Bool, x. stop, 123 )
633+ @test pyeq (Bool, x. step, 1 )
634+ x = pyrange (12 , 123 , 3 )
635+ @test pyisinstance (x, pybuiltins. range)
636+ @test pyeq (Bool, x. start, 12 )
637+ @test pyeq (Bool, x. stop, 123 )
638+ @test pyeq (Bool, x. step, 3 )
639+ end
640+
641+ @testitem " none" begin
642+ # TODO
643+ end
644+
645+ @testitem " type" begin
646+ x = pytype (pyint ())
647+ @test pyisinstance (x, pybuiltins. type)
648+ @test pyis (x, pybuiltins. int)
649+ x = pytype (pybuiltins. type)
650+ @test pyisinstance (x, pybuiltins. type)
651+ @test pyis (x, pybuiltins. type)
652+ x = pytype (" Foo" , (), [" foo" => 1 , " bar" => 2 ])
653+ @test pyisinstance (x, pybuiltins. type)
654+ @test pyeq (Bool, x. __name__, " Foo" )
655+ @test pyeq (Bool, x. foo, 1 )
656+ @test pyeq (Bool, x. bar, 2 )
657+ end
658+
659+ @testitem " fraction" begin
660+ # TODO
661+ end
662+
663+ @testitem " method" begin
664+ # TODO
665+ end
666+
667+ @testitem " datetime" begin
668+ using Dates
669+ dt = pyimport (" datetime" )
670+ x1 = pydate (2001 , 2 , 3 )
671+ @test pyisinstance (x1, dt. date)
672+ @test pyeq (Bool, x1, dt. date (2001 , 2 , 3 ))
673+ x2 = pydate (Date (2002 , 3 , 4 ))
674+ @test pyisinstance (x2, dt. date)
675+ @test pyeq (Bool, x2, dt. date (2002 , 3 , 4 ))
676+ x3 = pytime (12 , 3 , 4 , 5 )
677+ @test pyisinstance (x3, dt. time)
678+ @test pyeq (Bool, x3, dt. time (12 , 3 , 4 , 5 ))
679+ x4 = pytime (Time (23 , 4 , 5 , 0 , 6 ))
680+ @test pyisinstance (x4, dt. time)
681+ @test pyeq (Bool, x4, dt. time (23 , 4 , 5 , 6 ))
682+ x5 = pydatetime (2001 , 2 , 3 , 4 , 5 , 6 , 7 )
683+ @test pyisinstance (x5, dt. datetime)
684+ @test pyeq (Bool, x5, dt. datetime (2001 , 2 , 3 , 4 , 5 , 6 , 7 ))
685+ x6 = pydatetime (Date (2007 , 8 , 9 ))
686+ @test pyisinstance (x6, dt. datetime)
687+ @test pyeq (Bool, x6, dt. datetime (2007 , 8 , 9 ))
688+ x7 = pydatetime (DateTime (2001 , 2 , 3 , 4 , 5 , 6 , 7 ))
689+ @test pyisinstance (x7, dt. datetime)
690+ @test pyeq (Bool, x7, dt. datetime (2001 , 2 , 3 , 4 , 5 , 6 , 7000 ))
691+ end
692+
693+ @testitem " code" begin
694+ # check for ArgumentError when inputs are mixed up
695+ @test_throws ArgumentError pyeval (Main, " 1+1" )
696+ @test_throws ArgumentError pyeval (Main, Main)
697+ @test_throws ArgumentError pyeval (" 1+1" , " 1+1" )
698+ @test_throws ArgumentError pyexec (Main, " 1+1" )
699+ @test_throws ArgumentError pyexec (Main, Main)
700+ @test_throws ArgumentError pyexec (" 1+1" , " 1+1" )
701+ # basic code execution
702+ m = Module (:test )
703+ g = pydict ()
704+ @test pyeq (Bool, pyeval (" 1+1" , m), 2 )
705+ @test pyeq (Bool, pyeval (" 1+1" , g), 2 )
706+ @test pyeq (Bool, pyeval (pystr (" 1+1" ), g), 2 )
707+ @test pyexec (" 1+1" , m) === nothing
708+ @test pyexec (" 1+1" , g) === nothing
709+ @test pyexec (pystr (" 1+1" ), g) === nothing
710+ # check the globals are what we think they are
711+ @test pyis (pyeval (" globals()" , g), g)
712+ mg = pyeval (" globals()" , m)
713+ @test pyisinstance (mg, pybuiltins. dict)
714+ # these automatically gain 1 item, __builtins__
715+ @test length (g) == 1
716+ @test length (mg) == 1
717+ @test pycontains (g, " __builtins__" )
718+ @test pycontains (mg, " __builtins__" )
719+ # code should fail when x does not exist
720+ @test_throws PyException pyeval (" x+1" , g)
721+ @test_throws PyException pyeval (" x+1" , g)
722+ # now set x and try again
723+ g[" x" ] = 1
724+ @test pyeq (Bool, pyeval (" x+1" , g), 2 )
725+ # set x using pyexec this time
726+ pyexec (" x=2" , g)
727+ @test pyeq (Bool, g[" x" ], 2 )
728+ @test pyeq (Bool, pyeval (" x+1" , g), 3 )
729+ # now use locals
730+ # check empty locals have no effect
731+ l = pydict ()
732+ @test pyeq (Bool, pyeval (" x+1" , g, l), 3 )
733+ @test pyeq (Bool, pyeval (" x+1" , g, Dict ()), 3 )
734+ # now set x locally
735+ l[" x" ] = 3
736+ @test pyeq (Bool, pyeval (" x+1" , g, l), 4 )
737+ @test pyeq (Bool, pyeval (" x+1" , g, Dict ()), 3 )
738+ @test pyeq (Bool, pyeval (" x+1" , g, Dict (" x" => 0 )), 1 )
739+ @test pyeq (Bool, pyeval (" x+1" , g, (x= 1 ,)), 2 )
740+ # check pyexec runs in local scope
741+ pyexec (" x=4" , g, l)
742+ @test pyeq (Bool, g[" x" ], 2 )
743+ @test pyeq (Bool, l[" x" ], 4 )
744+ # check global code runs in global scope
745+ pyexec (" global y; y=x+1" , g, l)
746+ @test pyeq (Bool, g[" y" ], 5 )
747+ @test ! pycontains (l, " y" )
748+ # check pyeval converts types correctly
749+ @test pyeval (Int, " 1+1" , g) === 2
750+ @test pyeval (Nothing, " None" , g) === nothing
751+ end
752+
753+ @testitem " @pyconst" begin
754+ f () = @pyconst " hello"
755+ g () = @pyconst " hello"
756+ @test f () === f ()
757+ @test f () === f ()
758+ @test g () === g ()
759+ @test g () != = f ()
760+ @test f () isa Py
761+ @test pyeq (Bool, f (), " hello" )
762+ end
763+
764+ @testitem " Base.jl" begin
765+ @testset " broadcast" begin
766+ # Py always broadcasts as a scalar
767+ x = [1 2 ; 3 4 ] .+ Py (1 )
768+ @test isequal (x, [Py (2 ) Py (3 ); Py (4 ) Py (5 )])
769+ x = Py (" foo" ) .* [1 2 ; 3 4 ]
770+ @test isequal (x, [Py (" foo" ) Py (" foofoo" ); Py (" foofoofoo" ) Py (" foofoofoofoo" )])
771+ # this previously treated the list as a shape (2,) object
772+ # but now tries to do `1 + [1, 2]` which properly fails
773+ @test_throws PyException [1 2 ; 3 4 ] .+ pylist ([1 , 2 ])
774+ end
775+ end
776+
777+ @testitem " pywith" begin
778+ @testset " no error" begin
779+ tdir = pyimport (" tempfile" ). TemporaryDirectory ()
780+ tname = pyconvert (String, tdir. name)
781+ @test isdir (tname)
782+ pywith (tdir) do name
783+ @test pyconvert (String, name) == tname
784+ end
785+ @test ! isdir (tname)
786+ end
787+ @testset " error" begin
788+ tdir = pyimport (" tempfile" ). TemporaryDirectory ()
789+ tname = pyconvert (String, tdir. name)
790+ @test isdir (tname)
791+ @test_throws PyException pywith (name -> name. invalid_attr, tdir)
792+ @test ! isdir (tname)
793+ end
794+ end
0 commit comments