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).