[ANN] Higher productivity (fewer Julia restarts) with Revise.jl

package
announcement
productivity
development

#1

I’m announcing a new package that may speed up your development: https://github.com/timholy/Revise.jl. Revise makes it easier to update code and have the changes take effect immediately, without having to restart Julia. Please see the README for more information.


How do you develop a package without having to commit, push and pull all your changes all the time
__precompile__() is not resulting in precompiled code
Julia development workflow
Deployment questions: AOT and Portability
1.0 progress/status
#2

This package revolutionized my workflow in a few days. Thanks!


#3

Glad to hear it! I find that old habits die hard—I still sometimes restart Julia unnecessarily—but it’s been very good for my workflow too.

Thanks to good feedback from users, v0.0.3 has been released with better support for vim, IJulia, and a broader collection of Julia packages. If it doesn’t work for your use-cases, please continue to report issues.


#4

Suggestion for even smoother workflow if you are working with Juno:
Besides working with Revise or CloberringReload(if you are on 0.5), set Juno to cycler mode
this way it makes a Julia instance ready in the background.

Moreover add a .junorc.jl in your home folder and have it import any commonly used packages.
This saves additional few seconds of start-up time

I also added an asynchronous task with many yields , that does a “workout” for commonly used functions
that are slow the first time… many yields because I want it to be responsive even if it didn’t finish its workout.

This way when I do restart Julia , it starts in a nice warm and nimble state.


#5

To break my habit I wanted ctrl-d to ask Exit? [Y/n] before exiting. I came up with this .juliarc.jl bit but it hangs at the readline. Anyone know how to do this?

    ctrl_d_ask = Dict{Any,Any}(
        "^D" =>
        function (s,o...)
            if Base.LineEdit.buffer(s).size > 0
                Base.LineEdit.edit_delete(s)
            else
                println("Exit? [Y/n]")
                answer = readline(STDIN)
                if answer=="" || lowercase(answer)=="y"
                    println(Base.LineEdit.terminal(s))
                    return :abort
                else
                    return nothing
                end
            end
        end
        )
    function customize_keys(repl)
        repl.interface = Base.REPL.setup_interface(repl; extra_repl_keymap = ctrl_d_ask)
    end
    atreplinit(customize_keys)

#6

I really like the idea of this package, even though it currently only works for the first few file changes. I’m not really sure what is going on, need to test it some more.
EDIT: It seems to be working now. Yay.

Anyway, I’m kind of curious why you have to do:

@schedule begin
    sleep(0.1)
    @eval using Revise
end

in .juliarc.jl?


#7

Do you mean, "as opposed to simply using Revise in .juliarc.jl"? The @schedule is needed because .juliarc.jl gets executed before the REPL code is fully initialized. Revise’s __init__ function alters an important component of the REPL code (shutting down one “service” and starting a modified one), so you have to do something to wait until the REPL code is initialized.


#8

Thanks for this trick!! I have a function to change the prompt (to include the current branh-name, to no be lost when I have a handful of open prompts to compare e.g. performances for a PR), but never could find a way to call this function from my “.juliarrc.jl”. This is really neat, you solve one of my longstanding problems (although I believe there should eventually be a cleaner to achieve the same).


#9

If you know for certain that you’re using the REPL, something like wait(Task(() -> while !isdefined(Base, :active_repl_backend) end)) might be even better than sleep(0.1). In Requires I want to make sure I support Juno/IJulia too, so there may never be a REPL. So I chose the more braindead solution.


#10

Looks neat, but after playing a bit with it, I can’t make it work (I will need to read more about tasks to understand); it’s looks like the task is never run.


#11

Even with @schedule I have to manually call revise().


#12

That’s unexpected. Is there an error message? What happens if you increase the sleep time? Are you loading any other packages in your .juliarc.jl? If so, they won’t get tracked unless you make sure you load Revise first.


#13

No error message. Here’s my current rc:

@schedule begin
    sleep(0.1)
    @eval using Revise
end

atreplinit((_)->begin
  Base.require(:TerminalExtensions)
  Base.require(:MiscGraphUtils)
end)

#14

Maybe TeminalExtensions does its own messing with things, perhaps try commenting that one out.


#15

It wasn’t TerminalExtensions, it was MiscGraphUtils which depended on LightGraphs (which was what I was revising). This seems to indicate that atreplinit happens BEFORE @schedule, right?

Also: if you remove a function from the source, it persists even after a revise() (this is not terribly surprising, I guess):

julia> LightGraphs.foo(g)
33

# remove foo from source, save.
julia> revise()

julia> LightGraphs.foo(g)
33

#16

This seems to indicate that atreplinit happens BEFORE @schedule, right?

Well, technically the @schedule happens immediately, but then execution progresses. Meanwhile the Task’s first action is to sleep 0.1s; if that takes longer than REPL initialization, then Revise gets loaded after those other packages. If you never use IJulia/Juno, you could just add Revise to the atreplinit list.

if you remove a function from the source, it persists even after a revise

Yes, this is documented in the README.


#17

If you never use IJulia/Juno, you could just add Revise to the atreplinit list.

I don’t, but it doesn’t work as I might expect:

atreplinit((_)->begin
  @eval using Revise
  Base.require(:TerminalExtensions)
  Base.require(:MiscGraphUtils)
end)

requires me to use revise() for LightGraphs (after a using LightGraphs typed into the REPL), and

atreplinit((_)->begin
  Base.require(:Revise)
  Base.require(:TerminalExtensions)
  Base.require(:MiscGraphUtils)
end)

doesn’t load Revise at all.


#18

This is a lifesaver! How hard would it be to make it a part of standard Julia REPL?


#19

I personally think it’s worth considering, but the scary thing would be that if it doesn’t work for everyone then it could handicap the REPL (which would be pretty catastrophic). If we want to make progress towards such a future, the most important thing is for anyone who experiences trouble with the package to please report bugs!

(Along these lines, OSX users please upgrade to the 0.0.4 release once the METADATA tag gets merged.)


#20

Just found out about this… This will change my life ! Thank you @tim.holy for doing this.