From 885594abced99d0d7aa76cf65e051fa4ca3824c7 Mon Sep 17 00:00:00 2001 From: David Varela <00.varela.david@gmail.com> Date: Thu, 30 May 2019 14:54:47 -0700 Subject: [PATCH] Introduce query API --- docs/src/api.md | 1 + src/API.jl | 33 +++++++++++++++++++---------- src/Operations.jl | 13 ++++++++++++ src/Pkg.jl | 44 ++++++++++++++++++++++++++++++++++++--- src/Types.jl | 14 ++++++++++++- test/pkg.jl | 53 +++++++++++++++++++++++++++++++---------------- test/repl.jl | 33 +++++++++++++++-------------- 7 files changed, 143 insertions(+), 48 deletions(-) diff --git a/docs/src/api.md b/docs/src/api.md index 0132085779..ffbfd34143 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -32,6 +32,7 @@ Pkg.gc Pkg.status Pkg.precompile Pkg.setprotocol! +Pkg.info ``` diff --git a/src/API.jl b/src/API.jl index 70a9dcacdd..9856ca7c1f 100644 --- a/src/API.jl +++ b/src/API.jl @@ -18,6 +18,28 @@ preview_info() = printstyled("───── Preview mode ─────\n"; c include("generate.jl") +info(;kwargs...) = info(Context(), PackageSpec[]; kwargs...) +info(uuid::UUID; kwargs...) = info([uuid]; kwargs...) +info(uuids::Vector{UUID}; kwargs...) = info(Context(), [PackageSpec(;uuid=uuid) for uuid in uuids]; kwargs...) +function info(ctx::Context, pkgs::Vector{PackageSpec}; + active=false, direct=false, all=false, kwargs... + )::Dict{UUID, PackageInfo} + if active + if ctx.env.pkg === nothing + @warn "Active environment is not a project" + else + pkg = deepcopy(ctx.env.pkg) + pkg.path = dirname(ctx.env.project_file) + push!(pkgs, pkg) + end + end + direct && Operations.load_direct_deps!(ctx, pkgs) + all && Opeartions.load_all_deps!(ctx, pkgs) + + find_registered!(ctx.env, UUID[pkg.uuid for pkg in pkgs]) + return Dict(pkg.uuid => Operations.package_info(ctx, pkg; kwargs...) for pkg in pkgs) +end + function check_package_name(x::AbstractString, mode=nothing) if !(occursin(Pkg.REPLMode.name_re, x)) message = "$x is not a valid packagename." @@ -246,17 +268,6 @@ function test(ctx::Context, pkgs::Vector{PackageSpec}; return end -installed() = __installed(PKGMODE_PROJECT) -function __installed(mode::PackageMode=PKGMODE_MANIFEST) - diffs = Display.status(Context(), PackageSpec[], mode=mode, use_as_api=true) - version_status = Dict{String, Union{VersionNumber,Nothing}}() - diffs == nothing && return version_status - for entry in diffs - version_status[entry.name] = entry.new.ver - end - return version_status -end - function gc(ctx::Context=Context(); kwargs...) Context!(ctx; kwargs...) ctx.preview && preview_info() diff --git a/src/Operations.jl b/src/Operations.jl index f95c5cf3b4..e1df357bdd 100644 --- a/src/Operations.jl +++ b/src/Operations.jl @@ -64,6 +64,7 @@ end function load_all_deps!(ctx::Context, pkgs::Vector{PackageSpec}; version::Bool=true) for (uuid, entry) in ctx.env.manifest + pkgs[uuid] === nothing || continue # dont duplicate packages push!(pkgs, PackageSpec(name=entry.name, uuid=uuid, path=entry.path, version = version ? something(entry.version, VersionSpec()) : VersionSpec(), repo=entry.repo, tree_hash=entry.tree_hash)) @@ -1298,4 +1299,16 @@ function test(ctx::Context, pkgs::Vector{PackageSpec}; coverage=false, test_fn=n end end +function package_info(ctx::Context, pkg::PackageSpec; + name=false, version=false, pinned=false, developed=false, source=false + )::PackageInfo + info = PackageInfo() + name && (info.name = pkg.name) + version && (info.version = (pkg.version == VersionSpec() ? nothing : pkg.version)) + pinned && (info.pinned = pkg.pinned) + source && (info.source = project_rel_path(ctx, source_path(pkg))) + developed && (info.developed = pkg.path !== nothing) + return info +end + end # module diff --git a/src/Pkg.jl b/src/Pkg.jl index c3ae7db859..3ae79ed88a 100644 --- a/src/Pkg.jl +++ b/src/Pkg.jl @@ -198,9 +198,6 @@ redirecting to the `build.log` file. """ const build = API.build -# TODO: decide what to do with this -const installed = API.installed - """ Pkg.pin(pkg::Union{String, Vector{String}}) Pkg.pin(pkgs::Union{PackageSpec, Vector{PackageSpec}}) @@ -264,6 +261,47 @@ const develop = API.develop #TODO: Will probably be deprecated for something in PkgDev const generate = API.generate +""" + Pkg.info(; keywords...)::Dict{UUID, PackageInfo} + Pkg.info(uuid::UUID; keywords...)::Dict{UUID, PackageInfo} + Pkg.info(uuids::Vector{UUID}; keywords...)::Dict{UUID, PackageInfo} + +Query the dependecy graph. +You can choose which packages to query by specyfing a list of uuids or by using *selector* keywords. + +`Pkg.info` accepts two classes of keywords **selectors** and **properties**. +**Selectors** will add certain packages to the set of packages to be queried. +**Properties** determine which package properties will be queried and written to the `PackageInfo` structs. +`nothing` is used as the default value of `PackageInfo` fields when no query has been executed. + +Selectors: + +| `keyword` | `API` | +|:----------|:------------------------------------------------------------------------------------| +| `active` | If the active environment is a project, add it to the set of packages to be queried | +| `direct` | Add all direct dependecies to the set of packages to be queried | +| `all` | Add all dependencies to the set of packages to be queried | + +Properites: + +| `keyword` | `Type` | `Description` | +|:------------|:----------------|:---------------------------------------------------| +| `name` | `String` | The name of the package | +| `version` | `VersionNumber` | The version of the package | +| `developed` | `Bool` | Whether a package is directly tracking a directory | +| `pinned` | `Bool` | Whether a package is pinned | +| `source` | `String` | The directory containing the package's source code | + +# Examples +``` +Pkg.info(;direct=true, name=true, version=true) +Pkg.info(UUID("7876af07-990d-54b4-ab0e-23690620f79a"); developed=true) +Pkg.info(UUID("7876af07-990d-54b4-ab0e-23690620f79a"); direct=true, developed=true) +``` + +""" +const info = API.info + """ Pkg.instantiate() diff --git a/src/Types.jl b/src/Types.jl index 7588a3e02f..6ecac82333 100644 --- a/src/Types.jl +++ b/src/Types.jl @@ -19,7 +19,7 @@ using SHA export UUID, pkgID, SHA1, VersionRange, VersionSpec, empty_versionspec, Requires, Fixed, merge_requires!, satisfies, ResolverError, - PackageSpec, EnvCache, Context, GitRepo, Context!, get_deps, + PackageSpec, EnvCache, Context, PackageInfo, GitRepo, Context!, get_deps, PkgError, pkgerror, has_name, has_uuid, is_stdlib, write_env, parse_toml, find_registered!, project_resolve!, project_deps_resolve!, manifest_resolve!, registry_resolve!, stdlib_resolve!, handle_repos_develop!, handle_repos_add!, ensure_resolved, instantiate_pkg_repo!, manifest_info, registered_uuids, registered_paths, registered_uuid, registered_name, @@ -1348,4 +1348,16 @@ function write_env(ctx::Context; display_diff=true) write_manifest(env.manifest, env, old_env, ctx; display_diff=display_diff) end +### +### PackageInfo +### + +Base.@kwdef mutable struct PackageInfo + name::Union{Nothing,String} = nothing + version::Union{Nothing,VersionNumber} = nothing + pinned::Union{Nothing,Bool} = nothing + developed::Union{Nothing,Bool} = nothing + source::Union{Nothing,String} = nothing +end + end # module diff --git a/test/pkg.jl b/test/pkg.jl index f338e7aac2..ce5f45bc06 100644 --- a/test/pkg.jl +++ b/test/pkg.jl @@ -147,21 +147,21 @@ temp_pkg_dir() do project_path @testset "adding and upgrading different versions" begin # VersionNumber Pkg.add(PackageSpec(TEST_PKG.name, v"0.3")) - @test Pkg.API.__installed()[TEST_PKG.name] == v"0.3" + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version == v"0.3" Pkg.add(PackageSpec(TEST_PKG.name, v"0.3.1")) - @test Pkg.API.__installed()[TEST_PKG.name] == v"0.3.1" + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version == v"0.3.1" Pkg.rm(TEST_PKG.name) # VersionRange Pkg.add(PackageSpec(TEST_PKG.name, VersionSpec(VersionRange("0.3.0-0.3.2")))) - @test Pkg.API.__installed()[TEST_PKG.name] == v"0.3.2" + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version == v"0.3.2" # Check that adding another packages doesn't upgrade other packages Pkg.add("Test") - @test Pkg.API.__installed()[TEST_PKG.name] == v"0.3.2" + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version == v"0.3.2" Pkg.update(; level = UPLEVEL_PATCH) - @test Pkg.API.__installed()[TEST_PKG.name] == v"0.3.3" + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version == v"0.3.3" Pkg.update(; level = UPLEVEL_MINOR) - @test Pkg.API.__installed()[TEST_PKG.name].minor != 3 + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version.minor != 3 Pkg.rm(TEST_PKG.name) end @@ -177,27 +177,27 @@ temp_pkg_dir() do project_path @testset "pinning / freeing" begin Pkg.add(TEST_PKG.name) - old_v = Pkg.API.__installed()[TEST_PKG.name] + old_v = Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version Pkg.pin(PackageSpec(TEST_PKG.name, v"0.2")) - @test Pkg.API.__installed()[TEST_PKG.name].minor == 2 + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version.minor == 2 Pkg.update(TEST_PKG.name) - @test Pkg.API.__installed()[TEST_PKG.name].minor == 2 + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version.minor == 2 Pkg.free(TEST_PKG.name) Pkg.update() - @test Pkg.API.__installed()[TEST_PKG.name] == old_v + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version == old_v Pkg.rm(TEST_PKG.name) end @testset "develop / freeing" begin Pkg.add(TEST_PKG.name) - old_v = Pkg.API.__installed()[TEST_PKG.name] + old_v = Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version Pkg.rm(TEST_PKG.name) mktempdir() do devdir withenv("JULIA_PKG_DEVDIR" => devdir) do @test_throws PkgError Pkg.develop(Pkg.PackageSpec(url="bleh", rev="blurg")) Pkg.develop(TEST_PKG.name) @test isinstalled(TEST_PKG) - @test Pkg.API.__installed()[TEST_PKG.name] > old_v + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version > old_v test_pkg_main_file = joinpath(devdir, TEST_PKG.name, "src", TEST_PKG.name * ".jl") @test isfile(test_pkg_main_file) # Pkg #152 @@ -223,7 +223,7 @@ temp_pkg_dir() do project_path @test isfile(joinpath(devdir, TEST_PKG.name, "deps", "deps.jl")) Pkg.test(TEST_PKG.name) Pkg.free(TEST_PKG.name) - @test Pkg.API.__installed()[TEST_PKG.name] == old_v + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version == old_v end end end @@ -235,7 +235,7 @@ temp_pkg_dir() do project_path @testset "stdlibs as direct dependency" begin uuid_pkg = (name = "CRC32c", uuid = UUID("8bf52ea8-c179-5cab-976a-9e18b702a9bc")) Pkg.add("CRC32c") - @test haskey(Pkg.API.__installed(), uuid_pkg.name) + @test haskey(Pkg.info(;direct=true,version=true), TEST_PKG.uuid) Pkg.update() # Disable until fixed in Base # Pkg.test("CRC32c") @@ -332,7 +332,7 @@ end temp_pkg_dir() do project_path @testset "libgit2 downloads" begin Pkg.add(TEST_PKG.name; use_libgit2_for_all_downloads=true) - @test haskey(Pkg.installed(), TEST_PKG.name) + @test haskey(Pkg.info(;direct=true,version=true), TEST_PKG.uuid) @eval import $(Symbol(TEST_PKG.name)) @test_throws SystemError open(pathof(eval(Symbol(TEST_PKG.name))), "w") do io end # check read-only Pkg.rm(TEST_PKG.name) @@ -344,7 +344,7 @@ temp_pkg_dir() do project_path cd(joinpath(dir, "UnregisteredWithProject")) do with_current_env() do Pkg.update() - @test haskey(Pkg.API.__installed(), "Example") + @test haskey(Pkg.info(;direct=true), TEST_PKG.uuid) end end end @@ -354,12 +354,12 @@ end temp_pkg_dir() do project_path @testset "libgit2 downloads" begin Pkg.add(TEST_PKG.name; use_libgit2_for_all_downloads=true) - @test haskey(Pkg.API.__installed(), TEST_PKG.name) + @test haskey(Pkg.info(;direct=true), TEST_PKG.uuid) Pkg.rm(TEST_PKG.name) end @testset "tarball downloads" begin Pkg.add("JSON"; use_only_tarballs_for_downloads=true) - @test haskey(Pkg.API.__installed(), "JSON") + @test "JSON" in [pkg.name for (uuid, pkg) in Pkg.info(;direct=true,name=true)] Pkg.rm("JSON") end end @@ -775,6 +775,23 @@ end end end end +@testset "query interface basic tests" begin + temp_pkg_dir() do project_path; with_temp_env() do + Pkg.develop("Example") + Pkg.add("Unicode") + Pkg.add("Markdown") + info = Pkg.info(;direct=true, name=true, developed=true) + @test length(info) == 3 + xs = Dict(uuid => pkg for (uuid, pkg) in info if pkg.developed) + @test length(xs) == 1 + @test xs[TEST_PKG.uuid].name == "Example" + @test xs[TEST_PKG.uuid].pinned == nothing + @test xs[TEST_PKG.uuid].version == nothing + @test xs[TEST_PKG.uuid].developed == true + @test xs[TEST_PKG.uuid].source == nothing + end end +end + include("repl.jl") include("api.jl") include("registry.jl") diff --git a/test/repl.jl b/test/repl.jl index 29eb43df0f..639e2d5f34 100644 --- a/test/repl.jl +++ b/test/repl.jl @@ -66,7 +66,7 @@ temp_pkg_dir(;rm=false) do project_path; cd(project_path) do; pkg"activate ." pkg"add Example@0.5" @test isinstalled(TEST_PKG) - v = Pkg.API.__installed()[TEST_PKG.name] + v = Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version pkg"rm Example" pkg"add Example#master" @@ -79,15 +79,17 @@ temp_pkg_dir(;rm=false) do project_path; cd(project_path) do; pkg"test Example" @test isinstalled(TEST_PKG) - @test Pkg.API.__installed()[TEST_PKG.name] > v + @test Pkg.info(;direct=true,version=true)[TEST_PKG.uuid].version > v pkg = "UnregisteredWithoutProject" p = git_init_package(tmp_pkg_path, joinpath(@__DIR__, "test_packages/$pkg")) Pkg.REPLMode.pkgstr("add $p; precompile") @eval import $(Symbol(pkg)) - @test Pkg.API.__installed()[pkg] == v"0.0" + @show Pkg.info(;direct=true, version=true) + @test first([info for (uuid, info) in Pkg.info(;direct=true,name=true,version=true) if info.name == pkg]).version == v"0.0" Pkg.test("UnregisteredWithoutProject") pkg2 = "UnregisteredWithProject" + pkg2_uuid = UUID("58262bb0-2073-11e8-3727-4fe182c12249") p2 = git_init_package(tmp_pkg_path, joinpath(@__DIR__, "test_packages/$pkg2")) Pkg.REPLMode.pkgstr("add $p2") Pkg.REPLMode.pkgstr("pin $pkg2") @@ -95,7 +97,7 @@ temp_pkg_dir(;rm=false) do project_path; cd(project_path) do; # FIXME: why isn't this testing the Pkg after importing, rather than after freeing it #@eval import Example #@eval import $(Symbol(pkg2)) - @test Pkg.API.__installed()[pkg2] == v"0.1.0" + @test Pkg.info(;direct=true,version=true)[pkg2_uuid].version == v"0.1.0" Pkg.REPLMode.pkgstr("free $pkg2") @test_throws PkgError Pkg.REPLMode.pkgstr("free $pkg2") Pkg.test("UnregisteredWithProject") @@ -110,7 +112,7 @@ temp_pkg_dir(;rm=false) do project_path; cd(project_path) do; LibGit2.add!(repo, "*") LibGit2.commit(repo, "bump version"; author = TEST_SIG, committer=TEST_SIG) pkg"update" - @test Pkg.API.__installed()[pkg2] == v"0.2.0" + @test Pkg.info(;direct=true,version=true)[pkg2_uuid].version == v"0.2.0" Pkg.REPLMode.pkgstr("rm $pkg2") c = LibGit2.commit(repo, "empty commit"; author = TEST_SIG, committer=TEST_SIG) @@ -135,7 +137,7 @@ temp_pkg_dir(;rm=false) do project_path; cd(project_path) do; mktempdir() do depot_dir pushfirst!(DEPOT_PATH, depot_dir) pkg"instantiate" - @test Pkg.API.__installed()[pkg2] == v"0.2.0" + @test Pkg.info(;direct=true,version=true)[pkg2_uuid].version == v"0.2.0" end finally empty!(DEPOT_PATH) @@ -176,8 +178,9 @@ temp_pkg_dir() do project_path; cd(project_path) do Pkg.REPLMode.pkgstr("build; precompile") @test Base.find_package("UnregisteredWithProject") == joinpath(p1_new_path, "src", "UnregisteredWithProject.jl") @test Base.find_package("UnregisteredWithoutProject") == joinpath(p2_new_path, "src", "UnregisteredWithoutProject.jl") - @test Pkg.API.__installed()["UnregisteredWithProject"] == v"0.1.0" - @test Pkg.API.__installed()["UnregisteredWithoutProject"] == v"0.0.0" + @test Pkg.info(;direct=true,version=true)[UUID("58262bb0-2073-11e8-3727-4fe182c12249")].version == v"0.1.0" + @test first([pkg for (uuid, pkg) in Pkg.info(;direct=true,version=true,name=true) if + pkg.name == "UnregisteredWithoutProject"]).version == v"0.0.0" Pkg.test("UnregisteredWithoutProject") Pkg.test("UnregisteredWithProject") end @@ -200,8 +203,8 @@ temp_pkg_dir() do project_path; cd(project_path) do mkdir("tests") cd("tests") pkg"develop ../SubModule2" - @test Pkg.API.__installed()["SubModule1"] == v"0.1.0" - @test Pkg.API.__installed()["SubModule2"] == v"0.1.0" + @test Pkg.info(;direct=true,version=true)[uuid1].version == v"0.1.0" + @test Pkg.info(;direct=true,version=true)[uuid2].version == v"0.1.0" # make sure paths to SubModule1 and SubModule2 are relative manifest = Pkg.Types.Context().env.manifest @test manifest[uuid1].path == "SubModule1" @@ -287,8 +290,8 @@ cd_tempdir() do tmp pkg"activate ." pkg"develop .." # HelloWorld pkg"develop ../SubModule" - @test Pkg.installed()["HelloWorld"] == v"0.1.0" - @test Pkg.installed()["SubModule"] == v"0.1.0" + @test Pkg.info(;direct=true,version=true)[uuid1].version == v"0.1.0" + @test Pkg.info(;direct=true,version=true)[uuid2].version == v"0.1.0" manifest = Pkg.Types.Context().env.manifest @test manifest[uuid1].path == ".." @test manifest[uuid2].path == joinpath("..", "SubModule") @@ -467,7 +470,7 @@ temp_pkg_dir() do project_path; cd(project_path) do test BigProject test """) - current_json = Pkg.API.installed()["JSON"] + current_json = first([pkg for (uuid, pkg) in Pkg.info(;direct=true,version=true,name=true) if pkg.name == "JSON"]).version old_project = read("Project.toml", String) open("Project.toml"; append=true) do io print(io, """ @@ -478,10 +481,10 @@ temp_pkg_dir() do project_path; cd(project_path) do ) end pkg"up" - @test Pkg.API.installed()["JSON"].minor == 18 + @test first([pkg for (uuid, pkg) in Pkg.info(;direct=true,version=true,name=true) if pkg.name == "JSON"]).version.minor == 18 write("Project.toml", old_project) pkg"up" - @test Pkg.API.installed()["JSON"] == current_json + @test first([pkg for (uuid, pkg) in Pkg.info(;direct=true,version=true,name=true) if pkg.name == "JSON"]).version == current_json end end end; end