# Julia's equivalent of Python's importlib.import\_module(path)

**URL:** <https://discourse.julialang.org/t/julias-equivalent-of-pythons-importlib-import-module-path/76746>\
**Category:** New to Julia\
**Created:** [February 19, 2022, 1:39pm UTC](https://discourse.julialang.org/t/julias-equivalent-of-pythons-importlib-import-module-path/76746 "2022-02-19T13:39:43Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![lodo1995](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lodo1995/32/33978_2.png) [@lodo1995](https://discourse.julialang.org/u/lodo1995)\
**Post date:** [February 19, 2022, 1:39pm UTC](https://discourse.julialang.org/t/julias-equivalent-of-pythons-importlib-import-module-path/76746/1 "2022-02-19T13:39:43Z")

</div>

I come from Python and I very often find myself loading code dynamically based on filesystem paths provided by the user (typically through command line). Typically they are either plugin code or configuration files (I like having my configurations as code). So each of these dynamically loaded files contains some “standard” methods / objects that are named the same in each file (which I then use from my main codebase) plus some internal components (which are typically file-specific and may or may not have name clashes).

In python I would do something like this:

```python
import importlib
import os
import sys

plugins = []
for plugin_path in requested_plugins:
    plugin_folder, plugin_name = os.path.split(plugin_path)
    sys.path.append(plugin_folder)
    plugin = importlib.import_module(plugin_name)
    plugin.activate() # every plugin file defines an activate() function
    plugins.append(plugin) # store every plugin to later call other functions defined by them

```

However, I don’t know how to achieve the same in Julia.  
I could not find a way to make `import` and `using` work with dynamic file paths.  
`include` works fine with dynamic paths, but has many problems:

1. it imports everything into the global scope; if the various plugins define structures with the same name but conflicting definitions, Julia throws an error
2. because everything is imported in the standard scope, there’s no need to return (and probably no way in Julia to build) a reference to the imported file; therefore, there’s no way to store a list of enabled plugins and the “standardized” functions in the global scope are those from the last plugin loaded
3. if for some reason I end up loading the same plugin again, Julia will happily rerun the file, potentially causing all sorts of issues, while Python caches all imported files and always returns the same reference to them.

So, what is the best way to achieve in Julia a similar behaviour to that piece of Python code above?

---

<div class="post-metadata">

**Author:** ![lrnv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lrnv/32/19373_2.png) [@lrnv](https://discourse.julialang.org/u/lrnv)\
**Post date:** [February 20, 2022, 1:25am UTC](https://discourse.julialang.org/t/julias-equivalent-of-pythons-importlib-import-module-path/76746/2 "2022-02-20T01:25:50Z")

</div>

Use modules.

In your files, wrap everything into a module. Then including the file would allow you tu use the module without clashes.

The bast way is then to put all your personal modules into a package, for easier use.

---

<div class="post-metadata">

**Author:** ![patrick-kidger](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrick-kidger/32/20378_2.png) [@patrick-kidger](https://discourse.julialang.org/u/patrick-kidger)\
**Post date:** [February 20, 2022, 3:52am UTC](https://discourse.julialang.org/t/julias-equivalent-of-pythons-importlib-import-module-path/76746/3 "2022-02-20T03:52:54Z")

</div>

Try [FromFile.jl](https://github.com/Roger-luo/FromFile.jl). This keeps scopes clean and won’t re-load files unnecessarily / buggily.

---

<div class="post-metadata">

**Author:** ![lodo1995](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lodo1995/32/33978_2.png) [@lodo1995](https://discourse.julialang.org/u/lodo1995)\
**Post date:** [February 20, 2022, 11:20am UTC](https://discourse.julialang.org/t/julias-equivalent-of-pythons-importlib-import-module-path/76746/4 "2022-02-20T11:20:20Z")

</div>

@lrnv thanks for your input!

I had thought about using modules before, but I couldn’t really understand how I would access the contents of each module without statically knowing the module name, which I don’t know because the loaded plugins will be different at each execution.

However, I now realized that I can use Julia’s metaprogramming capabilities to do that. So I came up with this code:

```julia
# Plugin loading phase
plugins = map(requested_plugins) do plugin_name
    include("$plugin_name.jl")
    Symbol(plugin_name)
end
# Unrelated code here...
do_other_unrelated_things()
# Plugin usage phase
for plugin in plugins
    eval(:($plugin.foo()))
end

```

With this, I can store my plugins and call their functions whenever I need to without polluting the global scope. It’s not perfect (as I will explain now), but it’s good enough that I can use it without issues in my current project.

There are still two things that Python’s `import_module` does better:

1. with this Julia code, I need to ensure that each plugin defines a module with a different name. It’s something to keep in mind, but in my scenario it’s not a deal breaker and so I consider it only a minor issue.
2. with this code, there’s still the problem of potentially loading the same module multiple times, which leads at best to an annoying warning on the console, and at worst to wrong behaviour if some magic is going on in the modules. While I don’t plan any magic in my modules, I still find this slightly more annoying than issue 1. It’s not a problem if I call my code from the command line (I can dedup any plugins passed as command line arguments), but it becomes annoying if I load my code in a REPL and run it multiple times (Spawning a Julia interpreter seems awfully slow, so I’m using this second option a lot more than in other languages)

@patrick-kidger I hoped I could replace `include` with `@from` as you suggested, to fix issue 2. But it does not seem to work in my case, or maybe I’m just too inexperienced with Julia to make it work properly. The issue seems to be that I need to use a statically-known string as the path to the file. If I try to use string interpolation as I do in my code above with `include`, I get a `MethodError`.

---

<div class="post-metadata">

**Author:** ![lodo1995](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lodo1995/32/33978_2.png) [@lodo1995](https://discourse.julialang.org/u/lodo1995)\
**Post date:** [February 20, 2022, 11:35am UTC](https://discourse.julialang.org/t/julias-equivalent-of-pythons-importlib-import-module-path/76746/5 "2022-02-20T11:35:25Z")

</div>

So, after a few more experiments, I think I might settle on the following code:

```julia
# Plugin loading phase
plugins = []
map(requested_plugins) do plugin_name
    plugin = Symbol(plugin_name)
    if ! @eval @isdefined($plugin)
        include("$plugin.jl")
    end
    push!(plugins, plugin)
end
# Unrelated code here...
do_other_unrelated_things()
# Plugin usage phase
for plugin in plugins
    @eval $plugin.foo()
end

```

It puts symbolic names of all requested plugin modules in a vector for later use, but only includes the corresponding files if they were not already included (which may happen when using the REPL).

---

<div class="post-metadata">

**Author:** ![patrick-kidger](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrick-kidger/32/20378_2.png) [@patrick-kidger](https://discourse.julialang.org/u/patrick-kidger)\
**Post date:** [February 20, 2022, 12:49pm UTC](https://discourse.julialang.org/t/julias-equivalent-of-pythons-importlib-import-module-path/76746/6 "2022-02-20T12:49:43Z")

</div>

You can use FromFile with dynamic names by doing `@eval @from $filename import foo`.

---

<div class="post-metadata">

**Author:** ![lodo1995](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lodo1995/32/33978_2.png) [@lodo1995](https://discourse.julialang.org/u/lodo1995)\
**Post date:** [February 20, 2022, 12:59pm UTC](https://discourse.julialang.org/t/julias-equivalent-of-pythons-importlib-import-module-path/76746/7 "2022-02-20T12:59:45Z")

</div>

@patrick-kidger ahah! Thanks! I knew there must have been some way to do it that I did not know.

Ok, here’s the new code using that syntax. I also switched `requested_plugins` from plugin names to plugin paths, to make it more similar to my original Python code

```julia
using FromFile: @from

plugins = []
map(requested_plugins) do plugin_path
    plugin_name = splitext(splitdir(plugin_path)[2])[1]
    plugin = Symbol(plugin_name)
    @eval @from $plugin_path import $plugin
    @eval $plugin.activate()
    push!(plugins, plugin)
end

```

---

<div class="post-metadata">

**Author:** ![lrnv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lrnv/32/19373_2.png) [@lrnv](https://discourse.julialang.org/u/lrnv)\
**Post date:** [February 20, 2022, 1:52pm UTC](https://discourse.julialang.org/t/julias-equivalent-of-pythons-importlib-import-module-path/76746/8 "2022-02-20T13:52:13Z")

</div>

If you have modules in files there is also the `impoet XXX as YYY` syntax
