Skip to content
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
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "ExplicitImports"
uuid = "7d51a73a-1435-4ff3-83d9-f097790105c7"
authors = ["Eric P. Hanson"]
version = "1.13.2"
version = "1.14.0"

[deps]
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
Expand Down
40 changes: 38 additions & 2 deletions src/checks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ end
"""
check_all_qualified_accesses_are_public(mod::Module, file=pathof(mod); ignore::Tuple=(),
skip::$(TUPLE_MODULE_PAIRS)=(Base => Core,),
allow_internal_accesses=true)
from=nothing, allow_internal_accesses=true)

Checks that neither `mod` nor any of its submodules has qualified accesses to names which are non-public (i.e. not exported, nor declared public on Julia 1.11+)
throwing an `NonPublicQualifiedAccessException` if so, and returning `nothing` otherwise.
Expand Down Expand Up @@ -372,6 +372,15 @@ that are allowed to be accessed from modules in which they are not public. For e

would check there were no non-public qualified accesses besides that of the name `DataFrame`.

Alternatively, the `from` keyword can be used to apply the public names check to only certain modules (and their submodules).
For example,

```julia
@test check_all_qualified_accesses_are_public(MyPackage; from=(DataFrames,)) === nothing
```

would check only that qualified accesses from the `DataFrames` module (or its submodules) are all public.

## non-fully-analyzable modules do not cause exceptions

Note that if a module is not fully analyzable (e.g. it has dynamic `include` calls), qualified accesess of non-public names which could not be analyzed will be missed. Unlike [`check_no_stale_explicit_imports`](@ref) and [`check_no_implicit_imports`](@ref), this function will *not* throw an `UnanalyzableModuleException` in such cases.
Expand All @@ -380,6 +389,7 @@ See also: [`improper_qualified_accesses`](@ref) for programmatic access and the
"""
function check_all_qualified_accesses_are_public(mod::Module, file=pathof(mod);
skip::TUPLE_MODULE_PAIRS=(Base => Core,),
from=nothing,
ignore::Tuple=(),
allow_internal_accesses=true)
check_file(file)
Expand All @@ -403,6 +413,14 @@ function check_all_qualified_accesses_are_public(mod::Module, file=pathof(mod);
end
end

if !isnothing(from)
filter!(problematic) do row
return any(from) do fmod
has_ancestor(row.accessing_from, fmod)
end
end
end

# Discard imports from names that are public in their module; that's OK
filter!(problematic) do nt
return !nt.public_access
Expand Down Expand Up @@ -562,7 +580,7 @@ end
"""
check_all_explicit_imports_are_public(mod::Module, file=pathof(mod); ignore::Tuple=(),
skip::$(TUPLE_MODULE_PAIRS)=(Base => Core,),
allow_internal_imports=true)
from=nothing, allow_internal_imports=true)

Checks that neither `mod` nor any of its submodules has imports to names which are non-public (i.e. not exported, nor declared public on Julia 1.11+)
throwing an `NonPublicExplicitImportsException` if so, and returning `nothing` otherwise.
Expand Down Expand Up @@ -594,6 +612,15 @@ that are allowed to be imported from modules in which they are not public. For e

would check there were no non-public explicit imports besides that of the name `DataFrame`.

Alternatively, the `from` keyword can be used to apply the public names check to only certain modules (and their submodules).
For example,

```julia
@test check_all_explicit_imports_are_public(MyPackage; from=(DataFrames,)) === nothing
```

would check only that explicit imports from the `DataFrames` module (or its submodules) are all public.

## non-fully-analyzable modules do not cause exceptions

Note that if a module is not fully analyzable (e.g. it has dynamic `include` calls), explicit imports of non-public names which could not be analyzed will be missed. Unlike [`check_no_stale_explicit_imports`](@ref) and [`check_no_implicit_imports`](@ref), this function will *not* throw an `UnanalyzableModuleException` in such cases.
Expand All @@ -602,6 +629,7 @@ See also: [`improper_explicit_imports`](@ref) for programmatic access to such im
"""
function check_all_explicit_imports_are_public(mod::Module, file=pathof(mod);
skip::TUPLE_MODULE_PAIRS=(Base => Core,),
from=nothing,
ignore::Tuple=(),
allow_internal_imports=true)
check_file(file)
Expand All @@ -620,6 +648,14 @@ function check_all_explicit_imports_are_public(mod::Module, file=pathof(mod);
end
end

if !isnothing(from)
filter!(problematic) do row
return any(from) do fmod
has_ancestor(row.importing_from, fmod)
end
end
end

# Discard imports from names that are public in their module; that's OK
filter!(problematic) do nt
return !nt.public_import
Expand Down
25 changes: 25 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,24 @@ include("issue_129.jl")
end
@test contains(str, "- `ABC` is not public in")

str = exception_string() do
return check_all_qualified_accesses_are_public(TestQualifiedAccess,
"test_qualified_access.jl";
from=(LinearAlgebra,
TestQualifiedAccess),
allow_internal_accesses=false)
end
@test contains(str, "- `ABC` is not public in")
@test contains(str, "- `map` is not public in `LinearAlgebra`")
str = exception_string() do
return check_all_qualified_accesses_are_public(TestQualifiedAccess,
"test_qualified_access.jl";
from=(LinearAlgebra,),
allow_internal_accesses=false)
end
@test !contains(str, "- `ABC` is not public in")
@test contains(str, "- `map` is not public in `LinearAlgebra`")

@test check_all_qualified_accesses_are_public(TestQualifiedAccess,
"test_qualified_access.jl";
ignore=(:X, :ABC, :map),
Expand Down Expand Up @@ -489,6 +507,13 @@ include("issue_129.jl")
@test check_all_explicit_imports_are_public(ModImports,
"imports.jl"; ignore=(:map, :_svd!)) ===
nothing
@test check_all_explicit_imports_are_public(ModImports,
"imports.jl"; from=(DataFrames,)) ===
nothing
@test_throws NonPublicExplicitImportsException check_all_explicit_imports_are_public(ModImports,
"imports.jl";
from=(LinearAlgebra,
DataFrames))
end

@testset "structs" begin
Expand Down
Loading