Skip to content

Commit 6813a89

Browse files
author
Christopher Doris
committed
new meta format, much faster to load
1 parent d5b9b6e commit 6813a89

File tree

4 files changed

+101
-58
lines changed

4 files changed

+101
-58
lines changed

.gitignore

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
Manifest.toml
2-
PythonCallMeta.json
3-
deps/deps.jl
4-
deps/build.log
2+
PythonCallMeta
53
.ipynb_checkpoints
64
__pycache__
75
conda_env

juliacall/init.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
else:
3737
jlenv = os.path.join(prefix, "julia_env")
3838
CONFIG['jlenv'] = os.path.join(jlenv)
39-
CONFIG['meta'] = os.path.join(jlenv, "PythonCallMeta.json")
39+
CONFIG['meta'] = os.path.join(jlenv, "PythonCallPyMeta")
4040

4141
# Determine whether or not to skip resolving julia/package versions
4242
skip = deps.can_skip_resolve()

src/cpython/context.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function init_context()
3232
if is_pythoncall || depends_on_pythoncall
3333
jlenv = proj isa String ? dirname(proj) : env
3434
CTX.jlenv = jlenv
35-
Deps._meta_file[] = joinpath(jlenv, "PythonCallMeta.json")
35+
Deps._meta_file[] = joinpath(jlenv, "PythonCallMeta")
3636
break
3737
end
3838
end

src/deps.jl

Lines changed: 98 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,90 @@ import Conda, JSON, TOML, Pkg, Dates, ..PythonCall
44

55
### META
66

7-
const _meta_file = Ref("")
8-
9-
meta_file() = _meta_file[]
10-
11-
function load_meta()
12-
fn = meta_file()
13-
isfile(fn) ? open(JSON.parse, fn) : Dict{String,Any}()
7+
# increment whenever the format changes
8+
const META_VERSION = 1
9+
10+
Base.@kwdef struct Meta
11+
timestamp::Float64
12+
load_path::Vector{String}
13+
version::VersionNumber
14+
files::Vector{String}
15+
conda_packages::Vector{String}
16+
conda_channels::Vector{String}
17+
pip_packages::Vector{String}
18+
pip_indexes::Vector{String}
19+
scripts::Vector{String}
1420
end
1521

16-
save_meta(meta) = open(io -> JSON.print(io, meta), meta_file(), "w")
22+
function write_meta(io::IO, meta::Meta)
23+
write(io, Int(META_VERSION))
24+
write_meta(io, meta.timestamp)
25+
write_meta(io, meta.load_path)
26+
write_meta(io, meta.version)
27+
write_meta(io, meta.files)
28+
write_meta(io, meta.conda_packages)
29+
write_meta(io, meta.conda_channels)
30+
write_meta(io, meta.pip_packages)
31+
write_meta(io, meta.pip_indexes)
32+
write_meta(io, meta.scripts)
33+
end
1734

18-
function get_meta(keys...)
19-
meta = load_meta()
20-
for key in keys
21-
if haskey(meta, key)
22-
meta = meta[key]
23-
else
24-
return
25-
end
35+
write_meta(io::IO, x::Float64) = write(io, x)
36+
write_meta(io::IO, x::VersionNumber) = write_meta(io, string(x))
37+
function write_meta(io::IO, x::String)
38+
write(io, Int(sizeof(x)))
39+
write(io, x)
40+
end
41+
function write_meta(io::IO, x::Vector)
42+
write(io, Int(length(x)))
43+
for y in x
44+
write_meta(io, y)
2645
end
27-
meta
2846
end
2947

30-
function set_meta(args...)
31-
length(args) < 2 && error("setmeta() takes at least 2 arguments")
32-
here = meta = load_meta()
33-
for key in args[1:end-2]
34-
here = get!(Dict{String,Any}, here, key)
48+
function read_meta(io::IO)
49+
if read(io, Int) == META_VERSION
50+
Meta(
51+
timestamp = read_meta(io, Float64),
52+
load_path = read_meta(io, Vector{String}),
53+
version = read_meta(io, VersionNumber),
54+
files = read_meta(io, Vector{String}),
55+
conda_packages = read_meta(io, Vector{String}),
56+
conda_channels = read_meta(io, Vector{String}),
57+
pip_packages = read_meta(io, Vector{String}),
58+
pip_indexes = read_meta(io, Vector{String}),
59+
scripts = read_meta(io, Vector{String}),
60+
)
61+
end
62+
end
63+
read_meta(io::IO, ::Type{Float64}) = read(io, Float64)
64+
read_meta(io::IO, ::Type{VersionNumber}) = VersionNumber(read_meta(io, String))
65+
function read_meta(io::IO, ::Type{String})
66+
n = read(io, Int)
67+
bytes = read(io, n)
68+
length(bytes) == n || error()
69+
return String(bytes)
70+
end
71+
function read_meta(io::IO, ::Type{Vector{T}}) where {T}
72+
n = read(io, Int)
73+
x = Vector{T}()
74+
for i in 1:n
75+
push!(x, read_meta(io, T))
3576
end
36-
here[args[end-1]] = args[end]
37-
save_meta(meta)
77+
return x
78+
end
79+
80+
const _meta_file = Ref("")
81+
82+
meta_file() = _meta_file[]
83+
84+
function load_meta()
85+
fn = meta_file()
86+
isfile(fn) ? open(read_meta, fn) : nothing
3887
end
3988

89+
save_meta(meta::Meta) = open(io -> write_meta(io, meta), meta_file(), "w")
90+
4091
### CONDA
4192

4293
const _conda_env = Ref("")
@@ -150,19 +201,13 @@ function can_skip_resolve()
150201
# resolve if the conda environment doesn't exist yet
151202
isdir(conda_env()) || return false
152203
# resolve if we haven't resolved before
153-
deps = get_meta("jldeps")
204+
deps = load_meta()
154205
deps === nothing && return false
155206
# resolve whenever the PythonCall version changes
156-
version = get(deps, "version", nothing)
157-
version === nothing && return false
158-
version != string(PythonCall.VERSION) && return false
207+
deps.version == PythonCall.VERSION || return false
159208
# resolve whenever any of the environments in the load_path changes
160-
timestamp = get(deps, "timestamp", nothing)
161-
timestamp === nothing && return false
162-
timestamp = max(timestamp, stat(meta_file()).mtime)
163-
load_path = get(deps, "load_path", nothing)
164-
load_path === nothing && return false
165-
load_path != Base.load_path() && return false
209+
timestamp = max(deps.timestamp, stat(meta_file()).mtime)
210+
deps.load_path == Base.load_path() || return false
166211
for env in Base.load_path()
167212
proj = Base.env_project_file(env)
168213
dir = nothing
@@ -286,14 +331,14 @@ function resolve(; create=true, force=false)
286331
env = conda_env()
287332
skip = !force && isdir(env)
288333
if skip
289-
depinfo = get_meta("jldeps")
290-
skip &= (
334+
depinfo = load_meta()
335+
skip = (
291336
depinfo !== nothing &&
292-
conda_channels == get(depinfo, "conda_channels", nothing) &&
293-
conda_packages == get(depinfo, "conda_packages", nothing) &&
294-
pip_indexes == get(depinfo, "pip_indexes", nothing) &&
295-
pip_packages == get(depinfo, "pip_packages", nothing) &&
296-
scripts == get(depinfo, "scripts", nothing)
337+
conda_channels == depinfo.conda_channels &&
338+
conda_packages == depinfo.conda_packages &&
339+
pip_indexes == depinfo.pip_indexes &&
340+
pip_packages == depinfo.pip_packages &&
341+
scripts == depinfo.scripts
297342
)
298343
end
299344

@@ -311,6 +356,7 @@ function resolve(; create=true, force=false)
311356
append!(conda_args, conda_packages)
312357
if create || !isdir(env)
313358
ispath(env) && conda_run_root(`env remove --yes --prefix $env`)
359+
ispath(env) && Base.rm(env, force=true, recursive=true)
314360
conda_run_root(`create --yes --no-default-packages --no-channel-priority --prefix $env $conda_args`)
315361
conda_activate()
316362
else
@@ -336,18 +382,17 @@ function resolve(; create=true, force=false)
336382
end
337383

338384
# record what we did
339-
depinfo = Dict(
340-
"timestamp" => time(),
341-
"load_path" => Base.load_path(),
342-
"version" => string(PythonCall.VERSION),
343-
"files" => all_deps_files,
344-
"conda_packages" => conda_packages,
345-
"conda_channels" => conda_channels,
346-
"pip_packages" => pip_packages,
347-
"pip_indexes" => pip_indexes,
348-
"scripts" => scripts,
349-
)
350-
set_meta("jldeps", depinfo)
385+
save_meta(Meta(
386+
timestamp = time(),
387+
load_path = Base.load_path(),
388+
version = PythonCall.VERSION,
389+
files = all_deps_files,
390+
conda_packages = conda_packages,
391+
conda_channels = conda_channels,
392+
pip_packages = pip_packages,
393+
pip_indexes = pip_indexes,
394+
scripts = scripts,
395+
))
351396
end
352397

353398
return

0 commit comments

Comments
 (0)