Trying and trying with Revise

Hi all,

I’m really trying here… I’ve been fighting with Revise most of the day. I’ve read the docs and the forums several times over and am just missing something. Maybe somebody can tell me what I’m doing wrong. Here is my setup.

  1. First, I’m on Windows 10 if that matters, running Julia 1.2.0 in the Atom 1.40.1 environment.

  2. I have startup.jl as shown here. The little print statement at the end does print out when the Julia session starts, so I know it’s working:

Start up revise

atreplinit() do repl
try
@eval using Revise
@async Revise.wait_steal_repl_backend()
catch
end
end

println(“End of /users/brad/.julia/config/startup.jl”)

  1. I have organized my code into modules. For example, SiteInfo.jl implements “module SiteInfo”.

  2. My top-level application is a script (I tried making it a module and Revise seemed more unhappy). It includes “using” and “import” lines to pull in the other modules.

  3. Here’s another sign that I don’t really know what I’m doing, probably… Julia doesn’t seem to find my code by default, so I do this in a little script when I start a session. MyMainApp defines a function DoMyApp() that I can call from the REPL to run my main app.

    thisdir = dirname(@FILE())
    any(path -> path==thisdir, LOAD_PATH) || push!(LOAD_PATH, thisdir)

    includet(“MyMainApp.jl”)

  4. Now I go and make changes to things. What happens is that the changes only take effect if I manually do includet(“ChangedFile.jl”) after I make my edits. I thought I wasn’t supposed to have to do that?

Also, I have tried making MyMainApp into a module, or doing includet on my sub-modules, but then I get error messages like

ERROR: KeyError: key MyMainApp [top-level] not found

It’s taken me quite a long sequence of successive approximation steps and sleuthing just to get this far, where typing includet manually seems to work. Most of the day until I figured that out, I’ve had to restart Julia every time I made a change.

What am I doing wrong?

It would be great if there was a simple workflow example as part of the Revise docs. It’s a good reference, but it’s missing handholds for a newbie.

Thanks,
Brad

2 Likes

Sorry about the frustrations, and thanks for the detailed report. If you do the demo on the first page (https://timholy.github.io/Revise.jl/stable/#Usage-example-1), does it work for you?

This looks fine.

What project are you in? That is, if you press ] to enter the package REPL, do you see the Julia version (eg 1.1 or 1.2)? It might also be useful to know what your workflow is. Are you working in the REPL? In Juno?

I’ve never had to muck around with LOAD_PATH. My usual workflow is to start or activate a project in my working directory (] activate ., or start the REPL with julia --project=., then add packages I’m not working on, and use ] dev for the one(s) I am working on.

Then using DevdPackage just works - you can change stuff and it should update. Revise needs to be loaded first, but your startup file takes care of that.

I haven’t used includet much, so if you want to use that workflow I can’t help :woman_shrugging:

This activate thing sounds really useful.

I tried it, I got this.

(v1.2) pkg> activate .

Activating new environment at C:\Users\brad\Documents\Erskine\Raytracer\Project.toml

It then changed my prompt to

(Raytracer) pkg>

Then I have a module SiteInfo in that folder. So I tried one of its exports and got this:

SiteInfo.UTCTime(DateTime(2019,6,22,12,00,0), site)

ERROR: UndefVarError: SiteInfo not defined

Stacktrace:

[1] top-level scope at none:0

Do I need to say “using SiteInfo”? (And likewise for all my other modules?) If so, I’m not sure what I’m gaining over the LOAD_PATH route. I"m sure I’m missing something.

Sorry, it’s just rough being a newbie. I mean, I’ve been coding for 40 years. But back then it was just “LDX #40” and stuff like that. I think we had something called BASIC as well. Very foggy…

Thanks in advance…

Yes, it works.

Well, it didn’t work at first. Following the instructions, I added this line to Example.jl:

function f() = pi

That caused barfing.

I looked at Example.jl in more detail and realized I shouldn’t add the word “function”. Once I fixed that, the example worked as described.

As a curiosity, what is happening when the prompt disappears for two minutes after “dev Example” prior to “Updating registry…”? Is it doing some really hard thinking, or is there some problem with my setup?

See if this helps. Comments welcome.

As a curiosity, what is happening when the prompt disappears for two minutes after “dev Example” prior to “Updating registry…”?

That amount of silence is weird. I get a pause while it compiles some of the Pkg code, but it’s only a couple of seconds. After the first time, you can toggle back and forth between dev and free and it should be fast.

Okay, I’ll give it a try.

FWIW, I see this kind of delay almost every time I do anything in the package manager.

It takes several minutes to “add Revise”, for example (if it’s not already added).

More… Yes I see the Julia version number once I do ].

I’m using the Juno / Atom environment.

This ] dev and ] activate and DevdPackage stuff sounds useful. How do I learn about it? I’m not at all wedded to my workflow. I’m trying to arrive at any useful workflow, but am having a hard time finding any documentation.

The overwhelming advice seems to be to use Revise. I’ve spent a total of 6 or 8 hours trying to figure this out, and have not seen anything until your email about somehow using the package manager, so this gives me hope that I have just been missing something.

Thanks for any pointers.

Here I wrote some documentation for developing a package:

And here, I wrote a simple code which switches between usage and developing mode. Just add this segment to the beginning of your usage or runtests files.

These are written for my package but applicable to others with little adjustment.

I agree about adding simple guides somewhere. Personally, I don’t find Julia documentation that straight forward.

1 Like

I’m not sure I fully understand the use case for usage vs developer mode. I’m writing an application for myself to run, to do some scientific modeling, and pretty much continuously tweak and enhance as I get results.

The app is not meant to be made into a package or committed back to Git or shared outside of one or two other people.

So just seeing the words, without a full understanding of the intended use case, I would think that I would just be in dev mode all the time and thus have a simpler workflow.

My perception is probably colored by my Matlab experience - I just code it and run it, and that’s all I’m wanting to do here.

Does that mean that the dev/usage switching something I’ll want or need? Is there a simplified workflow that allows me to get going in the language for now, and then add this in later?

Thanks for your help.

1 Like

Hopefully this won’t derail the thread too much, but if you don’t mind the question: what functionality of Revise do you need? Usually I’m fine with just Juno. For example, after changing a function Shift+Enter reruns/updates it. If you have a different file (maybe with a module in there), you can include it, if you changed it just rerun that. Maybe it’s not the most sophisticated method but especially for the beginning it should be enough?

Edit: For the next step up (especially for scientific work) I like DrWatson. The ] dev and ] activate stuff is included for free, you get one folder that’ll run anywhere (because it’s one environment with the package info and versions you used to develop your code), local Git which you can use in case you want to revert/branch something or put on Github later, plus a whole lot more.

2 Likes

This is described in the documentation for the package manager https://docs.julialang.org/en/v1/stdlib/Pkg/ with a longer version here: https://julialang.github.io/Pkg.jl/v1/
One could perhaps argue that it comes fairly late in the docs.

You should dev any package or personal project you want to do any work on. add is for installed packages you never modify.

2 Likes

I hadn’t gotten the idea that was possible. If that works, that would meet my needs.

My memory is fuzzy now after so many iterations, but I believe I kept getting a “symbol conflict in main” or something like that when I tried to reload a module, and would have to restart Julia.

There was one thing I wondered about though. One of my modules had a bunch of const declarations.

At one point I got rid of all the const keywords to see if that would help. That may have been after I started trying Revise. It may have helped somewhat. Maybe that’s when my approach of doing “includet” on everything started working?

Maybe the approach you suggest will work for me now. If that works, it gets me over the hump for this first project, and then I can try to sort out Revise and ] activate and all that once my head stops spinning a little.

Thanks for the suggestion, will let you know how it goes.

Okay. I do remember seeing that section but thinking “I’m just writing an app, not deploying a package”, so I figured I it was not newbie stuff.

Sounds like I need to dive in?

The weird thing is, I did write an app one other time about 8 months back, at my last job, and didn’t seem to have these problems or to need pkg. But it was a smaller app - perhaps I was just restarting Julia every time, I don’t recall now. I do recall that I used Julia Pro (1.0.something) in that case. That was the test case that led me to wait until a debugger was ready, so now I’m back with a new machine and new resolve.

But I don’t recall having any of the kinds of issues I’m currently having. It may be that the app was simpler and I just went through fewer cycles.

Take a look at @ChrisRackauckas’ video on development with Juno:

In general, Juno can do most things that Revise does as well, you just need to tell it what to do manually. If you change something, then you need to eval it to get updated definitions. In some cases this doesn’t work (e.g. redefining structs will throw an invalid redefinition of constant Bar error, but Revise doesn’t handle this case either), you need to re-evaluate the module that contains your struct.
You might get warnings in the REPL (like WARNING: replacing module Foo.), but those are usually harmless when developing.

1 Like

Ah yes, I remember a little more now.

The last thing I tried before the includet stuff…

I would edit a module. I’d hit the Play arrow icon in the toolbar. The module would load. (I know because I include a line ‘println(“thisMod loaded”)’ at the end of every module.

Then I’d run my main app, and it wouldn’t see the change. I’d try pressing the Play button on my main app. It would load but not see the change. Only includet would actually effect the change. It’s like the other approaches were compiling but not linking or using a stale cache file or something.

Ok thanks I’ll have a look.

The showstopper errors that would cause me to have to restart Julia were always these “ERROR: Key symbol MySym already defined in Main” messages, or something like that. Once I start seeing those, my REPL seems to permit no more changes and has to be restarted.

const declarations shouldn’t be a problem as long as the type doesn’t change. For example:

julia> const A = 1
1

julia> A = 2
WARNING: redefining constant A
2

julia> A = 3.0
ERROR: invalid redefinition of constant A
Stacktrace:
 [1] top-level scope at none:0

I would edit a module. I’d hit the Play arrow icon in the toolbar. The module would load. (I know because I include a line ‘println(“thisMod loaded”)’ at the end of every module.
Then I’d run my main app, and it wouldn’t see the change.

That’s curious… this works for me:
File 1:

module A
    test() = "hello"
end

and run (Ctrl+Shift+Enter) File 1. In File 2/REPL

julia> A.test()
"hello"

Edit File 1, don’t even need to save:

module A
    test() = "world"
end

and rerun:

WARNING: replacing module A.
julia> A.test()
"world"

The same thing works with include("File 1.jl") in File 2 but then you need to save File 1.jl before you rerun that line (Ctrl+Enter).

Hey, this Shift-Enter thing works well. I like this approach, thanks!

After poking around, I now see that Juno has its own set of docs separate from Julia and from Atom. I’m now trying to make sense of them.

Given how well this works, what do I do to ask Juno to recompile the whole file and not just that one function? I"m trying what I see in the Juno docs but it’s not working.

The “Play” icon doesn’t seem to do the trick,. I click play and it says “WARNING: replacing module SiteInfo. // SiteInfo loaded” But it doesn’t take effect. I also tried “Ctrl-Shift-Enter”, which the Juno docs say should reload the whole file. That gives the same result as the Play button - it appears to get processed but does not take effect.

I try 'include(“SiteInfo.jl”) from the REPL, and it says even more: “WARNING: replacing module SiteInfo. // SiteInfo loaded // Main.SiteInfo”, but still doesn’t take effect when I run my main app.

The change only takes effect if I Shift-Enter from within the function that has changed. (Sometimes I have other things that change, like the definition of a structure, that aren’t part of a function.) (BTW, I’ve stopped using Revise for the moment, to see if I can figure out just Juno.)

I feel like this was a really great suggestion, and am trying to figure out how to take this route to the finish line.

Thanks,

Brad

Okay, more info.

It turns out that, in Juno, once I do Ctrl-Shift-Enter on the entire file and the module (SiteInfo) gets reloaded, Shift-Enter no longer works after that either.

It’s like the new module was re-compiled but not re-linked.

So I try to re-load my main program, to pick up the new assignment, and I get LoadError: importing SiteInfo into Main conflicts with an existing identifier Which puts me at the point I always get to where I have to restart Julia.

This is the conundrum that sent me looking on the web and led me to Revise, but I’ve had the same sort of troubles with it.

So I’m better off than I was in that I can edit individual functions now without restarting, but I can’t redefine data structures.