You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I've written a package called Mocking.jl which allows developers to temporarily overwrite a method for testing purposes. Due to some changes in Julia 0.5 it appears that a method is not overwritten until the executing function is complete.
I've distilled Mocking.jl into a single method that should work in both 0.4 and 0.5:
functionoverwrite(body::Function, o::Function, n::Function, sig::Array{DataType}, restore::Bool=true)
old_methods =collect(methods(o, Tuple{sig...}))
new_methods =collect(methods(n, Tuple{sig...}))
length(old_methods) !=1&&error("old function has too many methods")
length(new_methods) !=1&&error("new function has too many methods")
old_method = old_methods[1]
new_method = new_methods[1]
# Save the original implementation
org_impl = old_method.func
# Generate a method expression to overwrite the existing functionifisa(old_method.func, Function)
mod = old_method.func.code.module
name = old_method.func.code.name
else
mod = old_method.func.module
name = old_method.func.name
end
types = [:(::$t) for t in sig]
expr = :($name($(types...)) =nothing)
# Overwrite a method such that Julia uses updated implementation on future calls
Core.eval(mod, expr)
# Replace implementation
old_method.func = new_method.func
tryreturnbody()
finally# Restore the overwritten function body with the original implementationif restore
Core.eval(mod, expr)
old_method.func = org_impl
endendendrep(x::AbstractString) = x
Here is the expected behaviour as demonstrated in Julia 0.4.3:
julia>overwrite(open, rep, [AbstractString], true) doopen("foo")
end
WARNING: Method definition open(AbstractString) inmodule Main at none:1 overwritten inmodule Base at none:25."foo"
julia>open("foo")
ERROR: SystemError: opening file foo: No such file or directory
in open at ./iostream.jl:90in open at iostream.jl:99
In Julia 0.5.0-dev+2444 the behaviour has changed:
julia>overwrite(open, rep, [AbstractString], true) doopen("foo")
end
WARNING: Method definition open(AbstractString) inmodule Base at iostream.jl:99 overwritten at none:25.
WARNING: Method definition open(AbstractString) inmodule Main at none:1 overwritten inmodule Base at none:25.
ERROR: SystemError: opening file foo: No such file or directory
insystemerror(Base.#systemerror, ASCIIString, Bool) at /Users/omus/Development/Julia/latest/usr/lib/julia/sys.dylib:-1
[inlined code] from ./c.jl:91inopen(Base.#open, ASCIIString, Bool, Bool, Bool, Bool, Bool) at ./iostream.jl:90inoverwrite(#overwrite, ##1#2, Any, Any, Array{DataType,1}, Bool) at ./none:34ineval(Core.#eval, Module, Any) at ./boot.jl:267
julia>open("foo")
ERROR: SystemError: opening file foo: No such file or directory
insystemerror(Base.#systemerror, ASCIIString, Bool) at /Users/omus/Development/Julia/latest/usr/lib/julia/sys.dylib:-1
[inlined code] from ./c.jl:91inopen(Base.#open, ASCIIString, Bool, Bool, Bool, Bool, Bool) at ./iostream.jl:90inopen(Base.#open, ASCIIString) at ./iostream.jl:99ineval(Core.#eval, Module, Any) at ./boot.jl:267
If we don't restore the old function you can see that the behaviour does change but only once we get back to the REPL (0.5.0-dev+2444):
julia>overwrite(open, rep, [AbstractString], false) doopen("foo")
end
WARNING: Method definition open(AbstractString) inmodule Base at iostream.jl:99 overwritten at none:25.
ERROR: SystemError: opening file foo: No such file or directory
insystemerror(Base.#systemerror, ASCIIString, Bool) at /Users/omus/Development/Julia/latest/usr/lib/julia/sys.dylib:-1
[inlined code] from ./c.jl:91inopen(Base.#open, ASCIIString, Bool, Bool, Bool, Bool, Bool) at ./iostream.jl:90inoverwrite(#overwrite, ##1#2, Any, Any, Array{DataType,1}, Bool) at ./none:34ineval(Core.#eval, Module, Any) at ./boot.jl:267
julia>open("foo")
"foo"
Is this new behaviour intentional? Should I be implementing this in a different way? Should method overriding be integrated into Base to ensure its continued availability?
The text was updated successfully, but these errors were encountered:
omus
changed the title
Programmatically Overloading Methods
Programmatically overloading methods
Feb 2, 2016
It's an increasingly official rule that method changes aren't guaranteed to take effect until you return to the top level (see e.g. #4688). An alternate approach that might work is to use a macro that returns a :toplevel expression, such that the overwriting is done in one top level form and used in the next.
Should method overriding be integrated into Base disallowed entirely?
I've written a package called Mocking.jl which allows developers to temporarily overwrite a method for testing purposes. Due to some changes in Julia 0.5 it appears that a method is not overwritten until the executing function is complete.
I've distilled Mocking.jl into a single method that should work in both 0.4 and 0.5:
Here is the expected behaviour as demonstrated in Julia 0.4.3:
In Julia 0.5.0-dev+2444 the behaviour has changed:
If we don't restore the old function you can see that the behaviour does change but only once we get back to the REPL (0.5.0-dev+2444):
Is this new behaviour intentional? Should I be implementing this in a different way? Should method overriding be integrated into Base to ensure its continued availability?
The text was updated successfully, but these errors were encountered: