Skip to content

Commit

Permalink
Create revisableproto macro for compatibility with Revise
Browse files Browse the repository at this point in the history
  • Loading branch information
Tortar committed Feb 11, 2024
1 parent b1849da commit 5ac598e
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 2 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ c = DevType(2, 4.0, nothing)

Redefine at will, but remove the `@proto` macro after developing to ensure correctness and improve performance of your code.

# Compatibility with Revise.jl

For workflows using `Revise.jl` use the `@revisableproto` macro. This works almost equivalently to `@proto`
except that docstring can't be attached to structs.


---
Expand Down
11 changes: 10 additions & 1 deletion src/ProtoStruct.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@

macro revisableproto(expr)
@eval __module__ $(_proto(expr))
return
end

macro proto(expr)
return esc(_proto(expr))
end

function _proto(expr)
if expr.head == :macrocall && expr.args[1] == Symbol("@kwdef")
expr = expr.args[3]
end
Expand Down Expand Up @@ -230,6 +239,6 @@ macro proto(expr)
end
end
end
return esc(ex)
return ex
end

2 changes: 1 addition & 1 deletion src/ProtoStructs.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module ProtoStructs

export @proto
export @proto, @revisableproto

include("ProtoStruct.jl")

Expand Down
179 changes: 179 additions & 0 deletions test/test_ProtoStruct.jl
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,182 @@ end
@testset "Docstring" begin
@test string(@doc DocTestMe) == "This is a docstring.\n"
end

# revisableproto macro tests

@revisableproto struct SimpleTestMe2
A::Int
end

@revisableproto struct TestMe2{T, V <: Real}
A::Int
B
C::T
D::V
end
test_me = @test_nowarn TestMe2(1, "2", complex(1), 5)
test_me_kw = @test_nowarn TestMe2(A=1, B="2", C=complex(1), D=5)

@testset "Construction" begin
@test SimpleTestMe2(1) isa SimpleTestMe2
@test test_me isa TestMe2
@test_throws UndefKeywordError TestMe2(A=1)
end

@testset "Printing" begin
@test repr(test_me) == "TestMe2{Complex{$Int}, $Int}(1, \"2\", 1 + 0im, 5)"
end

@testset "Access" begin
@test test_me.A == 1
@test test_me.B == "2"
@test test_me.C == complex(1)
end

@testset "Properties" begin
@test propertynames(test_me) == (:A, :B, :C, :D)
end

@revisableproto struct TestMe2{T, V <: Real}
A::Int
B
C::T
D::V
E::String
end
test_me2 = @test_nowarn TestMe2(1, "2", complex(1), 5, "tadaa")
test_me_kw2 = @test_nowarn TestMe2(A=1, B="2", C=complex(1), D=5, E="tadaa")

@testset "Redefinition" begin
@test length(methods(TestMe2)) == 2
end

@revisableproto struct TestKw2{T, V <: Real}
A::Int = 1
B = :no
C::T = nothing
D::V
E::String
end

@testset "kwdef" begin
tw = TestKw2(D = 1.2, E = "yepp")
@test tw isa TestKw2
@test tw.A == 1
@test tw.B == :no
@test tw.C === nothing
@test tw.D == 1.2
@test tw.E == "yepp"
end

@revisableproto @kwdef struct TestMacroOutside2
A::Int = 1
end

@testset "@kwdef macro outside" begin
tw = TestMacroOutside2()
@test tw isa TestMacroOutside2
@test tw.A == 1
end

@proto mutable struct TestMutation2
F::Int
G::Float64
end

@testset "Mutation" begin
tm = @test_nowarn TestMutation2(4, 2.0)
@test tm.F == 4 && tm.G == 2.0
tm.F = 8
@test tm.F == 8 && tm.G == 2.0
@test_throws MethodError tm.F = "2"
@test propertynames(tm) == (:F, :G)
end

abstract type AbstractMutation2 end

@revisableproto mutable struct TestParametricMutation2{T, V <: Real} <: AbstractMutation2
A::Int = 1
B = :no
C::T = nothing
D::V
E::String
end

@testset "Parametric Mutation" begin
tpm = @test_nowarn TestParametricMutation2(D = 1.2, E = "yepp")
tpm.A = 2
tpm.E = "nope"
@test repr(tpm) == "TestParametricMutation2{Nothing, Float64}(2, no, nothing, 1.2, \"nope\")"
@test_throws ErrorException tpm.this = "is wrong"
@test tpm isa TestParametricMutation2
@test tpm isa TestParametricMutation2{Nothing, Float64}
@test !(tpm isa TestParametricMutation2{Nothing, Int})
@test tpm.A == 2
@test tpm.B == :no
@test tpm.C === nothing
@test tpm.D == 1.2
@test tpm.E == "nope"
@test TestParametricMutation2 <: AbstractMutation2
@test_throws MethodError tpm2 = TestParametricMutation2{Int, Float64}(D = 1.2, E = "yepp")
tpm2 = @test_nowarn TestParametricMutation2{Nothing, Float64}(D = 1.2, E = "yepp")
@test tpm2 isa TestParametricMutation2
@test tpm2 isa TestParametricMutation2{Nothing, Float64}
@test !(tpm2 isa TestParametricMutation2{Nothing, Int})
@test_throws MethodError tpm3 = TestParametricMutation2{Int, Float64}(1, :no, nothing, 1.2, "yepp")
tpm3 = @test_nowarn TestParametricMutation2{Nothing, Float64}(1, :no, nothing, 1.2, "yepp")
@test tpm3 isa TestParametricMutation2
@test tpm3 isa TestParametricMutation2{Nothing, Float64}
@test !(tpm3 isa TestParametricMutation2{Nothing, Int})
end

@revisableproto mutable struct TestParametricMutation2{V <: Integer} <: AbstractMutation2
A::Int = 1
B = :no
C::Nothing = nothing
D::V
E::String
end

@testset "Parametric Redefinition" begin
tpm = @test_nowarn TestParametricMutation2(D = 1, E = "yepp")
@test tpm isa TestParametricMutation2{Int}
@test tpm isa AbstractMutation2
end

@static if VERSION >= v"1.8"
@revisableproto mutable struct WithConstFields2{T}
A::Int = 1
const B = :no
const C::T = 3
D
const E::Vector{Int} = Int[]
end

@testset "const fields" begin
cf = @test_nowarn WithConstFields2(D = 1.2)
@test cf.A == 1
@test cf.B == :no
@test cf.C == 3
@test_nowarn show(devnull, cf)
cf.A = 5
@test_throws ErrorException cf.B = :yes
@test_throws ErrorException cf.C = 5
end
end

@revisableproto struct TestMethods2 end

@testset "Constuctor updating I" begin
@test length(collect(methods(TestMethods2))) == 1
end

@revisableproto struct TestMethods2
a
b
end

@testset "Constuctor updating II" begin
@test length(collect(methods(TestMethods2))) == 2
end

0 comments on commit 5ac598e

Please sign in to comment.