Julia <-> Python object conversions for relatively complex objects using juliacall

Hello everyone,
This is a pretty long question and it’s pretty complicated (at least to me it is) so I will highly appreciate all answers.
I am currently writing a python wrapper around the Extremes.jl package. That package has a “BlockMaxima’’ structure, which I need to be able to convert back and forth from python to Julia. BlockMaxima has two different constructors :

struct BlockMaxima{T} <: AbstractExtremeValueModel

function BlockMaxima{GeneralizedExtremeValue}(data::Variable;
    locationcov::Vector{<:DataItem} = Vector{Variable}(),
    logscalecov::Vector{<:DataItem} = Vector{Variable}(),
    shapecov::Vector{<:DataItem} = Vector{Variable}())::BlockMaxima

    # …A lot of unnecessary code for my question…

    return BlockMaxima{GeneralizedExtremeValue}(data, paramfun(locationcov, locationfun), paramfun(logscalecov, logscalefun), paramfun(shapecov, shapefun))


function BlockMaxima{Gumbel}(data::Variable;
    locationcov::Vector{<:DataItem} = Vector{Variable}(),
    logscalecov::Vector{<:DataItem} = Vector{Variable}())::BlockMaxima

    # …A lot of unnecessary code for my question…
    return BlockMaxima{Gumbel}(data, paramfun(locationcov, locationfun), paramfun(logscalecov, logscalefun), paramfun(shapecov, shapefun))


Here is the BlockMaxima class I defined in python:

class AbstractExtremeValueModel:

class BlockMaxima(AbstractExtremeValueModel):
    data: Variable
    location: paramfun
    logscale: paramfun
    shape: paramfun
    type: str
    def __init__(self, data: Variable, location: paramfun, logscale: paramfun, shape: paramfun, type: str):
        self.data, self.location, self.logscale, self.shape, self.type = data, location, logscale, shape, type

The MAJOR problem I am encountering is that the BlockMaxima constructor in Julia has 2 different signatures, BloackMaxima{GeneralizedExtremeValue}(…) and BloackMaxima{Gumbel}(…), and I am unable to recreate the call to any of those signatures from python. For instance, the first implementation of python → Julia conversion I tried was:

def py_blockmaxima_to_jl_blockmaxima(py_blockmaxima: BlockMaxima):
    jl_data = py_blockmaxima.data.py_variable_to_jl_variable()
    jl_locationcov = py_blockmaxima.location.covariate
    jl_logscalecov = py_blockmaxima.logscale.covariate
    jl_shapecov = py_blockmaxima.shape.covariate

    if py_blockmaxima.type == "BlockMaxima{Distributions.GeneralizedExtremeValue}":
        return Extremes.BlockMaxima(jl_data, jl_locationcov, jl_logscalecov, jl_shapecov)
    elif py_blockmaxima.type == "BlockMaxima{Distributions.Gumbel}":
        return Extremes.BlockMaxima(jl_data, jl_locationcov, jl_logscalecov)
        raise ValueError("Unsupported BlockMaxima type: {}".format(py_blockmaxima.type))

When I call this function by doing

jl_blockmaxima = py_blockmaxima_to_jl_blockmaxima(fm1.model) #fm1.model, in this case, is a python BlockMaxima object whose type attribute is the string “BlockMaxima{Distributions.GeneralizedExtremeValue}”

I get the error

TypeError: Julia: MethodError: no method matching BlockMaxima(::Variable, ::Vector{Variable}, ::Vector{Variable}, ::Vector{Variable})

I suspect that the issue is that I am calling BlockMaxima(…) instead of BlockMaxima{GeneralizedExtremeValue}(…), but I don’t know how to do that with juliacall… For instance, simply doing

return Extremes.BlockMaxima{GeneralizedExtremeValue}(jl_data, jl_locationcov, jl_logscalecov, jl_shapecov)

of course makes no sense in python and results in a syntax error.
If anyone knows how I could solve this conversion problem, I would be extremely grateful for some help!

Additional information:
• The Variable and paramfun types in my python code are classes I defined
• The ‘’Extremes’’ keyword in my python code is defined like so:

from juliacall import Main as jl
from juliapkg import add

add("Extremes", "fe3fe864-1b39-11e9-20b8-1f96fa57382d")
jl.seval("using Extremes")
Extremes = jl.Extremes

• Here’s a few examples of other conversion functions I wrote before for simpler structures:

def py_str_to_jl_symbol(str: str):
    return jl.Symbol(str)

def py_list_to_jl_vector(py_list: list):
    if all(isinstance(i, float) or isinstance(i, int) for i in py_list):
        return jl_convert(jl.Vector[jl.Real], py_list) 
    if all(isinstance(i, str) for i in py_list):
        return jl_convert(jl.Vector[jl.String], py_list) 
        return jl_convert(jl.Vector[jl.Any], py_list) # for other types of values
def jl_vector_to_py_list(jl_vector) -> list:
    return list(jl_vector)