Skip to content

Commit

Permalink
RFC: Base.propertynames(x), analogous to fieldnames(typeof(x)) (#25311)
Browse files Browse the repository at this point in the history
  • Loading branch information
stevengj authored and JeffBezanson committed Jan 18, 2018
1 parent 3d84bbb commit 4d93b66
Show file tree
Hide file tree
Showing 15 changed files with 60 additions and 3 deletions.
3 changes: 2 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ New language features
and implements three-valued logic, similar to SQLs `NULL` and R's `NA`.

* Field access via dot-syntax can now be overloaded by adding methods to
`Base.getproperty` and `Base.setproperty!` ([#1974]).
`Base.getproperty` and `Base.setproperty!` ([#1974]), optionally along with
a corresponding `Base.propertynames` method for reflection ([#25311]).

* Values for `Enum`s can now be specified inside of a `begin` block when using the
`@enum` macro ([#25424]).
Expand Down
1 change: 1 addition & 0 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,7 @@ export
fieldname,
fieldnames,
fieldcount,
# propertynames,
isconcrete,
oftype,
promote,
Expand Down
2 changes: 2 additions & 0 deletions base/linalg/bunchkaufman.jl
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ function getproperty(B::BunchKaufman{T}, d::Symbol) where {T<:BlasFloat}
end
end

Base.propertynames(B::BunchKaufman, private::Bool=false) = append!([:p,:P,:L,:U,:D], private ? fieldnames(typeof(B)) : Symbol[])

issuccess(B::BunchKaufman) = B.info == 0

function Base.show(io::IO, mime::MIME{Symbol("text/plain")}, B::BunchKaufman)
Expand Down
3 changes: 3 additions & 0 deletions base/linalg/cholesky.jl
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ function getproperty(C::Cholesky, d::Symbol)
return getfield(C, d)
end
end
Base.propertynames(F::Cholesky, private::Bool=false) = append!([:U,:L,:UL], private ? fieldnames(typeof(F)) : Symbol[])

function getproperty(C::CholeskyPivoted{T}, d::Symbol) where T<:BlasFloat
Cfactors = getfield(C, :factors)
Cuplo = getfield(C, :uplo)
Expand All @@ -413,6 +415,7 @@ function getproperty(C::CholeskyPivoted{T}, d::Symbol) where T<:BlasFloat
return getfield(C, d)
end
end
Base.propertynames(F::CholeskyPivoted, private::Bool=false) = append!([:U,:L,:p,:P], private ? fieldnames(typeof(F)) : Symbol[])

issuccess(C::Cholesky) = C.info == 0

Expand Down
2 changes: 2 additions & 0 deletions base/linalg/hessenberg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ function getproperty(F::Hessenberg, d::Symbol)
return getfield(F, d)
end

Base.propertynames(F::Hessenberg, private::Bool=false) = append!([:Q,:H], private ? fieldnames(typeof(F)) : Symbol[])

function getindex(A::HessenbergQ, i::Integer, j::Integer)
x = zeros(eltype(A), size(A, 1))
x[i] = 1
Expand Down
2 changes: 2 additions & 0 deletions base/linalg/lq.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ function getproperty(F::LQ, d::Symbol)
end
end

Base.propertynames(F::LQ, private::Bool=false) = append!([:L,:Q], private ? fieldnames(typeof(F)) : Symbol[])

getindex(A::LQPackedQ, i::Integer, j::Integer) =
mul!(A, setindex!(zeros(eltype(A), size(A, 2)), 1, j))[i]

Expand Down
2 changes: 2 additions & 0 deletions base/linalg/lu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ function getproperty(F::LU{T,<:StridedMatrix}, d::Symbol) where T
end
end

Base.propertynames(F::LU, private::Bool=false) = append!([:L,:U,:p,:P], private ? fieldnames(typeof(F)) : Symbol[])

issuccess(F::LU) = F.info == 0

function show(io::IO, mime::MIME{Symbol("text/plain")}, F::LU)
Expand Down
2 changes: 2 additions & 0 deletions base/linalg/qr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ function getproperty(F::QRCompactWY, d::Symbol)
getfield(F, d)
end
end
Base.propertynames(F::Union{QR,QRCompactWY}, private::Bool=false) = append!([:R,:Q], private ? fieldnames(typeof(F)) : Symbol[])
function getproperty(F::QRPivoted{T}, d::Symbol) where T
m, n = size(F)
if d == :R
Expand All @@ -469,6 +470,7 @@ function getproperty(F::QRPivoted{T}, d::Symbol) where T
getfield(F, d)
end
end
Base.propertynames(F::QRPivoted, private::Bool=false) = append!([:R,:Q,:p,:P], private ? fieldnames(typeof(F)) : Symbol[])

abstract type AbstractQ{T} <: AbstractMatrix{T} end

Expand Down
4 changes: 4 additions & 0 deletions base/linalg/schur.jl
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ function getproperty(F::Schur, d::Symbol)
end
end

Base.propertynames(F::Schur) = append!([:Schur,:vectors], fieldnames(typeof(F)))

function show(io::IO, F::Schur)
println(io, "$(typeof(F)) with factors T and Z:")
show(io, F.T)
Expand Down Expand Up @@ -274,6 +276,8 @@ function getproperty(F::GeneralizedSchur, d::Symbol)
end
end

Base.propertynames(F::GeneralizedSchur) = append!([:values,:left,:right], fieldnames(typeof(F)))

"""
schur(A::StridedMatrix, B::StridedMatrix) -> S::StridedMatrix, T::StridedMatrix, Q::StridedMatrix, Z::StridedMatrix, α::Vector, β::Vector
Expand Down
4 changes: 4 additions & 0 deletions base/linalg/svd.jl
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ function getproperty(F::SVD, d::Symbol)
end
end

Base.propertynames(F::SVD, private::Bool=false) = private ? append!([:V], fieldnames(typeof(F))) : [:U,:S,:V,:Vt]

"""
svdvals!(A)
Expand Down Expand Up @@ -461,6 +463,8 @@ svd(x::Number, y::Number) = first.(svd(fill(x, 1, 1), fill(y, 1, 1)))
end
end

Base.propertynames(F::GeneralizedSVD) = append!([:alpha,:beta,:vals,:S,:D1,:D2,:R0], fieldnames(typeof(F)))

"""
svdvals!(A, B)
Expand Down
17 changes: 17 additions & 0 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1105,3 +1105,20 @@ min_world(m::Method) = reinterpret(UInt, m.min_world)
max_world(m::Method) = typemax(UInt)
min_world(m::Core.MethodInstance) = reinterpret(UInt, m.min_world)
max_world(m::Core.MethodInstance) = reinterpret(UInt, m.max_world)

"""
propertynames(x, private=false)
Get an array of the properties (`x.property`) of an object `x`. This
is typically the same as [`fieldnames(typeof(x))`](@ref), but types
that overload [`getproperty`](@ref) should generally overload `propertynames`
as well to get the properties of an instance of the type.
`propertynames(x)` may return only "public" property names that are part
of the documented interface of `x`. If you want it to also return "private"
fieldnames intended for internal use, pass `true` for the optional second argument.
REPL tab completion on `x.` shows only the `private=false` properties.
"""
propertynames(x) = fieldnames(typeof(x))
propertynames(m::Module) = names(m)
propertynames(x, private) = propertynames(x) # ignore private flag by default
11 changes: 10 additions & 1 deletion base/repl/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module REPLCompletions
export completions, shell_completions, bslash_completions

using Base.Meta
using Base: coalesce
using Base: propertynames, coalesce

function completes_global(x, name)
return startswith(x, name) && !('#' in x)
Expand Down Expand Up @@ -41,6 +41,7 @@ function complete_symbol(sym, ffunc)

lookup_module = true
t = Union{}
val = nothing
if coalesce(findlast(occursin(non_identifier_chars), sym), 0) < coalesce(findlast(equalto('.'), sym), 0)
# Find module
lookup_name, name = rsplit(sym, ".", limit=2)
Expand All @@ -49,6 +50,7 @@ function complete_symbol(sym, ffunc)

b, found = get_value(ex, context_module)
if found
val = b
if isa(b, Module)
mod = b
lookup_module = true
Expand Down Expand Up @@ -82,6 +84,13 @@ function complete_symbol(sym, ffunc)
else
append!(suggestions, filtered_mod_names(p, mod, name, true, false))
end
elseif val !== nothing # looking for a property of an instance
for property in propertynames(val, false)
s = string(property)
if startswith(s, name)
push!(suggestions, s)
end
end
else
# Looking for a member of a type
if t isa DataType && t != Any
Expand Down
1 change: 1 addition & 0 deletions doc/src/base/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ Core.nfields
Base.fieldnames
Base.fieldname
Base.fieldcount
Base.propertynames
Base.datatype_module
Base.datatype_name
Base.isconst
Expand Down
7 changes: 7 additions & 0 deletions test/linalg/lu.jl
Original file line number Diff line number Diff line change
Expand Up @@ -264,3 +264,10 @@ U factor:
0.0 0.0 1.0 0.0
0.0 0.0 0.0 1.0"""
end

@testset "propertynames" begin
names = sort!(string.(Base.propertynames(lufact(rand(3,3)))))
@test names == ["L", "P", "U", "p"]
allnames = sort!(string.(Base.propertynames(lufact(rand(3,3)), true)))
@test allnames == ["L", "P", "U", "factors", "info", "ipiv", "p"]
end
2 changes: 1 addition & 1 deletion test/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ mutable struct TLayout
z::Int32
end
tlayout = TLayout(5,7,11)
@test fieldnames(TLayout) == [:x, :y, :z]
@test fieldnames(TLayout) == [:x, :y, :z] == Base.propertynames(tlayout)
@test [(fieldoffset(TLayout,i), fieldname(TLayout,i), fieldtype(TLayout,i)) for i = 1:fieldcount(TLayout)] ==
[(0, :x, Int8), (2, :y, Int16), (4, :z, Int32)]
@test_throws BoundsError fieldtype(TLayout, 0)
Expand Down

0 comments on commit 4d93b66

Please sign in to comment.