Skip to content

Latest commit

 

History

History
112 lines (78 loc) · 5.86 KB

File metadata and controls

112 lines (78 loc) · 5.86 KB

API

The standard entrypoint for interactive use is print_explicit_imports. ExplicitImports.jl API also includes several other functions to provide programmatic access to the information gathered by the package, as well as utilities to use in regression testing.

Detecting implicit imports which could be made explicit

print_explicit_imports
explicit_imports

Detecting "improper" explicit imports

improper_explicit_imports

Detecting "improper" access of names from other modules

improper_qualified_accesses

Checks to use in testing

ExplicitImports.jl provides several functions (all starting with check_) which introspect a module for various kinds of potential issues, and throws errors if these issues are encountered. These "check" functions are designed to be narrowly scoped to detect one specific type of issue, and stable so that they can be used in testing environments (with the aim that non-breaking releases of ExplicitExports.jl will generally not cause new test failures).

ExplicitImports also provides Test.jl wrappers for each check_* function. These are named test_* (e.g. test_no_implicit_imports) and mirror the keyword arguments of their corresponding checks.

Additionally, one can use test_explicit_imports to run all of the tests from one simple interface. This functionality is modeled after Aqua's Aqua.test_all.

test_explicit_imports

Now let us go over each check in more detail. The first such check is check_no_implicit_imports which aims to ensure there are no implicit exports used in the package.

check_no_implicit_imports

Next, we have several checks related to detecting "improper" explicit imports. The function check_no_stale_explicit_imports checks that a module has no "stale" (unused) explicit imports. Next check_all_explicit_imports_via_owners and check_all_explicit_imports_are_public provide related checks. check_all_explicit_imports_via_owners is a weaker check which errors for particularly problematic imports of non-public names, namely those for which the module they are being imported from does not "own" the name (since it was not defined there). The typical scenario here is that the name may be public in some other module, but just happens to be present in the namespace of that module (consider using LinearAlgebra: map which imports Base's map function). Next, check_all_explicit_imports_are_public provides a stricter check that all names being explicitly imported are in fact public in the module they are being imported from, whether or not they are "owned" by that module.

check_no_stale_explicit_imports
check_all_explicit_imports_via_owners
check_all_explicit_imports_are_public

Lastly, we have two checks related to detecting "improper" qualified accesses to names, which are analogous to checks related to improper explicit imports. check_all_qualified_accesses_via_owners checks that all qualified accesses (e.g. usage of names in the form Foo.bar) are such that the name being accessed is "owned" by the module it is being accessed from (just like check_all_explicit_imports_via_owners). This would detect, e.g., LinearAlgebra.map. Likewise, check_all_qualified_accesses_are_public is a stricter check which verifies all qualified accesses to names are via modules in which that name is public. Additionally, check_no_self_qualified_accesses checks there are no self-qualified accesses, like accessing Foo.foo from within the module Foo.

check_all_qualified_accesses_via_owners
check_all_qualified_accesses_are_public
check_no_self_qualified_accesses

There are also Test.jl versions of these which use testsets instead of throwing errors:

test_no_implicit_imports
test_no_stale_explicit_imports
test_all_explicit_imports_via_owners
test_all_explicit_imports_are_public
test_all_qualified_accesses_via_owners
test_all_qualified_accesses_are_public
test_no_self_qualified_accesses

Usage with scripts (such as runtests.jl)

We also provide a helper function to analyze scripts (rather than modules). If you are using a module in your script (e.g. if your script starts with module), then use the ordinary print_explicit_imports function instead. This functionality is somewhat experimental and attempts to filter the relevant names in Main to those used in your script.

print_explicit_imports_script

Non-recursive variants

The above functions all recurse through submodules of the provided module, providing information about each. Here, we provide non-recursive variants (which in fact power the recursive ones), in case it is useful, perhaps for building other tooling on top of ExplicitImports.jl.

explicit_imports_nonrecursive
improper_qualified_accesses_nonrecursive
improper_explicit_imports_nonrecursive

Advanced: telling ExplicitImports to ignore parts of source code

You can use the #! explicit-imports: off and #! explicit-imports: on comments to disable/enable ExplicitImports from looking at parts of the code. For example, if you don't want ExplicitImports to recurse into some file, you could turn it off with:

using Foo # will be checked for implicit imports

#! explicit-imports: off
include("file.jl") # won't be checked
#! explicit-imports: on

using Bar # again will be checked

Note that this can of course compromise the analysis. If packages are loaded but ExplicitImports can't see them (as it has been turned off) then it will not know where the names came from. User beware!

This can be paired with telling ExplicitImports to ignore submodules via ExplicitImports.ignore_submodules:

ExplicitImports.ignore_submodules