Problem with referencing modules

import/using in Julia actually behaves similarly to how Python’s import reuses code, it’s the include that’s tripping you up. If you’re used to IPython, its runfile does the same thing of evaluating code in a file. With Base Python, you can do the same with with open("TimeFrames.py") as f: exec(f.read()). Of course, these are not used as regularly as include is in Julia because Python turns every file into a module and the file directory into a module tree, so you’re forced to make a module the same size as a file. On the other hand, Julia makes a module tree with nested module statements, and include lets you write a global scope (such as a module) across multiple files. The concepts are orthogonal; a module block can include multiple files, and an included file can have multiple module statements in it.

The problem with multiple evaluations is that duplicated code is separate. For example, if you run the code x = 3 in two different .py files, those x’s will both exist independently, even if you import both of those files as modules. Let’s see a stripped down structure of your CandlesTest code just to see modules and imports, with marked import fixes and without includes for now:

Click to see modules structure
module CandlesTest
  using Test, Dates, Printf
  module Prices
    using Dates
  end
  using .Prices
  module TimeFrames
    using Dates
  end
  using .TimeFrames
  module Candles  ### move here where TimeFrames and Prices exist
    using Base: String, Float64, Vector
    using Printf
    import Base: show, open, close, print, string
    using ..TimeFrames  ### reuse CandlesTest.TimeFrames
    using ..Prices  ### reuse CandlesTest.Prices
  end
  using .Candles
end

The modules imported without the dots . are fine, they are packages searched from an independent place, and the code is not reevaluated. To reuse modules you evaluated only once, you just needed to adjust the order of evaluating your modules and the number of dots, which is like the number of modules levels you traverse. 1 dot using .Prices stays in the module CandlesTest, and 2 dots using ..Prices goes along Candles to CandlesTest.

Now let’s introduce some includes, the code itself may run as one nested unit but you certainly don’t want to lump all the actual code into one massive file.

Click to see files structure
## prices.jl
module Prices
  using Dates
end

## timeframes.jl
module TimeFrames
  using Dates
end

## candles.jl
module Candles
    using Base: String, Float64, Vector
    using Printf
    import Base: show, open, close, print, string
    using ..TimeFrames  ### reuse CandlesTest.TimeFrames
    using ..Prices  ### reuse CandlesTest.Prices
end

## candlestest.jl, this is the one you run ##
module CandlesTest
  using Test, Dates, Printf
  include("prices.jl")
  using .Prices
  include("timeframes.jl")
  using .TimeFrames
  include("candles.jl")   ### move here where TimeFrames and Prices exist
  using .Candles
end

That’ll take care of the repeated evaluation issue, but you probably noticed that CandlesTest is surrounding everything, and you might have intended the test module to be independent of the rest. It’s ok, you just have to refactor the CandlesTest module as a module on the same level as Prices, TimeFrames, and Candles, and adjust your using statements’ accordingly.

Click to see adjusted CandlesTest
## candlestest.jl ##
module CandlesTest
  using Test, Dates, Printf
  using ..Prices
  using ..TimeFrames
  using ..Candles
end

## main file you run ##
include("prices.jl")
using .Prices
include("timeframes.jl")
using .TimeFrames
include("candles.jl")   ### move here where TimeFrames and Prices exist
using .Candles
include("candlestest.jl")
using .CandlesTest

As you can see, all the includes of independent modules show up in the same file, which is good practice to keep track of them if you ever refactor your module structure. includes elsewhere are fine if those are guaranteed to run in unchanged locations, like if I’m splitting the code specific inside a module across smaller files (note that those files would not have a top-level module statement because I want every file’s code to reside in and share the one module’s scope).

2 Likes