diff --git a/README.md b/README.md index 357cdd7..a898c22 100644 --- a/README.md +++ b/README.md @@ -191,18 +191,26 @@ almostbandedrank finish_part_setindex! ``` -Because the documented constructor is `AlmostBandedMatrix(bands::BandedMatrix, fill)`, -`using FastAlmostBandedMatrices` also brings along the -[BandedMatrices.jl](https://github.com/JuliaLinearAlgebra/BandedMatrices.jl) names needed to -build, populate and query that `bands` argument. They remain owned and documented by -BandedMatrices.jl: - -``` -BandedMatrix brand brandn -Band BandRange band bandrange -bandwidth bandwidths colrange rowrange -BandError -``` +### Reexported from BandedMatrices.jl + +`AlmostBandedMatrix(bands::BandedMatrix, fill)` is the documented constructor and the +examples above build `bands` with `brand`, so `using FastAlmostBandedMatrices` also brings +in the [BandedMatrices.jl](https://juliaLinearAlgebra.github.io/BandedMatrices.jl/stable/) +names needed to build that banded argument, populate it and query it: + + - Building the bands: `BandedMatrix`, `brand`, `brandn` + - Populating them: `Band`, `BandRange`, `band`, `bandrange` + - Querying them: `bandwidth`, `bandwidths`, `colrange`, `rowrange` + - Errors: `BandError` + +These names are owned and documented by +[BandedMatrices.jl](https://juliaLinearAlgebra.github.io/BandedMatrices.jl/stable/); +FastAlmostBandedMatrices.jl only re-exports them and does not document them. Anything else +from BandedMatrices.jl must be imported from BandedMatrices.jl directly. In particular, +the FillArrays.jl names it reexports in turn (`Fill`, `Ones`, `Zeros`, `Eye`), the +`BandedMatrices` module binding itself, and `symrcm` are deliberately not passed along. +See the [API page](https://docs.sciml.ai/FastAlmostBandedMatrices/stable/api/) for the +details. ## Some Considerations diff --git a/docs/src/api.md b/docs/src/api.md index 93eb415..8538891 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -31,20 +31,40 @@ finish_part_setindex! ## Reexported from BandedMatrices.jl -The documented constructor is `AlmostBandedMatrix(bands::BandedMatrix, fill)`, and the +`AlmostBandedMatrix(bands::BandedMatrix, fill)` is the documented constructor, and the examples on the home page build `bands` with `brand`. So `using FastAlmostBandedMatrices` -also brings the -[BandedMatrices.jl](https://github.com/JuliaLinearAlgebra/BandedMatrices.jl) names needed to -construct, populate and query that banded argument: - -| Purpose | Names | -|:--------|:------| -| Construction | `BandedMatrix`, `brand`, `brandn` | -| Band indexing | `Band`, `BandRange`, `band`, `bandrange` | -| Structure queries | `bandwidth`, `bandwidths`, `colrange`, `rowrange` | -| Errors | `BandError` | - -These names are owned and documented by BandedMatrices.jl; see its documentation for their -full behaviour. Nothing else from BandedMatrices.jl is reexported, and neither are the -names it reexports from its own dependencies (for example the FillArrays.jl names `Fill`, -`Ones`, `Zeros` and `Eye`). Use `using BandedMatrices` or `using FillArrays` for those. +also brings in the [BandedMatrices.jl](https://juliaLinearAlgebra.github.io/BandedMatrices.jl/stable/) +names needed to build that banded argument, populate it and query it, so they do not have +to be imported separately: + + - Building the bands: `BandedMatrix`, `brand`, `brandn` + - Populating them: `Band`, `BandRange`, `band`, `bandrange` + - Querying them: `bandwidth`, `bandwidths`, `colrange`, `rowrange` + - Errors: `BandError` + +These names are owned and documented by +[BandedMatrices.jl](https://juliaLinearAlgebra.github.io/BandedMatrices.jl/stable/). +FastAlmostBandedMatrices.jl only re-exports them; it does not document them and does not +define their behaviour, so BandedMatrices.jl's own documentation is the reference for what +each one does. + +Anything else from BandedMatrices.jl must be imported from BandedMatrices.jl directly. +In particular, the following are deliberately **not** reexported: + + - The [FillArrays.jl](https://juliaarrays.github.io/FillArrays.jl/stable/) names + `Fill`, `Ones`, `Zeros` and `Eye`. BandedMatrices.jl reexports those from a + dependency of its own, so passing them along here would be reexporting a whole + dependency chain; none of them is needed to build an `AlmostBandedMatrix`. Use + `using FillArrays`. + - The `BandedMatrices` module binding itself. Use `import BandedMatrices` if you want + the qualified form. + - `symrcm`, a sparse-matrix reordering unrelated to this package. + +`Band` and `BandError` have no docstring upstream in BandedMatrices.jl, so they are listed +in the `api_docs_kwargs` ignore list in `test/qa/qa.jl` rather than documented here; that +docstring is owed by BandedMatrices.jl. Every other reexported name above carries its own +upstream docstring, reachable from the REPL help mode. + +The list above is kept in sync with the reexport `export` block in +`src/FastAlmostBandedMatrices.jl` and with `REEXPORTED_API` in `test/qa/qa.jl`, and the +Core test suite asserts that all three agree. diff --git a/test/core_tests.jl b/test/core_tests.jl index e990663..14729fd 100644 --- a/test/core_tests.jl +++ b/test/core_tests.jl @@ -4,8 +4,8 @@ using SafeTestsets using FastAlmostBandedMatrices import BandedMatrices - # Kept in sync with the reexport `export` block in src/FastAlmostBandedMatrices.jl and - # with `REEXPORTED_API` in test/qa/qa.jl. + # Kept in sync with the reexport `export` block in src/FastAlmostBandedMatrices.jl, + # `REEXPORTED_API` in test/qa/qa.jl, and the reexport section of docs/src/api.md. reexports = ( :Band, :BandError, :BandRange, :BandedMatrix, :band, :bandrange, :bandwidth, :bandwidths, :brand, :brandn, :colrange, :rowrange, @@ -26,6 +26,72 @@ using SafeTestsets @test name ∉ exported end + # The same list lives in three places: the `export` block in src (checked above via + # `names`), `REEXPORTED_API` in test/qa/qa.jl, and the "Reexported from + # BandedMatrices.jl" section of docs/src/api.md. They must not drift apart. + # + # Both files are read line-wise after normalising the line endings: git checks these + # files out with CRLF on Windows, so nothing here may assume "\n". + readlines_lf(path) = split(replace(read(path, String), "\r\n" => "\n", "\r" => "\n"), '\n') + + qa_file = joinpath(@__DIR__, "qa", "qa.jl") + if isfile(qa_file) + qa_source = join(readlines_lf(qa_file), '\n') + block = match(r"REEXPORTED_API = \((.*?)\)"s, qa_source) + block === nothing && + @error "reexport sync: no `REEXPORTED_API = (...)` found" file = qa_file + @test block !== nothing + if block !== nothing + declared = Set(Symbol(m[1]) for m in eachmatch(r":(\w+)", block[1])) + declared == Set(reexports) || @error( + "reexport sync: REEXPORTED_API disagrees with the exports", + file = qa_file, only_in_file = setdiff(declared, Set(reexports)), + only_in_exports = setdiff(Set(reexports), declared) + ) + @test declared == Set(reexports) + end + end + + docs_file = joinpath(@__DIR__, "..", "docs", "src", "api.md") + if isfile(docs_file) + heading = "## Reexported from BandedMatrices.jl" + lines = readlines_lf(docs_file) + # Structural, not prose-anchored: the section runs from its heading to the next + # `## ` heading (or the end of the file). + is_bullet(line) = startswith(line, " - ") + start = findfirst(line -> rstrip(line) == heading, lines) + start === nothing && + @error "reexport sync: heading not found in docs" file = docs_file heading + @test start !== nothing + if start !== nothing + stop = findnext(line -> startswith(line, "## "), lines, start + 1) + section = lines[start:(stop === nothing ? lastindex(lines) : stop - 1)] + # Only the first contiguous run of bullets under the heading is the list of + # reexported names (" - Building the bands: `BandedMatrix`, ..."); the later + # bullet list in the same section is the deliberate *exclusions*. + bullets = String[] + idx = findfirst(is_bullet, section) + while idx !== nothing && idx <= lastindex(section) && is_bullet(section[idx]) + push!(bullets, String(section[idx])) + idx += 1 + end + isempty(bullets) && @error( + "reexport sync: no ` - ` role bullets under the heading", + file = docs_file, heading + ) + @test !isempty(bullets) + documented = Set( + Symbol(m[1]) for line in bullets for m in eachmatch(r"`(\w+)`", line) + ) + documented == Set(reexports) || @error( + "reexport sync: the docs section disagrees with the exports", + file = docs_file, only_in_docs = setdiff(documented, Set(reexports)), + only_in_exports = setdiff(Set(reexports), documented) + ) + @test documented == Set(reexports) + end + end + # The documented construction path works with `using FastAlmostBandedMatrices` alone. A = AlmostBandedMatrix(brand(Float64, 10, 10, 3, 2), rand(Float64, 2, 10)) B = AlmostBandedMatrix(BandedMatrix(fill(1.0, 10, 10), (3, 2)), rand(Float64, 2, 10))