# Using or import search current directory?

**URL:** <https://discourse.julialang.org/t/using-or-import-search-current-directory/123463>\
**Category:** New to Julia\
**Tags:** question\
**Created:** [December 4, 2024, 4:06pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463 "2024-12-04T16:06:17Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 4, 2024, 4:06pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/1 "2024-12-04T16:06:18Z")

</div>

This is really an idea which comes from Python. (Rust uses it too, actually.) Please don’t write it off just for this reason, as there is a practical problem to be solved here.

Consider the following example package.

```julia-auto
MyRepoName/
  PackageC/
    src/
      PackageC.jl
      Module1.jl
    test/
      runtests.jl
      test_PackageC.jl

```

With a few details:

```julia-auto
# test_PackageC.jl

using PackageC
using Test

@test PackageC.exampleC("helloworld") == "helloworld"

@test PackageC.Module1.exampleModule1() == 1

```

```julia-auto
# runtests.jl

println("running tests for PackageC")
include("test_PackageC.jl")

```

```julia-auto
# PackageC.jl

module PackageC

using Module1

function exampleC(arg::String)
    return arg
end

end

```

```julia-auto
# Module1.jl
module Module1

function exampleModule1()
    return 1
end

end

```

Finally, you would need to know something about how the environment has been setup.

The idea is to simply use `JULIA_LOAD_PATH` to set the installation location of the repository `MyRepoName`, regardless of what type of system it is installed to.

There are two good example usecases for this.

1. Developers own machine. The dev clones the repo and `export`s `JULIA_LOAD_PATH` to wherever he has cloned it. He can run `julia` from anywhere in the command line, and by running `using PackageC`, he can load `PackageC`. He can then test it, play with it, measure its performance, etc.
2. Deployment with Dockerfile. This one is simpler. Just write a Dockerfile. Copy `MyRepoName` to the container, and export `JULIA_LOAD_PATH`, pointing to the destination location where `MyRepoName` has been copied to.

Either way, what this means is:

- There is an entry in `LOAD_PATH` which points to `MyRepoName`. So, `using PackageX` works, because this directory will be searched for `PackageX`.

Ok that’s all the minimal information required to see what we are trying to do, and to undersand what doesn’t work.

# Why does this not work?

- `using Module1` does not work, because `Module1` cannot be loaded through the same mechanism by which `using PackageC` is loaded.
- The reason for it is Julia does not search the working directory of the source file containing a `using` or `import` statement.

# Difference with Python

In contrast, this will work in Python, because `import` will also search the directory containing the file from which a `using` or `import` statement is executed.

(Actually, to be fully accurate here, the `import` statement has to be of the form `import PackageName.ModuleName`, or `import .ModuleName` if using a relative import path.)

# Why does it matter?

The obvious solution here is to use `include`. However, this creates a problem.

The use of `include` explicitly causes code to be loaded from a file.

In contrast, with `import X`, Python looks to see if it already knows what `X` is, and if it does, it creates a reference to it. If it does not know what `X` is, then it will fall back to loading from file, searching the various locations specified in `PYTHON_PATH`. (Which might include the current working directory, or a directory local to the current working directory of the Python interpreter.)

The reason why we care about this is it would be useful for us to be able to dynamically load and replace code in a similar way to which Python is able to.

With `include` currently “locked” to loading code from files, and `using` and `import` currently “locked” to searching only the locations specified in `LOAD_PATH`, this currently isn’t possible - at least not in a straightforward way.

# Python example

It might be useful to see a short Python example which demonstrates this behavior.

Directory structure:

```julia-auto
MyRepoNamePython/
  src/
    PackagePy/
      __init__.py
      __main__.py
      Module1.py

```

```julia-auto
# __init__.py

import PackagePy.Module1

```

```julia-auto
# __main__.py

```

```julia-auto
# Module1.py

def exampleModule1():
    return 1

```

Run `python3` from `src`:

```julia-auto
>>> import PackagePy
>>> PackagePy.Module1.exampleModule1()
1
>>> def exampleModule1b():
      return 2

>>> PackagePy.Module1.exampleModule1 = exampleModule1b
>>> PackagePy.Module1.exampleModule1()
2

```

# Attempts at a Julia solution

Here I show some of my attempts to resolve this, by hacking around with `LOAD_PATH`. This doesn’t seem to work. I am not sure exactly why.

```julia-auto
module PackageC

push!(LOAD_PATH, "$(@ __DIR__ )")

using Module1

export exampleC

function exampleC(arg::String)
    return arg
end

pop!(LOAD_PATH)

end

```

However, even with these dynamic changes to `LOAD_PATH`, Julia still does not seem to be able to find `Module1.jl`.

Does anyone know why this is the case?

If you don’t find anything presented here compelling, perhaps the more succinct argument is more compelling:

- Since Julia is a dynamic language, it should be possible to dynamically replace types, functions, modules and packages.

Such a thing isn’t possible in Rust of course because it is statically typed.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [December 4, 2024, 5:23pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/2 "2024-12-04T17:23:06Z")

</div>

> [@world-peace](#):
>
> `using Module1`

The standard way to do this in Julia is

```julia
include("Module1.jl")
using .Module1

```

(I tend to think of the top-leve package file as analogous to a `Makefile` — it is where you put the information about which source files and directories make up the package. Each subdirectory can have its own top-level “Makefile” that gets included at the root level.)

There have been some proposals for an abbreviated syntax that lets you omit the `include`, e.g. `using ./Module1`, but this has never seemed to have been a big priority. Requiring one extra line in your “makefile” for submodules, which aren’t very common to begin with, hasn’t been an onerous requirement in practice. See

> <https://github.com/JuliaLang/julia/issues/4600>
>
> When I do
> 
> \`\`\` julia
> \# Main.jl
> module Main
> using .Foo
> end
> \`\`\`
> 
> and there's a f…ile called \`Foo.jl\` in the same directory as \`Main.jl\` it should be loaded. I suspect that relative \`using\` should also \_not\_ look in the global require places – i.e. \`Pkg.dir()\` and then \`LOAD\_PATH\`. The same applies to \`import\`.

There have also been several previous discourse threads about this. Just google “Julia import module current directory” — it’s pretty common for people coming from Python to expect files and modules to be interchangeable.

---

<div class="post-metadata">

**Author:** ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)\
**Post date:** [December 4, 2024, 6:49pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/3 "2024-12-04T18:49:29Z")

</div>

In particular, the GitHub issue you linked got pulled in at least two very different directions, which kind of blocked any further progress on the matter.

---

<div class="post-metadata">

**Author:** ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)\
**Post date:** [December 4, 2024, 7:07pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/4 "2024-12-04T19:07:43Z")

</div>

This is one of the ugly and annoying issues of Julia… But not everything can be perfect.

---

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 5, 2024, 12:14pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/5 "2024-12-05T12:14:27Z")

</div>

> [@stevengj](#):
>
> The standard way to do this in Julia is
> 
> ```julia
> include("Module1.jl")
> using .Module1
> 
> ```

Just wanted to draw attention to this quote from my post.

> [@world-peace](#):
>
> The use of `include` explicitly causes code to be loaded from a file.

My post was quite long, so it’s easy to miss this important detail.

Python doesn’t behave the same way. If the module has already been loaded, it won’t go and re-load it again. With `include` and Julia, the behavior is different. It always re-loads the code from disk, even if it has already been loaded.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [December 5, 2024, 12:37pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/6 "2024-12-05T12:37:11Z")

</div>

> [@world-peace](#):
>
> With `include` and Julia, the behavior is different. It always re-loads the code from disk, even if it has already been loaded.

So what? Why would you `include` it more than once? e.g. you do that a the top-level of your package (your “makefile”), and thereafter you do `using .Module1`, or `using ..Module1` from submodules of your package.

If you are `using` the module from completely _unrelated_ code (so that it doesn’t know what has already been included), then it should probably be put into a separate package.

---

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 5, 2024, 12:48pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/7 "2024-12-05T12:48:46Z")

</div>

> [@stevengj](#):
>
> So what?

I explained in detail in the first post.

If there are parts of it you don’t understand please ask specific questions.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [December 5, 2024, 1:08pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/8 "2024-12-05T13:08:04Z")

</div>

> [@world-peace](#):
>
> I explained in detail in the first post.
> 
> If there are parts of it you don’t understand please ask specific questions.

You only wrote

> The reason why we care about this is it would be useful for us to be able to dynamically load and replace code in a similar way to which Python is able to.

Could you give an example problem that you are trying to solve this way? (e.g. for replacing code during development/debugging, but for that we have Revise.jl.)

Julia is definitely less dynamic than Python in some ways, e.g. you can’t monkey-patch objects with new methods.

---

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 5, 2024, 2:36pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/9 "2024-12-05T14:36:58Z")

</div>

> [@stevengj](#):
>
> Could you give an example problem that you are trying to solve this way?

Yes. See my first post.

---

<div class="post-metadata">

**Author:** ![mhinsch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhinsch/32/26676_2.png) [@mhinsch](https://discourse.julialang.org/u/mhinsch)\
**Post date:** [December 5, 2024, 3:57pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/10 "2024-12-05T15:57:17Z")

</div>

The problem with the “standard” solution of `include` + `using .` is that it’s not 100% equivalent to just `using` as `include` effectively creates a copy of the module. Not an issue as long as your dependencies are neatly chained, but in more complicated situations it becomes a hassle.

I just recently stumbled over this. I have a function that is declared in a base module `B`. It is used in derived module `D` but needs to be overridden/specialised in support module `S`. Ideally I would have simply used `using B` in `D` and `import B` in `S`, but that doesn’t work of course. Setting the load path doesn’t work either since all of this is happening in a package. And `include` + `using .` doesn’t work, since then `D` and `S` both have their own copy of `B` and `D` doesn’t see `S`’ version of the function. What I ended up doing was to create a package module that `include`+`using .`s all the other packages. In `D` and `S` I then use `..B` to get access to the function. It works, but I think it’s really not a pretty solution.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [December 5, 2024, 4:47pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/11 "2024-12-05T16:47:13Z")

</div>

> [@mhinsch](#):
>
> What I ended up doing was to create a package module that `include`+`using .`s all the other packages. In `D` and `S` I then use `..B` to get access to the function. It works, but I think it’s really not a pretty solution.

If they don’t belong in the same package, why not just put B in its own package? If they are separate packages, why do you need include, as opposed to just adding the package to your current env?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [December 5, 2024, 4:52pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/12 "2024-12-05T16:52:13Z")

</div>

> [@world-peace](#):
>
> Yes. See my first post

You keep claiming that, but I’m not seeing an actual problem other than “I want it to work like Python modules”. You don’t explain why PackageC.jl can’t just use include, because you don’t say _why_ you need it to be more dynamic, other than re-iterating that this is what you want.

---

<div class="post-metadata">

**Author:** ![mhinsch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhinsch/32/26676_2.png) [@mhinsch](https://discourse.julialang.org/u/mhinsch)\
**Post date:** [December 5, 2024, 4:56pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/13 "2024-12-05T16:56:03Z")

</div>

So, `D` is a macro package. The generated code makes use of some functionality the interface functions of which are defined in `B`. `S` provides some simple generic implementations of these (some of which most users will probably want to use). I could have put everything into one big module of course but that seems rather messy. On the other hand `S` isn’t big enough to warrant the overhead of creating a package for it.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [December 5, 2024, 4:58pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/14 "2024-12-05T16:58:12Z")

</div>

> [@mhinsch](#):
>
> I could have put everything into one big module of course but that seems rather messy. On the other hand `S` isn’t big enough to warrant the overhead of creating a package for it.

If it’s easier for you to have it all in a single repo/package, I fail to see the problem with `using ..B` … this is how it’s supposed to work in a single integrated repo.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [December 5, 2024, 5:06pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/15 "2024-12-05T17:06:32Z")

</div>

It’s still not clear from your descriptions why the typical practice of evaluating (`include` possibly) a module once in one place and importing it everywhere else wouldn’t have worked.

---

<div class="post-metadata">

**Author:** ![mhinsch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhinsch/32/26676_2.png) [@mhinsch](https://discourse.julialang.org/u/mhinsch)\
**Post date:** [December 5, 2024, 5:07pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/16 "2024-12-05T17:07:01Z")

</div>

> [@stevengj](#):
>
> If it’s easier for you to have it all in a single repo/package, I fail to see the problem with `using ..B` … this is how it’s supposed to work in a single integrated repo.

It’s unnecessarily complicated and it enforces a very specific import structure (package-global module that imports everything else). I could also imagine that my solution would lead to plenty of knock-on headaches in a bigger/more complicated project. If local using/import was a thing all of that could be avoided.

---

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 5, 2024, 5:08pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/17 "2024-12-05T17:08:37Z")

</div>

It’s so we can dynamically replace modules, at runtime.

---

<div class="post-metadata">

**Author:** ![mhinsch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhinsch/32/26676_2.png) [@mhinsch](https://discourse.julialang.org/u/mhinsch)\
**Post date:** [December 5, 2024, 5:09pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/18 "2024-12-05T17:09:31Z")

</div>

> [@Benny](#):
>
> It’s still not clear from your descriptions why the typical practice of evaluating (`include` possibly) a module once in one place and importing it everywhere else wouldn’t have worked.

I _can’t_ use import/using since it is a local module. Changing the load path (which I usually use for non-package code) doesn’t work inside of a package.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [December 5, 2024, 5:12pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/19 "2024-12-05T17:12:13Z")

</div>

> [@world-peace](#):
>
> In contrast, this will work in Python, because `import` will also search the directory containing the file from which a `using` or `import` statement is executed.

And it won’t happen in Julia because files do not encapsulate modules, `module` expressions do. Note that Python doesn’t even have a `module` expression, it automatically considers each file its own module.

> [@world-peace](#):
>
> In contrast, with `import X`, Python looks to see if it already knows what `X` is, and if it does, it creates a reference to it.

This happens for Julia packages. If separating into a package to `add` to your environment is overkill, then evaluating the module is the only way you can let Julia know of its existence. Only do it once per session; think of it as a session-wise `add` and `rm`, not `import`.

> [@world-peace](#):
>
> dynamically replace modules, at runtime.

Watch out, this is risky for correctness and isn’t as easily done in Python like you’re implying. Like you said, after a Python module is loaded and imported, it’s referenced on subsequent imports. That means if you change the file’s contents, subsequent imports will ignore it. You need `importlib.reload` to actually reload the module, then you’ll have to repeat any from-import statements to update those references. They’re not forcing you to do busy work, it’s genuinely difficult to track down and sometimes impossible to replace an obsolete module and its contents. You have to juggle 2 modules with the same name, and that is awful to work with. You don’t even have to experiment with modules and files to see this, just redefine a superclass and verify that obsolete instances and superclassing remains. Python’s as dynamic as it gets but that has its practical limits.

The same applies to Julia, only more so because compilation bakes in references to modules; it’s one of a few ways it needs to be less dynamic for optimization. Revise is used because it doesn’t evaluate the `module` expression again, you just modify the original module from file edits.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [December 5, 2024, 5:20pm UTC](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463/20 "2024-12-05T17:20:55Z")

</div>

> [@mhinsch](#):
>
> I _can’t_ use import/using since it is a local module. Changing the load path (which I usually use for non-package code) doesn’t work inside of a package.

Sorry, there must be some disconnect in language here that is making it hard for us to understand what you’re doing exactly. Evaluating the module once and importing it in other modules is routine, no need to change load paths because the import statement searches through the nested `module`s. This is what stevengj and I were talking about, to be precise:

```julia
julia> module A # possibly `include`d from a file
        export x
        x = "hello world"
       end
Main.A

julia> module B
        using ..A # searches parent module of B for A
        println(x)
       end
hello world
Main.B

julia> module C
        using ..A # searches parent module of C for A
        println(x)
       end
hello world
Main.C

julia> B.A === C.A # same module, didn't "reload" it
true

```

You’re probably trying to do something different, but it’s not clear what.

[Next page](https://discourse.julialang.org/t/using-or-import-search-current-directory/123463.md?page=2)
