How to canonically load and run user-provided functions from a file?

Let’s consider a Julia package OurPackage.jl that creates a long-running process. In this process, from time to time we want to load a user-provided file and execute a function implemented in it. For example, the user would provide files setup1.jl, setup2.jl, each with a function init_setup(), and then trigger loading them, in which case the file should be read and then the latest init_setup is executed.

At the moment, the relevant code we use looks something like

Base.include(Main, filename)
state = Base.invokelatest(Main.init_setup)

Is the best way to achieve our goal or is there is another, more canonical approach?

One downside is, for example, that the user files typically require to load a package OtherPackage.jl. If include this in Main, we need to make sure that the Project.toml already has OtherPackage.jl installed.

On the other hand, we could call Base.include(OurPackage, filename) and just have OtherPackage.jl as a dependency in it. However, in that case there is a risk that the user defines auxiliary variables which will overwrite something we have defined in OurPackage.jl.

A third option could be to ask the users to always wrap their files in a module, e.g., module UserModule .... end, and we’d change the code above then to

Base.include(OurPackage, filename)
state = Base.invokelatest(OurPackage.UserModule.init_setup)

Has anyone grappled with such a use caes before and can give us some pointers as to which is the “cleanest” approach here?

Does the function have to execute within the long running process? Could you instead start a new Julia process (via Distributed) to activate the user environment and run that function?

Yes.

No. This is - potentially - running in an MPI parallel environment, so starting new processes is out of the question.