-
-
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
Artifacts: disallow downloading via reflection hacks on Pkg #37844
Conversation
We can’t do this; this is necessary functionality for lazy artifacts. |
What is the issue with calling |
We simply cannot be depending on the presence of Pkg at runtime. And that example is for LinearAlgebra, which should also not be doing that, but it's for |
How about if Artifacts allow one to set a "download backed" and Pkg sets that and if it isn't set it errors? Same way how Base gets a handle to the REPL. |
Nothing should depend on the existence of |
I assume this is because we want to prepare for a future where e.g. Pkg_modules = filter(p-> p[1].name == "Pkg", Base.loaded_modules)
if isempty(Pkg_modules)
error(...)
end
return jointail(first(Pkg_modules)[2].Artifacts.ensure_artifact_installed(string(name), artifacts_toml; platform), path_tail) |
I'm planning for a future (hopefully near, since we've talked about it every month this summer on our internal compiler call), where Pkg is never available, except if you explicitly load it (and, unless you are Pkg or the REPL, take the corresponding performance hit). So that suggestion is identical to this one. |
So in that case, the |
I'd like to get this merged for the v1.6 release, before anyone is depending on this hack, as this is preparation for various future improvements (as demonstrated in #38119 for example) where Pkg cannot be accessed by packages during compilation. As can be seen there, this PR is necessary first to be able to continue to pass tests in such as world, where Pkg cannot be assumed to be reliably accessed. |
From triage: We should have artifact_str expand to explicitly do an |
So the behavior is that if you want to support lazy artifacts, you |
yes, that's the idea |
9999d5d
to
6552d7a
Compare
Yes, that's what this PR should do now |
stdlib/Artifacts/src/Artifacts.jl
Outdated
Pkg = first(filter(p-> p[1].name == "Pkg", Base.loaded_modules))[2] | ||
return jointail(Pkg.Artifacts.ensure_artifact_installed(string(name), artifacts_toml; platform), path_tail) | ||
if ensure_artifact_installed === nothing | ||
error("Artifact $(repr(name)) was not installed correctly. Try `using Pkg; Pkg.build(\"$__module__\"); Pkg.Artifacts.ensure_all_artifacts_installed($(repr(artifacts_toml)))`?") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this error tell the package author to add using Pkg
to their package?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be telling them to prefer worse behavior. However, I like staticfloat's suggestion of examining the toml to figure out which was probably intended.
stdlib/Artifacts/src/Artifacts.jl
Outdated
@@ -605,14 +607,17 @@ macro artifact_str(name, platform=nothing) | |||
# Invalidate calling .ji file if Artifacts.toml file changes | |||
Base.include_dependency(artifacts_toml) | |||
|
|||
# Check if the user has run `using Pkg.Artifacts: ensure_artifact_installed`, and thus supports lazy artifacts | |||
ensure_artifact_installed = isdefined(__module__, :ensure_artifact_installed) ? GlobalRef(__module__, :ensure_artifact_installed) : nothing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need to be this explicit? Why not just using Pkg
or using Pkg.Artifacts
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of looking for :ensure_artifact_installed
I think it's better to look for :Pkg
, so that users can use import Pkg
to avoid polluting their namespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did that initially, but eventually felt this polluted the namespace the least and gives the most forward-compatibility. using Pkg.Artifacts
is a non-starter (since it'll conflict with using Artifacts
), while Pkg
is just a rather short name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ensure_artifact_installed
is a private API, and we can change that name at any time. It would be better to ask the user to import something that will not change, like Pkg
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, I'm wrong, ensure_artifact_installed
is not private, it's exported. I still think it's better (in the event that we switch names in the future, after an appropriate deprecation period) to depend on Pkg
though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, I'm wrong, ensure_artifact_installed is not private, it's exported.
Well, it should really have been private.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. It's not exported from Pkg
, but it is exported from Artifacts
because it's used in other parts of Pkg
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Writing using Pkg: Pkg
also pollutes the namespace, but uses a more common variable name than using Pkg: Artifacts.ensure_artifact_installed
uses. Since the existing lazy
feature here already means that changing the name of this would be breaking (once we release Artifacts v1.6 and users are depending on it), so you've already committed yourself to having Pkg
provide this function. So the question here is whether you want the API to be requiring the existence of a global variable named Pkg
to signal the existence of Pkg.Artifacts
and Pkg.Artifacts.ensure_artifact_installed
(with possibility for there to be additional functions added later that this macro may select instead), or whether the user should be more explicit about the name ensure_artifact_installed
(with possibility that the user can provide other implementations of this function in the future, and thus aren't forced to always depend upon Pkg in the future).
Or as a combination, we could rename Pkg.Artifacts
to Pkg.ArtifactsDownloader
(or some other name of your liking), so that it doesn't conflict with the name of this package, and thus gives you the flexibility to make this also a toplevel package in the future (of the same name, or with using ArtifactsNext: ArtifactsNext as ArtifactsDownloader
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To me it's a clear choice: looking at Pkg
gives something that users will most likely already have (much more likely than ensure_artifact_installed
, for instance) and also gives us the flexibility in code to polymorph to future Pkg
releases where we re-organize things. It pushes the work of backwards-compatibility onto Artifacts
instead of onto user code, which is always a good thing when it has no performance impact.
stdlib/Artifacts/src/Artifacts.jl
Outdated
@@ -605,14 +607,17 @@ macro artifact_str(name, platform=nothing) | |||
# Invalidate calling .ji file if Artifacts.toml file changes | |||
Base.include_dependency(artifacts_toml) | |||
|
|||
# Check if the user has run `using Pkg.Artifacts: ensure_artifact_installed`, and thus supports lazy artifacts | |||
ensure_artifact_installed = isdefined(__module__, :ensure_artifact_installed) ? GlobalRef(__module__, :ensure_artifact_installed) : nothing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of looking for :ensure_artifact_installed
I think it's better to look for :Pkg
, so that users can use import Pkg
to avoid polluting their namespace.
stdlib/Artifacts/src/Artifacts.jl
Outdated
Pkg = first(filter(p-> p[1].name == "Pkg", Base.loaded_modules))[2] | ||
return jointail(Pkg.Artifacts.ensure_artifact_installed(string(name), artifacts_toml; platform), path_tail) | ||
if ensure_artifact_installed === nothing | ||
error("Artifact $(repr(name)) was not installed correctly. Try `using Pkg; Pkg.build(\"$__module__\"); Pkg.Artifacts.ensure_all_artifacts_installed($(repr(artifacts_toml)))`?") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
error("Artifact $(repr(name)) was not installed correctly. Try `using Pkg; Pkg.build(\"$__module__\"); Pkg.Artifacts.ensure_all_artifacts_installed($(repr(artifacts_toml)))`?") | |
meta = artifact_meta(name, artifact_dict, artifacts_toml; platform) | |
if meta["lazy"] | |
error("Artifact $(repr(name)) is a lazy artifact; package developers must `import Pkg` before using lazy artifacts.") | |
else | |
error("Artifact $(repr(name)) was not installed correctly. Try `using Pkg; Pkg.instantiate()` to re-install all missing resources.") | |
end |
There are two cases where this can go wrong; the first is something terrible happened during Pkg.add()
and we're in an inconsistent state. As far as I know, this has never happened, but in the event that it does, Pkg.build()
isn't the right fix, Pkg.instantiate()
is, I think.
The second is that this is a package developer adding an artifact to their package and being surprised when lazy artifacts don't work.
We can disambiguate the two by looking to see if the corresponding entry in artifact_dict
marks the artifact as lazy.
stdlib/Artifacts/test/runtests.jl
Outdated
with_artifacts_directory(tempdir) do | ||
ex = @test_throws ErrorException artifact"socrates" | ||
@test startswith(ex.value.msg, "Artifact \"socrates\" was not installed correctly.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should test both the "not installed correctly" branch and the "must import Pkg
" branch, and the branch that actually succeeds in downloading the lazy artifact.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm only testing the new code here, but someone should write/have written a test for the lazy code too
6552d7a
to
fa37e72
Compare
fa37e72
to
c5d4168
Compare
If we go with the |
Ah, right, I forget from triage that we were intending to add a deprecation warning for that situation |
c5d4168
to
4379411
Compare
Okay, added support for the old solution of loading all of Pkg. We don't want to require people to always load all of Pkg, so it's marked depwarn. I think this now fully covers what we decided on the triage call? |
I've lost track, what's the recommended way to use artifacts now? |
Packages should never access Base.loaded_modules() to call functions from it, as it can be brittle and create future incompatibilities, so instead we require the user to explicitly declare a dependency on the lazy-download machinery, if they requiring the ability to use it (for lazy artifacts). As a deprecation, if the user has `using Pkg`, that will be used instead, with a depwarn.
4379411
to
2ef080e
Compare
There's no major changes for typical users. Lazy users now need to explicitly add LazyArtifacts to your Manifest, rather than just hoping it's available at runtime. |
…JuliaLang/julia#37844) Packages should never access Base.loaded_modules() to call functions from it, as it can be brittle and create future incompatibilities, so instead we require the user to explicitly declare a dependency on the lazy-download machinery, if they requiring the ability to use it (for lazy artifacts). As a deprecation, if the user has `using Pkg`, that will be used instead, with a depwarn.
Packages should never access
Base.loaded_modules()
to call functions from it. This avoids #37731 (in Artifacts, though the new LazyArtifacts test still would have triggered it), though doesn't fix the Pkg bug that causes it (which is now fixed).Example: