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

Query interface #1086

Merged
merged 1 commit into from
Aug 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Pkg.gc
Pkg.status
Pkg.precompile
Pkg.setprotocol!
Pkg.dependencies
```


Expand Down
31 changes: 20 additions & 11 deletions src/API.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,26 @@ preview_info() = printstyled("───── Preview mode ─────\n"; c

include("generate.jl")

dependencies() = dependencies(Context())
function dependencies(ctx::Context)::Dict{UUID, PackageInfo}
pkgs = PackageSpec[]
Operations.load_all_deps!(ctx, pkgs)
find_registered!(ctx, UUID[pkg.uuid for pkg in pkgs])
return Dict(pkg.uuid => Operations.package_info(ctx, pkg) for pkg in pkgs)
end

project() = project(Context())
function project(ctx::Context)
ctx.env.pkg !== nothing || pkgerror("Active environment is not a project.")
return ProjectInfo(
name = ctx.env.pkg.name,
uuid = ctx.env.pkg.uuid,
version = ctx.env.pkg.version,
dependencies = ctx.env.project.deps,
path = ctx.env.project_file
)
end

function check_package_name(x::AbstractString, mode=nothing)
if !(occursin(Pkg.REPLMode.name_re, x))
message = "$x is not a valid packagename."
Expand Down Expand Up @@ -254,17 +274,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

"""
gc(ctx::Context=Context(); collect_delay::Period=Day(30), kwargs...)

Expand Down
24 changes: 23 additions & 1 deletion src/Operations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ is_dep(ctx::Context, pkg::PackageSpec) =
function load_direct_deps!(ctx::Context, pkgs::Vector{PackageSpec}; version::Bool=true)
# load rest of deps normally
for (name::String, uuid::UUID) in ctx.env.project.deps
pkgs[uuid] === nothing || continue # dont duplicate packages
pkgs[uuid] === nothing || continue # do not duplicate packages
entry = manifest_info(ctx, uuid)
push!(pkgs, entry === nothing ?
PackageSpec(;uuid=uuid, name=name) :
Expand All @@ -66,6 +66,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 # do not 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))
Expand Down Expand Up @@ -1338,4 +1339,25 @@ function test(ctx::Context, pkgs::Vector{PackageSpec};
end
end

function package_info(ctx::Context, pkg::PackageSpec)::PackageInfo
entry = manifest_info(ctx, pkg.uuid)
if entry === nothing
pkgerror("Can not query `$(pkg.name)` because it does not exist in the manifest.",
" Use `Pkg.resolve()` to populate the manifest.")
end
package_info(ctx, pkg, entry)
end

function package_info(ctx::Context, pkg::PackageSpec, entry::PackageEntry)::PackageInfo
info = PackageInfo(
name = pkg.name,
version = pkg.version != VersionSpec() ? pkg.version : nothing,
ispinned = pkg.pinned,
isdeveloped = pkg.path !== nothing,
source = project_rel_path(ctx, source_path(pkg)),
dependencies = collect(values(entry.deps)),
)
return info
end
00vareladavid marked this conversation as resolved.
Show resolved Hide resolved

end # module
44 changes: 41 additions & 3 deletions src/Pkg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,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}})
Expand Down Expand Up @@ -273,6 +270,47 @@ const develop = API.develop
#TODO: Will probably be deprecated for something in PkgDev
const generate = API.generate

"""
!!! note
This feature is experimental.

Pkg.dependencies()::Dict{UUID, PackageInfo}
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this include things only in the Project, or also in the Manifest or should that be an option?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that you mention it, perhaps people might want to use for (uuid, pkg) in Pkg.dependencies()? In which case a filter keyword does seem useful.


00vareladavid marked this conversation as resolved.
Show resolved Hide resolved
Query the dependecy graph.
The result is a `Dict` that maps a package UUID to a `PackageInfo` struct representing the dependency (a package).

PackageInfo fields:

| `field` | `Description` |
|:---------------|:-----------------------------------------------------------|
| `name` | The name of the package |
| `version` | The version of the package (this is `Nothing` for stdlibs) |
| `isdeveloped` | Whether a package is directly tracking a directory |
| `ispinned` | Whether a package is pinned |
| `source` | The directory containing the source code for that package |
| `dependencies` | The dependencies of that package as a vector of UUIDs |
"""
const dependencies = API.dependencies

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which of these will be nothing for stdlibs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All fields should "Just Work" for stdlibs, with the exception of version which will always be nothing.

"""
!!! note
This feature is experimental.

Pkg.project()::ProjectInfo
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if ProjectInfo(::UUID) would be a good constructor? Otherwise it is a bit hard to do something with the .dependencies field.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean. What would be the result of ProjectInfo(::UUID)? What UUID would you give it?


Request a `ProjectInfo` struct which contains information about the active project.

ProjectInfo fields:
| `field` | `Description` |
|:---------------|:--------------------------------------------------------------------------------------------|
| `name` | The project's name |
| `uuid` | The project's UUID |
| `version` | The project's version |
| `dependencies` | The project's direct dependencies as a `Dict` which maps dependency name to dependency UUID |
| `path` | The location of the project file which defines the active project |
"""
const project = API.project

"""
Pkg.instantiate(; verbose = false)

Expand Down
27 changes: 26 additions & 1 deletion src/Types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,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, ProjectInfo, GitRepo, Context!, get_deps,
PkgError, pkgerror, has_name, has_uuid, is_stdlib, write_env, write_env_usage, 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,
Expand Down Expand Up @@ -1381,4 +1381,29 @@ function write_env(ctx::Context; display_diff=true)
write_manifest(env.manifest, env, old_env, ctx; display_diff=display_diff)
end

###
### PackageInfo
###

Base.@kwdef struct PackageInfo
name::String
version::Union{Nothing,VersionNumber}
ispinned::Bool
isdeveloped::Bool
source::String
dependencies::Vector{UUID}
end

###
### ProjectInfo
###

Base.@kwdef struct ProjectInfo
name::String
uuid::UUID
version::VersionNumber
dependencies::Dict{String,UUID}
path::String
end

end # module
48 changes: 30 additions & 18 deletions test/pkg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -151,21 +151,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.dependencies()[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.dependencies()[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.dependencies()[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.dependencies()[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.dependencies()[TEST_PKG.uuid].version == v"0.3.3"
Pkg.update(; level = UPLEVEL_MINOR)
@test Pkg.API.__installed()[TEST_PKG.name].minor != 3
@test Pkg.dependencies()[TEST_PKG.uuid].version.minor != 3
Pkg.rm(TEST_PKG.name)
end

Expand All @@ -181,27 +181,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.dependencies()[TEST_PKG.uuid].version
Pkg.pin(PackageSpec(TEST_PKG.name, v"0.2"))
@test Pkg.API.__installed()[TEST_PKG.name].minor == 2
@test Pkg.dependencies()[TEST_PKG.uuid].version.minor == 2
Pkg.update(TEST_PKG.name)
@test Pkg.API.__installed()[TEST_PKG.name].minor == 2
@test Pkg.dependencies()[TEST_PKG.uuid].version.minor == 2
Pkg.free(TEST_PKG.name)
Pkg.update()
@test Pkg.API.__installed()[TEST_PKG.name] == old_v
@test Pkg.dependencies()[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.dependencies()[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.dependencies()[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
Expand All @@ -227,7 +227,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.dependencies()[TEST_PKG.uuid].version == old_v
end
end
end
Expand All @@ -239,7 +239,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.dependencies(), TEST_PKG.uuid)
Pkg.update()
# Disable until fixed in Base
# Pkg.test("CRC32c")
Expand Down Expand Up @@ -337,7 +337,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.dependencies(), 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)
Expand All @@ -349,7 +349,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.dependencies(), TEST_PKG.uuid)
end
end
end
Expand All @@ -359,12 +359,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.dependencies(), 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.dependencies()]
Pkg.rm("JSON")
end
end
Expand Down Expand Up @@ -808,6 +808,18 @@ 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")
@test length(Pkg.project().dependencies) == 3
xs = Dict(uuid => pkg for (uuid, pkg) in Pkg.dependencies() if pkg.isdeveloped)
@test length(xs) == 1
@test xs[TEST_PKG.uuid].ispinned == false
end end
end

include("repl.jl")
include("api.jl")
include("registry.jl")
Expand Down