Using or import search current directory?

It’s actually not that rare. Modules are the only way to create namespaces in Julia, and namespaces can have quite fine-grained use cases. An example is EnumX.jl, which provides an improved @enum using a module under the hood. It wouldn’t be very ergonomic if each enum had to live in a separate file.

Do you know about the ability to enter into a module at the REPL? The Julia REPL · The Julia Language


This has definitely been discussed a lot in the past. You can find almost all of them just by searching for the number 4600. It’s also something that’s fundamentally very near-and-dear to folks hearts: how you prefer to organize your code is very opinionated and widely varies and can be quite different between languages.

4 Likes

I did not know about that, that is interesting, thanks for the tip.

Maybe I’m just misinterpreting this, but how is include-ing a file to evaluate an unpackaged module and importing its names into other modules substantially different from Rust’s option to insert a file’s text into a mod definition and use to import names into other modules? Rust even made that decision despite 1-file-per-module being idiomatic, see Why would rust use both mod and use? - help - The Rust Programming Language Forum

Well, yes. Packages are (and must be) modules, but not all modules are packages. An installed, versioned, and compiled unit can’t be handled the same way as your own source code. Rust has packages, crates, and mod-ules too. If anything it seems like Python is the odd one out here.

Sorry, I likely misunderstood your intent, specifically your post’s phrasing “dynamically load and replace code” and “dynamically replace types, functions, modules and packages.” That said, I’m evidently not alone in failing to see what the intent is; all this stuff about imports don’t really seem relevant to your initial Python example for reassigning exampleModule1 to a different function in the REPL.

I largely agree with that assessment.

A positive effect of this discussion has been that it prompted me to read up on the details of the module/package system again. This made me realise that I had misunderstood a few things and that the system is actually very consistent.

However, in my opinion there are two issues:

  • Having use/import handle modules and packages without any obvious visible difference on the users’ side leads to endless amounts of confusion, especially for people coming from languages with a file ↔ module correspondence.
  • There is no obvious, lightweight solution for the (in my mind very common) use case “encapsulate a local chunk of code and load it like a package”.
5 Likes

The lightest-weight solution is probably to modify LOAD_PATH manually. Although this has been discouraged previously in the thread, there’s currently no alternative if you want to load modules like packages without dealing with the overhead of creating a new package.

Although it’s not framed this way, the manual section on package directories is really an explanation of how Julia deals with entries in LOAD_PATH that don’t have the full structure of a package. It’s not written in a user-friendly way, so here’s the CliffsNotes version:

  • Put your code in a file
  • Wrap it in a top-level module such that the module name exactly matches the file name
  • Put the file in the package directory[1]

Now the module can be loaded like a package. Example:

  • My package directory is ./code
  • It contains a single file code/Foo.jl with the following contents:
    module Foo
    
    export foo
    
    foo() = println("Hello from Foo.jl")
    
    end
    
  • Now let’s load Foo like a package:
    julia> push!(LOAD_PATH, "code");  # add ./code to LOAD_PATH
    
    julia> using Foo
    [ Info: Precompiling Foo [top-level]
    
    julia> foo()
    Hello from Foo.jl
    

I think this is the closest you can currently get to a python-like import .file experience. The only extra overhead is making sure the module and file names match, and a single push to LOAD_PATH.


  1. Alternatively, put the file in a package-like folder structure inside the package directory, that is, instead of adding Foo.jl you can add Foo/src/Foo.jl ↩︎

4 Likes

Yes, I’ve been doing that for my own projects for a while. The problem is that this stops working as soon as a project.toml exists, since then all package dependencies have to be declared explicitly.

1 Like

Can you give an example of when this occurs? (None of the previous examples in the thread use package directories.) I assume you’re referring to something caused by the following rules (from the manual):

  1. A package with a project file cannot depend on one without a project file since packages with project files can only load packages in graph and packages without project files do not appear in graph.
  2. A package with a project file but no explicit UUID can only be depended on by packages without project files since dummy UUIDs assigned to these packages are strictly internal.

I assume that’s the reason, yes. In practice I realised it when I started preparing a package from a project I had only used locally until then.