Skip to content

Commit 4d93b66

Browse files
stevengjJeffBezanson
authored andcommitted
RFC: Base.propertynames(x), analogous to fieldnames(typeof(x)) (#25311)
1 parent 3d84bbb commit 4d93b66

File tree

15 files changed

+60
-3
lines changed

15 files changed

+60
-3
lines changed

NEWS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ New language features
3838
and implements three-valued logic, similar to SQLs `NULL` and R's `NA`.
3939

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

4344
* Values for `Enum`s can now be specified inside of a `begin` block when using the
4445
`@enum` macro ([#25424]).

base/exports.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,7 @@ export
851851
fieldname,
852852
fieldnames,
853853
fieldcount,
854+
# propertynames,
854855
isconcrete,
855856
oftype,
856857
promote,

base/linalg/bunchkaufman.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ function getproperty(B::BunchKaufman{T}, d::Symbol) where {T<:BlasFloat}
205205
end
206206
end
207207

208+
Base.propertynames(B::BunchKaufman, private::Bool=false) = append!([:p,:P,:L,:U,:D], private ? fieldnames(typeof(B)) : Symbol[])
209+
208210
issuccess(B::BunchKaufman) = B.info == 0
209211

210212
function Base.show(io::IO, mime::MIME{Symbol("text/plain")}, B::BunchKaufman)

base/linalg/cholesky.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,8 @@ function getproperty(C::Cholesky, d::Symbol)
393393
return getfield(C, d)
394394
end
395395
end
396+
Base.propertynames(F::Cholesky, private::Bool=false) = append!([:U,:L,:UL], private ? fieldnames(typeof(F)) : Symbol[])
397+
396398
function getproperty(C::CholeskyPivoted{T}, d::Symbol) where T<:BlasFloat
397399
Cfactors = getfield(C, :factors)
398400
Cuplo = getfield(C, :uplo)
@@ -413,6 +415,7 @@ function getproperty(C::CholeskyPivoted{T}, d::Symbol) where T<:BlasFloat
413415
return getfield(C, d)
414416
end
415417
end
418+
Base.propertynames(F::CholeskyPivoted, private::Bool=false) = append!([:U,:L,:p,:P], private ? fieldnames(typeof(F)) : Symbol[])
416419

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

base/linalg/hessenberg.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ function getproperty(F::Hessenberg, d::Symbol)
6666
return getfield(F, d)
6767
end
6868

69+
Base.propertynames(F::Hessenberg, private::Bool=false) = append!([:Q,:H], private ? fieldnames(typeof(F)) : Symbol[])
70+
6971
function getindex(A::HessenbergQ, i::Integer, j::Integer)
7072
x = zeros(eltype(A), size(A, 1))
7173
x[i] = 1

base/linalg/lq.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ function getproperty(F::LQ, d::Symbol)
8585
end
8686
end
8787

88+
Base.propertynames(F::LQ, private::Bool=false) = append!([:L,:Q], private ? fieldnames(typeof(F)) : Symbol[])
89+
8890
getindex(A::LQPackedQ, i::Integer, j::Integer) =
8991
mul!(A, setindex!(zeros(eltype(A), size(A, 2)), 1, j))[i]
9092

base/linalg/lu.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,8 @@ function getproperty(F::LU{T,<:StridedMatrix}, d::Symbol) where T
268268
end
269269
end
270270

271+
Base.propertynames(F::LU, private::Bool=false) = append!([:L,:U,:p,:P], private ? fieldnames(typeof(F)) : Symbol[])
272+
271273
issuccess(F::LU) = F.info == 0
272274

273275
function show(io::IO, mime::MIME{Symbol("text/plain")}, F::LU)

base/linalg/qr.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ function getproperty(F::QRCompactWY, d::Symbol)
449449
getfield(F, d)
450450
end
451451
end
452+
Base.propertynames(F::Union{QR,QRCompactWY}, private::Bool=false) = append!([:R,:Q], private ? fieldnames(typeof(F)) : Symbol[])
452453
function getproperty(F::QRPivoted{T}, d::Symbol) where T
453454
m, n = size(F)
454455
if d == :R
@@ -469,6 +470,7 @@ function getproperty(F::QRPivoted{T}, d::Symbol) where T
469470
getfield(F, d)
470471
end
471472
end
473+
Base.propertynames(F::QRPivoted, private::Bool=false) = append!([:R,:Q,:p,:P], private ? fieldnames(typeof(F)) : Symbol[])
472474

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

base/linalg/schur.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ function getproperty(F::Schur, d::Symbol)
7777
end
7878
end
7979

80+
Base.propertynames(F::Schur) = append!([:Schur,:vectors], fieldnames(typeof(F)))
81+
8082
function show(io::IO, F::Schur)
8183
println(io, "$(typeof(F)) with factors T and Z:")
8284
show(io, F.T)
@@ -274,6 +276,8 @@ function getproperty(F::GeneralizedSchur, d::Symbol)
274276
end
275277
end
276278

279+
Base.propertynames(F::GeneralizedSchur) = append!([:values,:left,:right], fieldnames(typeof(F)))
280+
277281
"""
278282
schur(A::StridedMatrix, B::StridedMatrix) -> S::StridedMatrix, T::StridedMatrix, Q::StridedMatrix, Z::StridedMatrix, α::Vector, β::Vector
279283

base/linalg/svd.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ function getproperty(F::SVD, d::Symbol)
186186
end
187187
end
188188

189+
Base.propertynames(F::SVD, private::Bool=false) = private ? append!([:V], fieldnames(typeof(F))) : [:U,:S,:V,:Vt]
190+
189191
"""
190192
svdvals!(A)
191193
@@ -461,6 +463,8 @@ svd(x::Number, y::Number) = first.(svd(fill(x, 1, 1), fill(y, 1, 1)))
461463
end
462464
end
463465

466+
Base.propertynames(F::GeneralizedSVD) = append!([:alpha,:beta,:vals,:S,:D1,:D2,:R0], fieldnames(typeof(F)))
467+
464468
"""
465469
svdvals!(A, B)
466470

base/reflection.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,3 +1105,20 @@ min_world(m::Method) = reinterpret(UInt, m.min_world)
11051105
max_world(m::Method) = typemax(UInt)
11061106
min_world(m::Core.MethodInstance) = reinterpret(UInt, m.min_world)
11071107
max_world(m::Core.MethodInstance) = reinterpret(UInt, m.max_world)
1108+
1109+
"""
1110+
propertynames(x, private=false)
1111+
1112+
Get an array of the properties (`x.property`) of an object `x`. This
1113+
is typically the same as [`fieldnames(typeof(x))`](@ref), but types
1114+
that overload [`getproperty`](@ref) should generally overload `propertynames`
1115+
as well to get the properties of an instance of the type.
1116+
1117+
`propertynames(x)` may return only "public" property names that are part
1118+
of the documented interface of `x`. If you want it to also return "private"
1119+
fieldnames intended for internal use, pass `true` for the optional second argument.
1120+
REPL tab completion on `x.` shows only the `private=false` properties.
1121+
"""
1122+
propertynames(x) = fieldnames(typeof(x))
1123+
propertynames(m::Module) = names(m)
1124+
propertynames(x, private) = propertynames(x) # ignore private flag by default

base/repl/REPLCompletions.jl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module REPLCompletions
55
export completions, shell_completions, bslash_completions
66

77
using Base.Meta
8-
using Base: coalesce
8+
using Base: propertynames, coalesce
99

1010
function completes_global(x, name)
1111
return startswith(x, name) && !('#' in x)
@@ -41,6 +41,7 @@ function complete_symbol(sym, ffunc)
4141

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

5051
b, found = get_value(ex, context_module)
5152
if found
53+
val = b
5254
if isa(b, Module)
5355
mod = b
5456
lookup_module = true
@@ -82,6 +84,13 @@ function complete_symbol(sym, ffunc)
8284
else
8385
append!(suggestions, filtered_mod_names(p, mod, name, true, false))
8486
end
87+
elseif val !== nothing # looking for a property of an instance
88+
for property in propertynames(val, false)
89+
s = string(property)
90+
if startswith(s, name)
91+
push!(suggestions, s)
92+
end
93+
end
8594
else
8695
# Looking for a member of a type
8796
if t isa DataType && t != Any

doc/src/base/base.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ Core.nfields
317317
Base.fieldnames
318318
Base.fieldname
319319
Base.fieldcount
320+
Base.propertynames
320321
Base.datatype_module
321322
Base.datatype_name
322323
Base.isconst

test/linalg/lu.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,10 @@ U factor:
264264
0.0 0.0 1.0 0.0
265265
0.0 0.0 0.0 1.0"""
266266
end
267+
268+
@testset "propertynames" begin
269+
names = sort!(string.(Base.propertynames(lufact(rand(3,3)))))
270+
@test names == ["L", "P", "U", "p"]
271+
allnames = sort!(string.(Base.propertynames(lufact(rand(3,3)), true)))
272+
@test allnames == ["L", "P", "U", "factors", "info", "ipiv", "p"]
273+
end

test/reflection.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ mutable struct TLayout
339339
z::Int32
340340
end
341341
tlayout = TLayout(5,7,11)
342-
@test fieldnames(TLayout) == [:x, :y, :z]
342+
@test fieldnames(TLayout) == [:x, :y, :z] == Base.propertynames(tlayout)
343343
@test [(fieldoffset(TLayout,i), fieldname(TLayout,i), fieldtype(TLayout,i)) for i = 1:fieldcount(TLayout)] ==
344344
[(0, :x, Int8), (2, :y, Int16), (4, :z, Int32)]
345345
@test_throws BoundsError fieldtype(TLayout, 0)

0 commit comments

Comments
 (0)