Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conflicting environments are handled incorrectly #35663

Open
timholy opened this issue Apr 30, 2020 · 15 comments
Open

Conflicting environments are handled incorrectly #35663

timholy opened this issue Apr 30, 2020 · 15 comments
Labels
packages Package management and loading

Comments

@timholy
Copy link
Member

timholy commented Apr 30, 2020

I'm reporting this to julia itself (rather than in Pkg.jl) because key components of the fix seemingly must be made in base/loading.jl. No objections, though, if folks want to transfer this to Pkg.jl.

This bug comes in two closely-related flavors.

Flavor 1: switching between incompatible environments should be disallowed

Suppose I have two projects with incompatible [compat] requirements:

Proj1.toml:

name = "Proj1"
uuid = "61c9ae6d-79d8-47c4-9a39-97bb6365db78"
authors = ["Tim Holy <tim.holy@gmail.com>"]
version = "0.1.0"

[deps]
ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"

[compat]
ColorTypes = "0.8"

Proj2.toml:

name = "Proj2"
uuid = "7bf079ca-24d6-42d5-9ea6-67ba343487f2"
authors = ["Tim Holy <tim.holy@gmail.com>"]
version = "0.1.0"

[deps]
ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"

[compat]
ColorTypes = "0.10"
Colors = "0.12"

Now let's use these in a Julia session:

tim@diva:/tmp/envs/Proj1$ julia --project
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.4.2-pre.0 (2020-04-15)
 _/ |\__'_|_|_|\__'_|  |  release-1.4/ef4fe83698* (fork: 122 commits, 121 days)
|__/                   |

julia> using ColorTypes

shell> cd ../Proj2/
/tmp/envs/Proj2

(Proj1) pkg> activate .
 Activating environment at `/tmp/envs/Proj2/Project.toml`

julia> using Colors
[ Info: Precompiling Colors [5ae59095-9a9b-59fe-a467-6f913c188581]
ERROR: LoadError: UndefVarError: XRGB not defined
Stacktrace:
 [1] top-level scope at /home/tim/.julia/packages/Colors/k4h4b/src/Colors.jl:7 (repeats 2 times)
 [2] include(::Module, ::String) at ./Base.jl:377
 [3] top-level scope at none:2
 [4] eval at ./boot.jl:331 [inlined]
 [5] eval(::Expr) at ./client.jl:449
 [6] top-level scope at ./none:3
in expression starting at /home/tim/.julia/packages/Colors/k4h4b/src/Colors.jl:7
ERROR: Failed to precompile Colors [5ae59095-9a9b-59fe-a467-6f913c188581] to /home/tim/.julia/compiled/v1.4/Colors/NKjaT_sPqJx.ji.
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] compilecache(::Base.PkgId, ::String) at ./loading.jl:1272
 [3] _require(::Base.PkgId) at ./loading.jl:1029
 [4] require(::Base.PkgId) at ./loading.jl:927
 [5] require(::Module, ::Symbol) at ./loading.jl:922
 [6] eval(::Module, ::Any) at ./boot.jl:331
 [7] eval_user_input(::Any, ::REPL.REPLBackend) at /home/tim/src/julia-1/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86
 [8] run_backend(::REPL.REPLBackend) at /home/tim/.julia/packages/Revise/WkyNB/src/Revise.jl:1023
 [9] top-level scope at none:0

julia> 

That error is very confusing for the developer. XRGB was defined in ColorTypes v0.10, and I've declared that Colors v0.12 depends on it. So how can XRGB be undefined? The key information---that I've switched environments---is rarely reported by users. (timholy/Revise.jl#468, JuliaIO/QuartzImageIO.jl#62)

To me, it seems that the right way to handle it is to throw

(Proj1) pkg> activate .
 Activating environment at `/tmp/envs/Proj2/Project.toml`
ERROR: `/tmp/envs/Proj2/Project.toml` depends on ColorTypes v0.10, but ColorTypes v0.8.1 is already loaded.
Consider starting a fresh Julia session in this environment.

AFAICT, currently we don't save any information about the version of loaded packages. This is why I suspect we need to fix this partially in base/loading.jl.

Flavor 2: creation of new environments must preserve session compatibility

If you've loaded one version of a package, creation of new environments should probably write compatible version info into the Manifest of a newly-created environment.

(@v1.4) pkg> generate Proj1
 Generating  project Proj1:
    Proj1/Project.toml
    Proj1/src/Proj1.jl

shell> cd Proj1/
/tmp/envs/Proj1

(@v1.4) pkg> activate .
 Activating environment at `/tmp/envs/Proj1/Project.toml`

(Proj1) pkg> add ColorTypes@0.8
   Updating registry at `~/.julia/registries/General`
   Updating git-repo `git@github.com:JuliaRegistries/General.git`
   Updating registry at `~/.julia/registries/HolyLabRegistry`
   Updating git-repo `git@github.com:HolyLab/HolyLabRegistry.git`
  Resolving package versions...
   Updating `/tmp/envs/Proj1/Project.toml`
  [3da002f7] + ColorTypes v0.8.1
   Updating `/tmp/envs/Proj1/Manifest.toml`
  [3da002f7] + ColorTypes v0.8.1
  [53c48c17] + FixedPointNumbers v0.7.1
  [9a3f8284] + Random 
  [9e88b42a] + Serialization 

julia> using ColorTypes
[ Info: Precompiling ColorTypes [3da002f7-5984-5a60-b8a6-cbb66c0b333f]

shell> cd ..
/tmp/envs

(Proj1) pkg> generate Proj2
 Generating  project Proj2:
    Proj2/Project.toml
    Proj2/src/Proj2.jl

shell> cd Proj2/
/tmp/envs/Proj2

(Proj1) pkg> activate .
 Activating environment at `/tmp/envs/Proj2/Project.toml`

(Proj2) pkg> add ColorTypes
  Resolving package versions...
   Updating `/tmp/envs/Proj2/Project.toml`
  [3da002f7] + ColorTypes v0.10.2
   Updating `/tmp/envs/Proj2/Manifest.toml`
  [3da002f7] + ColorTypes v0.10.2
  [53c48c17] + FixedPointNumbers v0.8.0
  [9a3f8284] + Random 
  [9e88b42a] + Serialization 

(Proj2) pkg> add Colors
  Resolving package versions...
   Updating `/tmp/envs/Proj2/Project.toml`
  [5ae59095] + Colors v0.12.0
   Updating `/tmp/envs/Proj2/Manifest.toml`
  [5ae59095] + Colors v0.12.0
  [189a3867] + Reexport v0.2.0
  [2a0f44e3] + Base64 
  [ade2ca70] + Dates 
  [b77e0a4c] + InteractiveUtils 
  [76f85450] + LibGit2 
  [8f399da3] + Libdl 
  [56ddb016] + Logging 
  [d6f4376e] + Markdown 
  [44cfe95a] + Pkg 
  [de0858da] + Printf 
  [3fa0cd96] + REPL 
  [ea8e919c] + SHA 
  [6462fe0b] + Sockets 
  [cf7118a7] + UUIDs 
  [4ec0a83e] + Unicode 

julia> using Colors
[ Info: Precompiling Colors [5ae59095-9a9b-59fe-a467-6f913c188581]
ERROR: LoadError: UndefVarError: XRGB not defined
Stacktrace:
 [1] top-level scope at /home/tim/.julia/packages/Colors/k4h4b/src/Colors.jl:7 (repeats 2 times)
 [2] include(::Module, ::String) at ./Base.jl:377
 [3] top-level scope at none:2
 [4] eval at ./boot.jl:331 [inlined]
 [5] eval(::Expr) at ./client.jl:449
 [6] top-level scope at ./none:3
in expression starting at /home/tim/.julia/packages/Colors/k4h4b/src/Colors.jl:7
ERROR: Failed to precompile Colors [5ae59095-9a9b-59fe-a467-6f913c188581] to /home/tim/.julia/compiled/v1.4/Colors/NKjaT_sPqJx.ji.
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] compilecache(::Base.PkgId, ::String) at ./loading.jl:1272
 [3] _require(::Base.PkgId) at ./loading.jl:1029
 [4] require(::Base.PkgId) at ./loading.jl:927
 [5] require(::Module, ::Symbol) at ./loading.jl:922

It would be a little weird that a new project gets affected by the environment of an unrelated package, but until we can load multiple versions of the same module I don't see a good alternative.

@KristofferC
Copy link
Member

KristofferC commented Apr 30, 2020

Just to give a third flavor where you get a compat problem without switching project after loading a package:

export JULIA_DEPOT_PATH="/tmp/.julia"
julia -q
(v1.4) pkg> add Colors@0.12
...
   Updating `/private/tmp/.julia/environments/v1.4/Project.toml`
  [5ae59095] + Colors v0.12.0
   Updating `/private/tmp/.julia/environments/v1.4/Manifest.toml`
  [3da002f7] + ColorTypes v0.10.2
  [5ae59095] + Colors v0.12.0
...

(v1.4) pkg> activate CustomProject
 Activating new environment at `/private/tmp/envs/CustomProject/Project.toml`

(CustomProject) pkg> add ColorTypes@0.8
...
  [3da002f7] + ColorTypes v0.8.1 # Colors is incompatible with this
   Updating `/private/tmp/envs/CustomProject/Manifest.toml`
  [3da002f7] + ColorTypes v0.8.1
  [53c48c17] + FixedPointNumbers v0.7.1

julia> using Colors # this is the first package load
[ Info: Precompiling Colors [5ae59095-9a9b-59fe-a467-6f913c188581]
ERROR: LoadError: UndefVarError: XRGB not defined

The reason for the error here is that while Colors is loaded from the global project, it pulls in dependencies from the manifest of the active project and these might be incompatible.

@KristofferC
Copy link
Member

KristofferC commented Apr 30, 2020

Note that the behavior in my last comment is explicitly documented https://docs.julialang.org/en/v1/manual/code-loading/#Environment-stacks-1Th but I thought it was worth pointing out anyway since it is related.

  • Packages in non-primary environments can end up using incompatible versions of their dependencies even if their own environments are entirely compatible. This can happen when one of their dependencies is shadowed by a version in an earlier environment in the stack (either by graph or path, or both).

Since the primary environment is typically the environment of a project you're working on, while environments later in the stack contain additional tools, this is the right trade-off: it's better to break your development tools but keep the project working. When such incompatibilities occur, you'll typically want to upgrade your dev tools to versions that are compatible with the main project.

@timholy
Copy link
Member Author

timholy commented Apr 30, 2020

Interesting. One could modify my suggestion in the OP to warn users about the incompatibility only if a package operation fails. OTOH, this would fail to catch cases where there is no compile-fatal mismatch, only a functionality-correctness-fatal mismatch. I worry the last class of problems is really hard to debug properly.

@KristofferC
Copy link
Member

I think it would at least make sense to warn when loading a package and that package is already loaded from a different path than where it would be otherwise be loaded from.

@StefanKarpinski
Copy link
Member

I think that warning upon activating a different environment (i.e. Flavor 1) is the most viable option. I'm don't think that putting a warning in the code loading path when an earlier stack environment shadows the dependency of a package from later in the stack since that is fine 95% of the time and just works. That seems like it's going to be a very common warning that people will just learn to ignore and will become a pointless annoyance that makes Julia look janky—like method ambiguity warnings of old.

@StefanKarpinski
Copy link
Member

While warning when a dependency is already loaded from somewhere other than where it would be loaded if a later environment was used in isolation would be too fiddly, it might be ok if we recorded compat info in manifests when resolving them and checked if the already-loaded version is within the compat bounds or not. Recording in the manifest is helpful since otherwise you have to look in potentially a lot of random places to figure out what the compat bounds are, whereas you already had to find a dependency in the manifest in order to know what to load, so we're already doing that work mostly.

@tkf
Copy link
Member

tkf commented Apr 30, 2020

How about manually editing LOAD_PATH and DEPOT_PATH? Isn't it easier to deal with it if you warn while loading?

@StefanKarpinski
Copy link
Member

Code loading needs to be pretty simple: we are already pushing the limits of what it's reasonable to do while loading code. Anything that avoids complicating code loading further is better.

@tkf
Copy link
Member

tkf commented Apr 30, 2020

Isn't it impossible to detect LOAD_PATH and DEPOT_PAT manually edited by the user other than during code loading? Or do you mean to create a custom vector type so that it can be detected when, e.g., a new element is pushfirst!-ed? Or just not support conflict detection in such case?

@timholy
Copy link
Member Author

timholy commented Sep 21, 2021

This is causing massive problems in ArrayInterface: JuliaArrays/ArrayInterface.jl#206, JuliaArrays/ArrayInterface.jl#207, JuliaArrays/ArrayInterface.jl#208.

@KristofferC
Copy link
Member

I would say the real flavor 2 is when you load a package from e.g. the default environment but one of the dependencies of that package exists in the currently active environment. Julia will then just load the dependency in the active environment which very well may be incompatible with the package.

This is described in https://docs.julialang.org/en/v1/manual/code-loading/#Environment-stacks:

Packages in non-primary environments can end up using incompatible versions of their dependencies even if their own environments are entirely compatible. This can happen when one of their dependencies is shadowed by a version in an earlier environment in the stack (either by graph or path, or both).

Regarding flavor 1. The way to do this is IMO to recompute the path when we try load an already loaded package and compare that to the path where the package got originally loaded from. If these two differ, then warn. Right now, I think it is a bit too expensive to do that path recomputation every time, but I am working on some code loading caching (in a similar vein to #37906) that would make this cheap enough to be feasible.

@junglegobs
Copy link

junglegobs commented Sep 23, 2021

I would add that this is an annoying issue in VSCode, since you always "go through" your principal environment before activating a more specific environment. This means that currently I have to* run everything from the terminal and explicitly specifying julia --project=@. before I run.

* I'm sure I could fix this if I spent enough time trying to update all my packages, but I've already spent 2 days on this and I have no wish to spend more.

@davidanthoff
Copy link
Contributor

@junglegobs are you starting Julia from a shell within VS Code? If so, then yes you have to do the steps you describe, but that is very much not the recommended way! If you start the Julia REPL via the Julia: Start REPL command, then it will automatically activate the environment that is active in VS Code at the moment. VS Code itself looks at the root of the folder that you open for a Project.toml/Manifest.toml combo, and if there is one, it automatically activates that. If you change the active environment in VS Code with the Julia: Change environment command (might be named slightly differently), then it will save that choice in a .vscode/settings.json file, and the next time you open that folder it will automatically activate that environment. This works well for say a global environment.

The short version of this is: if you use Julia: Start REPL then VS Code should handle environment activation for you and there is no need to do anything manually or use command line flags etc.

Some mod might mark both the original comment and my response here as off-topic, they are probably more of a distraction from the original problem here :)

@junglegobs
Copy link

junglegobs commented Sep 24, 2021

@davidanthoff I was technically using the Julia: Start REPL command, but by right clicking on a file and doing Julia: Execute file. The bit that I missed was that I wasn't changing environment using Julia: Change environment but just pkg> activate <env>. Anyway, despite this, I still get the same issue i.e. using AxisArrays leads to an @constprop not defined error.

To mods who might deem this off topic, I would say this highlights how confusing this conflicting environments issue can be, since I do not understand what the difference between shell -> Julia and VSCode -> Julia could be at this point.

EDIT: The issue was that I was using the VSCode Julia REPL with a system image, so I had to re-compile this and then it worked.

bors bot added a commit to CliMA/TurbulenceConvection.jl that referenced this issue Oct 11, 2021
373: Use consistent environments r=charleskawczynski a=charleskawczynski

See [this issue](JuliaLang/julia#35663).

Co-authored-by: Charles Kawczynski <kawczynski.charles@gmail.com>
@rntz
Copy link

rntz commented Mar 21, 2023

Any progress on this? This is still causing regular issues for me. I don't understand the details, but in timholy/Revise.jl#727, @timholy suggests this should be fixable now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
packages Package management and loading
Projects
None yet
Development

No branches or pull requests

8 participants