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

Base.url is broken for non-base functions #47709

Open
adrhill opened this issue Nov 25, 2022 · 8 comments
Open

Base.url is broken for non-base functions #47709

adrhill opened this issue Nov 25, 2022 · 8 comments
Labels
bug Indicates an unexpected problem or unintended behavior

Comments

@adrhill
Copy link
Contributor

adrhill commented Nov 25, 2022

Base.url currently only returns a valid URL when applied to Julia base methods.
For functions from stdlib and external packages, a path to the local file is returned instead of a URL.

The relevant branch of Base.url looks like dead code to me:

julia/base/methodshow.jl

Lines 374 to 396 in 04214ec

elseif root_module_exists(libgit2_id)
LibGit2 = root_module(libgit2_id)
try
d = dirname(file)
return LibGit2.with(LibGit2.GitRepoExt(d)) do repo
LibGit2.with(LibGit2.GitConfig(repo)) do cfg
u = LibGit2.get(cfg, "remote.origin.url", "")
u = (match(LibGit2.GITHUB_REGEX,u)::AbstractMatch).captures[1]
commit = string(LibGit2.head_oid(repo))
root = LibGit2.path(repo)
if startswith(file, root) || startswith(realpath(file), root)
"https://github.com/$u/tree/$commit/"*file[length(root)+1:end]*"#L$line"
else
fileurl(file)
end
end
end
catch
return fileurl(file)
end
else
return fileurl(file)
end

(I'm guessing repository information might have previously been pulled by Pkg.)

I've tried to work around this in BrowserMacros.jl, but haven't found out a way to obtain commit hashes to construct permalinks that aren't based on tags (which don't always exist).

Related issues: #44151, #44165

@brenhinkeller brenhinkeller added the bug Indicates an unexpected problem or unintended behavior label Nov 25, 2022
@adrhill
Copy link
Contributor Author

adrhill commented Feb 28, 2024

This issue popped up again in fonsp/Pluto.jl#2813, where we had to limit URLs to code from Julia Base:

You can now click on file locations from Julia Base, and it will send you to the source code on github :) Adding this functionality for stdlibs and remote packages would be nice!

CC @fonsp

@adrhill
Copy link
Contributor Author

adrhill commented Oct 2, 2024

The need for this pops up again in fonsp/Pluto.jl#3038.

@fonsp
Copy link
Member

fonsp commented Oct 2, 2024

In Pluto we want to make stack traces clickable, clicking inference:288 should open the function source in a new github tab:

image

Currently, this only works for base functions (error in this example), but it would be great to have this for packages too.

@giordano
Copy link
Contributor

giordano commented Oct 2, 2024

Note that Base.url is undocumented and non-public, so it isn't a surprise it doesn't work with non-Base functions, since it wasn't meant to be used outside of Base.

@adrhill
Copy link
Contributor Author

adrhill commented Oct 2, 2024

Then maybe this issue should be split in two:

  • a bug report that the code linked above is likely dead code
  • a feature request to signal interest in fixing the code and making it public

@KristofferC
Copy link
Member

Here is some code that could potentially be used to get the URL for a package function. It uses RegistryInstances.jl to pull out the repo for a package and join that with the local path and line of the method:

using RegistryInstances

function repo_and_path_to_url(repo, version, path, line)
    repo = chopsuffix(repo, ".git")
    # TODO: Handle more git forges
    if startswith(repo, "https://github.com") 
        return join([repo, "blob", "v" * version, path * "#L$line"], "/")
    else
        error("failed to handle $repo")
    end
end

function repos_package(uuid)
    repos = String[]
    for reg = RegistryInstances.reachable_registries()
        entry = get(reg, uuid, nothing)
        if entry !== nothing
            info = RegistryInstances.registry_info(entry)
            push!(repos, info.repo)
        end
    end
    return repos
end

# TODO: If package is devved use local path
# TODO: If package is added by URL, use that
function url(m::Method)
    M = parentmodule(m)
    uuid = Base.PkgId(M).uuid
    line = m.line

    pkg_splitpath = splitpath(pkgdir(M))
    file_splitpath = splitpath(String(m.file))
    while !isempty(pkg_splitpath) && first(pkg_splitpath) == first(file_splitpath)
        popfirst!(pkg_splitpath)
        popfirst!(file_splitpath)
    end
    local_dir = join(file_splitpath, "/")

    v = string(pkgversion(M))
    urls = String[]   
    for repo in repos_package(uuid)
        url = repo_and_path_to_url(repo, v, local_dir, line)
        push!(urls, url)
    end
    return urls
end

using Plots
m = @which Plots.plot(rand(2,2))
url(m)

gives:

julia> using Plots

julia> m = @which Plots.plot(rand(2,2))
plot(args...; kw...)
     @ Plots C:\Users\Kristoffer\.julia\packages\Plots\kLeqV\src\plot.jl:93

julia> url(m)
1-element Vector{String}:
 "https://github.com/JuliaPlots/Plots.jl/blob/v1.40.8/src/plot.jl#L93"

https://github.com/JuliaPlots/Plots.jl/blob/v1.40.8/src/plot.jl#L93


There are some things that probably should be improved:

  • Use the tree hash for the version instead of assuming that there is a v$version tag tagged
  • Check that the package is not devved or added by URL
  • Add support for gitlab etc.

@adrhill
Copy link
Contributor Author

adrhill commented Oct 2, 2024

@KristofferC thanks a lot! I'm guessing this won't make it into Base due to the dependency on RegistryInstances?

@KristofferC
Copy link
Member

Probably not, it is also a little bit weird of a Base function already.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Indicates an unexpected problem or unintended behavior
Projects
None yet
Development

No branches or pull requests

5 participants