Skip to content

Commit 3856252

Browse files
use getproperty
1 parent da2e416 commit 3856252

File tree

3 files changed

+77
-14
lines changed

3 files changed

+77
-14
lines changed

src/keypath.jl

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,46 @@ A type for representing a path of keys to a value in a nested structure.
99
Can be constructed with a sequence of keys, or by concatenating other `KeyPath`s.
1010
Keys can be of type `Symbol`, `String`, or `Int`.
1111
12+
For custom types, access through symbol keys is assumed to be done with `getproperty`.
13+
For consistency, the method `Base.propertynames` is used to get the viable property names.
14+
15+
For string and integer keys instead, the access is done with `getindex`.
16+
17+
See also [`getkeypath`](@ref), [`haskeypath`](@ref).
18+
1219
# Examples
1320
1421
```jldoctest
1522
julia> kp = KeyPath(:b, 3)
1623
KeyPath(:b, 3)
1724
18-
julia> KeyPath(:a, kp, :c, 4)
25+
julia> KeyPath(:a, kp, :c, 4) # construct mixing keys and keypaths
1926
KeyPath(:a, :b, 3, :c, 4)
27+
28+
julia> struct T
29+
a
30+
b
31+
end
32+
33+
julia> function Base.getproperty(x::T, k::Symbol)
34+
if k in fieldnames(T)
35+
return getfield(x, k)
36+
elseif k === :ab
37+
return "ab"
38+
else
39+
error()
40+
end
41+
end;
42+
43+
julia> Base.propertynames(::T) = (:a, :b, :ab);
44+
45+
julia> x = T(3, Dict(:c => 4, :d => 5));
46+
47+
julia> getkeypath(x, KeyPath(:ab)) # equivalent to x.ab
48+
"ab"
49+
50+
julia> getkeypath(x, KeyPath(:b, :c)) # equivalent to (x.b)[:c]
51+
4
2052
```
2153
"""
2254
struct KeyPath{T<:Tuple}
@@ -52,14 +84,14 @@ end
5284
keypathstr(kp::KeyPath) = join(kp.keys, ".")
5385

5486
_getkey(x, k::Integer) = x[k]
55-
_getkey(x, k::Symbol) = getfield(x, k)
87+
_getkey(x, k::Symbol) = getproperty(x, k)
5688
_getkey(x::AbstractDict, k::Symbol) = x[k]
5789
_getkey(x, k::AbstractString) = x[k]
5890

5991
_haskey(x, k::Integer) = haskey(x, k)
6092
_haskey(x::Tuple, k::Integer) = 1 <= k <= length(x)
6193
_haskey(x::AbstractArray, k::Integer) = 1 <= k <= length(x) # TODO: extend to generic indexing
62-
_haskey(x, k::Symbol) = k in fieldnames(typeof(x))
94+
_haskey(x, k::Symbol) = k in propertynames(x)
6395
_haskey(x::AbstractDict, k::Symbol) = haskey(x, k)
6496
_haskey(x, k::AbstractString) = haskey(x, k)
6597

@@ -68,7 +100,7 @@ _haskey(x, k::AbstractString) = haskey(x, k)
68100
69101
Return the value in `x` at the path `kp`.
70102
71-
See also [`haskeypath`](@ref).
103+
See also [`KeyPath`](@ref) and [`haskeypath`](@ref).
72104
73105
# Examples
74106
```jldoctest
@@ -94,7 +126,7 @@ end
94126
95127
Return `true` if `x` has a value at the path `kp`.
96128
97-
See also [`getkeypath`](@ref).
129+
See also [`KeyPath`](@ref) and [`getkeypath`](@ref).
98130
99131
# Examples
100132
```jldoctest

test/basics.jl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -400,10 +400,13 @@ end
400400
@functor A
401401
a = A(1)
402402
@test Functors.children(a) === (x = 1,)
403-
Functors.@leaf A
404-
children, re = Functors.functor(a)
403+
404+
struct B; x; end
405+
Functors.@leaf B
406+
b = B(1)
407+
children, re = Functors.functor(b)
405408
@test children == Functors.NoChildren()
406-
@test re(children) === a
409+
@test re(children) === b
407410
end
408411

409412
@testset "IterateWalk" begin

test/keypath.jl

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,40 @@
1616
kp0 = KeyPath()
1717
@test (kp0...,) === ()
1818

19+
struct Tkp
20+
a
21+
b
22+
c
23+
end
24+
25+
function Base.getproperty(x::Tkp, k::Symbol)
26+
if k in fieldnames(Tkp)
27+
return getfield(x, k)
28+
elseif k === :ab
29+
return "ab"
30+
else
31+
error()
32+
end
33+
end
34+
35+
Base.propertynames(::Tkp) = (:a, :b, :c, :ab)
36+
1937
@testset "getkeypath" begin
2038
x = Dict(:a => 3, :b => Dict(:c => 4, "d" => [5, 6, 7]))
2139
@test getkeypath(x, KeyPath(:a)) == 3
2240
@test getkeypath(x, KeyPath(:b, :c)) == 4
2341
@test getkeypath(x, KeyPath(:b, "d", 2)) == 6
24-
25-
struct Tkp
26-
a
27-
b
28-
c
29-
end
42+
3043
x = Tkp(3, Tkp(4, 5, (6, 7)), 8)
3144
kp = KeyPath(:b, :c, 2)
3245
@test getkeypath(x, kp) == 7
46+
47+
@testset "access through getproperty" begin
48+
x = Tkp(3, Dict(:c => 4, :d => 5), 6);
49+
50+
@test getkeypath(x, KeyPath(:ab)) == "ab"
51+
@test getkeypath(x, KeyPath(:b, :c)) == 4
52+
end
3353
end
3454

3555
@testset "haskeypath" begin
@@ -39,5 +59,13 @@
3959
@test haskeypath(x, KeyPath(:b, "d", 2))
4060
@test !haskeypath(x, KeyPath(:b, "d", 4))
4161
@test !haskeypath(x, KeyPath(:b, "e"))
62+
63+
@testset "access through getproperty" begin
64+
x = Tkp(3, Dict(:c => 4, :d => 5), 6);
65+
66+
@test haskeypath(x, KeyPath(:ab))
67+
@test haskeypath(x, KeyPath(:b, :c))
68+
@test !haskeypath(x, KeyPath(:b, :e))
69+
end
4270
end
4371
end

0 commit comments

Comments
 (0)