diff --git a/pkg/JuliaInterface/gap/JuliaInterface.gd b/pkg/JuliaInterface/gap/JuliaInterface.gd
index 95c47589b..d2eda1eeb 100644
--- a/pkg/JuliaInterface/gap/JuliaInterface.gd
+++ b/pkg/JuliaInterface/gap/JuliaInterface.gd
@@ -224,6 +224,24 @@ DeclareGlobalFunction( "JuliaImportPackage" );
#! @Section Access to &Julia; objects
+#! Not all &Julia; syntax features are supported in ⪆.
+#! For important ones, the interface provides ⪆ functions or
+#! helper functions written in &Julia; to use them in ⪆.
+#! For example, allows one to use
+#! &Julia;'s try/catch statements.
+#!
+#! Here is a selection of other workarounds for &Julia; syntax features.
+#!
+#! -
+#! &Julia;'s RefValue objects can be handled as follows.
+#! If x is such an object then its value can be fetched with
+#! Julia.GAP.getindex( x ),
+#! a value v of the right type can be set with
+#! Julia.GAP.setindex( x, v ),
+#! and one can check with Julia.GAP.isassigned( x )
+#! whether x has a value.
+#!
+#!
#! @Description
#! This global variable represents the &Julia; module Main,
@@ -242,6 +260,19 @@ DeclareGlobalFunction( "JuliaImportPackage" );
#! gap> Julia.Main.x;
#! 1
#! @EndExampleSession
+#!
+#! Note that not all &Julia; variables are directly visible in its
+#! Main module.
+#! For example, &Julia; variables from the interface to ⪆ are defined
+#! in the &Julia; module GAP or its submodules.
+#! It is safe to access this module as Julia.GAP.
+#!
+#! @BeginExampleSession
+#! gap> Julia.GAP;
+#!
+#! gap> Julia.GAP.prompt;
+#!
+#! @EndExampleSession
DeclareGlobalVariable( "Julia" );
#! @Arguments juliaobj
@@ -259,11 +290,13 @@ DeclareGlobalVariable( "Julia" );
#! @EndExampleSession
DeclareGlobalFunction( "JuliaTypeInfo" );
-#! @Arguments juliafunc, arguments
+#! @Arguments juliafunc, arguments[, kwargs]
#! @Returns a record.
#! @Description
#! The function calls the &Julia; function juliafunc
-#! with arguments in the ⪆ list arguments,
+#! with ordinary arguments in the ⪆ list arguments
+#! and optionally with keyword arguments given by the component names (keys)
+#! and values of the ⪆ record kwargs,
#! and returns a record with the components ok and value.
#! If no error occurred then ok has the value true,
#! and value is the value returned by juliafunc.
@@ -286,20 +319,29 @@ DeclareGlobalFunction( "JuliaTypeInfo" );
#! false
#! gap> res.value{ [ 1 .. Position( res.value, '(' )-1 ] };
#! "LinearAlgebra.SingularException"
+#! gap> fun:= Julia.range;;
+#! gap> CallJuliaFunctionWithCatch( fun, [ 2, 10 ], rec( step:= 2 ) );
+#! rec( ok := true, value := )
+#! gap> res:= CallJuliaFunctionWithCatch( fun, [ 2, 10 ],
+#! > rec( step:= GAPToJulia( "a" ) ) );;
+#! gap> res.ok;
+#! false
+#! gap> res.value{ [ 1 .. Position( res.value, '(' )-1 ] };
+#! "MethodError"
#! @EndExampleSession
DeclareGlobalFunction( "CallJuliaFunctionWithCatch" );
-#! @Arguments juliafunc, arguments, arec
+#! @Arguments juliafunc, arguments, kwargs
#! @Returns the result of the &Julia; function call.
#! @Description
#! The function calls the &Julia; function juliafunc
#! with ordinary arguments in the ⪆ list arguments
#! and keyword arguments given by the component names (keys) and values
-#! of the record arec,
+#! of the record kwargs,
#! and returns the function value.
#!
#! Note that the entries of arguments and the components of
-#! arec are not implicitly converted to &Julia;.
+#! kwargs are not implicitly converted to &Julia;.
#! @BeginExampleSession
#! gap> CallJuliaFunctionWithKeywordArguments( Julia.Base.round,
#! > [ GAPToJulia( Float( 1/3 ) ) ], rec( digits:= 5 ) );
@@ -388,8 +430,8 @@ DeclareGlobalFunction( "CallJuliaFunctionWithKeywordArguments" );
#!
#! -
#!
,
-#! delegating to Julia.Core._apply
-#! (this yields the function call syntax in ⪆,
+#! delegating to &Julia;'s func(args...) syntax;
+#! this yields the function call syntax in ⪆,
#! it is installed also for objects in
#! ,
#!
@@ -397,10 +439,8 @@ DeclareGlobalFunction( "CallJuliaFunctionWithKeywordArguments" );
#! access to and assignment of entries of arrays, via
#! ,
#! ,
-#!
-#! and the (up to ⪆ 4.11 undocumented) operations MatElm and
-#! SetMatElm,
+#! , and
+#! ,
#! delegating to
#! Julia.Base.getindex and
#! Julia.Base.setindex,
@@ -459,7 +499,6 @@ DeclareGlobalFunction( "CallJuliaFunctionWithKeywordArguments" );
#! gap> m + m;
#!
#! @EndExampleSession
-#TODO: add the cross-references to MatElm, SetMatElm when they are documented
#! @InsertChunk JuliaHelpInGAP
diff --git a/pkg/JuliaInterface/gap/calls.gi b/pkg/JuliaInterface/gap/calls.gi
index e01b0ff43..45f25c1a5 100644
--- a/pkg/JuliaInterface/gap/calls.gi
+++ b/pkg/JuliaInterface/gap/calls.gi
@@ -12,7 +12,7 @@ InstallMethod( CallFuncList,
[ "IsJuliaObject", "IsList" ],
function( julia_obj, args )
args := GAPToJulia( _JL_Vector_Any, args, false );
- return Julia.Core._apply( julia_obj, args );
+ return Julia.GAP._apply( julia_obj, args );
end );
InstallMethod( CallFuncList,
@@ -22,14 +22,21 @@ InstallMethod( CallFuncList,
end );
InstallGlobalFunction( CallJuliaFunctionWithCatch,
- function( julia_obj, args )
+ function( julia_obj, args, kwargs... )
local res;
args := GAPToJulia( _JL_Vector_Any, args, false );
if IsFunction( julia_obj ) then
julia_obj:= Julia.GAP.UnwrapJuliaFunc( julia_obj );
fi;
- res:= Julia.GAP.call_with_catch( julia_obj, args );
+ if Length( kwargs ) = 0 then
+ res:= Julia.GAP.call_with_catch( julia_obj, args );
+ elif Length( kwargs ) = 1 and IsRecord( kwargs[1] ) then
+ kwargs := GAPToJulia( _JL_Dict_Any, kwargs[1], false );
+ res:= Julia.GAP.call_with_catch( julia_obj, args, kwargs );
+ else
+ Error( "usage: CallJuliaFunctionWithCatch( , [, ]" );
+ fi;
if res[1] then
return rec( ok:= true, value:= res[2] );
else
@@ -38,7 +45,7 @@ InstallGlobalFunction( CallJuliaFunctionWithCatch,
end );
InstallGlobalFunction( CallJuliaFunctionWithKeywordArguments,
- { julia_obj, args, arec } -> Julia.GAP.kwarg_wrapper( julia_obj,
+ { julia_obj, args, kwargs } -> Julia.GAP.kwarg_wrapper( julia_obj,
# non-recursive conversions
GAPToJulia( _JL_Vector_Any, args, false ),
- GAPToJulia( _JL_Dict_Any, arec, false ) ) );
+ GAPToJulia( _JL_Dict_Any, kwargs, false ) ) );
diff --git a/pkg/JuliaInterface/tst/utils.tst b/pkg/JuliaInterface/tst/utils.tst
index 734a06ca6..612a02b7c 100644
--- a/pkg/JuliaInterface/tst/utils.tst
+++ b/pkg/JuliaInterface/tst/utils.tst
@@ -19,6 +19,8 @@ gap> res.ok;
false
gap> StartsWith( res.value, "DomainError" );
true
+gap> CallJuliaFunctionWithCatch( Julia.Base.sqrt, [ 4 ], rec() );
+rec( ok := true, value := )
##
gap> JuliaEvalString(fail);
diff --git a/src/packages.jl b/src/packages.jl
index 615a6d2c7..10dcadc9f 100644
--- a/src/packages.jl
+++ b/src/packages.jl
@@ -10,8 +10,8 @@ const DOWNLOAD_HELPER = Ref{Downloads.Downloader}()
function init_packagemanager()
#TODO:
-# As soon as PackageManager uses utils' Download function,
-# we need not replace code from PackageManager anymore.
+# As soon as GAP.jl can rely on a good enough version of PackageManager
+# we need not replace `PKGMAN_DownloadURL` anymore.
# (And the function should be renamed.)
res = load("PackageManager")
@assert res
@@ -59,15 +59,15 @@ function init_packagemanager()
# put the new method in the first position
meths = Globals.Download_Methods
Wrappers.Add(meths, GapObj(r, recursive=true), 1)
-
- # monkey patch PackageManager so that we can disable removal of
- # package directories for debugging purposes
- orig_PKGMAN_RemoveDir = Globals.PKGMAN_RemoveDir
- replace_global!(:PKGMAN_RemoveDir, function(dir)
- Globals.ValueOption(GapObj("debug")) == true && return
- orig_PKGMAN_RemoveDir(dir)
- end)
end
+
+ # monkey patch PackageManager so that we can disable removal of
+ # package directories for debugging purposes
+ orig_PKGMAN_RemoveDir = Globals.PKGMAN_RemoveDir
+ replace_global!(:PKGMAN_RemoveDir, function(dir)
+ Globals.ValueOption(GapObj("debug")) == true && return
+ orig_PKGMAN_RemoveDir(dir)
+ end)
end
"""
diff --git a/src/utils.jl b/src/utils.jl
index be9769cdc..33860d9a2 100644
--- a/src/utils.jl
+++ b/src/utils.jl
@@ -15,6 +15,9 @@ function _setglobal(M::Module, name::Symbol, val::Any)
end
end
+# avoid the deprecated `Core._apply`
+_apply(func, args) = func(args...)
+
"""
get_symbols_in_module(m::Module) :: Vector{Symbol}
@@ -30,20 +33,24 @@ function get_symbols_in_module(m::Module)
end
"""
- call_with_catch(juliafunc, arguments)
+ call_with_catch(func, args::Vector)
+ call_with_catch(func, args::Vector, kwargs::Dict{Symbol,T}) where T
Return a tuple `(ok, val)`
-where `ok` is either `true`, meaning that calling the function `juliafunc`
-with `arguments` returns the value `val`,
+where `ok` is either `true`, meaning that calling `func`
+with arguments `args` (and optionally with keyword arguments given by
+the keys and values of `kwargs`) returns the value `val`,
or `false`, meaning that the function call runs into an error;
in the latter case, `val` is set to the string of the error message.
+This function is used on the GAP side.
+
# Examples
```jldoctest
-julia> GAP.call_with_catch(sqrt, 2)
+julia> GAP.call_with_catch(sqrt, [2])
(true, 1.4142135623730951)
-julia> flag, res = GAP.call_with_catch(sqrt, -2);
+julia> flag, res = GAP.call_with_catch(sqrt, [-2]);
julia> flag
false
@@ -51,11 +58,30 @@ false
julia> startswith(res, "DomainError")
true
+julia> GAP.call_with_catch(range, [2, 10], Dict(:step => 2))
+(true, 2:2:10)
+
+julia> flag, res = GAP.call_with_catch(range, [2, 10], Dict(:step => "a"));
+
+julia> flag
+false
+
+julia> startswith(res, "MethodError")
+true
```
"""
-function call_with_catch(juliafunc, arguments)
+function call_with_catch(func, args)
try
- res = Core._apply(juliafunc, arguments)
+ res = func(args...)
+ return (true, res)
+ catch e
+ return (false, string(e))
+ end
+end
+
+function call_with_catch(func, args::Vector, kwargs::Dict{Symbol,T}) where T
+ try
+ res = func(args...; [k => kwargs[k] for k in keys(kwargs)]...)
return (true, res)
catch e
return (false, string(e))
@@ -70,7 +96,6 @@ given by the keys and values of `kwargs`.
This function is used on the GAP side, in calls of Julia functions that
require keyword arguments.
-Note that `jl_call` and `Core._apply` do not support keyword arguments.
# Examples
```jldoctest
@@ -79,7 +104,6 @@ julia> range(2, length = 5, step = 2)
julia> GAP.kwarg_wrapper(range, [2], Dict(:length => 5, :step => 2))
2:2:10
-
```
"""
function kwarg_wrapper(func, args::Vector{T1}, kwargs::Dict{Symbol,T2}) where {T1,T2}