-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
1.11.1: breaking change in extension loading #56204
Comments
Juliahub search indicates that quite a few packages call get_extension in their extensions, so maybe this bug manifests itself somewhere else as well. Or maybe not, depends on the order I guess, didn't check myself. |
AFAIK |
Yeah, this is a bit unfortunate. The issue was that loading extensions in other extensions as was done previously was "unsound" and could easily cause "circular dependencies" leading to bad behavior like:
I didn't see any new regressions from PkgEval from backporting it which is why I assumed it wouldn't have a big impact on the ecosystem but maybe this was hidden by other errors. In hindsight it was probably wrong to backport it. The use case people want seem to basically be #48734 so we should probably work on fixing that feature. In the mean time the question is if we revert the extension loading change for 1.11.2. |
It's mentioned and explained in Pkg docs, https://pkgdocs.julialang.org/v1/creating-packages/. Afaiu, in 1.10 and earlier this means this function is public. As for loading, the same Pkg docs page writes that
Naturally, after loading MyPkg + WeakDepA I expect MyPkg -> WeakDepAExt to be loaded as well. So, I found a workaround: add
Unfortunately, yet another case of PkgEval not catching an actual issue :( Have to I say, I don't have a good understanding about it in practice. At least UnionCollections tests (example from the first post) tests succeed on 1.10 and 1.11.0, but fail on 1.11.1. |
I looked in the PkgEval log and UnionCollections is here: https://s3.amazonaws.com/julialang-reports/nanosoldier/pkgeval/by_hash/0de7b6c_vs_501a4f2/UnionCollections.primary.log. And we can see the extension fails to load but the package tests end up passing anyway... |
Indeed, you are right, I wasn't looking carefully enough! Still not 100% used to seeing "big scary errors" that are practically just warnings :) |
I guess there is no test that checks that the function in the extension works? |
Indeed, maybe it slipped through the cracks over time... Anyway, I confirm that with |
Although, in some scenarios it leads to
... |
You might try using a guarded # workaround for https://github.com/JuliaLang/julia/issues/56204
function __init__()
if !Base.generating_output()
Base.eval(quote
const DictionariesExt = Base.get_extension(UnionCollections, :DictionariesExt)
FlexiMaps.mapview(f, d::DictionariesExt.UnionDictionary) = @modify(vs -> mapview(f, vs), DictionariesExt._values(d))
# TODO disambiguation:
# FlexiMaps.mapview(p::Union{Symbol,Int,String}, A::UnionDictionary) = mapview(PropertyLens(p), A)
end)
end
end
Using |
I think the above workaround works with an appropriate pre-compile guard (the |
Another affected package is Transformers.js which relies on Flux's CUDA extension in its own CUDA extension. https://github.com/chengchingwen/Transformers.jl/blob/master/ext/TransformersCUDAExt/TransformersCUDAExt.jl It compiles just fine but crashes in runtime with |
As this 1.11.1 change turned out to be quite breaking (5 packages even mentioned this issue) – any plans to fix/revert it in 1.11.2? |
This was very much meant to go into 1.11.0 but due to human error didn't. We could revert it but it will still have to come for 1.12 because the way things were were fundamentally unsound (as can be seen by the linked issues). |
A workaround would be to use Requires.jl style of conditional loading since that doesn't support precompilation which is the issue here. |
This also seems to break precompilation of the Sunny.jl package. Sunny defines its plotting extensions in a module PlottingExt that depends on any variant of Makie (e.g. GLMakie or CarioMakie both pull in the Makie package, and therefore trigger loading of PlottingExt). Additionally, Sunny defines an extension module GLMakiePrecompilesExt that precompiles code specific to the GLMakie backend, if it is loaded. Somehow this workflow became broken in 1.11.1, perhaps due to the order in which modules load (GLMakiePrecompilesExt depends on PlottingExt, which maybe requires something like #48734 or #55589 (comment)?) |
Sounds somewhat similar to "it will still have to come for Julia 2.0 anyway, so we are putting it into minor version already" :) The workaround for this regression is quite nontrivial, if !Base.generating_output()
Base.eval(quote ... and this only works in limited circumstances IIUC. Also, the solution isn't even explained anywhere aside from this issue. At the very least, could the changelog/NEWS for each version include a section like "known breaking changes" with recommended workarounds? |
If #56368 lands, there will be a proper solution for this in 1.11.2:
For example: [extensions]
PlottingExt = "Plots"
StatisticsPlottingExt = ["Plots", "Statistics"] With this, You can even do this across packages. For example, for the Transformers.jl + Flux.jl case above you'd need: [extensions]
TransformersCUDAExt = ["Flux", "CUDA"] TransformersCUDAExt triggers on |
Not really since the 1.10 behavior is buggy and would leave extensions in a state where their use should be actively discouraged since you at any point (from e.g. someone adding a dependency downstream) could cause your package to break. |
… strict superset (#56368) This is an effort at a proper workaround (feature?) to resolve #56204. For example: ```toml [extensions] PlottingExt = "Plots" StatisticsPlottingExt = ["Plots", "Statistics"] ``` Here `StatisticsPlottingExt` is allowed to depend on `PlottingExt` This provides a way to declare `ext → ext` dependencies while still avoiding any extension cycles. The same trick can also be used to make an extension in one package depend on an extension provided in another. We'll also need to land #49891, so that we guarantee these load in the right order.
…ng#56368) This is an effort at a proper workaround (feature?) to resolve JuliaLang#56204. For example: ```toml [extensions] PlottingExt = "Plots" StatisticsPlottingExt = ["Plots", "Statistics"] ``` Here `StatisticsPlottingExt` is allowed to depend on `PlottingExt` This provides a way to declare `ext → ext` dependencies while still avoiding any extension cycles. The same trick can also be used to make an extension in one package depend on an extension provided in another. We'll also need to land JuliaLang#49891, so that we guarantee these load in the right order.
I tested on the backports branch for Julia 1.11.2 using |
This should now be resolved in 1.11.2, using the pattern / feature described in #56204 (comment) |
Not too often I see packages broken by patch versions of Julia, but it's one of those cases :(
As a specific example,
using UnionCollections, Dictionaries, FlexiMaps
throwsERROR: LoadError: type Nothing has no field UnionDictionary
on this line: https://github.com/JuliaAPlavin/UnionCollections.jl/blob/0000000018ecd297a2e7448671da83c035b3da4c/ext/FlexiMapsDictionariesExt.jl#L8.Effectively, it does
get_extension(MyPackage, :WeakDepA)
in the extension for WeakDepA + WeakDepB.Judging by the documentation, it should work:
get_extension
returns the extension if it's loadedIn the specific case with UnionCollections, this is what I expect to happen on that
using
line:And that's indeed how the observable behavior worked on earlier Julia versions, including 1.11.0. But not on 1.11.1.
The text was updated successfully, but these errors were encountered: