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

Deal with normalization collisions in reimport #532

Merged
merged 13 commits into from
Jul 17, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,14 @@ jobs:
- '4.0'
- '3.4'
os: [ubuntu-latest]
arch: [x64]
experimental: [false]
include:
# test macOS and Windows with latest Julia only
- os: macOS-latest
arch: x64
version: 1
experimental: false
R: 'release'
- os: windows-latest
arch: x64
version: 1
experimental: false
R: 'release'
Expand All @@ -43,7 +40,6 @@ jobs:
- uses: julia-actions/setup-julia@v2
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v2
- uses: r-lib/actions/setup-r@v2
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ docs/gh-pages
docs/site
deps/build.log
lcov.info
*.cov
4 changes: 1 addition & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "RCall"
uuid = "6f49c342-dc21-5d91-9882-a32aef131414"
authors = ["Douglas Bates <dmbates@gmail.com>", "Randy Lai <randy.cs.lai@gmail.com>", "Simon Byrne <simonbyrne@gmail.com>"]
version = "0.14.1"
version = "0.14.2"

[deps]
CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597"
Expand All @@ -10,7 +10,6 @@ DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
Missings = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Expand All @@ -24,7 +23,6 @@ CategoricalArrays = "0.8, 0.9, 0.10"
Conda = "1.4"
DataFrames = "0.21, 0.22, 1.0"
DataStructures = "0.5, 0.6, 0.7, 0.8, 0.9, 0.10, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18"
Missings = "0.2, 0.3, 0.4, 1.0"
Preferences = "1"
Requires = "0.5.2, 1"
StatsModels = "0.6, 0.7"
Expand Down
3 changes: 0 additions & 3 deletions src/RCall.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ using Dates
using Libdl
using Random
using REPL
if VERSION v"1.1.1"
using Missings
end
using CategoricalArrays
using DataFrames
using StatsModels
Expand Down
29 changes: 23 additions & 6 deletions src/namespaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const reserved = Set{String}([
"false", "true", "Tuple", "rmember", "__package__"])


cached_namespaces = Dict{String, Module}()
const cached_namespaces = Dict{String, Module}()

"""
Import an R package as a julia module.
Expand All @@ -17,18 +17,27 @@ E.g.: `PerformanceAnalytics::charts.Bar` in R becomes `PerformanceAnalytics.char
```
gg = rimport("ggplot2")
```

`normalization` is passed directly to `replace` via splatting.
"""
function rimport(pkg::String, s::Symbol=:__anonymous__; normalizenames::Bool=true)
function rimport(pkg::String, s::Symbol=gensym(:rimport);
normalizenames::Bool=true, normalization=['.' => '_'])
if pkg in keys(cached_namespaces)
m = cached_namespaces[pkg]
else
ns = rcall(:asNamespace, pkg)
# XXX note that R is sensitive to definition order, so do not change the order!
members = rcopy(Vector{String}, rcall(:getNamespaceExports, ns))

m = Module(s, false)
id = Expr(:const, Expr(:(=), :__package__, pkg))
if normalizenames
exports = [Symbol(replace(x, '.' => '_')) for x in members]
exports = [Symbol(replace(x, normalization...)) for x in members]
dupes = [k for (k, v) in countmap(exports) if v > 1]
if !isempty(dupes)
error("Normalized names are no longer unique: " *
join(dupes, ", ", " and "))
end
else
exports = [Symbol(x) for x in members]
end
Expand All @@ -37,13 +46,21 @@ function rimport(pkg::String, s::Symbol=:__anonymous__; normalizenames::Bool=tru
collect(eachindex(exports)))
consts = [Expr(:const, Expr(:(=),
exports[i],
rcall(Symbol("::"), pkg, members[i]))) for i in filtered_indices ]
rcall(Symbol("::"), pkg, members[i]))) for i in filtered_indices]
Core.eval(m, Expr(:toplevel, id, consts..., Expr(:export, exports...), :(rmember(x) = ($getindex)($ns, x))))
cached_namespaces[pkg] = m
end
m
return m
end
rimport(pkg::Symbol, args...; kwargs...) = rimport(string(pkg), args...; kwargs...)

function countmap(v::Vector{T}) where {T}
occurrences = Dict{Symbol,Int}()
for el in v
occurrences[el] = get(occurrences, el, 0) + 1
end
return occurrences
end
rimport(pkg::Symbol, s::Symbol=:__anonymous__) = rimport(string(pkg), s)

"""
Import an R Package as a Julia module. For example,
Expand Down
22 changes: 19 additions & 3 deletions test/namespaces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ module NamespaceTests
using RCall
using Test

RCall.R"""
has_mass_package = require("MASS")
"""
reval("""has_mass_package = require("MASS")""")

RCall.@rget has_mass_package

Expand All @@ -23,4 +21,22 @@ module NamespaceTests
@test rcopy(rcall(ginv, RObject([1 2; 0 4]))) ≈ [1 -0.5; 0 0.25]
end

# why exclude Windows? because the tempdir could potentially include
# unicode characters and R can't handle that
if !Sys.iswindows()
if !rcopy(reval("""require("ape")""")) # 418
@info "installing ape to temporary lib"
tmp = mktempdir()
reval("""lib <- "$(tmp)"
.libPaths(lib)
install.packages("ape", repos="https://cloud.r-project.org", method="wget", lib=lib)
library("ape")""")
end

@test_throws ErrorException rimport("ape")

rimport(:ape; normalization=["." => "__"])
end
# stats is always available
rimport(:stats; normalizenames=false)
end
Loading