From f348aa5493365f3ae4e17b5fcfd2f0a96325511a Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Tue, 24 Sep 2024 11:28:35 +0000 Subject: [PATCH] build based on f2729ab --- previews/PR846/.documenter-siteinfo.json | 2 +- previews/PR846/config/index.html | 2 +- previews/PR846/cookbook/index.html | 2 +- previews/PR846/debugging/index.html | 2 +- previews/PR846/dev_reference/index.html | 10 +++++----- previews/PR846/index.html | 2 +- previews/PR846/internals/index.html | 2 +- previews/PR846/limitations/index.html | 2 +- previews/PR846/objects.inv | Bin 2362 -> 2361 bytes previews/PR846/search_index.js | 2 +- previews/PR846/user_reference/index.html | 12 ++++++------ 11 files changed, 19 insertions(+), 19 deletions(-) diff --git a/previews/PR846/.documenter-siteinfo.json b/previews/PR846/.documenter-siteinfo.json index 8ac7eb1e..b3905a5a 100644 --- a/previews/PR846/.documenter-siteinfo.json +++ b/previews/PR846/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.5","generation_timestamp":"2024-09-24T09:20:12","documenter_version":"1.7.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.5","generation_timestamp":"2024-09-24T11:28:28","documenter_version":"1.7.0"}} \ No newline at end of file diff --git a/previews/PR846/config/index.html b/previews/PR846/config/index.html index 5ec1c385..be2c821a 100644 --- a/previews/PR846/config/index.html +++ b/previews/PR846/config/index.html @@ -15,4 +15,4 @@ end END

Configuring the revise mode

By default, in packages all changes are tracked, but with includet only method definitions are tracked. This behavior can be overridden by defining a variable __revise_mode__ in the module(s) containing your methods and/or data. __revise_mode__ must be a Symbol taking one of the following values:

If you're using includet from the REPL, you can enter __revise_mode__ = :eval to set it throughout Main. __revise_mode__ can be set independently in each module.

Optional global configuration

Revise can be configured by setting environment variables. These variables have to be set before you execute using Revise, because these environment variables are parsed only during execution of Revise's __init__ function.

There are several ways to set these environment variables:

The function of specific environment variables is described below.

Manual revision: JULIA_REVISE

By default, Revise processes any modified source files every time you enter a command at the REPL. However, there might be times where you'd prefer to exert manual control over the timing of revisions. Revise looks for an environment variable JULIA_REVISE, and if it is set to anything other than "auto" it will require that you manually call revise() to update code.

User scripts: JULIA_REVISE_INCLUDE

By default, Revise only tracks files that have been required as a consequence of a using or import statement; files loaded by include are not tracked, unless you explicitly use includet or Revise.track(filename). However, you can turn on automatic tracking by setting the environment variable JULIA_REVISE_INCLUDE to the string "1" (e.g., JULIA_REVISE_INCLUDE=1 in a bash script).

Note

Most users should avoid setting JULIA_REVISE_INCLUDE. Try includet instead.

Configurations for fixing errors

No space left on device

Note

This applies only to Linux

Revise needs to be notified by your filesystem about changes to your code, which means that the files that define your modules need to be watched for updates. Some systems impose limits on the number of files and directories that can be watched simultaneously; if such a limit is hit, on Linux this can result in Revise silently ceasing to work (albeit with unit tests failing) or in a fairly cryptic error like

ERROR: start_watching (File Monitor): no space left on device (ENOSPC)

The cure is to investigate and possibly increase the number of files that can be watched.

Invoking

$ sysctl fs.inotify

at the linux prompt may e.g. result in

fs.inotify.max_queued_events = 16384
 fs.inotify.max_user_instances = 128
-fs.inotify.max_user_watches = 524288

For Revise usage, max_user_watches >= 65536 is recommended, and more can be helpful; the value of 524288 above is common on modern systems. One can set higher values as needed, e.g.,

$ sudo sysctl fs.inotify.max_user_instances=2048

After changing these values, it is advised to run Revise's unit tests to see if they pass.

This change can be made permanent.

For more information see issues #26 and #778.

Polling and NFS-mounted code directories: JULIA_REVISE_POLL

Note

This applies only to Unix systems with code on network-mounted drives

Revise works by monitoring your filesystem for changes to the files that define your code. On most operating systems, Revise can work "passively" and wait to be signaled that one or more watched directories has changed.

Unfortunately, a few file systems (notably, the Unix-based Network File System NFS) don't support this approach. In such cases, Revise needs to "actively" check each file periodically to see whether it has changed since the last check. This active process is called polling. You turn on polling by setting the environment variable JULIA_REVISE_POLL to the string "1" (e.g., JULIA_REVISE_POLL=1 in a bash script).

Warning

If you're using polling, you may have to wait several seconds before changes take effect. Polling is not recommended unless you have no other alternative.

Note

NFS stands for Network File System and is typically only used to mount shared network drives on Unix file systems. Despite similarities in the acronym, NTFS, the standard filesystem on Windows, is completely different from NFS; Revise's default configuration should work fine on Windows without polling. However, WSL2 users currently need polling due to this bug.

+fs.inotify.max_user_watches = 524288

For Revise usage, max_user_watches >= 65536 is recommended, and more can be helpful; the value of 524288 above is common on modern systems. One can set higher values as needed, e.g.,

$ sudo sysctl fs.inotify.max_user_instances=2048

After changing these values, it is advised to run Revise's unit tests to see if they pass.

This change can be made permanent.

For more information see issues #26 and #778.

Polling and NFS-mounted code directories: JULIA_REVISE_POLL

Note

This applies only to Unix systems with code on network-mounted drives

Revise works by monitoring your filesystem for changes to the files that define your code. On most operating systems, Revise can work "passively" and wait to be signaled that one or more watched directories has changed.

Unfortunately, a few file systems (notably, the Unix-based Network File System NFS) don't support this approach. In such cases, Revise needs to "actively" check each file periodically to see whether it has changed since the last check. This active process is called polling. You turn on polling by setting the environment variable JULIA_REVISE_POLL to the string "1" (e.g., JULIA_REVISE_POLL=1 in a bash script).

Warning

If you're using polling, you may have to wait several seconds before changes take effect. Polling is not recommended unless you have no other alternative.

Note

NFS stands for Network File System and is typically only used to mount shared network drives on Unix file systems. Despite similarities in the acronym, NTFS, the standard filesystem on Windows, is completely different from NFS; Revise's default configuration should work fine on Windows without polling. However, WSL2 users currently need polling due to this bug.

diff --git a/previews/PR846/cookbook/index.html b/previews/PR846/cookbook/index.html index 90f2ff4c..e27575e4 100644 --- a/previews/PR846/cookbook/index.html +++ b/previews/PR846/cookbook/index.html @@ -49,4 +49,4 @@ julia> mygreeting() "Hello, world!"

Now, in your editor modify mygreeting to do this:

mygreeting() = "Hello, revised world!"

and then try it in the same session:

julia> mygreeting()
-"Hello, revised world!"

As described above, the first revision you make may be very slow, but later revisions should be fast.

+"Hello, revised world!"

As described above, the first revision you make may be very slow, but later revisions should be fast.

diff --git a/previews/PR846/debugging/index.html b/previews/PR846/debugging/index.html index 89cd8fa0..4d530a1e 100644 --- a/previews/PR846/debugging/index.html +++ b/previews/PR846/debugging/index.html @@ -48,4 +48,4 @@ 3x end)))) Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, "/home/tim/.julia/dev/Revise/src/Revise.jl", 296, (time=1.557996459391642e9, deltainfo=(Any[Tuple{typeof(unchanged),Any}], :(#= /tmp/revisetest.jl:18 =#) => :(#= /tmp/revisetest.jl:19 =#)))) - Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, "/home/tim/.julia/dev/Revise/src/Revise.jl", 296, (time=1.557996459391695e9, deltainfo=(Any[Tuple{typeof(unchanged2),Any}], :(#= /tmp/revisetest.jl:20 =#) => :(#= /tmp/revisetest.jl:21 =#))))

You can see that Revise started by deleting three methods, followed by evaluating three new versions of those methods. Interspersed are various changes to the line numbering.

In rare cases it might be helpful to independently record the sequence of edits to the file. You can make copies cp editedfile.jl > /tmp/version1.jl, edit code, cp editedfile.jl > /tmp/version2.jl, etc. diff version1.jl version2.jl can be used to capture a compact summary of the changes and pasted into the bug report.

Debugging problems with paths

During certain types of usage you might receive messages like

Warning: /some/system/path/stdlib/v1.0/SHA/src is not an existing directory, Revise is not watching

Unless you've just deleted that directory, this indicates that some of Revise's functionality is broken.

In the majority of cases, failures come down to Revise having trouble locating source code on your drive. This problem should be fixable, because Revise includes functionality to update its links to source files, as long as it knows what to do.

One of the best approaches is to run Revise's own tests via pkg> test Revise. Here are some possible test warnings and errors, and steps you might take to fix them:

+ Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, "/home/tim/.julia/dev/Revise/src/Revise.jl", 296, (time=1.557996459391695e9, deltainfo=(Any[Tuple{typeof(unchanged2),Any}], :(#= /tmp/revisetest.jl:20 =#) => :(#= /tmp/revisetest.jl:21 =#))))

You can see that Revise started by deleting three methods, followed by evaluating three new versions of those methods. Interspersed are various changes to the line numbering.

In rare cases it might be helpful to independently record the sequence of edits to the file. You can make copies cp editedfile.jl > /tmp/version1.jl, edit code, cp editedfile.jl > /tmp/version2.jl, etc. diff version1.jl version2.jl can be used to capture a compact summary of the changes and pasted into the bug report.

Debugging problems with paths

During certain types of usage you might receive messages like

Warning: /some/system/path/stdlib/v1.0/SHA/src is not an existing directory, Revise is not watching

Unless you've just deleted that directory, this indicates that some of Revise's functionality is broken.

In the majority of cases, failures come down to Revise having trouble locating source code on your drive. This problem should be fixable, because Revise includes functionality to update its links to source files, as long as it knows what to do.

One of the best approaches is to run Revise's own tests via pkg> test Revise. Here are some possible test warnings and errors, and steps you might take to fix them:

diff --git a/previews/PR846/dev_reference/index.html b/previews/PR846/dev_reference/index.html index 10781892..429015fb 100644 --- a/previews/PR846/dev_reference/index.html +++ b/previews/PR846/dev_reference/index.html @@ -1,9 +1,9 @@ -Developer reference · Revise.jl

Developer reference

Internal global variables

These are set during execution of Revise's __init__ function.

Revise.watching_filesConstant
Revise.watching_files[]

Returns true if we watch files rather than their containing directory. FreeBSD and NFS-mounted systems should watch files, otherwise we prefer to watch directories.

source
Revise.polling_filesConstant
Revise.polling_files[]

Returns true if we should poll the filesystem for changes to the files that define loaded code. It is preferable to avoid polling, instead relying on operating system notifications via FileWatching.watch_file. However, NFS-mounted filesystems (and perhaps others) do not support file-watching, so for code stored on such filesystems you should turn polling on.

See the documentation for the JULIA_REVISE_POLL environment variable.

source
Revise.tracking_Main_includesConstant
Revise.tracking_Main_includes[]

Returns true if files directly included from the REPL should be tracked. The default is false. See the documentation regarding the JULIA_REVISE_INCLUDE environment variable to customize it.

source
Revise.juliadirConstant
Revise.juliadir

Constant specifying full path to julia top-level source directory. This should be reliable even for local builds, cross-builds, and binary installs.

source
Revise.basesrccacheConstant
Revise.basesrccache

Full path to the running Julia's cache of source code defining Base.

source
Revise.basebuilddirConstant
Revise.basebuilddir

Julia's top-level directory when Julia was built, as recorded by the entries in Base._included_files.

source

Internal state management

Revise.pkgdatasConstant
Revise.pkgdatas

pkgdatas is the core information that tracks the relationship between source code and julia objects, and allows re-evaluation of code in the proper module scope. It is a dictionary indexed by PkgId: pkgdatas[id] returns a value of type Revise.PkgData.

source
Revise.watched_filesConstant
Revise.watched_files

Global variable, watched_files[dirname] returns the collection of files in dirname that we're monitoring for changes. The returned value has type Revise.WatchList.

This variable allows us to watch directories rather than files, reducing the burden on the OS.

source
Revise.revision_queueConstant
Revise.revision_queue

Global variable, revision_queue holds (pkgdata,filename) pairs that we need to revise, meaning that these files have changed since we last processed a revision. This list gets populated by callbacks that watch directories for updates.

source
Revise.NOPACKAGEConstant
Revise.NOPACKAGE

Global variable; default PkgId used for files which do not belong to any package, but still have to be watched because user callbacks have been registered for them.

source
Revise.queue_errorsConstant
Revise.queue_errors

Global variable, maps (pkgdata, filename) pairs that errored upon last revision to (exception, backtrace).

source
Revise.included_filesConstant
Revise.included_files

Global variable, included_files gets populated by callbacks we register with include. It's used to track non-precompiled packages and, optionally, user scripts (see docs on JULIA_REVISE_INCLUDE).

source
Revise.watched_manifestsConstant
Revise.watched_manifests

Global variable, a set of Manifest.toml files from the active projects used during this session.

source

The following are specific to user callbacks (see Revise.add_callback) and the implementation of entr:

Revise.revision_eventConstant
Revise.revision_event

This Condition is used to notify entr that one of the watched files has changed.

source
Revise.user_callbacks_queueConstant
Revise.user_callbacks_queue

Global variable, user_callbacks_queue holds key values for which the file has changed but the user hooks have not yet been called.

source
Revise.user_callbacks_by_fileConstant
Revise.user_callbacks_by_file

Global variable, maps files (identified by their absolute path) to the set of callback keys registered for them.

source

Types

Revise.RelocatableExprType

A RelocatableExpr wraps an Expr to ensure that comparisons between RelocatableExprs ignore line numbering information. This allows one to detect that two expressions are the same no matter where they appear in a file.

source
Revise.ModuleExprsSigsType
ModuleExprsSigs

For a particular source file, the corresponding ModuleExprsSigs is a mapping mod=>exprs=>sigs of the expressions exprs found in mod and the signatures sigs that arise from them. Specifically, if mes is a ModuleExprsSigs, then mes[mod][ex] is a list of signatures that result from evaluating ex in mod. It is possible that this returns nothing, which can mean either that ex does not define any methods or that the signatures have not yet been cached.

The first mod key is guaranteed to be the module into which this file was included.

To create a ModuleExprsSigs from a source file, see Revise.parse_source.

source
Revise.FileInfoType
FileInfo(mexs::ModuleExprsSigs, cachefile="")

Structure to hold the per-module expressions found when parsing a single file. mexs holds the Revise.ModuleExprsSigs for the file.

Optionally, a FileInfo can also record the path to a cache file holding the original source code. This is applicable only for precompiled modules and Base. (This cache file is distinct from the original source file that might be edited by the developer, and it will always hold the state of the code when the package was precompiled or Julia's Base was built.) When a cache is available, mexs will be empty until the file gets edited: the original source code gets parsed only when a revision needs to be made.

Source cache files greatly reduce the overhead of using Revise.

source
Revise.PkgDataType
PkgData(id, path, fileinfos::Dict{String,FileInfo})

A structure holding the data required to handle a particular package. path is the top-level directory defining the package, and fileinfos holds the Revise.FileInfo for each file defining the package.

For the PkgData associated with Main (e.g., for files loaded with includet), the corresponding path entry will be empty.

source
Revise.WatchListType
Revise.WatchList

A struct for holding files that live inside a directory. Some platforms (OSX) have trouble watching too many files. So we watch parent directories, and keep track of which files in them should be tracked.

Fields:

  • timestamp: mtime of last update
  • trackedfiles: Set of filenames, generally expressed as a relative path
source
Revise.TaskThunkType
thunk = TaskThunk(f, args)

To facilitate precompilation and reduce latency, we avoid creation of anonymous thunks. thunk can be used as an argument in schedule(Task(thunk)).

source
Revise.ReviseEvalExceptionType
ReviseEvalException(loc::String, exc::Exception, stacktrace=nothing)

Provide additional location information about exc.

When running via the interpreter, the backtraces point to interpreter code rather than the original culprit. This makes it possible to use loc to provide information about the frame backtrace, and even to supply a fake backtrace.

If stacktrace is supplied it must be a Vector{Any} containing (::StackFrame, n) pairs where n is the recursion count (typically 1).

source
Revise.MethodSummaryType
MethodSummary(method)

Create a portable summary of a method. In particular, a MethodSummary can be saved to a JLD2 file.

source

Function reference

Functions called when you load a new package

Revise.watch_packageFunction
watch_package(id::Base.PkgId)

Start watching a package for changes to the files that define it. This function gets called via a callback registered with Base.require, at the completion of module-loading by using or import.

source
Revise.parse_pkg_filesFunction
parse_pkg_files(id::PkgId)

This function gets called by watch_package and runs when a package is first loaded. Its job is to organize the files and expressions defining the module so that later we can detect and process revisions.

source

Monitoring for changes

These functions get called on each directory or file that you monitor for revisions. These block execution until the file(s) are updated, so you should only call them from within an @async block. They work recursively: once an update has been detected and execution resumes, they schedule a revision (see Revise.revision_queue) and then call themselves on the same directory or file to wait for the next set of changes.

The following functions support user callbacks, and are used in the implementation of entr but can be used more broadly:

Revise.add_callbackFunction
key = Revise.add_callback(f, files, modules=nothing; key=gensym())

Add a user-specified callback, to be executed during the first run of revise() after a file in files or a module in modules is changed on the file system. If all is set to true, also execute the callback whenever any file already monitored by Revise changes. In an interactive session like the REPL, Juno or Jupyter, this means the callback executes immediately before executing a new command / cell.

You can use the return value key to remove the callback later (Revise.remove_callback) or to update it using another call to Revise.add_callback with key=key.

source
Revise.remove_callbackFunction
Revise.remove_callback(key)

Remove a callback previously installed by a call to Revise.add_callback(...). See its docstring for details.

source

Evaluating changes (revising) and computing diffs

revise is the primary entry point for implementing changes. Additionally,

Revise.revise_file_nowFunction
Revise.revise_file_now(pkgdata::PkgData, file)

Process revisions to file. This parses file and computes an expression-level diff between the current state of the file and its most recently evaluated state. It then deletes any removed methods and re-evaluates any changed expressions. Note that generally it is better to use revise as it properly handles methods that move from one file to another.

id must be a key in Revise.pkgdatas, and file a key in Revise.pkgdatas[id].fileinfos.

source

Caching the definition of methods

Revise.get_defFunction
success = get_def(method::Method)

As needed, load the source file necessary for extracting the code defining method. The source-file defining method must be tracked. If it is in Base, this will execute track(Base) if necessary.

This is a callback function used by CodeTracking.jl's definition.

source

Parsing source code

Revise.parse_sourceFunction
mexs = parse_source(filename::AbstractString, mod::Module)

Parse the source filename, returning a ModuleExprsSigs mexs. mod is the "parent" module for the file (i.e., the one that included the file); if filename defines more module(s) then these will all have separate entries in mexs.

If parsing filename fails, nothing is returned.

source
Revise.parse_source!Function
parse_source!(mexs::ModuleExprsSigs, filename, mod::Module)

Top-level parsing of filename as included into module mod. Successfully-parsed expressions will be added to mexs. Returns mexs if parsing finished successfully, otherwise nothing is returned.

See also Revise.parse_source.

source
success = parse_source!(mod_exprs_sigs::ModuleExprsSigs, src::AbstractString, filename::AbstractString, mod::Module)

Parse a string src obtained by reading file as a single string. pos is the 1-based byte offset from which to begin parsing src.

See also Revise.parse_source.

source

Lowered source code

Much of the "brains" of Revise comes from doing analysis on lowered code. This part of the package is not as well documented.

Revise.minimal_evaluation!Function
isrequired, evalassign = minimal_evaluation!([predicate,] methodinfo, src::Core.CodeInfo, mode::Symbol)

Mark required statements in src: isrequired[i] is true if src.code[i] should be evaluated. Statements are analyzed by isreq, haseval = predicate(stmt), and predicate defaults to Revise.is_method_or_eval. haseval is true if the statement came from @eval or eval(...) call. Since the contents of such expression are difficult to analyze, it is generally safest to execute all such evals.

source
Revise.methods_by_execution!Function
methods_by_execution!(recurse=JuliaInterpreter.Compiled(), methodinfo, docexprs, mod::Module, ex::Expr;
-                      mode=:eval, disablebp=true, skip_include=mode!==:eval, always_rethrow=false)

Evaluate or analyze ex in the context of mod. Depending on the setting of mode (see the Extended help), it supports full evaluation or just the minimal evaluation needed to extract method signatures. recurse controls JuliaInterpreter's evaluation of any non-intercepted statement; likely choices are JuliaInterpreter.Compiled() or JuliaInterpreter.finish_and_return!. methodinfo is a cache for storing information about any method definitions (see CodeTrackingMethodInfo). docexprs is a cache for storing documentation expressions; obtain an empty one with Revise.DocExprs().

Extended help

The action depends on mode:

  • :eval evaluates the expression in mod, similar to Core.eval(mod, ex) except that methodinfo and docexprs will be populated with information about any signatures or docstrings. This mode is used to implement includet.
  • :sigs analyzes ex and extracts signatures of methods and docstrings (specifically, statements flagged by Revise.minimal_evaluation!), but does not evaluate ex in the traditional sense. It will selectively execute statements needed to form the signatures of defined methods. It will also expand any @evaled expressions, since these might contain method definitions.
  • :evalmeth analyzes ex and extracts signatures and docstrings like :sigs, but takes the additional step of evaluating any :method statements.
  • :evalassign acts similarly to :evalmeth, and also evaluates assignment statements.

When selectively evaluating an expression, Revise will incorporate required dependencies, even for minimal-evaluation modes like :sigs. For example, the method definition

max_values(T::Union{map(X -> Type{X}, Base.BitIntegerSmall_types)...}) = 1 << (8*sizeof(T))

found in base/abstractset.jl requires that it create the anonymous function in order to compute the signature.

The other keyword arguments are more straightforward:

  • disablebp controls whether JuliaInterpreter's breakpoints are disabled before stepping through the code. They are restored on exit.
  • skip_include prevents execution of include statements, instead inserting them into methodinfo's cache. This defaults to true unless mode is :eval.
  • always_rethrow, if true, causes an error to be thrown if evaluating ex triggered an error. If false, the error is logged with @error. InterruptExceptions are always rethrown. This is primarily useful for debugging.
source
Revise.CodeTrackingMethodInfoType
CodeTrackingMethodInfo(ex::Expr)

Create a cache for storing information about method definitions. Adding signatures to such an object inserts them into CodeTracking.method_info, which maps signature Tuple-types to (lnn::LineNumberNode, ex::Expr) pairs. Because method signatures are unique within a module, this is the foundation for identifying methods in a manner independent of source-code location.

It also has the following fields:

  • exprstack: used when descending into @eval statements (via push_expr and pop_expr!) ex (used in creating the CodeTrackingMethodInfo object) is the first entry in the stack.
  • allsigs: a list of all method signatures defined by a given expression
  • deps: list of top-level named objects (Symbols and GlobalRefs) that method definitions in this block depend on. For example, if Sys.iswindows() f() = 1 else f() = 2 end would store Sys.iswindows here.
  • includes: a list of module=>filename for any include statements encountered while the expression was parsed.
source

Modules and paths

Revise.modulefilesFunction
parentfile, included_files = modulefiles(mod::Module)

Return the parentfile in which mod was defined, as well as a list of any other files that were included to define mod. If this operation is unsuccessful, (nothing, nothing) is returned.

All files are returned as absolute paths.

source

Handling errors

Revise.trim_toplevel!Function
trim_toplevel!(bt)

Truncate a list of instruction pointers, as obtained from backtrace() or catch_backtrace(), at the first "top-level" call (e.g., as executed from the REPL prompt) or the first entry corresponding to a method in Revise or its dependencies.

This is used to make stacktraces obtained with Revise more similar to those obtained without Revise, while retaining one entry to reveal Revise's involvement.

source

In current releases of Julia, hitting Ctrl-C from the REPL can stop tasks running in the background. This risks stopping Revise's ability to watch for changes in files and directories. Revise has a work-around for this problem.

Revise.throwto_replFunction
success = throwto_repl(e::Exception)

Try throwing e from the REPL's backend task. Returns true if the necessary conditions were met and the throw can be expected to succeed. The throw is generated from another task, so a yield will need to occur before it happens.

source

Git integration

Revise.git_sourceFunction
Revise.git_source(file::AbstractString, reference)

Read the source-text for file from a git commit reference. The reference may be a string, Symbol, or LibGit2.Tree.

Example:

Revise.git_source("/path/to/myfile.jl", "HEAD")
-Revise.git_source("/path/to/myfile.jl", :abcd1234)  # by commit SHA
source
Revise.git_repoFunction
repo, repo_path = git_repo(path::AbstractString)

Return the repo::LibGit2.GitRepo containing the file or directory path. path does not necessarily need to be the top-level directory of the repository. Also returns the repo_path of the top-level directory for the repository.

source

Distributed computing

Revise.init_workerFunction
Revise.init_worker(p)

Define methods on worker p that Revise needs in order to perform revisions on p. Revise itself does not need to be running on p.

source

Teaching Revise about non-julia source codes

Revise can be made to work for transpilers from non-Julia languages to Julia with a little effort. For example, if you wrote a transpiler from C to Julia, you can define a struct CFile which overrides enough of the common String methods (abspath,isabspath, joinpath, normpath,isfile,findfirst, and String), it will be supported by Revise if you define a method like

function Revise.parse_source!(mod_exprs_sigs::Revise.ModuleExprsSigs, file::CFile, mod::Module; kwargs...)
+Developer reference · Revise.jl

Developer reference

Internal global variables

These are set during execution of Revise's __init__ function.

Revise.watching_filesConstant
Revise.watching_files[]

Returns true if we watch files rather than their containing directory. FreeBSD and NFS-mounted systems should watch files, otherwise we prefer to watch directories.

source
Revise.polling_filesConstant
Revise.polling_files[]

Returns true if we should poll the filesystem for changes to the files that define loaded code. It is preferable to avoid polling, instead relying on operating system notifications via FileWatching.watch_file. However, NFS-mounted filesystems (and perhaps others) do not support file-watching, so for code stored on such filesystems you should turn polling on.

See the documentation for the JULIA_REVISE_POLL environment variable.

source
Revise.tracking_Main_includesConstant
Revise.tracking_Main_includes[]

Returns true if files directly included from the REPL should be tracked. The default is false. See the documentation regarding the JULIA_REVISE_INCLUDE environment variable to customize it.

source
Revise.juliadirConstant
Revise.juliadir

Constant specifying full path to julia top-level source directory. This should be reliable even for local builds, cross-builds, and binary installs.

source
Revise.basesrccacheConstant
Revise.basesrccache

Full path to the running Julia's cache of source code defining Base.

source
Revise.basebuilddirConstant
Revise.basebuilddir

Julia's top-level directory when Julia was built, as recorded by the entries in Base._included_files.

source

Internal state management

Revise.pkgdatasConstant
Revise.pkgdatas

pkgdatas is the core information that tracks the relationship between source code and julia objects, and allows re-evaluation of code in the proper module scope. It is a dictionary indexed by PkgId: pkgdatas[id] returns a value of type Revise.PkgData.

source
Revise.watched_filesConstant
Revise.watched_files

Global variable, watched_files[dirname] returns the collection of files in dirname that we're monitoring for changes. The returned value has type Revise.WatchList.

This variable allows us to watch directories rather than files, reducing the burden on the OS.

source
Revise.revision_queueConstant
Revise.revision_queue

Global variable, revision_queue holds (pkgdata,filename) pairs that we need to revise, meaning that these files have changed since we last processed a revision. This list gets populated by callbacks that watch directories for updates.

source
Revise.NOPACKAGEConstant
Revise.NOPACKAGE

Global variable; default PkgId used for files which do not belong to any package, but still have to be watched because user callbacks have been registered for them.

source
Revise.queue_errorsConstant
Revise.queue_errors

Global variable, maps (pkgdata, filename) pairs that errored upon last revision to (exception, backtrace).

source
Revise.included_filesConstant
Revise.included_files

Global variable, included_files gets populated by callbacks we register with include. It's used to track non-precompiled packages and, optionally, user scripts (see docs on JULIA_REVISE_INCLUDE).

source
Revise.watched_manifestsConstant
Revise.watched_manifests

Global variable, a set of Manifest.toml files from the active projects used during this session.

source

The following are specific to user callbacks (see Revise.add_callback) and the implementation of entr:

Revise.revision_eventConstant
Revise.revision_event

This Condition is used to notify entr that one of the watched files has changed.

source
Revise.user_callbacks_queueConstant
Revise.user_callbacks_queue

Global variable, user_callbacks_queue holds key values for which the file has changed but the user hooks have not yet been called.

source
Revise.user_callbacks_by_fileConstant
Revise.user_callbacks_by_file

Global variable, maps files (identified by their absolute path) to the set of callback keys registered for them.

source

Types

Revise.RelocatableExprType

A RelocatableExpr wraps an Expr to ensure that comparisons between RelocatableExprs ignore line numbering information. This allows one to detect that two expressions are the same no matter where they appear in a file.

source
Revise.ModuleExprsSigsType
ModuleExprsSigs

For a particular source file, the corresponding ModuleExprsSigs is a mapping mod=>exprs=>sigs of the expressions exprs found in mod and the signatures sigs that arise from them. Specifically, if mes is a ModuleExprsSigs, then mes[mod][ex] is a list of signatures that result from evaluating ex in mod. It is possible that this returns nothing, which can mean either that ex does not define any methods or that the signatures have not yet been cached.

The first mod key is guaranteed to be the module into which this file was included.

To create a ModuleExprsSigs from a source file, see Revise.parse_source.

source
Revise.FileInfoType
FileInfo(mexs::ModuleExprsSigs, cachefile="")

Structure to hold the per-module expressions found when parsing a single file. mexs holds the Revise.ModuleExprsSigs for the file.

Optionally, a FileInfo can also record the path to a cache file holding the original source code. This is applicable only for precompiled modules and Base. (This cache file is distinct from the original source file that might be edited by the developer, and it will always hold the state of the code when the package was precompiled or Julia's Base was built.) When a cache is available, mexs will be empty until the file gets edited: the original source code gets parsed only when a revision needs to be made.

Source cache files greatly reduce the overhead of using Revise.

source
Revise.PkgDataType
PkgData(id, path, fileinfos::Dict{String,FileInfo})

A structure holding the data required to handle a particular package. path is the top-level directory defining the package, and fileinfos holds the Revise.FileInfo for each file defining the package.

For the PkgData associated with Main (e.g., for files loaded with includet), the corresponding path entry will be empty.

source
Revise.WatchListType
Revise.WatchList

A struct for holding files that live inside a directory. Some platforms (OSX) have trouble watching too many files. So we watch parent directories, and keep track of which files in them should be tracked.

Fields:

  • timestamp: mtime of last update
  • trackedfiles: Set of filenames, generally expressed as a relative path
source
Revise.TaskThunkType
thunk = TaskThunk(f, args)

To facilitate precompilation and reduce latency, we avoid creation of anonymous thunks. thunk can be used as an argument in schedule(Task(thunk)).

source
Revise.ReviseEvalExceptionType
ReviseEvalException(loc::String, exc::Exception, stacktrace=nothing)

Provide additional location information about exc.

When running via the interpreter, the backtraces point to interpreter code rather than the original culprit. This makes it possible to use loc to provide information about the frame backtrace, and even to supply a fake backtrace.

If stacktrace is supplied it must be a Vector{Any} containing (::StackFrame, n) pairs where n is the recursion count (typically 1).

source
Revise.MethodSummaryType
MethodSummary(method)

Create a portable summary of a method. In particular, a MethodSummary can be saved to a JLD2 file.

source

Function reference

Functions called when you load a new package

Revise.watch_packageFunction
watch_package(id::Base.PkgId)

Start watching a package for changes to the files that define it. This function gets called via a callback registered with Base.require, at the completion of module-loading by using or import.

source
Revise.parse_pkg_filesFunction
parse_pkg_files(id::PkgId)

This function gets called by watch_package and runs when a package is first loaded. Its job is to organize the files and expressions defining the module so that later we can detect and process revisions.

source

Monitoring for changes

These functions get called on each directory or file that you monitor for revisions. These block execution until the file(s) are updated, so you should only call them from within an @async block. They work recursively: once an update has been detected and execution resumes, they schedule a revision (see Revise.revision_queue) and then call themselves on the same directory or file to wait for the next set of changes.

The following functions support user callbacks, and are used in the implementation of entr but can be used more broadly:

Revise.add_callbackFunction
key = Revise.add_callback(f, files, modules=nothing; key=gensym())

Add a user-specified callback, to be executed during the first run of revise() after a file in files or a module in modules is changed on the file system. If all is set to true, also execute the callback whenever any file already monitored by Revise changes. In an interactive session like the REPL, Juno or Jupyter, this means the callback executes immediately before executing a new command / cell.

You can use the return value key to remove the callback later (Revise.remove_callback) or to update it using another call to Revise.add_callback with key=key.

source
Revise.remove_callbackFunction
Revise.remove_callback(key)

Remove a callback previously installed by a call to Revise.add_callback(...). See its docstring for details.

source

Evaluating changes (revising) and computing diffs

revise is the primary entry point for implementing changes. Additionally,

Revise.revise_file_nowFunction
Revise.revise_file_now(pkgdata::PkgData, file)

Process revisions to file. This parses file and computes an expression-level diff between the current state of the file and its most recently evaluated state. It then deletes any removed methods and re-evaluates any changed expressions. Note that generally it is better to use revise as it properly handles methods that move from one file to another.

id must be a key in Revise.pkgdatas, and file a key in Revise.pkgdatas[id].fileinfos.

source

Caching the definition of methods

Revise.get_defFunction
success = get_def(method::Method)

As needed, load the source file necessary for extracting the code defining method. The source-file defining method must be tracked. If it is in Base, this will execute track(Base) if necessary.

This is a callback function used by CodeTracking.jl's definition.

source

Parsing source code

Revise.parse_sourceFunction
mexs = parse_source(filename::AbstractString, mod::Module)

Parse the source filename, returning a ModuleExprsSigs mexs. mod is the "parent" module for the file (i.e., the one that included the file); if filename defines more module(s) then these will all have separate entries in mexs.

If parsing filename fails, nothing is returned.

source
Revise.parse_source!Function
parse_source!(mexs::ModuleExprsSigs, filename, mod::Module)

Top-level parsing of filename as included into module mod. Successfully-parsed expressions will be added to mexs. Returns mexs if parsing finished successfully, otherwise nothing is returned.

See also Revise.parse_source.

source
success = parse_source!(mod_exprs_sigs::ModuleExprsSigs, src::AbstractString, filename::AbstractString, mod::Module)

Parse a string src obtained by reading file as a single string. pos is the 1-based byte offset from which to begin parsing src.

See also Revise.parse_source.

source

Lowered source code

Much of the "brains" of Revise comes from doing analysis on lowered code. This part of the package is not as well documented.

Revise.minimal_evaluation!Function
isrequired, evalassign = minimal_evaluation!([predicate,] methodinfo, src::Core.CodeInfo, mode::Symbol)

Mark required statements in src: isrequired[i] is true if src.code[i] should be evaluated. Statements are analyzed by isreq, haseval = predicate(stmt), and predicate defaults to Revise.is_method_or_eval. haseval is true if the statement came from @eval or eval(...) call. Since the contents of such expression are difficult to analyze, it is generally safest to execute all such evals.

source
Revise.methods_by_execution!Function
methods_by_execution!(recurse=JuliaInterpreter.Compiled(), methodinfo, docexprs, mod::Module, ex::Expr;
+                      mode=:eval, disablebp=true, skip_include=mode!==:eval, always_rethrow=false)

Evaluate or analyze ex in the context of mod. Depending on the setting of mode (see the Extended help), it supports full evaluation or just the minimal evaluation needed to extract method signatures. recurse controls JuliaInterpreter's evaluation of any non-intercepted statement; likely choices are JuliaInterpreter.Compiled() or JuliaInterpreter.finish_and_return!. methodinfo is a cache for storing information about any method definitions (see CodeTrackingMethodInfo). docexprs is a cache for storing documentation expressions; obtain an empty one with Revise.DocExprs().

Extended help

The action depends on mode:

  • :eval evaluates the expression in mod, similar to Core.eval(mod, ex) except that methodinfo and docexprs will be populated with information about any signatures or docstrings. This mode is used to implement includet.
  • :sigs analyzes ex and extracts signatures of methods and docstrings (specifically, statements flagged by Revise.minimal_evaluation!), but does not evaluate ex in the traditional sense. It will selectively execute statements needed to form the signatures of defined methods. It will also expand any @evaled expressions, since these might contain method definitions.
  • :evalmeth analyzes ex and extracts signatures and docstrings like :sigs, but takes the additional step of evaluating any :method statements.
  • :evalassign acts similarly to :evalmeth, and also evaluates assignment statements.

When selectively evaluating an expression, Revise will incorporate required dependencies, even for minimal-evaluation modes like :sigs. For example, the method definition

max_values(T::Union{map(X -> Type{X}, Base.BitIntegerSmall_types)...}) = 1 << (8*sizeof(T))

found in base/abstractset.jl requires that it create the anonymous function in order to compute the signature.

The other keyword arguments are more straightforward:

  • disablebp controls whether JuliaInterpreter's breakpoints are disabled before stepping through the code. They are restored on exit.
  • skip_include prevents execution of include statements, instead inserting them into methodinfo's cache. This defaults to true unless mode is :eval.
  • always_rethrow, if true, causes an error to be thrown if evaluating ex triggered an error. If false, the error is logged with @error. InterruptExceptions are always rethrown. This is primarily useful for debugging.
source
Revise.CodeTrackingMethodInfoType
CodeTrackingMethodInfo(ex::Expr)

Create a cache for storing information about method definitions. Adding signatures to such an object inserts them into CodeTracking.method_info, which maps signature Tuple-types to (lnn::LineNumberNode, ex::Expr) pairs. Because method signatures are unique within a module, this is the foundation for identifying methods in a manner independent of source-code location.

It also has the following fields:

  • exprstack: used when descending into @eval statements (via push_expr and pop_expr!) ex (used in creating the CodeTrackingMethodInfo object) is the first entry in the stack.
  • allsigs: a list of all method signatures defined by a given expression
  • deps: list of top-level named objects (Symbols and GlobalRefs) that method definitions in this block depend on. For example, if Sys.iswindows() f() = 1 else f() = 2 end would store Sys.iswindows here.
  • includes: a list of module=>filename for any include statements encountered while the expression was parsed.
source

Modules and paths

Revise.modulefilesFunction
parentfile, included_files = modulefiles(mod::Module)

Return the parentfile in which mod was defined, as well as a list of any other files that were included to define mod. If this operation is unsuccessful, (nothing, nothing) is returned.

All files are returned as absolute paths.

source

Handling errors

Revise.trim_toplevel!Function
trim_toplevel!(bt)

Truncate a list of instruction pointers, as obtained from backtrace() or catch_backtrace(), at the first "top-level" call (e.g., as executed from the REPL prompt) or the first entry corresponding to a method in Revise or its dependencies.

This is used to make stacktraces obtained with Revise more similar to those obtained without Revise, while retaining one entry to reveal Revise's involvement.

source

In current releases of Julia, hitting Ctrl-C from the REPL can stop tasks running in the background. This risks stopping Revise's ability to watch for changes in files and directories. Revise has a work-around for this problem.

Revise.throwto_replFunction
success = throwto_repl(e::Exception)

Try throwing e from the REPL's backend task. Returns true if the necessary conditions were met and the throw can be expected to succeed. The throw is generated from another task, so a yield will need to occur before it happens.

source

Git integration

Revise.git_sourceFunction
Revise.git_source(file::AbstractString, reference)

Read the source-text for file from a git commit reference. The reference may be a string, Symbol, or LibGit2.Tree.

Example:

Revise.git_source("/path/to/myfile.jl", "HEAD")
+Revise.git_source("/path/to/myfile.jl", :abcd1234)  # by commit SHA
source
Revise.git_repoFunction
repo, repo_path = git_repo(path::AbstractString)

Return the repo::LibGit2.GitRepo containing the file or directory path. path does not necessarily need to be the top-level directory of the repository. Also returns the repo_path of the top-level directory for the repository.

source

Distributed computing

Revise.init_workerFunction
Revise.init_worker(p)

Define methods on worker p that Revise needs in order to perform revisions on p. Revise itself does not need to be running on p.

source

Teaching Revise about non-julia source codes

Revise can be made to work for transpilers from non-Julia languages to Julia with a little effort. For example, if you wrote a transpiler from C to Julia, you can define a struct CFile which overrides enough of the common String methods (abspath,isabspath, joinpath, normpath,isfile,findfirst, and String), it will be supported by Revise if you define a method like

function Revise.parse_source!(mod_exprs_sigs::Revise.ModuleExprsSigs, file::CFile, mod::Module; kwargs...)
     ex = # julia Expr returned from running transpiler
     Revise.process_source!(mod_exprs_sigs, ex, file, mod; kwargs...)
 end
-
+
diff --git a/previews/PR846/index.html b/previews/PR846/index.html index 5771c9ea..fcd60655 100644 --- a/previews/PR846/index.html +++ b/previews/PR846/index.html @@ -21,4 +21,4 @@ build_webpages(args...) end

will execute build_webpages(args...) whenever you save updates to the listed files or MyWebCode.

If you want to regenerate the web page as soon as any change is detected, not only in MyWebCode but also in any package tracked by Revise, you can provide the all keyword argument to entr:

entr(["file.js", "assets"]; all=true) do
     build_webpages(args...)
-end

Taking advantage of Revise in other packages

To make it easier for other packages to benefit from Revise without needing to add it as a dependency or understand Revise's internals, Revise interfaces with CodeTracking, which is a small package acting as Revise's "query" interface.

What else do I need to know?

Except in cases of problems (see below), that's it! Revise is a tool that runs in the background, and when all is well it should be essentially invisible, except that you don't have to restart Julia so often.

Revise can also be used as a "library" by developers who want to add other new capabilities to Julia; the sections How Revise works and Developer reference are particularly relevant for them.

If Revise doesn't work as expected

If Revise isn't working for you, here are some steps to try:

If you still encounter problems, please file an issue. Especially if you think Revise is making mistakes in adding or deleting methods, please see the page on Debugging Revise for information about how to attach logs to your bug report.

+end

Taking advantage of Revise in other packages

To make it easier for other packages to benefit from Revise without needing to add it as a dependency or understand Revise's internals, Revise interfaces with CodeTracking, which is a small package acting as Revise's "query" interface.

What else do I need to know?

Except in cases of problems (see below), that's it! Revise is a tool that runs in the background, and when all is well it should be essentially invisible, except that you don't have to restart Julia so often.

Revise can also be used as a "library" by developers who want to add other new capabilities to Julia; the sections How Revise works and Developer reference are particularly relevant for them.

If Revise doesn't work as expected

If Revise isn't working for you, here are some steps to try:

If you still encounter problems, please file an issue. Especially if you think Revise is making mistakes in adding or deleting methods, please see the page on Debugging Revise for information about how to attach logs to your bug report.

diff --git a/previews/PR846/internals/index.html b/previews/PR846/internals/index.html index 4620457f..84243a0d 100644 --- a/previews/PR846/internals/index.html +++ b/previews/PR846/internals/index.html @@ -70,4 +70,4 @@ end), )

This is just a summary; to see the actual def=>sigts map, do the following:

julia> pkgdata.fileinfos[2].modexsigs[Items]
 OrderedCollections.OrderedDict{Revise.RelocatableExpr,Union{Nothing, Array{Any,1}}} with 2 entries:
   :(indent(::UInt16) = begin…                       => Any[Tuple{typeof(indent),UInt16}]
-  :(indent(::UInt8) = begin…                        => Any[Tuple{typeof(indent),UInt8}]

These are populated now because we specified __precompile__(false), which forces Revise to defensively parse all expressions in the package in case revisions are made at some future point. For precompiled packages, each pkgdata.fileinfos[i] can instead rely on the cachefile (another field stored in the Revise.FileInfo) as a record of the state of the file at the time the package was loaded; as a consequence, Revise can defer parsing the source file(s) until they are updated.

Items.jl is represented with a bit more complexity, "Items.jl"=>Dict(Main=>map1, Items=>map2). This is because Items.jl contains one expression (the __precompile__ statement) that is evaled in Main, and other expressions that are evaled in Items.

Revisions and computing diffs

When the file system notifies Revise that a file has been modified, Revise re-parses the file and assigns the expressions to the appropriate modules, creating a Revise.ModuleExprsSigs mexsnew. It then compares mexsnew against mexsref, the reference object that is synchronized to code as it was evaled. The following actions are taken:

Technically, a new mexsref is generated every time to ensure that the expressions are ordered as in mexsnew; however, conceptually this is better thought of as an updating of mexsref, after which mexsnew is discarded.

Note that one consequence is that modifying a method causes two actions, the deletion of the original followed by evaling a new version. During revision, all method deletions are performed first, followed by all the new evaled methods. This ensures that if a method gets moved from fileB.jl to fileA.jl, Revise doesn't mistakenly redefine and then delete the method simply because fileA.jl got processed before fileB.jl.

Internal API

You can find more detail about Revise's inner workings in the Developer reference.

+ :(indent(::UInt8) = begin… => Any[Tuple{typeof(indent),UInt8}]

These are populated now because we specified __precompile__(false), which forces Revise to defensively parse all expressions in the package in case revisions are made at some future point. For precompiled packages, each pkgdata.fileinfos[i] can instead rely on the cachefile (another field stored in the Revise.FileInfo) as a record of the state of the file at the time the package was loaded; as a consequence, Revise can defer parsing the source file(s) until they are updated.

Items.jl is represented with a bit more complexity, "Items.jl"=>Dict(Main=>map1, Items=>map2). This is because Items.jl contains one expression (the __precompile__ statement) that is evaled in Main, and other expressions that are evaled in Items.

Revisions and computing diffs

When the file system notifies Revise that a file has been modified, Revise re-parses the file and assigns the expressions to the appropriate modules, creating a Revise.ModuleExprsSigs mexsnew. It then compares mexsnew against mexsref, the reference object that is synchronized to code as it was evaled. The following actions are taken:

Technically, a new mexsref is generated every time to ensure that the expressions are ordered as in mexsnew; however, conceptually this is better thought of as an updating of mexsref, after which mexsnew is discarded.

Note that one consequence is that modifying a method causes two actions, the deletion of the original followed by evaling a new version. During revision, all method deletions are performed first, followed by all the new evaled methods. This ensures that if a method gets moved from fileB.jl to fileA.jl, Revise doesn't mistakenly redefine and then delete the method simply because fileA.jl got processed before fileB.jl.

Internal API

You can find more detail about Revise's inner workings in the Developer reference.

diff --git a/previews/PR846/limitations/index.html b/previews/PR846/limitations/index.html index 55e3f429..c6176682 100644 --- a/previews/PR846/limitations/index.html +++ b/previews/PR846/limitations/index.html @@ -115,4 +115,4 @@ remotecall_fetch(greetcaller, p) end -end # module

and the corresponding edit to the code would be to modify it to greetcaller(x) = greet("Bar") and remotecall_fetch(greetcaller, p, 1).

+end # module

and the corresponding edit to the code would be to modify it to greetcaller(x) = greet("Bar") and remotecall_fetch(greetcaller, p, 1).

diff --git a/previews/PR846/objects.inv b/previews/PR846/objects.inv index 5968b209e1795ac751c7f0e4d5d0a3d72bf1c436..c86f91bc4505bb7684d2d405fecb4852d3f04984 100644 GIT binary patch delta 14 Vcmdlbv{Pt;J+qmf!A9rr8~`PT1n~d> delta 15 Wcmdlfv`c7$J&UQHp~Xh0?;HRro&_lY diff --git a/previews/PR846/search_index.js b/previews/PR846/search_index.js index d9b056c8..a4ba7a06 100644 --- a/previews/PR846/search_index.js +++ b/previews/PR846/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"debugging/#Debugging-Revise","page":"Debugging Revise","title":"Debugging Revise","text":"","category":"section"},{"location":"debugging/#Handling-errors","page":"Debugging Revise","title":"Handling errors","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Revise attempts to make error reports mimic Julia's own stacktraces, and as a consequence it has to prevent stacktraces from containing lots of lines pointing to Revise's own code. If you're trying to debug a Revise error, you'd probably prefer to see the entire stacktrace. You can uncomment the obvious commented-out line in Revise.trim_toplevel!.","category":"page"},{"location":"debugging/#The-logging-framework","page":"Debugging Revise","title":"The logging framework","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"If Revise isn't behaving the way you expect it to, it can be useful to examine the decisions it made. Revise supports Julia's Logging framework and can optionally record its decisions in a format suitable for later inspection. What follows is a simple series of steps you can use to turn on logging, capture messages, and then submit them with a bug report. Alternatively, more advanced developers may want to examine the logs themselves to determine the source of Revise's error, and for such users a few tips about interpreting the log messages are also provided below.","category":"page"},{"location":"debugging/#Turning-on-logging","page":"Debugging Revise","title":"Turning on logging","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Currently, the best way to turn on logging is within a running Julia session:","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"julia> rlogger = Revise.debug_logger()\nRevise.ReviseLogger(Revise.LogRecord[], Debug)","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"You'll use rlogger at the end to retrieve the logs.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Now carry out the series of julia commands and code edits that reproduces the problem.","category":"page"},{"location":"debugging/#Capturing-the-logs-and-submitting-them-with-your-bug-report","page":"Debugging Revise","title":"Capturing the logs and submitting them with your bug report","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Once all the revisions have been triggered and the mistake has been reproduced, it's time to capture the logs. To capture all the logs, use","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"julia> using Base.CoreLogging: Debug\n\njulia> logs = filter(r->r.level==Debug, rlogger.logs);","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"You can capture just the changes that Revise made to running code with","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"julia> logs = Revise.actions(rlogger)","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"You can either let these print to the console and copy/paste the text output into the issue, or if they are extensive you can save logs to a file:","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"open(\"/tmp/revise.logs\", \"w\") do io\n for log in logs\n println(io, log)\n end\nend","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Then you can upload the logs somewhere (e.g., https://gist.github.com/) and link the url in your bug report. To assist in the resolution of the bug, please also specify additional relevant information such as the name of the function that was misbehaving after revision and/or any error messages that your received.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"See also A complete debugging demo below.","category":"page"},{"location":"debugging/#Logging-by-default","page":"Debugging Revise","title":"Logging by default","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"If you suspect a bug in Revise but have difficulty isolating it, you can include the lines","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":" # Turn on logging\n Revise.debug_logger()","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"within the Revise block of your ~/.julia/config/startup.jl file. This will ensure that you always log Revise's actions. Then carry out your normal Julia development. If a Revise-related problem arises, executing these lines","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"rlogger = Revise.debug_logger()\nusing Base.CoreLogging: Debug\nlogs = filter(r->r.level==Debug, rlogger.logs)\nopen(\"/tmp/revise.logs\", \"w\") do io\n for log in logs\n println(io, log)\n end\nend","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"within the same session will generate the /tmp/revise.logs file that you can submit with your bug report. (What makes this possible is that a second call to Revise.debug_logger() returns the same logger object created by the first call–it is not necessary to hold on to rlogger.)","category":"page"},{"location":"debugging/#The-structure-of-the-logs","page":"Debugging Revise","title":"The structure of the logs","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"For those who want to do a little investigating on their own, it may be helpful to know that Revise's core decisions are captured in the group called \"Action,\" and they come in three flavors:","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"log entries with message \"Eval\" signify a call to eval; for these events, keyword :deltainfo has value (mod, expr) where mod is the module of evaluation and expr is a Revise.RelocatableExpr containing the expression that was evaluated.\nlog entries with message \"DeleteMethod\" signify a method deletion; for these events, keyword :deltainfo has value (sigt, methsummary) where sigt is the signature of the method that Revise intended to delete and methsummary is a MethodSummary of the method that Revise actually found to delete.\nlog entries with message \"LineOffset\" correspond to updates to Revise's own internal estimates of how far a given method has become displaced from the line number it occupied when it was last evaluated. For these events, :deltainfo has value (sigt, newlineno, oldoffset=>newoffset).","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"If you're debugging mistakes in method creation/deletion, the \"LineOffset\" events may be distracting; by default Revise.actions excludes these events.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Note that Revise records the time of each revision, which can sometimes be useful in determining which revisions occur in conjunction with which user actions. If you want to make use of this, it can be handy to capture the start time with tstart = time() before commencing on a session.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"See Revise.debug_logger for information on groups besides \"Action.\"","category":"page"},{"location":"debugging/#A-complete-debugging-demo","page":"Debugging Revise","title":"A complete debugging demo","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"From within Revise's test/ directory, try the following:","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"julia> rlogger = Revise.debug_logger();\n\nshell> cp revisetest.jl /tmp/\n\njulia> includet(\"/tmp/revisetest.jl\")\n\njulia> ReviseTest.cube(3)\n81\n\nshell> cp revisetest_revised.jl /tmp/revisetest.jl\n\njulia> ReviseTest.cube(3)\n27\n\njulia> rlogger.logs\njulia> rlogger.logs\n9-element Array{Revise.LogRecord,1}:\n Revise.LogRecord(Debug, DeleteMethod, Action, Revise_4ac0f476, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 226, (time=1.557996459055345e9, deltainfo=(Tuple{typeof(Main.ReviseTest.cube),Any}, MethodSummary(:cube, :ReviseTest, Symbol(\"/tmp/revisetest.jl\"), 7, Tuple{typeof(Main.ReviseTest.cube),Any}))))\n Revise.LogRecord(Debug, DeleteMethod, Action, Revise_4ac0f476, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 226, (time=1.557996459167895e9, deltainfo=(Tuple{typeof(Main.ReviseTest.Internal.mult3),Any}, MethodSummary(:mult3, :Internal, Symbol(\"/tmp/revisetest.jl\"), 12, Tuple{typeof(Main.ReviseTest.Internal.mult3),Any}))))\n Revise.LogRecord(Debug, DeleteMethod, Action, Revise_4ac0f476, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 226, (time=1.557996459167956e9, deltainfo=(Tuple{typeof(Main.ReviseTest.Internal.mult4),Any}, MethodSummary(:mult4, :Internal, Symbol(\"/tmp/revisetest.jl\"), 13, Tuple{typeof(Main.ReviseTest.Internal.mult4),Any}))))\n Revise.LogRecord(Debug, Eval, Action, Revise_9147188b, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 276, (time=1.557996459259605e9, deltainfo=(Main.ReviseTest, :(cube(x) = begin\n #= /tmp/revisetest.jl:7 =#\n x ^ 3\n end))))\n Revise.LogRecord(Debug, Eval, Action, Revise_9147188b, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 276, (time=1.557996459330512e9, deltainfo=(Main.ReviseTest, :(fourth(x) = begin\n #= /tmp/revisetest.jl:9 =#\n x ^ 4\n end))))\n Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 296, (time=1.557996459331061e9, deltainfo=(Any[Tuple{typeof(mult2),Any}], :(#= /tmp/revisetest.jl:11 =#) => :(#= /tmp/revisetest.jl:13 =#))))\n Revise.LogRecord(Debug, Eval, Action, Revise_9147188b, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 276, (time=1.557996459391182e9, deltainfo=(Main.ReviseTest.Internal, :(mult3(x) = begin\n #= /tmp/revisetest.jl:14 =#\n 3x\n end))))\n Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 296, (time=1.557996459391642e9, deltainfo=(Any[Tuple{typeof(unchanged),Any}], :(#= /tmp/revisetest.jl:18 =#) => :(#= /tmp/revisetest.jl:19 =#))))\n Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 296, (time=1.557996459391695e9, deltainfo=(Any[Tuple{typeof(unchanged2),Any}], :(#= /tmp/revisetest.jl:20 =#) => :(#= /tmp/revisetest.jl:21 =#))))","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"You can see that Revise started by deleting three methods, followed by evaluating three new versions of those methods. Interspersed are various changes to the line numbering.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"In rare cases it might be helpful to independently record the sequence of edits to the file. You can make copies cp editedfile.jl > /tmp/version1.jl, edit code, cp editedfile.jl > /tmp/version2.jl, etc. diff version1.jl version2.jl can be used to capture a compact summary of the changes and pasted into the bug report.","category":"page"},{"location":"debugging/#Debugging-problems-with-paths","page":"Debugging Revise","title":"Debugging problems with paths","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"During certain types of usage you might receive messages like","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Warning: /some/system/path/stdlib/v1.0/SHA/src is not an existing directory, Revise is not watching","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Unless you've just deleted that directory, this indicates that some of Revise's functionality is broken.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"In the majority of cases, failures come down to Revise having trouble locating source code on your drive. This problem should be fixable, because Revise includes functionality to update its links to source files, as long as it knows what to do.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"One of the best approaches is to run Revise's own tests via pkg> test Revise. Here are some possible test warnings and errors, and steps you might take to fix them:","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Base & stdlib file paths: Test Failed at /some/path... Expression: isfile(Revise.basesrccache) This failure is quite serious, and indicates that you will be unable to access code in Base. To fix this, look for a file called \"base.cache\" somewhere in your Julia install or build directory (for the author, it is at /home/tim/src/julia-1.0/usr/share/julia/base.cache). Now compare this with the value of Revise.basesrccache. (If you're getting this failure, presumably they are different.) An important \"top level\" directory is Sys.BINDIR; if they differ already at this level, consider adding a symbolic link from the location pointed at by Sys.BINDIR to the corresponding top-level directory in your actual Julia installation. You'll know you've succeeded in specifying it correctly when, after restarting Julia, Revise.basesrccache points to the correct file and Revise.juliadir points to the directory that contains base/. If this workaround is not possible or does not succeed, please file an issue with a description of why you can't use it and/or\ndetails from versioninfo and information about how you obtained your Julia installation;\nthe values of Revise.basesrccache and Revise.juliadir, and the actual paths to base.cache and the directory containing the running Julia's base/;\nwhat you attempted when trying to fix the problem;\nif possible, your best understanding of why this failed to fix it.\nskipping Core.Compiler tests due to lack of git repo: this likely indicates that you downloaded a Julia binary rather than building Julia from source. While Revise should be able to access the code in Base and standard libraries, at the current time it is not possible for Revise to access julia's Core.Compiler module unless you clone Julia's repository and build it from source.\nskipping git tests because Revise is not under development: this warning should be harmless. Revise has built-in functionality for extracting source code using git, and it uses itself (i.e., its own git repository) for testing purposes. These tests run only if you have checked out Revise for development (pkg> dev Revise) or on the continuous integration servers (Travis and Appveyor).","category":"page"},{"location":"cookbook/#Revise-usage:-a-cookbook","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"","category":"section"},{"location":"cookbook/#Package-centric-usage","page":"Revise usage: a cookbook","title":"Package-centric usage","text":"","category":"section"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"For code that might be useful more than once, it's often a good idea to put it in a package. Revise cooperates with the package manager to enforce its distinction between \"versioned\" and \"under development\" packages; packages that you want to modify and have tracked by Revise should be deved rather than added.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"note: Note\nYou should never modify package files in your .julia/packages directory, because this breaks the \"contract\" that such package files correspond to registered versions of the code. In recent versions of Julia, the source files in .julia/packages are read-only, and you should leave them this way.In keeping with this spirit, Revise is designed to avoid tracking changes in such files. The correct way to make and track modifications is to dev the package.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"For creating packages, the author recommends PkgTemplates.jl. A fallback is to use \"plain\" Pkg commands. Both options are described below.","category":"page"},{"location":"cookbook/#PkgTemplates","page":"Revise usage: a cookbook","title":"PkgTemplates","text":"","category":"section"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"note: Note\nBecause PkgTemplates integrates nicely with git, this approach might require you to do some configuration. (Once you get things set up, you shouldn't have to do this part ever again.) PkgTemplates needs you to configure your git user name and email. Some instructions on configuration are here and here. It's also helpful to sign up for a GitHub account and set git's github.user variable. The PkgTemplates documentation may also be useful.If you struggle with this part, consider trying the \"plain\" Pkg variant below.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"note: Note\nIf the current directory in your Julia session is itself a package folder, PkgTemplates will use it as the parent environment (project) for your new package. To reduce confusion, before trying the commands below it may help to first ensure you're in a a \"neutral\" directory, for example by typing cd() at the Julia prompt.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Let's create a new package, MyPkg, to play with.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> using PkgTemplates\n\njulia> t = Template()\nTemplate:\n → User: timholy\n → Host: github.com\n → License: MIT (Tim Holy 2019)\n → Package directory: ~/.julia/dev\n → Minimum Julia version: v1.0\n → SSH remote: No\n → Add packages to main environment: Yes\n → Commit Manifest.toml: No\n → Plugins: None\n\njulia> t(\"MyPkg\")\nGenerating project MyPkg:\n /home/tim/.julia/dev/MyPkg/Project.toml\n /home/tim/.julia/dev/MyPkg/src/MyPkg.jl\n[lots more output suppressed]","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"In the first few lines you can see the location of your new package, here the directory /home/tim/.julia/dev/MyPkg.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Press ] to enter the Pkg REPL. Then add the new package to your current environment with the dev command.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"() pkg> dev MyPkg # the dev command will look in the ~/.julia/dev folder automatically","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Press the backspace key to return to the Julia REPL.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Now let's try it out:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> using Revise # you must do this before loading any revisable packages\n\njulia> using MyPkg\n[ Info: Precompiling MyPkg [102b5b08-597c-4d40-b98a-e9249f4d01f4]","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"(It's perfectly fine if you see a different string of digits and letters after the \"Precompiling MyPkg\" message.) You'll note that Julia found your package without you having to take any extra steps.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Without quitting this Julia session, open the MyPkg.jl file in an editor. You might be able to open it with","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> edit(pathof(MyPkg))","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"although that might require configuring your EDITOR environment variable.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"You should see something like this:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"module MyPkg\n\n# Write your package code here.\n\nend","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"This is the basic package created by PkgTemplates. Let's create a simple greet function to return a message:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"module MyPkg\n\ngreet() = print(\"Hello World!\")\n\nend # module","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Now go back to that same Julia session, and try calling greet. After a pause (while Revise's internal code compiles), you should see","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> MyPkg.greet()\nHello World!","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"From this point forward, revisions should be fast. You can modify MyPkg.jl quite extensively without quitting the Julia session, although there are some Limitations.","category":"page"},{"location":"cookbook/#Using-Pkg","page":"Revise usage: a cookbook","title":"Using Pkg","text":"","category":"section"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Pkg works similarly to PkgTemplates, but requires less configuration while also doing less on your behalf. Let's create a blank MyPkg using Pkg. (If you tried the PkgTemplates version above, you might first have to delete the package with Pkg.rm(\"MyPkg\") following by a complete removal from your dev directory.)","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> using Revise, Pkg\n\njulia> cd(Pkg.devdir()) # take us to the standard \"development directory\"\n\n(v1.2) pkg> generate MyPkg\nGenerating project MyPkg:\n MyPkg/Project.toml\n MyPkg/src/MyPkg.jl\n\n(v1.2) pkg> dev MyPkg\n[ Info: resolving package identifier `MyPkg` as a directory at `~/.julia/dev/MyPkg`.\n...","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"For the line starting (v1.2) pkg>, hit the ] key at the beginning of the line, then type generate MyPkg. The next line, dev MyPkg, is necessary to tell Pkg about the existence of this new package.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Now you can do the following:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> using MyPkg\n[ Info: Precompiling MyPkg [efe7ebfe-4313-4388-9b6c-3590daf47143]\n\njulia> edit(pathof(MyPkg))","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"and the rest should be similar to what's above under PkgTemplates. Note that with this approach, MyPkg has not been set up for version control.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"note: Note\nIf you add instead of dev the package, the package manager will make a copy of the MyPkg files in your .julia/packages directory. This will be the \"official\" version of the files, and Revise will not track changes.","category":"page"},{"location":"cookbook/#includet-usage","page":"Revise usage: a cookbook","title":"includet usage","text":"","category":"section"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"The alternative to creating packages is to manually load individual source files. This approach is intended for early stages of development; if you want to track multiple files and/or have some files include other files, you should consider switching to the package style above.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Open your editor and create a file like this:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"mygreeting() = \"Hello, world!\"","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Save it as mygreet.jl in some directory. Here we will assume it's being saved in /tmp/.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Now load the code with includet, which stands for \"include and track\":","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> using Revise\n\njulia> includet(\"/tmp/mygreet.jl\")\n\njulia> mygreeting()\n\"Hello, world!\"","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Now, in your editor modify mygreeting to do this:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"mygreeting() = \"Hello, revised world!\"","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"and then try it in the same session:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> mygreeting()\n\"Hello, revised world!\"","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"As described above, the first revision you make may be very slow, but later revisions should be fast.","category":"page"},{"location":"dev_reference/#Developer-reference","page":"Developer reference","title":"Developer reference","text":"","category":"section"},{"location":"dev_reference/#Internal-global-variables","page":"Developer reference","title":"Internal global variables","text":"","category":"section"},{"location":"dev_reference/#Configuration-related-variables","page":"Developer reference","title":"Configuration-related variables","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"These are set during execution of Revise's __init__ function.","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.watching_files\nRevise.polling_files\nRevise.tracking_Main_includes","category":"page"},{"location":"dev_reference/#Revise.watching_files","page":"Developer reference","title":"Revise.watching_files","text":"Revise.watching_files[]\n\nReturns true if we watch files rather than their containing directory. FreeBSD and NFS-mounted systems should watch files, otherwise we prefer to watch directories.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.polling_files","page":"Developer reference","title":"Revise.polling_files","text":"Revise.polling_files[]\n\nReturns true if we should poll the filesystem for changes to the files that define loaded code. It is preferable to avoid polling, instead relying on operating system notifications via FileWatching.watch_file. However, NFS-mounted filesystems (and perhaps others) do not support file-watching, so for code stored on such filesystems you should turn polling on.\n\nSee the documentation for the JULIA_REVISE_POLL environment variable.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.tracking_Main_includes","page":"Developer reference","title":"Revise.tracking_Main_includes","text":"Revise.tracking_Main_includes[]\n\nReturns true if files directly included from the REPL should be tracked. The default is false. See the documentation regarding the JULIA_REVISE_INCLUDE environment variable to customize it.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Path-related-variables","page":"Developer reference","title":"Path-related variables","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.juliadir\nRevise.basesrccache\nRevise.basebuilddir","category":"page"},{"location":"dev_reference/#Revise.juliadir","page":"Developer reference","title":"Revise.juliadir","text":"Revise.juliadir\n\nConstant specifying full path to julia top-level source directory. This should be reliable even for local builds, cross-builds, and binary installs.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.basesrccache","page":"Developer reference","title":"Revise.basesrccache","text":"Revise.basesrccache\n\nFull path to the running Julia's cache of source code defining Base.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.basebuilddir","page":"Developer reference","title":"Revise.basebuilddir","text":"Revise.basebuilddir\n\nJulia's top-level directory when Julia was built, as recorded by the entries in Base._included_files.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Internal-state-management","page":"Developer reference","title":"Internal state management","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.pkgdatas\nRevise.watched_files\nRevise.revision_queue\nRevise.NOPACKAGE\nRevise.queue_errors\nRevise.included_files\nRevise.watched_manifests","category":"page"},{"location":"dev_reference/#Revise.pkgdatas","page":"Developer reference","title":"Revise.pkgdatas","text":"Revise.pkgdatas\n\npkgdatas is the core information that tracks the relationship between source code and julia objects, and allows re-evaluation of code in the proper module scope. It is a dictionary indexed by PkgId: pkgdatas[id] returns a value of type Revise.PkgData.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.watched_files","page":"Developer reference","title":"Revise.watched_files","text":"Revise.watched_files\n\nGlobal variable, watched_files[dirname] returns the collection of files in dirname that we're monitoring for changes. The returned value has type Revise.WatchList.\n\nThis variable allows us to watch directories rather than files, reducing the burden on the OS.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.revision_queue","page":"Developer reference","title":"Revise.revision_queue","text":"Revise.revision_queue\n\nGlobal variable, revision_queue holds (pkgdata,filename) pairs that we need to revise, meaning that these files have changed since we last processed a revision. This list gets populated by callbacks that watch directories for updates.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.NOPACKAGE","page":"Developer reference","title":"Revise.NOPACKAGE","text":"Revise.NOPACKAGE\n\nGlobal variable; default PkgId used for files which do not belong to any package, but still have to be watched because user callbacks have been registered for them.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.queue_errors","page":"Developer reference","title":"Revise.queue_errors","text":"Revise.queue_errors\n\nGlobal variable, maps (pkgdata, filename) pairs that errored upon last revision to (exception, backtrace).\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.included_files","page":"Developer reference","title":"Revise.included_files","text":"Revise.included_files\n\nGlobal variable, included_files gets populated by callbacks we register with include. It's used to track non-precompiled packages and, optionally, user scripts (see docs on JULIA_REVISE_INCLUDE).\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.watched_manifests","page":"Developer reference","title":"Revise.watched_manifests","text":"Revise.watched_manifests\n\nGlobal variable, a set of Manifest.toml files from the active projects used during this session.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"The following are specific to user callbacks (see Revise.add_callback) and the implementation of entr:","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.revision_event\nRevise.user_callbacks_queue\nRevise.user_callbacks_by_file\nRevise.user_callbacks_by_key","category":"page"},{"location":"dev_reference/#Revise.revision_event","page":"Developer reference","title":"Revise.revision_event","text":"Revise.revision_event\n\nThis Condition is used to notify entr that one of the watched files has changed.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.user_callbacks_queue","page":"Developer reference","title":"Revise.user_callbacks_queue","text":"Revise.user_callbacks_queue\n\nGlobal variable, user_callbacks_queue holds key values for which the file has changed but the user hooks have not yet been called.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.user_callbacks_by_file","page":"Developer reference","title":"Revise.user_callbacks_by_file","text":"Revise.user_callbacks_by_file\n\nGlobal variable, maps files (identified by their absolute path) to the set of callback keys registered for them.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.user_callbacks_by_key","page":"Developer reference","title":"Revise.user_callbacks_by_key","text":"Revise.user_callbacks_by_key\n\nGlobal variable, maps callback keys to user hooks.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Types","page":"Developer reference","title":"Types","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.RelocatableExpr\nRevise.ModuleExprsSigs\nRevise.FileInfo\nRevise.PkgData\nRevise.WatchList\nRevise.TaskThunk\nRevise.ReviseEvalException\nMethodSummary","category":"page"},{"location":"dev_reference/#Revise.RelocatableExpr","page":"Developer reference","title":"Revise.RelocatableExpr","text":"A RelocatableExpr wraps an Expr to ensure that comparisons between RelocatableExprs ignore line numbering information. This allows one to detect that two expressions are the same no matter where they appear in a file.\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.ModuleExprsSigs","page":"Developer reference","title":"Revise.ModuleExprsSigs","text":"ModuleExprsSigs\n\nFor a particular source file, the corresponding ModuleExprsSigs is a mapping mod=>exprs=>sigs of the expressions exprs found in mod and the signatures sigs that arise from them. Specifically, if mes is a ModuleExprsSigs, then mes[mod][ex] is a list of signatures that result from evaluating ex in mod. It is possible that this returns nothing, which can mean either that ex does not define any methods or that the signatures have not yet been cached.\n\nThe first mod key is guaranteed to be the module into which this file was included.\n\nTo create a ModuleExprsSigs from a source file, see Revise.parse_source.\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.FileInfo","page":"Developer reference","title":"Revise.FileInfo","text":"FileInfo(mexs::ModuleExprsSigs, cachefile=\"\")\n\nStructure to hold the per-module expressions found when parsing a single file. mexs holds the Revise.ModuleExprsSigs for the file.\n\nOptionally, a FileInfo can also record the path to a cache file holding the original source code. This is applicable only for precompiled modules and Base. (This cache file is distinct from the original source file that might be edited by the developer, and it will always hold the state of the code when the package was precompiled or Julia's Base was built.) When a cache is available, mexs will be empty until the file gets edited: the original source code gets parsed only when a revision needs to be made.\n\nSource cache files greatly reduce the overhead of using Revise.\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.PkgData","page":"Developer reference","title":"Revise.PkgData","text":"PkgData(id, path, fileinfos::Dict{String,FileInfo})\n\nA structure holding the data required to handle a particular package. path is the top-level directory defining the package, and fileinfos holds the Revise.FileInfo for each file defining the package.\n\nFor the PkgData associated with Main (e.g., for files loaded with includet), the corresponding path entry will be empty.\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.WatchList","page":"Developer reference","title":"Revise.WatchList","text":"Revise.WatchList\n\nA struct for holding files that live inside a directory. Some platforms (OSX) have trouble watching too many files. So we watch parent directories, and keep track of which files in them should be tracked.\n\nFields:\n\ntimestamp: mtime of last update\ntrackedfiles: Set of filenames, generally expressed as a relative path\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.TaskThunk","page":"Developer reference","title":"Revise.TaskThunk","text":"thunk = TaskThunk(f, args)\n\nTo facilitate precompilation and reduce latency, we avoid creation of anonymous thunks. thunk can be used as an argument in schedule(Task(thunk)).\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.ReviseEvalException","page":"Developer reference","title":"Revise.ReviseEvalException","text":"ReviseEvalException(loc::String, exc::Exception, stacktrace=nothing)\n\nProvide additional location information about exc.\n\nWhen running via the interpreter, the backtraces point to interpreter code rather than the original culprit. This makes it possible to use loc to provide information about the frame backtrace, and even to supply a fake backtrace.\n\nIf stacktrace is supplied it must be a Vector{Any} containing (::StackFrame, n) pairs where n is the recursion count (typically 1).\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.MethodSummary","page":"Developer reference","title":"Revise.MethodSummary","text":"MethodSummary(method)\n\nCreate a portable summary of a method. In particular, a MethodSummary can be saved to a JLD2 file.\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Function-reference","page":"Developer reference","title":"Function reference","text":"","category":"section"},{"location":"dev_reference/#Functions-called-when-you-load-a-new-package","page":"Developer reference","title":"Functions called when you load a new package","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.watch_package\nRevise.parse_pkg_files\nRevise.init_watching","category":"page"},{"location":"dev_reference/#Revise.watch_package","page":"Developer reference","title":"Revise.watch_package","text":"watch_package(id::Base.PkgId)\n\nStart watching a package for changes to the files that define it. This function gets called via a callback registered with Base.require, at the completion of module-loading by using or import.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.parse_pkg_files","page":"Developer reference","title":"Revise.parse_pkg_files","text":"parse_pkg_files(id::PkgId)\n\nThis function gets called by watch_package and runs when a package is first loaded. Its job is to organize the files and expressions defining the module so that later we can detect and process revisions.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.init_watching","page":"Developer reference","title":"Revise.init_watching","text":"Revise.init_watching(files)\nRevise.init_watching(pkgdata::PkgData, files)\n\nFor every filename in files, monitor the filesystem for updates. When the file is updated, either Revise.revise_dir_queued or Revise.revise_file_queued will be called.\n\nUse the pkgdata version if the files are supplied using relative paths.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Monitoring-for-changes","page":"Developer reference","title":"Monitoring for changes","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"These functions get called on each directory or file that you monitor for revisions. These block execution until the file(s) are updated, so you should only call them from within an @async block. They work recursively: once an update has been detected and execution resumes, they schedule a revision (see Revise.revision_queue) and then call themselves on the same directory or file to wait for the next set of changes.","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.revise_dir_queued\nRevise.revise_file_queued","category":"page"},{"location":"dev_reference/#Revise.revise_dir_queued","page":"Developer reference","title":"Revise.revise_dir_queued","text":"revise_dir_queued(dirname)\n\nWait for one or more of the files registered in Revise.watched_files[dirname] to be modified, and then queue the corresponding files on Revise.revision_queue. This is generally called via a Revise.TaskThunk.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.revise_file_queued","page":"Developer reference","title":"Revise.revise_file_queued","text":"revise_file_queued(pkgdata::PkgData, filename)\n\nWait for modifications to filename, and then queue the corresponding files on Revise.revision_queue. This is generally called via a Revise.TaskThunk.\n\nThis is used only on platforms (like BSD) which cannot use Revise.revise_dir_queued.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"The following functions support user callbacks, and are used in the implementation of entr but can be used more broadly:","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.add_callback\nRevise.remove_callback","category":"page"},{"location":"dev_reference/#Revise.add_callback","page":"Developer reference","title":"Revise.add_callback","text":"key = Revise.add_callback(f, files, modules=nothing; key=gensym())\n\nAdd a user-specified callback, to be executed during the first run of revise() after a file in files or a module in modules is changed on the file system. If all is set to true, also execute the callback whenever any file already monitored by Revise changes. In an interactive session like the REPL, Juno or Jupyter, this means the callback executes immediately before executing a new command / cell.\n\nYou can use the return value key to remove the callback later (Revise.remove_callback) or to update it using another call to Revise.add_callback with key=key.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.remove_callback","page":"Developer reference","title":"Revise.remove_callback","text":"Revise.remove_callback(key)\n\nRemove a callback previously installed by a call to Revise.add_callback(...). See its docstring for details.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Evaluating-changes-(revising)-and-computing-diffs","page":"Developer reference","title":"Evaluating changes (revising) and computing diffs","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"revise is the primary entry point for implementing changes. Additionally,","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.revise_file_now","category":"page"},{"location":"dev_reference/#Revise.revise_file_now","page":"Developer reference","title":"Revise.revise_file_now","text":"Revise.revise_file_now(pkgdata::PkgData, file)\n\nProcess revisions to file. This parses file and computes an expression-level diff between the current state of the file and its most recently evaluated state. It then deletes any removed methods and re-evaluates any changed expressions. Note that generally it is better to use revise as it properly handles methods that move from one file to another.\n\nid must be a key in Revise.pkgdatas, and file a key in Revise.pkgdatas[id].fileinfos.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Caching-the-definition-of-methods","page":"Developer reference","title":"Caching the definition of methods","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.get_def","category":"page"},{"location":"dev_reference/#Revise.get_def","page":"Developer reference","title":"Revise.get_def","text":"success = get_def(method::Method)\n\nAs needed, load the source file necessary for extracting the code defining method. The source-file defining method must be tracked. If it is in Base, this will execute track(Base) if necessary.\n\nThis is a callback function used by CodeTracking.jl's definition.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Parsing-source-code","page":"Developer reference","title":"Parsing source code","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.parse_source\nRevise.parse_source!","category":"page"},{"location":"dev_reference/#Revise.parse_source","page":"Developer reference","title":"Revise.parse_source","text":"mexs = parse_source(filename::AbstractString, mod::Module)\n\nParse the source filename, returning a ModuleExprsSigs mexs. mod is the \"parent\" module for the file (i.e., the one that included the file); if filename defines more module(s) then these will all have separate entries in mexs.\n\nIf parsing filename fails, nothing is returned.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.parse_source!","page":"Developer reference","title":"Revise.parse_source!","text":"parse_source!(mexs::ModuleExprsSigs, filename, mod::Module)\n\nTop-level parsing of filename as included into module mod. Successfully-parsed expressions will be added to mexs. Returns mexs if parsing finished successfully, otherwise nothing is returned.\n\nSee also Revise.parse_source.\n\n\n\n\n\nsuccess = parse_source!(mod_exprs_sigs::ModuleExprsSigs, src::AbstractString, filename::AbstractString, mod::Module)\n\nParse a string src obtained by reading file as a single string. pos is the 1-based byte offset from which to begin parsing src.\n\nSee also Revise.parse_source.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Lowered-source-code","page":"Developer reference","title":"Lowered source code","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Much of the \"brains\" of Revise comes from doing analysis on lowered code. This part of the package is not as well documented.","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.minimal_evaluation!\nRevise.methods_by_execution!\nRevise.CodeTrackingMethodInfo","category":"page"},{"location":"dev_reference/#Revise.minimal_evaluation!","page":"Developer reference","title":"Revise.minimal_evaluation!","text":"isrequired, evalassign = minimal_evaluation!([predicate,] methodinfo, src::Core.CodeInfo, mode::Symbol)\n\nMark required statements in src: isrequired[i] is true if src.code[i] should be evaluated. Statements are analyzed by isreq, haseval = predicate(stmt), and predicate defaults to Revise.is_method_or_eval. haseval is true if the statement came from @eval or eval(...) call. Since the contents of such expression are difficult to analyze, it is generally safest to execute all such evals.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.methods_by_execution!","page":"Developer reference","title":"Revise.methods_by_execution!","text":"methods_by_execution!(recurse=JuliaInterpreter.Compiled(), methodinfo, docexprs, mod::Module, ex::Expr;\n mode=:eval, disablebp=true, skip_include=mode!==:eval, always_rethrow=false)\n\nEvaluate or analyze ex in the context of mod. Depending on the setting of mode (see the Extended help), it supports full evaluation or just the minimal evaluation needed to extract method signatures. recurse controls JuliaInterpreter's evaluation of any non-intercepted statement; likely choices are JuliaInterpreter.Compiled() or JuliaInterpreter.finish_and_return!. methodinfo is a cache for storing information about any method definitions (see CodeTrackingMethodInfo). docexprs is a cache for storing documentation expressions; obtain an empty one with Revise.DocExprs().\n\nExtended help\n\nThe action depends on mode:\n\n:eval evaluates the expression in mod, similar to Core.eval(mod, ex) except that methodinfo and docexprs will be populated with information about any signatures or docstrings. This mode is used to implement includet.\n:sigs analyzes ex and extracts signatures of methods and docstrings (specifically, statements flagged by Revise.minimal_evaluation!), but does not evaluate ex in the traditional sense. It will selectively execute statements needed to form the signatures of defined methods. It will also expand any @evaled expressions, since these might contain method definitions.\n:evalmeth analyzes ex and extracts signatures and docstrings like :sigs, but takes the additional step of evaluating any :method statements.\n:evalassign acts similarly to :evalmeth, and also evaluates assignment statements.\n\nWhen selectively evaluating an expression, Revise will incorporate required dependencies, even for minimal-evaluation modes like :sigs. For example, the method definition\n\nmax_values(T::Union{map(X -> Type{X}, Base.BitIntegerSmall_types)...}) = 1 << (8*sizeof(T))\n\nfound in base/abstractset.jl requires that it create the anonymous function in order to compute the signature.\n\nThe other keyword arguments are more straightforward:\n\ndisablebp controls whether JuliaInterpreter's breakpoints are disabled before stepping through the code. They are restored on exit.\nskip_include prevents execution of include statements, instead inserting them into methodinfo's cache. This defaults to true unless mode is :eval.\nalways_rethrow, if true, causes an error to be thrown if evaluating ex triggered an error. If false, the error is logged with @error. InterruptExceptions are always rethrown. This is primarily useful for debugging.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.CodeTrackingMethodInfo","page":"Developer reference","title":"Revise.CodeTrackingMethodInfo","text":"CodeTrackingMethodInfo(ex::Expr)\n\nCreate a cache for storing information about method definitions. Adding signatures to such an object inserts them into CodeTracking.method_info, which maps signature Tuple-types to (lnn::LineNumberNode, ex::Expr) pairs. Because method signatures are unique within a module, this is the foundation for identifying methods in a manner independent of source-code location.\n\nIt also has the following fields:\n\nexprstack: used when descending into @eval statements (via push_expr and pop_expr!) ex (used in creating the CodeTrackingMethodInfo object) is the first entry in the stack.\nallsigs: a list of all method signatures defined by a given expression\ndeps: list of top-level named objects (Symbols and GlobalRefs) that method definitions in this block depend on. For example, if Sys.iswindows() f() = 1 else f() = 2 end would store Sys.iswindows here.\nincludes: a list of module=>filename for any include statements encountered while the expression was parsed.\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Modules-and-paths","page":"Developer reference","title":"Modules and paths","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.modulefiles","category":"page"},{"location":"dev_reference/#Revise.modulefiles","page":"Developer reference","title":"Revise.modulefiles","text":"parentfile, included_files = modulefiles(mod::Module)\n\nReturn the parentfile in which mod was defined, as well as a list of any other files that were included to define mod. If this operation is unsuccessful, (nothing, nothing) is returned.\n\nAll files are returned as absolute paths.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Handling-errors","page":"Developer reference","title":"Handling errors","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.trim_toplevel!","category":"page"},{"location":"dev_reference/#Revise.trim_toplevel!","page":"Developer reference","title":"Revise.trim_toplevel!","text":"trim_toplevel!(bt)\n\nTruncate a list of instruction pointers, as obtained from backtrace() or catch_backtrace(), at the first \"top-level\" call (e.g., as executed from the REPL prompt) or the first entry corresponding to a method in Revise or its dependencies.\n\nThis is used to make stacktraces obtained with Revise more similar to those obtained without Revise, while retaining one entry to reveal Revise's involvement.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"In current releases of Julia, hitting Ctrl-C from the REPL can stop tasks running in the background. This risks stopping Revise's ability to watch for changes in files and directories. Revise has a work-around for this problem.","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.throwto_repl","category":"page"},{"location":"dev_reference/#Revise.throwto_repl","page":"Developer reference","title":"Revise.throwto_repl","text":"success = throwto_repl(e::Exception)\n\nTry throwing e from the REPL's backend task. Returns true if the necessary conditions were met and the throw can be expected to succeed. The throw is generated from another task, so a yield will need to occur before it happens.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Git-integration","page":"Developer reference","title":"Git integration","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.git_source\nRevise.git_files\nRevise.git_repo","category":"page"},{"location":"dev_reference/#Revise.git_source","page":"Developer reference","title":"Revise.git_source","text":"Revise.git_source(file::AbstractString, reference)\n\nRead the source-text for file from a git commit reference. The reference may be a string, Symbol, or LibGit2.Tree.\n\nExample:\n\nRevise.git_source(\"/path/to/myfile.jl\", \"HEAD\")\nRevise.git_source(\"/path/to/myfile.jl\", :abcd1234) # by commit SHA\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.git_files","page":"Developer reference","title":"Revise.git_files","text":"files = git_files(repo)\n\nReturn the list of files checked into repo.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.git_repo","page":"Developer reference","title":"Revise.git_repo","text":"repo, repo_path = git_repo(path::AbstractString)\n\nReturn the repo::LibGit2.GitRepo containing the file or directory path. path does not necessarily need to be the top-level directory of the repository. Also returns the repo_path of the top-level directory for the repository.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Distributed-computing","page":"Developer reference","title":"Distributed computing","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.init_worker","category":"page"},{"location":"dev_reference/#Revise.init_worker","page":"Developer reference","title":"Revise.init_worker","text":"Revise.init_worker(p)\n\nDefine methods on worker p that Revise needs in order to perform revisions on p. Revise itself does not need to be running on p.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Teaching-Revise-about-non-julia-source-codes","page":"Developer reference","title":"Teaching Revise about non-julia source codes","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise can be made to work for transpilers from non-Julia languages to Julia with a little effort. For example, if you wrote a transpiler from C to Julia, you can define a struct CFile which overrides enough of the common String methods (abspath,isabspath, joinpath, normpath,isfile,findfirst, and String), it will be supported by Revise if you define a method like","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"function Revise.parse_source!(mod_exprs_sigs::Revise.ModuleExprsSigs, file::CFile, mod::Module; kwargs...)\n ex = # julia Expr returned from running transpiler\n Revise.process_source!(mod_exprs_sigs, ex, file, mod; kwargs...)\nend\n","category":"page"},{"location":"user_reference/#User-reference","page":"User reference","title":"User reference","text":"","category":"section"},{"location":"user_reference/","page":"User reference","title":"User reference","text":"There are really only six functions that most users would be expected to call manually: revise, includet, Revise.track, entr, Revise.retry, and Revise.errors. Other user-level constructs might apply if you want to debug Revise or prevent it from watching specific packages, or for fine-grained handling of callbacks.","category":"page"},{"location":"user_reference/","page":"User reference","title":"User reference","text":"revise\nRevise.track\nincludet\nentr\nRevise.retry\nRevise.errors","category":"page"},{"location":"user_reference/#Revise.revise","page":"User reference","title":"Revise.revise","text":"revise(; throw=false)\n\neval any changes in the revision queue. See Revise.revision_queue. If throw is true, throw any errors that occur during revision or callback; otherwise these are only logged.\n\n\n\n\n\nrevise(mod::Module)\n\nReevaluate every definition in mod, whether it was changed or not. This is useful to propagate an updated macro definition, or to force recompiling generated functions.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.track","page":"User reference","title":"Revise.track","text":"Revise.track(Base)\nRevise.track(Core.Compiler)\nRevise.track(stdlib)\n\nTrack updates to the code in Julia's base directory, base/compiler, or one of its standard libraries.\n\n\n\n\n\nRevise.track(mod::Module, file::AbstractString)\nRevise.track(file::AbstractString)\n\nWatch file for updates and revise loaded code with any changes. mod is the module into which file is evaluated; if omitted, it defaults to Main.\n\nIf this produces many errors, check that you specified mod correctly.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.includet","page":"User reference","title":"Revise.includet","text":"includet(filename)\n\nLoad filename and track future changes. includet is intended for quick \"user scripts\"; larger or more established projects are encouraged to put the code in one or more packages loaded with using or import instead of using includet. See https://timholy.github.io/Revise.jl/stable/cookbook/ for tips about setting up the package workflow.\n\nBy default, includet only tracks modifications to methods, not data. See the extended help for details. Note that this differs from packages, which evaluate all changes by default. This default behavior can be overridden; see Configuring the revise mode.\n\nExtended help\n\nBehavior and justification for the default revision mode (:evalmeth)\n\nincludet uses a default __revise_mode__ = :evalmeth. The consequence is that if you change\n\na = [1]\nf() = 1\n\nto\n\na = [2]\nf() = 2\n\nthen Revise will update f but not a.\n\nThis is the default choice for includet because such files typically mix method definitions and data-handling. Data often has many untracked dependencies; later in the same file you might push!(a, 22), but Revise cannot determine whether you wish it to re-run that line after redefining a. Consequently, the safest default choice is to leave the user in charge of data.\n\nWorkflow tips\n\nIf you have a series of computations that you want to run when you redefine your methods, consider separating your method definitions from your computations:\n\nmethod definitions go in a package, or a file that you includet once\nthe computations go in a separate file, that you re-include (no \"t\" at the end) each time you want to rerun your computations.\n\nThis can be automated using entr.\n\nInternals\n\nincludet is essentially shorthand for\n\nRevise.track(Main, filename; mode=:includet, skip_include=true)\n\nDo not use includet for packages, as those should be handled by using or import. If using and import aren't working, you may have packages in a non-standard location; try fixing it with something like push!(LOAD_PATH, \"/path/to/my/private/repos\"). (If you're working with code in Base or one of Julia's standard libraries, use Revise.track(mod) instead, where mod is the module.)\n\nincludet is deliberately non-recursive, so if filename loads any other files, they will not be automatically tracked. (See Revise.track to set it up manually.)\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.entr","page":"User reference","title":"Revise.entr","text":"entr(f, files; all=false, postpone=false, pause=0.02)\nentr(f, files, modules; all=false, postpone=false, pause=0.02)\n\nExecute f() whenever files or directories listed in files, or code in modules, updates. If all is true, also execute f() as soon as code updates are detected in any module tracked by Revise.\n\nentr will process updates (and block your command line) until you press Ctrl-C. Unless postpone is true, f() will be executed also when calling entr, regardless of file changes. The pause is the period (in seconds) that entr will wait between being triggered and actually calling f(), to handle clusters of modifications, such as those produced by saving files in certain text editors.\n\nExample\n\nentr([\"/tmp/watched.txt\"], [Pkg1, Pkg2]) do\n println(\"update\")\nend\n\nThis will print \"update\" every time \"/tmp/watched.txt\" or any of the code defining Pkg1 or Pkg2 gets updated.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.retry","page":"User reference","title":"Revise.retry","text":"Revise.retry()\n\nAttempt to perform previously-failed revisions. This can be useful in cases of order-dependent errors.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.errors","page":"User reference","title":"Revise.errors","text":"Revise.errors()\n\nReport the errors represented in Revise.queue_errors. Errors are automatically reported the first time they are encountered, but this function can be used to report errors again.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise-logs-(debugging-Revise)","page":"User reference","title":"Revise logs (debugging Revise)","text":"","category":"section"},{"location":"user_reference/","page":"User reference","title":"User reference","text":"Revise.debug_logger\nRevise.actions\nRevise.diffs","category":"page"},{"location":"user_reference/#Revise.debug_logger","page":"User reference","title":"Revise.debug_logger","text":"logger = Revise.debug_logger(; min_level=Debug)\n\nTurn on debug logging (if min_level is set to Debug or better) and return the logger object. logger.logs contains a list of the logged events. The items in this list are of type Revise.LogRecord, with the following relevant fields:\n\ngroup: the event category. Revise currently uses the following groups:\n\"Action\": a change was implemented, of type described in the message field.\n\"Parsing\": a \"significant\" event in parsing. For these, examine the message field for more information.\n\"Watching\": an indication that Revise determined that a particular file needed to be examined for possible code changes. This is typically done on the basis of mtime, the modification time of the file, and does not necessarily indicate that there were any changes.\nmessage: a string containing more information. Some examples:\nFor entries in the \"Action\" group, message can be \"Eval\" when modifying old methods or defining new ones, \"DeleteMethod\" when deleting a method, and \"LineOffset\" to indicate that the line offset for a method was updated (the last only affects the printing of stacktraces upon error, it does not change how code runs)\nItems with group \"Parsing\" and message \"Diff\" contain sets :newexprs and :oldexprs that contain the expression unique to post- or pre-revision, respectively.\nkwargs: a pairs list of any other data. This is usually specific to particular group/message combinations.\n\nSee also Revise.actions and Revise.diffs.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.actions","page":"User reference","title":"Revise.actions","text":"actions(logger; line=false)\n\nReturn a vector of all log events in the \"Action\" group. \"LineOffset\" events are returned only if line=true; by default the returned items are the events that modified methods in your session.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.diffs","page":"User reference","title":"Revise.diffs","text":"diffs(logger)\n\nReturn a vector of all log events that encode a (non-empty) diff between two versions of a file.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Prevent-Revise-from-watching-specific-packages","page":"User reference","title":"Prevent Revise from watching specific packages","text":"","category":"section"},{"location":"user_reference/","page":"User reference","title":"User reference","text":"Revise.dont_watch_pkgs\nRevise.silence","category":"page"},{"location":"user_reference/#Revise.dont_watch_pkgs","page":"User reference","title":"Revise.dont_watch_pkgs","text":"Revise.dont_watch_pkgs\n\nGlobal variable, use push!(Revise.dont_watch_pkgs, :MyPackage) to prevent Revise from tracking changes to MyPackage. You can do this from the REPL or from your .julia/config/startup.jl file.\n\nSee also Revise.silence.\n\n\n\n\n\n","category":"constant"},{"location":"user_reference/#Revise.silence","page":"User reference","title":"Revise.silence","text":"Revise.silence(pkg)\n\nSilence warnings about not tracking changes to package pkg.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise-module","page":"User reference","title":"Revise module","text":"","category":"section"},{"location":"user_reference/","page":"User reference","title":"User reference","text":"Revise","category":"page"},{"location":"user_reference/#Revise","page":"User reference","title":"Revise","text":"Revise.jl tracks source code changes and incorporates the changes to a running Julia session.\n\nRevise.jl works behind-the-scenes. To track a package, e.g. Example:\n\n(@v1.6) pkg> dev Example # make a development copy of the package\n[...pkg output omitted...]\n\njulia> using Revise # this must come before the package under development\n\njulia> using Example\n\n[...develop the package...] # Revise.jl will automatically update package functionality to match code changes\n\n\nFunctions in Revise.jl that may come handy in special circumstances:\n\nRevise.track: track updates to Base Julia itself or Core.Compiler\nincludet: load a file and track future changes. Intended for small, quick works\nentr: call an additional function whenever code updates\nrevise: evaluate any changes in Revise.revision_queue or every definition in a module\nRevise.retry: perform previously-failed revisions. Useful in cases of order-dependent errors\nRevise.errors: report the errors represented in Revise.queue_errors\n\n\n\n\n\n","category":"module"},{"location":"limitations/#Limitations","page":"Limitations","title":"Limitations","text":"","category":"section"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"There are some kinds of changes that Revise (or often, Julia itself) cannot incorporate into a running Julia session:","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"changes to type definitions or consts\nconflicts between variables and functions sharing the same name\nremoval of exports","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"These kinds of changes require that you restart your Julia session.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"During early stages of development, it's quite common to want to change type definitions. You can work around Julia's/Revise's limitations by temporary renaming. We'll illustrate this below, using write to be explicit about when updates to the file happen. But in ordinary usage, these are changes you'd likely make with your editor.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"julia> using Pkg, Revise\n\njulia> Pkg.generate(\"MyPkg\")\n Generating project MyPkg:\n MyPkg/Project.toml\n MyPkg/src/MyPkg.jl\nDict{String, Base.UUID} with 1 entry:\n \"MyPkg\" => UUID(\"69940cda-0c72-4a1a-ae0b-fd3109336fe8\")\n\njulia> cd(\"MyPkg\")\n\njulia> write(\"src/MyPkg.jl\",\"\"\"\n module MyPkg\n\n export FooStruct, processFoo\n\n abstract type AbstractFooStruct end\n struct FooStruct1 <: AbstractFooStruct\n bar::Int\n end\n FooStruct = FooStruct1\n function processFoo(foo::AbstractFooStruct)\n @info foo.bar\n end\n\n end\n \"\"\")\n230\n\njulia> Pkg.activate(\".\")\n Activating project at `~/blah/MyPkg`\n\njulia> using MyPkg\n No Changes to `~/blah/MyPkg/Project.toml`\n No Changes to `~/blah/MyPkg/Manifest.toml`\nPrecompiling MyPkg\n 1 dependency successfully precompiled in 2 seconds\n\njulia> processFoo(FooStruct(1))\n[ Info: 1\n\njulia> write(\"src/MyPkg.jl\",\"\"\"\n module MyPkg\n\n export FooStruct, processFoo\n\n abstract type AbstractFooStruct end\n struct FooStruct2 <: AbstractFooStruct # change version nuumber\n bar::Float64 # change type of the field\n end\n FooStruct = FooStruct2 # update alias reference\n function processFoo(foo::AbstractFooStruct)\n @info foo.bar\n end\n\n end\n \"\"\")\n234\n\njulia> FooStruct # make sure FooStruct refers to FooStruct2\nMyPkg.FooStruct2\n\njulia> processFoo(FooStruct(3.5))\n[ Info: 3.5","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"Here, note that we made two changes: we updated the \"version number\" of FooStruct when we changed something about its fields, and we also re-assigned FooStruct to alias the new version. We did not change the definition of any methods that have been typed AbstractFooStruct.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"This works as long as the new type name doesn't conflict with an existing name; within a session you need to change the name each time you change the definition.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"Once your development has converged on a solution, it's best to switch to the \"permanent\" name: in the example above, FooStruct is a non-constant global variable, and if used internally in a function there will be consequent performance penalties. Switching to the permanent name will force you to restart your session.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"julia> isconst(MyPkg, :FooStruct)\ntrue\n\njulia> write(\"src/MyPkg.jl\",\"\"\"\n module MyPkg\n\n export FooStruct, processFoo\n\n abstract type AbstractFooStruct end # this could be removed\n struct FooStruct <: AbstractFooStruct # change to just FooStruct\n bar::Float64\n end\n\n function processFoo(foo::AbstractFooStruct) # consider changing to FooStruct\n @info foo.bar\n end\n\n end\n \"\"\")\n\njulia> run(Base.julia_cmd()) # start a new Julia session, alternatively exit() and restart julia\n\n\njulia> using Pkg, Revise # NEW Julia Session\n\njulia> Pkg.activate(\".\")\n Activating project at `~/blah/MyPkg`\n\njulia> using MyPkg\nPrecompiling MyPkg\n 1 dependency successfully precompiled in 2 seconds\n\njulia> isconst(MyPkg, :FooStruct)\ntrue\n","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"In addition, some situations may require special handling:","category":"page"},{"location":"limitations/#Macros-and-generated-functions","page":"Limitations","title":"Macros and generated functions","text":"","category":"section"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"If you change a macro definition or methods that get called by @generated functions outside their quote block, these changes will not be propagated to functions that have already evaluated the macro or generated function.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"You may explicitly call revise(MyModule) to force reevaluating every definition in module MyModule. Note that when a macro changes, you have to revise all of the modules that use it.","category":"page"},{"location":"limitations/#Distributed-computing-(multiple-workers)-and-anonymous-functions","page":"Limitations","title":"Distributed computing (multiple workers) and anonymous functions","text":"","category":"section"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"Revise supports changes to code in worker processes. The code must be loaded in the main process in which Revise is running.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"Revise cannot handle changes in anonymous functions used in remotecalls. Consider the following module definition:","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"module ParReviseExample\nusing Distributed\n\ngreet(x) = println(\"Hello, \", x)\n\nfoo() = for p in workers()\n remotecall_fetch(() -> greet(\"Bar\"), p)\nend\n\nend # module","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"Changing the remotecall to remotecall_fetch((x) -> greet(\"Bar\"), p, 1) will fail, because the new anonymous function is not defined on all workers. The workaround is to write the code to use named functions, e.g.,","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"module ParReviseExample\nusing Distributed\n\ngreet(x) = println(\"Hello, \", x)\ngreetcaller() = greet(\"Bar\")\n\nfoo() = for p in workers()\n remotecall_fetch(greetcaller, p)\nend\n\nend # module","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"and the corresponding edit to the code would be to modify it to greetcaller(x) = greet(\"Bar\") and remotecall_fetch(greetcaller, p, 1).","category":"page"},{"location":"#Introduction-to-Revise","page":"Home","title":"Introduction to Revise","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Revise.jl may help you keep your Julia sessions running longer, reducing the need to restart when you make changes to code. With Revise, you can be in the middle of a session and then edit source code, update packages, switch git branches, and/or stash/unstash code; typically, the changes will be incorporated into the very next command you issue from the REPL. This can save you the overhead of restarting, loading packages, and waiting for code to JIT-compile.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Using Revise also improves your experience when using the debuggers. Revise will keep track of changed locations of your methods in file, and ensure that the debugger displays the source code of what you're actually debugging.","category":"page"},{"location":"","page":"Home","title":"Home","text":"note: Automatically loading Revise\nMany users automatically load Revise on startup. On versions of Julia older than 1.5, this is slightly more involved than just adding using Revise to .julia/config/startup.jl: see Using Revise by default for details.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"You can obtain Revise using Julia's Pkg REPL-mode (hitting ] as the first character of the command prompt):","category":"page"},{"location":"","page":"Home","title":"Home","text":"(v1.0) pkg> add Revise","category":"page"},{"location":"","page":"Home","title":"Home","text":"or with using Pkg; Pkg.add(\"Revise\").","category":"page"},{"location":"#Usage-example","page":"Home","title":"Usage example","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"We'll make changes to Julia's \"Example\" package (a trivial package designed to illustrate the file and directory organization of typical packages). We have to \"develop\" it in order to make changes:","category":"page"},{"location":"","page":"Home","title":"Home","text":"(v1.0) pkg> dev Example\n[...output related to installation...]\n","category":"page"},{"location":"","page":"Home","title":"Home","text":"Now we load Revise (if we haven't already done so) and Example:","category":"page"},{"location":"","page":"Home","title":"Home","text":"julia> using Revise # importantly, this must come before `using Example`\n\njulia> using Example\n\njulia> hello(\"world\")\n\"Hello, world\"","category":"page"},{"location":"","page":"Home","title":"Home","text":"Now we're going to check that the Example module currently lacks a function named f:","category":"page"},{"location":"","page":"Home","title":"Home","text":"julia> Example.f()\nERROR: UndefVarError: f not defined","category":"page"},{"location":"","page":"Home","title":"Home","text":"But say we really want f, so let's add it. You can either navigate to the source code (at .julia/dev/Example/src/Example.jl) in an editor manually, or you can use Julia to open it for you:","category":"page"},{"location":"","page":"Home","title":"Home","text":"julia> edit(hello) # opens Example.jl in the editor you have configured","category":"page"},{"location":"","page":"Home","title":"Home","text":"Now, add a function f() = π and save the file. Go back to the REPL (the same REPL, don't restart Julia) and try this:","category":"page"},{"location":"","page":"Home","title":"Home","text":"julia> Example.f()\nπ = 3.1415926535897...","category":"page"},{"location":"","page":"Home","title":"Home","text":"Voila! Even though we'd loaded Example before adding this function, Revise noticed the change and inserted it into our running session.","category":"page"},{"location":"","page":"Home","title":"Home","text":"warning: Warning\nRevise's first revision has latency of several seconds–it's compiling all of its internal code, which includes a complete Julia interpreter and all of Revise's parse/diff/patch/cache machinery. After your first revision, future revisions will generally be fast enough that they will seem nearly instantaneous. (There are exceptions, but they occur only in specific circumstances, for example when Revise's own code gets invalidated by your changes.)","category":"page"},{"location":"","page":"Home","title":"Home","text":"Now suppose we realize we've made a horrible mistake: that f method will mess up everything, because it's part of a more complicated dispatch process and incorrectly intercepts certain f calls. No problem, just delete f in your editor, save the file, and you're back to this:","category":"page"},{"location":"","page":"Home","title":"Home","text":"julia> Example.f()\nERROR: UndefVarError: f not defined","category":"page"},{"location":"","page":"Home","title":"Home","text":"all without restarting Julia. While you can evaluate new methods without Revise using inline evaluation through your IDE, method deletion is just one example of a change that can only be made easily by Revise.","category":"page"},{"location":"","page":"Home","title":"Home","text":"If you need more examples, see Revise usage: a cookbook.","category":"page"},{"location":"#Other-key-features-of-Revise","page":"Home","title":"Other key features of Revise","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Revise updates its internal paths when you change versions of a package. To try this yourself, first re-insert that definition of f in the dev version of Example and save the file. Now try toggling back and forth between the dev and released versions of Example:","category":"page"},{"location":"","page":"Home","title":"Home","text":"(v1.0) pkg> free Example # switch to the released version of Example\n\njulia> Example.f()\nERROR: UndefVarError: f not defined\n\n(v1.0) pkg> dev Example\n\njulia> Example.f()\nπ = 3.1415926535897...","category":"page"},{"location":"","page":"Home","title":"Home","text":"Revise is not tied to any particular editor. (The EDITOR or JULIA_EDITOR environment variables can be used to specify your preference for which editor gets launched by Julia's edit function.)","category":"page"},{"location":"","page":"Home","title":"Home","text":"If you don't want to have to remember to say using Revise each time you start Julia, see Using Revise by default.","category":"page"},{"location":"#What-Revise-can-track","page":"Home","title":"What Revise can track","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Revise is fairly ambitious: if all is working, subject to a few Limitations you should be able to track changes to","category":"page"},{"location":"","page":"Home","title":"Home","text":"any package that you load with import or using\nany script you load with includet (see Configuring the revise mode for important default restrictions on includet)\nany file defining Base julia itself (with Revise.track(Base))\nany of Julia's standard libraries (with, e.g., using Unicode; Revise.track(Unicode))\nany file defining Core.Compiler (with Revise.track(Core.Compiler))","category":"page"},{"location":"","page":"Home","title":"Home","text":"The last one requires that you clone Julia and build it yourself from source.","category":"page"},{"location":"#Secrets-of-Revise-\"wizards\"","page":"Home","title":"Secrets of Revise \"wizards\"","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Revise can assist with methodologies like test-driven development. While it's often desirable to write the test first, sometimes when fixing a bug it's very difficult to write a good test until you understand the bug better. Often that means basically fixing the bug before your write the test. With Revise, you can","category":"page"},{"location":"","page":"Home","title":"Home","text":"fix the bug while simultaneously developing a high-quality test\nverify that your test passes with the fixed code\ngit stash your fix and check that your new test fails on the old code, thus verifying that your test captures the essence of the former bug (if it doesn't fail, you need a better test!)\ngit stash pop, test again, commit, and submit","category":"page"},{"location":"","page":"Home","title":"Home","text":"all without restarting your Julia session.","category":"page"},{"location":"#Other-Revise-workflows","page":"Home","title":"Other Revise workflows","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Revise can be used to perform work when files update. For example, let's say you want to regenerate a set of web pages whenever your code changes. Suppose you've placed your Julia code in a package called MyWebCode, and the pages depend on \"file.js\" and all files in the \"assets/\" directory; then","category":"page"},{"location":"","page":"Home","title":"Home","text":"entr([\"file.js\", \"assets\"], [MyWebCode]) do\n build_webpages(args...)\nend","category":"page"},{"location":"","page":"Home","title":"Home","text":"will execute build_webpages(args...) whenever you save updates to the listed files or MyWebCode.","category":"page"},{"location":"","page":"Home","title":"Home","text":"If you want to regenerate the web page as soon as any change is detected, not only in MyWebCode but also in any package tracked by Revise, you can provide the all keyword argument to entr:","category":"page"},{"location":"","page":"Home","title":"Home","text":"entr([\"file.js\", \"assets\"]; all=true) do\n build_webpages(args...)\nend","category":"page"},{"location":"#Taking-advantage-of-Revise-in-other-packages","page":"Home","title":"Taking advantage of Revise in other packages","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"To make it easier for other packages to benefit from Revise without needing to add it as a dependency or understand Revise's internals, Revise interfaces with CodeTracking, which is a small package acting as Revise's \"query\" interface.","category":"page"},{"location":"#What-else-do-I-need-to-know?","page":"Home","title":"What else do I need to know?","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Except in cases of problems (see below), that's it! Revise is a tool that runs in the background, and when all is well it should be essentially invisible, except that you don't have to restart Julia so often.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Revise can also be used as a \"library\" by developers who want to add other new capabilities to Julia; the sections How Revise works and Developer reference are particularly relevant for them.","category":"page"},{"location":"#If-Revise-doesn't-work-as-expected","page":"Home","title":"If Revise doesn't work as expected","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"If Revise isn't working for you, here are some steps to try:","category":"page"},{"location":"","page":"Home","title":"Home","text":"See Configuration for information on customization options. In particular, some file systems (like NFS) and current users of WSL2 might require special options.\nRevise can't handle all kinds of code changes; for more information, see the section on Limitations.\nTry running test Revise from the Pkg REPL-mode. If tests pass, check the documentation to make sure you understand how Revise should work. If they fail (especially if it mirrors functionality that you need and isn't working), see Debugging problems with paths for one set of suggestions.","category":"page"},{"location":"","page":"Home","title":"Home","text":"If you still encounter problems, please file an issue. Especially if you think Revise is making mistakes in adding or deleting methods, please see the page on Debugging Revise for information about how to attach logs to your bug report.","category":"page"},{"location":"internals/#How-Revise-works","page":"How Revise works","title":"How Revise works","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"In addition to the material below, see these talks:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"JuliaCon 2018\nJuliaCon 2019","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Revise is based on the fact that you can change functions even when they are defined in other modules. Here's an example showing how you do that manually (without using Revise):","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"julia> convert(Float64, π)\n3.141592653589793\n\njulia> # That's too hard, let's make life easier for students\n\njulia> @eval Base convert(::Type{Float64}, x::Irrational{:π}) = 3.0\nconvert (generic function with 714 methods)\n\njulia> convert(Float64, π)\n3.0","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Revise removes some of the tedium of manually copying and pasting code into @eval statements. To decrease the amount of re-JITting required, Revise avoids reloading entire modules; instead, it takes care to eval only the changes in your package(s), much as you would if you were doing it manually. Importantly, changes are detected in a manner that is independent of the specific line numbers in your code, so that you don't have to re-evaluate just because code moves around within the same file. (One unfortunate side effect is that line numbers may become inaccurate in backtraces, but Revise takes pains to correct these, see below.)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Conceptually, Revise implements diff and patch for a running Julia session. Schematically, Revise's inner loop (revise()) looks like this:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"for def in setdiff(oldexprs, newexprs)\n # `def` is an expression that defines a method.\n # It was in `oldexprs`, but is no longer present in `newexprs`--delete the method.\n delete_methods_corresponding_to_defexpr(mod, def)\nend\nfor def in setdiff(newexprs, oldexprs)\n # `def` is an expression for a new or modified method. Instantiate it.\n Core.eval(mod, def)\nend","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"In somewhat greater detail, Revise uses the following overall strategy:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"add callbacks to Base so that Revise gets notified when new packages are loaded or new files included\nprepare source-code caches for every new file. These caches will allow Revise to detect changes when files are updated. For precompiled packages this happens on an as-needed basis, using the cached source in the *.ji file. For non-precompiled packages, Revise parses the source for each included file immediately so that the initial state is known and changes can be detected.\nmonitor the file system for changes to any of the dependent files; it immediately appends any updates to a list of file names that need future processing\nintercept the REPL's backend to ensure that the list of files-to-be-revised gets processed each time you execute a new command at the REPL\nwhen a revision is triggered, the source file(s) are re-parsed, and a diff between the cached version and the new version is created. eval the diff in the appropriate module(s).\nreplace the cached version of each source file with the new version, so that further changes are diffed against the most recent update.","category":"page"},{"location":"internals/#The-structure-of-Revise's-internal-representation","page":"How Revise works","title":"The structure of Revise's internal representation","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"(Image: diagram)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Figure notes: Nodes represent primary objects in Julia's compilation pipeline. Arrows and their labels represent functions or data structures that allow you to move from one node to another. Red (\"destructive\") paths force recompilation of dependent functions.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Revise bridges between text files (your source code) and compiled code. Revise consequently maintains data structures that parallel Julia's own internal processing of code. When dealing with a source-code file, you start with strings, parse them to obtain Julia expressions, evaluate them to obtain Julia objects, and (where appropriate, e.g., for methods) compile them to machine code. This will be called the forward workflow. Revise sets up a few key structures that allow it to progress from files to modules to Julia expressions and types.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Revise also sets up a backward workflow, proceeding from compiled code to Julia types back to Julia expressions. This workflow is useful, for example, when dealing with errors: the stack traces displayed by Julia link from the compiled code back to the source files. To make this possible, Julia builds \"breadcrumbs\" into compiled code that store the filename and line number at which each expression was found. However, these links are static, meaning they are set up once (when the code is compiled) and are not updated when the source file changes. Because trivial manipulations to source files (e.g., the insertion of blank lines and/or comments) can change the line number of an expression without necessitating its recompilation, Revise implements a way of correcting these line numbers before they are displayed to the user. The same problem presents when using a debugger, in that one wants the debugger to display the correct code (at the correct line number) even after modifications have been made to the file. This capability requires that Revise proceed backward from the compiled objects to something resembling the original text file.","category":"page"},{"location":"internals/#Terminology","page":"How Revise works","title":"Terminology","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"A few convenience terms are used throughout: definition, signature-expression, and signature-type. These terms are illustrated using the following example:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"

function print_item(io::IO, item, ntimes::Integer=1, pre::String=\"\")\n    print(io, pre)\n    for i = 1:ntimes\n        print(io, item)\n    end\nend

","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"This represents the definition of a method. Definitions are stored as expressions, using a Revise.RelocatableExpr. The highlighted portion is the signature-expression, specifying the name, argument names and their types, and (if applicable) type-parameters of the method.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"From the signature-expression we can generate one or more signature-types. Since this function has two default arguments, this signature-expression generates three signature-types, each corresponding to a different valid way of calling this method:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Tuple{typeof(print_item),IO,Any} # print_item(io, item)\nTuple{typeof(print_item),IO,Any,Integer} # print_item(io, item, 2)\nTuple{typeof(print_item),IO,Any,Integer,String} # print_item(io, item, 2, \" \")","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"In Revise's internal code, a definition is often represented with a variable def, and a signature-type with sigt. Recent versions of Revise do not make extensive use of signature expressions.","category":"page"},{"location":"internals/#Computing-signatures","page":"How Revise works","title":"Computing signatures","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Since version 2.0, Revise works primarily with lowered-code representations, specifically using the lowered code to compute method signatures (if you don't know about lowered code, see this tutorial). There are several reasons that make this an attractive approach, of which the most important are:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"keyword-argument methods get \"expanded\" to multiple methods handling various ways of populating the arguments. The lowered code lists all of them, which ensures that Revise knows about them all. (There are some challenges regarding \"gensymmed\" names, see LoweredCodeUtils and julia#30908, but in short LoweredCodeUtils \"fixes\" those difficulties.)\nfor methods generated by code, the only really reliable mechanism to compute all the signatures is to step through the code that generates the methods. That is performed using JuliaInterpreter.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"As an example, suppose the following code is part of your module definition:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"for T in (Float16, Float32, Float64)\n @eval sizefloat(x::$T) = sizeof($T)\nend","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"clarification: Clarification\nThis is equivalent to the following explicit definitions:sizefloat(x::Float16) = 2\nsizefloat(x::Float32) = 4\nsizefloat(x::Float64) = 8","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"If you replace the loop with for T in (Float32, Float64), then Revise should delete the method for Float16. But this implies that Revise can deduce all the method-signatures created by this block, which essentially requires \"simulating\" the block that defines the methods. (In simple cases there are other approaches, but for complex cases stepping through the code seems to be the only viable answer.)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Because lowered code is far simpler than ordinary Julia code, it is much easier to interpret. Let's look briefly at a method definition:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"floatwins(x::AbstractFloat, y::Integer) = x","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"which has lowered representation approximately equal to","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"CodeInfo(\n│ $(Expr(:method, :floatwins))\n│ %2 = Core.Typeof(floatwins)\n│ %3 = Core.svec(%2, AbstractFloat, Integer)\n│ %4 = Core.svec()\n│ %5 = Core.svec(%3, %4)\n│ $(Expr(:method, :floatwins, :(%5), CodeInfo(quote\n return x\nend)))\n└── return floatwins\n)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"(I've edited this lightly for clarity.) As one steps through this, the first line tells us we're about to define a method for the function floatwins. Lines 2-5 compute the signature, in the representation svec(sig, params), where here sig = svec(typeof(floatwins), AbstractFloat, Integer) and params = svec(). (This example has no type parameters, which is why params is empty.)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"What Revise does is steps through the first 5 of these lines, and when it encounters the Expr(:method, :floatwins, :(%5), CodeInfo(...)) statement, it pulls out the signature (the %5, which refers to the result computed on the 5th line) and records this as a method generated by this block of code. (It does not, however, evaluate the Expr(:method, ...) expression as a whole, because that would force it to be recompiled.) Stepping through this code ensures that Revise can compute the exact signature, no matter how this method is defined at the level of ordinary Julia code.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Unfortunately, modules sometimes contain code blocks that perhaps shouldn't be interpreted:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"init_c_library() # library crashes if we call this twice","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Starting with version 2.3, Revise attempts to avoid interpreting any code not necessary for signature computation. If you are just tracking changes, Revise will skip over such blocks; if you're loading a file with includet for the first time, Revise will execute such blocks in compiled mode.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Revise achieves this by computing backedges, essentially a set of links encoding the dependencies among different lines of the lowered code. For the floatwins example above, the backedges would represent the fact that line 2 has one direct dependant, line 3 (which uses %2), that lines 3 and 4 both have line 5 as their dependents, and line 5 has line 6 as a dependent. As a consequence, to (nearly) execute line 6, we have to execute lines 2-5, because they set up the signature. If an interdependent block doesn't contain any :method or related (:struct_type, :eval) expressions, then it doesn't need to interpret the block at all.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"As should be evident, the lowered code makes it much easier to analyze the graph of these dependencies. There are, however, a few tricky cases. For example, any code inside an @eval might, or might not, expand into lowered code that contains a :method expression. Because Revise can't reliably predict what it will look like after expansion, Revise will execute any code in (or needed for) an @eval block. As a consequence, even after version 2.3 Revise may sometimes interpret more code than is strictly necessary.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"note: Note\nIf Revise executes code that still shouldn't be run twice, one good solution is to put all initialization inside your module's __init__ function. For files that you track with includet, you can also split \"code that defines methods\" into a separate file from \"code that does work,\" and have Revise track only the method-defining file. However, starting with version 2.3 Revise should be fairly good at doing this on its own; such manual interventions should not be necessary in most cases.","category":"page"},{"location":"internals/#Core-data-structures-and-representations","page":"How Revise works","title":"Core data structures and representations","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Most of Revise's magic comes down to just three internal variables:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Revise.watched_files: encodes information used by the filesystem (FileWatching) to detect changes in source files.\nRevise.revision_queue: a list of \"work to do,\" containing the files that have been modified since the last code update.\nRevise.pkgdatas: the central repository of parsed code, used to \"diff\" for changes and then \"patch\" the running session.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Two \"maps\" are central to Revise's inner workings: ExprsSigs maps link definition=>signature-types (the forward workflow), while CodeTracking (specifically, its internal variable method_info) links from signature-type=>definition (the backward workflow). Concretely, CodeTracking.method_info is just an IdDict mapping sigt=>(locationinfo, def). Of note, a stack frame typically contains a link to a method, which stores the equivalent of sigt; consequently, this information allows one to look up the corresponding locationinfo and def. (When methods move, the location information stored by CodeTracking gets updated by Revise.)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Some additional notes about Revise's ExprsSigs maps:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"For expressions that do not define a method, it is just def=>nothing\nFor expressions that do define a method, it is def=>[sigt1, ...]. [sigt1, ...] is the list of signature-types generated from def (often just one, but more in the case of methods with default arguments or keyword arguments).\nThey are represented as an OrderedDict so as to preserve the sequence in which expressions occur in the file. This can be important particularly for updating macro definitions, which affect the expansion of later code. The order is maintained so as to match the current ordering of the source-file, which is not necessarily the same as the ordering when these expressions were last evaled.\nEach key in the map (the definition RelocatableExpr) is the most recently evaled version of the expression. This has an important consequence: the line numbers in the def (which are still present, even though not used for equality comparisons) correspond to the ones in compiled code. Any discrepancy with the current line numbers in the file is handled through updates to the location information stored by CodeTracking.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"ExprsSigs are organized by module and then file, so that one can map filename=>module=>def=>sigts. Importantly, single-file modules can be \"reconstructed\" from the keys of the corresponding ExprsSigs (and multi-file modules from a collection of such items), since they hold the complete ordered set of expressions that would be evaled to define the module.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"The global variable that holds all this information is Revise.pkgdatas, organized into a dictionary of Revise.PkgData objects indexed by Base Julia's PkgId (a unique identifier for packages).","category":"page"},{"location":"internals/#An-example","page":"How Revise works","title":"An example","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Consider a module, Items, defined by the following two source files:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Items.jl:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"__precompile__(false)\n\nmodule Items\n\ninclude(\"indents.jl\")\n\nfunction print_item(io::IO, item, ntimes::Integer=1, pre::String=indent(item))\n print(io, pre)\n for i = 1:ntimes\n print(io, item)\n end\nend\n\nend","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"indents.jl:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"indent(::UInt16) = 2\nindent(::UInt8) = 4","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"If you create this as a mini-package and then say using Revise, Items, you can start examining internal variables in the following manner:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"julia> id = Base.PkgId(Items)\nItems [b24a5932-55ed-11e9-2a88-e52f99e65a0d]\n\njulia> pkgdata = Revise.pkgdatas[id]\nPkgData(Items [b24a5932-55ed-11e9-2a88-e52f99e65a0d]:\n \"src/Items.jl\": FileInfo(Main=>ExprsSigs(<1 expressions>, <0 signatures>), Items=>ExprsSigs(<2 expressions>, <3 signatures>), )\n \"src/indents.jl\": FileInfo(Items=>ExprsSigs(<2 expressions>, <2 signatures>), )","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"(Your specific UUID may differ.)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Path information is stored in pkgdata.info:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"julia> pkgdata.info\nPkgFiles(Items [b24a5932-55ed-11e9-2a88-e52f99e65a0d]):\n basedir: \"/tmp/pkgs/Items\"\n files: [\"src/Items.jl\", \"src/indents.jl\"]","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"basedir is the only part using absolute paths; everything else is encoded relative to that location. This facilitates, e.g., switching between develop and add mode in the package manager.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"src/indents.jl is particularly simple:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"julia> pkgdata.fileinfos[2]\nFileInfo(Items=>ExprsSigs with the following expressions:\n :(indent(::UInt16) = begin\n 2\n end)\n :(indent(::UInt8) = begin\n 4\n end), )","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"This is just a summary; to see the actual def=>sigts map, do the following:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"julia> pkgdata.fileinfos[2].modexsigs[Items]\nOrderedCollections.OrderedDict{Revise.RelocatableExpr,Union{Nothing, Array{Any,1}}} with 2 entries:\n :(indent(::UInt16) = begin… => Any[Tuple{typeof(indent),UInt16}]\n :(indent(::UInt8) = begin… => Any[Tuple{typeof(indent),UInt8}]","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"These are populated now because we specified __precompile__(false), which forces Revise to defensively parse all expressions in the package in case revisions are made at some future point. For precompiled packages, each pkgdata.fileinfos[i] can instead rely on the cachefile (another field stored in the Revise.FileInfo) as a record of the state of the file at the time the package was loaded; as a consequence, Revise can defer parsing the source file(s) until they are updated.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Items.jl is represented with a bit more complexity, \"Items.jl\"=>Dict(Main=>map1, Items=>map2). This is because Items.jl contains one expression (the __precompile__ statement) that is evaled in Main, and other expressions that are evaled in Items.","category":"page"},{"location":"internals/#Revisions-and-computing-diffs","page":"How Revise works","title":"Revisions and computing diffs","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"When the file system notifies Revise that a file has been modified, Revise re-parses the file and assigns the expressions to the appropriate modules, creating a Revise.ModuleExprsSigs mexsnew. It then compares mexsnew against mexsref, the reference object that is synchronized to code as it was evaled. The following actions are taken:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"if a def entry in mexsref is equal to one in mexsnew, the expression is \"unchanged\" except possibly for line number. The locationinfo in CodeTracking is updated as needed.\nif a def entry in mexsref is not present in mexsnew, that entry is deleted and any corresponding methods are also deleted.\nif a def entry in mexsnew is not present in mexsref, it is evaled and then added to mexsref.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Technically, a new mexsref is generated every time to ensure that the expressions are ordered as in mexsnew; however, conceptually this is better thought of as an updating of mexsref, after which mexsnew is discarded.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Note that one consequence is that modifying a method causes two actions, the deletion of the original followed by evaling a new version. During revision, all method deletions are performed first, followed by all the new evaled methods. This ensures that if a method gets moved from fileB.jl to fileA.jl, Revise doesn't mistakenly redefine and then delete the method simply because fileA.jl got processed before fileB.jl.","category":"page"},{"location":"internals/#Internal-API","page":"How Revise works","title":"Internal API","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"You can find more detail about Revise's inner workings in the Developer reference.","category":"page"},{"location":"config/#Configuration","page":"Configuration","title":"Configuration","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"compat: Compat\nThese instructions are applicable only for Julia 1.5 and higher. If you are running an older version of Julia, upgrading to at least 1.6 is recommended. If you cannot upgrade, see the documentation for Revise 3.2.x or earlier.","category":"page"},{"location":"config/#Using-Revise-by-default","page":"Configuration","title":"Using Revise by default","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"If you like Revise, you can ensure that every Julia session uses it by launching it from your ~/.julia/config/startup.jl file. Note that using Revise adds a small latency at Julia startup, generally about 0.7s when you first launch Julia and another 0.25s for your first package load. Users should weigh this penalty against whatever benefit they may derive from not having to restart their entire session.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"This can be as simple as adding","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"using Revise","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"as the first line in your startup.jl. If you have a Unix terminal available, simply run","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"mkdir -p ~/.julia/config/ && echo \"using Revise\" >> ~/.julia/config/startup.jl","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"If you use different package environments and do not always have Revise available,","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"try\n using Revise\ncatch e\n @warn \"Error initializing Revise\" exception=(e, catch_backtrace())\nend","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"is recommended instead.","category":"page"},{"location":"config/#Using-Revise-automatically-within-Jupyter/IJulia","page":"Configuration","title":"Using Revise automatically within Jupyter/IJulia","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"If you want Revise to launch automatically within IJulia, then you should also create a .julia/config/startup_ijulia.jl file with the contents","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"try\n @eval using Revise\ncatch e\n @warn \"Error initializing Revise\" exception=(e, catch_backtrace())\nend","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"or simply run","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"mkdir -p ~/.julia/config/ && tee -a ~/.julia/config/startup_ijulia.jl << END\ntry\n @eval using Revise\ncatch e\n @warn \"Error initializing Revise\" exception=(e, catch_backtrace())\nend\nEND","category":"page"},{"location":"config/#Configuring-the-revise-mode","page":"Configuration","title":"Configuring the revise mode","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"By default, in packages all changes are tracked, but with includet only method definitions are tracked. This behavior can be overridden by defining a variable __revise_mode__ in the module(s) containing your methods and/or data. __revise_mode__ must be a Symbol taking one of the following values:","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":":eval: evaluate everything (the default for packages)\n:evalmeth: evaluate changes to method definitions (the default for includet) This should work even for quite complicated method definitions, such as those that might be made within a for-loop and @eval block.\n:evalassign: evaluate method definitions and assignment statements. A top-level expression a = Int[] would be evaluated, but push!(a, 1) would not because the latter is not an assignment.\n:sigs: do not implement any changes, only scan method definitions for their signatures so that their location can be updated as changes to the file(s) are made.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"If you're using includet from the REPL, you can enter __revise_mode__ = :eval to set it throughout Main. __revise_mode__ can be set independently in each module.","category":"page"},{"location":"config/#Optional-global-configuration","page":"Configuration","title":"Optional global configuration","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"Revise can be configured by setting environment variables. These variables have to be set before you execute using Revise, because these environment variables are parsed only during execution of Revise's __init__ function.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"There are several ways to set these environment variables:","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"If you are Using Revise by default then you can include statements like ENV[\"JULIA_REVISE\"] = \"manual\" in your .julia/config/startup.jl file prior to the line containing using Revise.\nOn Unix systems, you can set variables in your shell initialization script (e.g., put lines like export JULIA_REVISE=manual in your .bashrc file if you use bash).\nOn Unix systems, you can launch Julia from the Unix prompt as $ JULIA_REVISE=manual julia to set options for just that session.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"The function of specific environment variables is described below.","category":"page"},{"location":"config/#Manual-revision:-JULIA_REVISE","page":"Configuration","title":"Manual revision: JULIA_REVISE","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"By default, Revise processes any modified source files every time you enter a command at the REPL. However, there might be times where you'd prefer to exert manual control over the timing of revisions. Revise looks for an environment variable JULIA_REVISE, and if it is set to anything other than \"auto\" it will require that you manually call revise() to update code.","category":"page"},{"location":"config/#User-scripts:-JULIA_REVISE_INCLUDE","page":"Configuration","title":"User scripts: JULIA_REVISE_INCLUDE","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"By default, Revise only tracks files that have been required as a consequence of a using or import statement; files loaded by include are not tracked, unless you explicitly use includet or Revise.track(filename). However, you can turn on automatic tracking by setting the environment variable JULIA_REVISE_INCLUDE to the string \"1\" (e.g., JULIA_REVISE_INCLUDE=1 in a bash script).","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"note: Note\nMost users should avoid setting JULIA_REVISE_INCLUDE. Try includet instead.","category":"page"},{"location":"config/#Configurations-for-fixing-errors","page":"Configuration","title":"Configurations for fixing errors","text":"","category":"section"},{"location":"config/#No-space-left-on-device","page":"Configuration","title":"No space left on device","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"note: Note\nThis applies only to Linux","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"Revise needs to be notified by your filesystem about changes to your code, which means that the files that define your modules need to be watched for updates. Some systems impose limits on the number of files and directories that can be watched simultaneously; if such a limit is hit, on Linux this can result in Revise silently ceasing to work (albeit with unit tests failing) or in a fairly cryptic error like","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"ERROR: start_watching (File Monitor): no space left on device (ENOSPC)","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"The cure is to investigate and possibly increase the number of files that can be watched.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"Invoking","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"$ sysctl fs.inotify","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"at the linux prompt may e.g. result in","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"fs.inotify.max_queued_events = 16384\nfs.inotify.max_user_instances = 128\nfs.inotify.max_user_watches = 524288","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"For Revise usage, max_user_watches >= 65536 is recommended, and more can be helpful; the value of 524288 above is common on modern systems. One can set higher values as needed, e.g.,","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"$ sudo sysctl fs.inotify.max_user_instances=2048","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"After changing these values, it is advised to run Revise's unit tests to see if they pass.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"This change can be made permanent.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"For more information see issues #26 and #778.","category":"page"},{"location":"config/#Polling-and-NFS-mounted-code-directories:-JULIA_REVISE_POLL","page":"Configuration","title":"Polling and NFS-mounted code directories: JULIA_REVISE_POLL","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"note: Note\nThis applies only to Unix systems with code on network-mounted drives","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"Revise works by monitoring your filesystem for changes to the files that define your code. On most operating systems, Revise can work \"passively\" and wait to be signaled that one or more watched directories has changed.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"Unfortunately, a few file systems (notably, the Unix-based Network File System NFS) don't support this approach. In such cases, Revise needs to \"actively\" check each file periodically to see whether it has changed since the last check. This active process is called polling. You turn on polling by setting the environment variable JULIA_REVISE_POLL to the string \"1\" (e.g., JULIA_REVISE_POLL=1 in a bash script).","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"warning: Warning\nIf you're using polling, you may have to wait several seconds before changes take effect. Polling is not recommended unless you have no other alternative.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"note: Note\nNFS stands for Network File System and is typically only used to mount shared network drives on Unix file systems. Despite similarities in the acronym, NTFS, the standard filesystem on Windows, is completely different from NFS; Revise's default configuration should work fine on Windows without polling. However, WSL2 users currently need polling due to this bug.","category":"page"}] +[{"location":"debugging/#Debugging-Revise","page":"Debugging Revise","title":"Debugging Revise","text":"","category":"section"},{"location":"debugging/#Handling-errors","page":"Debugging Revise","title":"Handling errors","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Revise attempts to make error reports mimic Julia's own stacktraces, and as a consequence it has to prevent stacktraces from containing lots of lines pointing to Revise's own code. If you're trying to debug a Revise error, you'd probably prefer to see the entire stacktrace. You can uncomment the obvious commented-out line in Revise.trim_toplevel!.","category":"page"},{"location":"debugging/#The-logging-framework","page":"Debugging Revise","title":"The logging framework","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"If Revise isn't behaving the way you expect it to, it can be useful to examine the decisions it made. Revise supports Julia's Logging framework and can optionally record its decisions in a format suitable for later inspection. What follows is a simple series of steps you can use to turn on logging, capture messages, and then submit them with a bug report. Alternatively, more advanced developers may want to examine the logs themselves to determine the source of Revise's error, and for such users a few tips about interpreting the log messages are also provided below.","category":"page"},{"location":"debugging/#Turning-on-logging","page":"Debugging Revise","title":"Turning on logging","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Currently, the best way to turn on logging is within a running Julia session:","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"julia> rlogger = Revise.debug_logger()\nRevise.ReviseLogger(Revise.LogRecord[], Debug)","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"You'll use rlogger at the end to retrieve the logs.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Now carry out the series of julia commands and code edits that reproduces the problem.","category":"page"},{"location":"debugging/#Capturing-the-logs-and-submitting-them-with-your-bug-report","page":"Debugging Revise","title":"Capturing the logs and submitting them with your bug report","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Once all the revisions have been triggered and the mistake has been reproduced, it's time to capture the logs. To capture all the logs, use","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"julia> using Base.CoreLogging: Debug\n\njulia> logs = filter(r->r.level==Debug, rlogger.logs);","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"You can capture just the changes that Revise made to running code with","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"julia> logs = Revise.actions(rlogger)","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"You can either let these print to the console and copy/paste the text output into the issue, or if they are extensive you can save logs to a file:","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"open(\"/tmp/revise.logs\", \"w\") do io\n for log in logs\n println(io, log)\n end\nend","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Then you can upload the logs somewhere (e.g., https://gist.github.com/) and link the url in your bug report. To assist in the resolution of the bug, please also specify additional relevant information such as the name of the function that was misbehaving after revision and/or any error messages that your received.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"See also A complete debugging demo below.","category":"page"},{"location":"debugging/#Logging-by-default","page":"Debugging Revise","title":"Logging by default","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"If you suspect a bug in Revise but have difficulty isolating it, you can include the lines","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":" # Turn on logging\n Revise.debug_logger()","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"within the Revise block of your ~/.julia/config/startup.jl file. This will ensure that you always log Revise's actions. Then carry out your normal Julia development. If a Revise-related problem arises, executing these lines","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"rlogger = Revise.debug_logger()\nusing Base.CoreLogging: Debug\nlogs = filter(r->r.level==Debug, rlogger.logs)\nopen(\"/tmp/revise.logs\", \"w\") do io\n for log in logs\n println(io, log)\n end\nend","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"within the same session will generate the /tmp/revise.logs file that you can submit with your bug report. (What makes this possible is that a second call to Revise.debug_logger() returns the same logger object created by the first call–it is not necessary to hold on to rlogger.)","category":"page"},{"location":"debugging/#The-structure-of-the-logs","page":"Debugging Revise","title":"The structure of the logs","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"For those who want to do a little investigating on their own, it may be helpful to know that Revise's core decisions are captured in the group called \"Action,\" and they come in three flavors:","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"log entries with message \"Eval\" signify a call to eval; for these events, keyword :deltainfo has value (mod, expr) where mod is the module of evaluation and expr is a Revise.RelocatableExpr containing the expression that was evaluated.\nlog entries with message \"DeleteMethod\" signify a method deletion; for these events, keyword :deltainfo has value (sigt, methsummary) where sigt is the signature of the method that Revise intended to delete and methsummary is a MethodSummary of the method that Revise actually found to delete.\nlog entries with message \"LineOffset\" correspond to updates to Revise's own internal estimates of how far a given method has become displaced from the line number it occupied when it was last evaluated. For these events, :deltainfo has value (sigt, newlineno, oldoffset=>newoffset).","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"If you're debugging mistakes in method creation/deletion, the \"LineOffset\" events may be distracting; by default Revise.actions excludes these events.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Note that Revise records the time of each revision, which can sometimes be useful in determining which revisions occur in conjunction with which user actions. If you want to make use of this, it can be handy to capture the start time with tstart = time() before commencing on a session.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"See Revise.debug_logger for information on groups besides \"Action.\"","category":"page"},{"location":"debugging/#A-complete-debugging-demo","page":"Debugging Revise","title":"A complete debugging demo","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"From within Revise's test/ directory, try the following:","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"julia> rlogger = Revise.debug_logger();\n\nshell> cp revisetest.jl /tmp/\n\njulia> includet(\"/tmp/revisetest.jl\")\n\njulia> ReviseTest.cube(3)\n81\n\nshell> cp revisetest_revised.jl /tmp/revisetest.jl\n\njulia> ReviseTest.cube(3)\n27\n\njulia> rlogger.logs\njulia> rlogger.logs\n9-element Array{Revise.LogRecord,1}:\n Revise.LogRecord(Debug, DeleteMethod, Action, Revise_4ac0f476, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 226, (time=1.557996459055345e9, deltainfo=(Tuple{typeof(Main.ReviseTest.cube),Any}, MethodSummary(:cube, :ReviseTest, Symbol(\"/tmp/revisetest.jl\"), 7, Tuple{typeof(Main.ReviseTest.cube),Any}))))\n Revise.LogRecord(Debug, DeleteMethod, Action, Revise_4ac0f476, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 226, (time=1.557996459167895e9, deltainfo=(Tuple{typeof(Main.ReviseTest.Internal.mult3),Any}, MethodSummary(:mult3, :Internal, Symbol(\"/tmp/revisetest.jl\"), 12, Tuple{typeof(Main.ReviseTest.Internal.mult3),Any}))))\n Revise.LogRecord(Debug, DeleteMethod, Action, Revise_4ac0f476, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 226, (time=1.557996459167956e9, deltainfo=(Tuple{typeof(Main.ReviseTest.Internal.mult4),Any}, MethodSummary(:mult4, :Internal, Symbol(\"/tmp/revisetest.jl\"), 13, Tuple{typeof(Main.ReviseTest.Internal.mult4),Any}))))\n Revise.LogRecord(Debug, Eval, Action, Revise_9147188b, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 276, (time=1.557996459259605e9, deltainfo=(Main.ReviseTest, :(cube(x) = begin\n #= /tmp/revisetest.jl:7 =#\n x ^ 3\n end))))\n Revise.LogRecord(Debug, Eval, Action, Revise_9147188b, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 276, (time=1.557996459330512e9, deltainfo=(Main.ReviseTest, :(fourth(x) = begin\n #= /tmp/revisetest.jl:9 =#\n x ^ 4\n end))))\n Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 296, (time=1.557996459331061e9, deltainfo=(Any[Tuple{typeof(mult2),Any}], :(#= /tmp/revisetest.jl:11 =#) => :(#= /tmp/revisetest.jl:13 =#))))\n Revise.LogRecord(Debug, Eval, Action, Revise_9147188b, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 276, (time=1.557996459391182e9, deltainfo=(Main.ReviseTest.Internal, :(mult3(x) = begin\n #= /tmp/revisetest.jl:14 =#\n 3x\n end))))\n Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 296, (time=1.557996459391642e9, deltainfo=(Any[Tuple{typeof(unchanged),Any}], :(#= /tmp/revisetest.jl:18 =#) => :(#= /tmp/revisetest.jl:19 =#))))\n Revise.LogRecord(Debug, LineOffset, Action, Revise_fb38a7f7, \"/home/tim/.julia/dev/Revise/src/Revise.jl\", 296, (time=1.557996459391695e9, deltainfo=(Any[Tuple{typeof(unchanged2),Any}], :(#= /tmp/revisetest.jl:20 =#) => :(#= /tmp/revisetest.jl:21 =#))))","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"You can see that Revise started by deleting three methods, followed by evaluating three new versions of those methods. Interspersed are various changes to the line numbering.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"In rare cases it might be helpful to independently record the sequence of edits to the file. You can make copies cp editedfile.jl > /tmp/version1.jl, edit code, cp editedfile.jl > /tmp/version2.jl, etc. diff version1.jl version2.jl can be used to capture a compact summary of the changes and pasted into the bug report.","category":"page"},{"location":"debugging/#Debugging-problems-with-paths","page":"Debugging Revise","title":"Debugging problems with paths","text":"","category":"section"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"During certain types of usage you might receive messages like","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Warning: /some/system/path/stdlib/v1.0/SHA/src is not an existing directory, Revise is not watching","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Unless you've just deleted that directory, this indicates that some of Revise's functionality is broken.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"In the majority of cases, failures come down to Revise having trouble locating source code on your drive. This problem should be fixable, because Revise includes functionality to update its links to source files, as long as it knows what to do.","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"One of the best approaches is to run Revise's own tests via pkg> test Revise. Here are some possible test warnings and errors, and steps you might take to fix them:","category":"page"},{"location":"debugging/","page":"Debugging Revise","title":"Debugging Revise","text":"Base & stdlib file paths: Test Failed at /some/path... Expression: isfile(Revise.basesrccache) This failure is quite serious, and indicates that you will be unable to access code in Base. To fix this, look for a file called \"base.cache\" somewhere in your Julia install or build directory (for the author, it is at /home/tim/src/julia-1.0/usr/share/julia/base.cache). Now compare this with the value of Revise.basesrccache. (If you're getting this failure, presumably they are different.) An important \"top level\" directory is Sys.BINDIR; if they differ already at this level, consider adding a symbolic link from the location pointed at by Sys.BINDIR to the corresponding top-level directory in your actual Julia installation. You'll know you've succeeded in specifying it correctly when, after restarting Julia, Revise.basesrccache points to the correct file and Revise.juliadir points to the directory that contains base/. If this workaround is not possible or does not succeed, please file an issue with a description of why you can't use it and/or\ndetails from versioninfo and information about how you obtained your Julia installation;\nthe values of Revise.basesrccache and Revise.juliadir, and the actual paths to base.cache and the directory containing the running Julia's base/;\nwhat you attempted when trying to fix the problem;\nif possible, your best understanding of why this failed to fix it.\nskipping Core.Compiler tests due to lack of git repo: this likely indicates that you downloaded a Julia binary rather than building Julia from source. While Revise should be able to access the code in Base and standard libraries, at the current time it is not possible for Revise to access julia's Core.Compiler module unless you clone Julia's repository and build it from source.\nskipping git tests because Revise is not under development: this warning should be harmless. Revise has built-in functionality for extracting source code using git, and it uses itself (i.e., its own git repository) for testing purposes. These tests run only if you have checked out Revise for development (pkg> dev Revise) or on the continuous integration servers (Travis and Appveyor).","category":"page"},{"location":"cookbook/#Revise-usage:-a-cookbook","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"","category":"section"},{"location":"cookbook/#Package-centric-usage","page":"Revise usage: a cookbook","title":"Package-centric usage","text":"","category":"section"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"For code that might be useful more than once, it's often a good idea to put it in a package. Revise cooperates with the package manager to enforce its distinction between \"versioned\" and \"under development\" packages; packages that you want to modify and have tracked by Revise should be deved rather than added.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"note: Note\nYou should never modify package files in your .julia/packages directory, because this breaks the \"contract\" that such package files correspond to registered versions of the code. In recent versions of Julia, the source files in .julia/packages are read-only, and you should leave them this way.In keeping with this spirit, Revise is designed to avoid tracking changes in such files. The correct way to make and track modifications is to dev the package.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"For creating packages, the author recommends PkgTemplates.jl. A fallback is to use \"plain\" Pkg commands. Both options are described below.","category":"page"},{"location":"cookbook/#PkgTemplates","page":"Revise usage: a cookbook","title":"PkgTemplates","text":"","category":"section"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"note: Note\nBecause PkgTemplates integrates nicely with git, this approach might require you to do some configuration. (Once you get things set up, you shouldn't have to do this part ever again.) PkgTemplates needs you to configure your git user name and email. Some instructions on configuration are here and here. It's also helpful to sign up for a GitHub account and set git's github.user variable. The PkgTemplates documentation may also be useful.If you struggle with this part, consider trying the \"plain\" Pkg variant below.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"note: Note\nIf the current directory in your Julia session is itself a package folder, PkgTemplates will use it as the parent environment (project) for your new package. To reduce confusion, before trying the commands below it may help to first ensure you're in a a \"neutral\" directory, for example by typing cd() at the Julia prompt.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Let's create a new package, MyPkg, to play with.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> using PkgTemplates\n\njulia> t = Template()\nTemplate:\n → User: timholy\n → Host: github.com\n → License: MIT (Tim Holy 2019)\n → Package directory: ~/.julia/dev\n → Minimum Julia version: v1.0\n → SSH remote: No\n → Add packages to main environment: Yes\n → Commit Manifest.toml: No\n → Plugins: None\n\njulia> t(\"MyPkg\")\nGenerating project MyPkg:\n /home/tim/.julia/dev/MyPkg/Project.toml\n /home/tim/.julia/dev/MyPkg/src/MyPkg.jl\n[lots more output suppressed]","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"In the first few lines you can see the location of your new package, here the directory /home/tim/.julia/dev/MyPkg.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Press ] to enter the Pkg REPL. Then add the new package to your current environment with the dev command.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"() pkg> dev MyPkg # the dev command will look in the ~/.julia/dev folder automatically","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Press the backspace key to return to the Julia REPL.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Now let's try it out:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> using Revise # you must do this before loading any revisable packages\n\njulia> using MyPkg\n[ Info: Precompiling MyPkg [102b5b08-597c-4d40-b98a-e9249f4d01f4]","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"(It's perfectly fine if you see a different string of digits and letters after the \"Precompiling MyPkg\" message.) You'll note that Julia found your package without you having to take any extra steps.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Without quitting this Julia session, open the MyPkg.jl file in an editor. You might be able to open it with","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> edit(pathof(MyPkg))","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"although that might require configuring your EDITOR environment variable.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"You should see something like this:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"module MyPkg\n\n# Write your package code here.\n\nend","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"This is the basic package created by PkgTemplates. Let's create a simple greet function to return a message:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"module MyPkg\n\ngreet() = print(\"Hello World!\")\n\nend # module","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Now go back to that same Julia session, and try calling greet. After a pause (while Revise's internal code compiles), you should see","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> MyPkg.greet()\nHello World!","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"From this point forward, revisions should be fast. You can modify MyPkg.jl quite extensively without quitting the Julia session, although there are some Limitations.","category":"page"},{"location":"cookbook/#Using-Pkg","page":"Revise usage: a cookbook","title":"Using Pkg","text":"","category":"section"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Pkg works similarly to PkgTemplates, but requires less configuration while also doing less on your behalf. Let's create a blank MyPkg using Pkg. (If you tried the PkgTemplates version above, you might first have to delete the package with Pkg.rm(\"MyPkg\") following by a complete removal from your dev directory.)","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> using Revise, Pkg\n\njulia> cd(Pkg.devdir()) # take us to the standard \"development directory\"\n\n(v1.2) pkg> generate MyPkg\nGenerating project MyPkg:\n MyPkg/Project.toml\n MyPkg/src/MyPkg.jl\n\n(v1.2) pkg> dev MyPkg\n[ Info: resolving package identifier `MyPkg` as a directory at `~/.julia/dev/MyPkg`.\n...","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"For the line starting (v1.2) pkg>, hit the ] key at the beginning of the line, then type generate MyPkg. The next line, dev MyPkg, is necessary to tell Pkg about the existence of this new package.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Now you can do the following:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> using MyPkg\n[ Info: Precompiling MyPkg [efe7ebfe-4313-4388-9b6c-3590daf47143]\n\njulia> edit(pathof(MyPkg))","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"and the rest should be similar to what's above under PkgTemplates. Note that with this approach, MyPkg has not been set up for version control.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"note: Note\nIf you add instead of dev the package, the package manager will make a copy of the MyPkg files in your .julia/packages directory. This will be the \"official\" version of the files, and Revise will not track changes.","category":"page"},{"location":"cookbook/#includet-usage","page":"Revise usage: a cookbook","title":"includet usage","text":"","category":"section"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"The alternative to creating packages is to manually load individual source files. This approach is intended for early stages of development; if you want to track multiple files and/or have some files include other files, you should consider switching to the package style above.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Open your editor and create a file like this:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"mygreeting() = \"Hello, world!\"","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Save it as mygreet.jl in some directory. Here we will assume it's being saved in /tmp/.","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Now load the code with includet, which stands for \"include and track\":","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> using Revise\n\njulia> includet(\"/tmp/mygreet.jl\")\n\njulia> mygreeting()\n\"Hello, world!\"","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"Now, in your editor modify mygreeting to do this:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"mygreeting() = \"Hello, revised world!\"","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"and then try it in the same session:","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"julia> mygreeting()\n\"Hello, revised world!\"","category":"page"},{"location":"cookbook/","page":"Revise usage: a cookbook","title":"Revise usage: a cookbook","text":"As described above, the first revision you make may be very slow, but later revisions should be fast.","category":"page"},{"location":"dev_reference/#Developer-reference","page":"Developer reference","title":"Developer reference","text":"","category":"section"},{"location":"dev_reference/#Internal-global-variables","page":"Developer reference","title":"Internal global variables","text":"","category":"section"},{"location":"dev_reference/#Configuration-related-variables","page":"Developer reference","title":"Configuration-related variables","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"These are set during execution of Revise's __init__ function.","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.watching_files\nRevise.polling_files\nRevise.tracking_Main_includes","category":"page"},{"location":"dev_reference/#Revise.watching_files","page":"Developer reference","title":"Revise.watching_files","text":"Revise.watching_files[]\n\nReturns true if we watch files rather than their containing directory. FreeBSD and NFS-mounted systems should watch files, otherwise we prefer to watch directories.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.polling_files","page":"Developer reference","title":"Revise.polling_files","text":"Revise.polling_files[]\n\nReturns true if we should poll the filesystem for changes to the files that define loaded code. It is preferable to avoid polling, instead relying on operating system notifications via FileWatching.watch_file. However, NFS-mounted filesystems (and perhaps others) do not support file-watching, so for code stored on such filesystems you should turn polling on.\n\nSee the documentation for the JULIA_REVISE_POLL environment variable.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.tracking_Main_includes","page":"Developer reference","title":"Revise.tracking_Main_includes","text":"Revise.tracking_Main_includes[]\n\nReturns true if files directly included from the REPL should be tracked. The default is false. See the documentation regarding the JULIA_REVISE_INCLUDE environment variable to customize it.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Path-related-variables","page":"Developer reference","title":"Path-related variables","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.juliadir\nRevise.basesrccache\nRevise.basebuilddir","category":"page"},{"location":"dev_reference/#Revise.juliadir","page":"Developer reference","title":"Revise.juliadir","text":"Revise.juliadir\n\nConstant specifying full path to julia top-level source directory. This should be reliable even for local builds, cross-builds, and binary installs.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.basesrccache","page":"Developer reference","title":"Revise.basesrccache","text":"Revise.basesrccache\n\nFull path to the running Julia's cache of source code defining Base.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.basebuilddir","page":"Developer reference","title":"Revise.basebuilddir","text":"Revise.basebuilddir\n\nJulia's top-level directory when Julia was built, as recorded by the entries in Base._included_files.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Internal-state-management","page":"Developer reference","title":"Internal state management","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.pkgdatas\nRevise.watched_files\nRevise.revision_queue\nRevise.NOPACKAGE\nRevise.queue_errors\nRevise.included_files\nRevise.watched_manifests","category":"page"},{"location":"dev_reference/#Revise.pkgdatas","page":"Developer reference","title":"Revise.pkgdatas","text":"Revise.pkgdatas\n\npkgdatas is the core information that tracks the relationship between source code and julia objects, and allows re-evaluation of code in the proper module scope. It is a dictionary indexed by PkgId: pkgdatas[id] returns a value of type Revise.PkgData.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.watched_files","page":"Developer reference","title":"Revise.watched_files","text":"Revise.watched_files\n\nGlobal variable, watched_files[dirname] returns the collection of files in dirname that we're monitoring for changes. The returned value has type Revise.WatchList.\n\nThis variable allows us to watch directories rather than files, reducing the burden on the OS.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.revision_queue","page":"Developer reference","title":"Revise.revision_queue","text":"Revise.revision_queue\n\nGlobal variable, revision_queue holds (pkgdata,filename) pairs that we need to revise, meaning that these files have changed since we last processed a revision. This list gets populated by callbacks that watch directories for updates.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.NOPACKAGE","page":"Developer reference","title":"Revise.NOPACKAGE","text":"Revise.NOPACKAGE\n\nGlobal variable; default PkgId used for files which do not belong to any package, but still have to be watched because user callbacks have been registered for them.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.queue_errors","page":"Developer reference","title":"Revise.queue_errors","text":"Revise.queue_errors\n\nGlobal variable, maps (pkgdata, filename) pairs that errored upon last revision to (exception, backtrace).\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.included_files","page":"Developer reference","title":"Revise.included_files","text":"Revise.included_files\n\nGlobal variable, included_files gets populated by callbacks we register with include. It's used to track non-precompiled packages and, optionally, user scripts (see docs on JULIA_REVISE_INCLUDE).\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.watched_manifests","page":"Developer reference","title":"Revise.watched_manifests","text":"Revise.watched_manifests\n\nGlobal variable, a set of Manifest.toml files from the active projects used during this session.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"The following are specific to user callbacks (see Revise.add_callback) and the implementation of entr:","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.revision_event\nRevise.user_callbacks_queue\nRevise.user_callbacks_by_file\nRevise.user_callbacks_by_key","category":"page"},{"location":"dev_reference/#Revise.revision_event","page":"Developer reference","title":"Revise.revision_event","text":"Revise.revision_event\n\nThis Condition is used to notify entr that one of the watched files has changed.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.user_callbacks_queue","page":"Developer reference","title":"Revise.user_callbacks_queue","text":"Revise.user_callbacks_queue\n\nGlobal variable, user_callbacks_queue holds key values for which the file has changed but the user hooks have not yet been called.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.user_callbacks_by_file","page":"Developer reference","title":"Revise.user_callbacks_by_file","text":"Revise.user_callbacks_by_file\n\nGlobal variable, maps files (identified by their absolute path) to the set of callback keys registered for them.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Revise.user_callbacks_by_key","page":"Developer reference","title":"Revise.user_callbacks_by_key","text":"Revise.user_callbacks_by_key\n\nGlobal variable, maps callback keys to user hooks.\n\n\n\n\n\n","category":"constant"},{"location":"dev_reference/#Types","page":"Developer reference","title":"Types","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.RelocatableExpr\nRevise.ModuleExprsSigs\nRevise.FileInfo\nRevise.PkgData\nRevise.WatchList\nRevise.TaskThunk\nRevise.ReviseEvalException\nMethodSummary","category":"page"},{"location":"dev_reference/#Revise.RelocatableExpr","page":"Developer reference","title":"Revise.RelocatableExpr","text":"A RelocatableExpr wraps an Expr to ensure that comparisons between RelocatableExprs ignore line numbering information. This allows one to detect that two expressions are the same no matter where they appear in a file.\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.ModuleExprsSigs","page":"Developer reference","title":"Revise.ModuleExprsSigs","text":"ModuleExprsSigs\n\nFor a particular source file, the corresponding ModuleExprsSigs is a mapping mod=>exprs=>sigs of the expressions exprs found in mod and the signatures sigs that arise from them. Specifically, if mes is a ModuleExprsSigs, then mes[mod][ex] is a list of signatures that result from evaluating ex in mod. It is possible that this returns nothing, which can mean either that ex does not define any methods or that the signatures have not yet been cached.\n\nThe first mod key is guaranteed to be the module into which this file was included.\n\nTo create a ModuleExprsSigs from a source file, see Revise.parse_source.\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.FileInfo","page":"Developer reference","title":"Revise.FileInfo","text":"FileInfo(mexs::ModuleExprsSigs, cachefile=\"\")\n\nStructure to hold the per-module expressions found when parsing a single file. mexs holds the Revise.ModuleExprsSigs for the file.\n\nOptionally, a FileInfo can also record the path to a cache file holding the original source code. This is applicable only for precompiled modules and Base. (This cache file is distinct from the original source file that might be edited by the developer, and it will always hold the state of the code when the package was precompiled or Julia's Base was built.) When a cache is available, mexs will be empty until the file gets edited: the original source code gets parsed only when a revision needs to be made.\n\nSource cache files greatly reduce the overhead of using Revise.\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.PkgData","page":"Developer reference","title":"Revise.PkgData","text":"PkgData(id, path, fileinfos::Dict{String,FileInfo})\n\nA structure holding the data required to handle a particular package. path is the top-level directory defining the package, and fileinfos holds the Revise.FileInfo for each file defining the package.\n\nFor the PkgData associated with Main (e.g., for files loaded with includet), the corresponding path entry will be empty.\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.WatchList","page":"Developer reference","title":"Revise.WatchList","text":"Revise.WatchList\n\nA struct for holding files that live inside a directory. Some platforms (OSX) have trouble watching too many files. So we watch parent directories, and keep track of which files in them should be tracked.\n\nFields:\n\ntimestamp: mtime of last update\ntrackedfiles: Set of filenames, generally expressed as a relative path\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.TaskThunk","page":"Developer reference","title":"Revise.TaskThunk","text":"thunk = TaskThunk(f, args)\n\nTo facilitate precompilation and reduce latency, we avoid creation of anonymous thunks. thunk can be used as an argument in schedule(Task(thunk)).\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.ReviseEvalException","page":"Developer reference","title":"Revise.ReviseEvalException","text":"ReviseEvalException(loc::String, exc::Exception, stacktrace=nothing)\n\nProvide additional location information about exc.\n\nWhen running via the interpreter, the backtraces point to interpreter code rather than the original culprit. This makes it possible to use loc to provide information about the frame backtrace, and even to supply a fake backtrace.\n\nIf stacktrace is supplied it must be a Vector{Any} containing (::StackFrame, n) pairs where n is the recursion count (typically 1).\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Revise.MethodSummary","page":"Developer reference","title":"Revise.MethodSummary","text":"MethodSummary(method)\n\nCreate a portable summary of a method. In particular, a MethodSummary can be saved to a JLD2 file.\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Function-reference","page":"Developer reference","title":"Function reference","text":"","category":"section"},{"location":"dev_reference/#Functions-called-when-you-load-a-new-package","page":"Developer reference","title":"Functions called when you load a new package","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.watch_package\nRevise.parse_pkg_files\nRevise.init_watching","category":"page"},{"location":"dev_reference/#Revise.watch_package","page":"Developer reference","title":"Revise.watch_package","text":"watch_package(id::Base.PkgId)\n\nStart watching a package for changes to the files that define it. This function gets called via a callback registered with Base.require, at the completion of module-loading by using or import.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.parse_pkg_files","page":"Developer reference","title":"Revise.parse_pkg_files","text":"parse_pkg_files(id::PkgId)\n\nThis function gets called by watch_package and runs when a package is first loaded. Its job is to organize the files and expressions defining the module so that later we can detect and process revisions.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.init_watching","page":"Developer reference","title":"Revise.init_watching","text":"Revise.init_watching(files)\nRevise.init_watching(pkgdata::PkgData, files)\n\nFor every filename in files, monitor the filesystem for updates. When the file is updated, either Revise.revise_dir_queued or Revise.revise_file_queued will be called.\n\nUse the pkgdata version if the files are supplied using relative paths.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Monitoring-for-changes","page":"Developer reference","title":"Monitoring for changes","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"These functions get called on each directory or file that you monitor for revisions. These block execution until the file(s) are updated, so you should only call them from within an @async block. They work recursively: once an update has been detected and execution resumes, they schedule a revision (see Revise.revision_queue) and then call themselves on the same directory or file to wait for the next set of changes.","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.revise_dir_queued\nRevise.revise_file_queued","category":"page"},{"location":"dev_reference/#Revise.revise_dir_queued","page":"Developer reference","title":"Revise.revise_dir_queued","text":"revise_dir_queued(dirname)\n\nWait for one or more of the files registered in Revise.watched_files[dirname] to be modified, and then queue the corresponding files on Revise.revision_queue. This is generally called via a Revise.TaskThunk.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.revise_file_queued","page":"Developer reference","title":"Revise.revise_file_queued","text":"revise_file_queued(pkgdata::PkgData, filename)\n\nWait for modifications to filename, and then queue the corresponding files on Revise.revision_queue. This is generally called via a Revise.TaskThunk.\n\nThis is used only on platforms (like BSD) which cannot use Revise.revise_dir_queued.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"The following functions support user callbacks, and are used in the implementation of entr but can be used more broadly:","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.add_callback\nRevise.remove_callback","category":"page"},{"location":"dev_reference/#Revise.add_callback","page":"Developer reference","title":"Revise.add_callback","text":"key = Revise.add_callback(f, files, modules=nothing; key=gensym())\n\nAdd a user-specified callback, to be executed during the first run of revise() after a file in files or a module in modules is changed on the file system. If all is set to true, also execute the callback whenever any file already monitored by Revise changes. In an interactive session like the REPL, Juno or Jupyter, this means the callback executes immediately before executing a new command / cell.\n\nYou can use the return value key to remove the callback later (Revise.remove_callback) or to update it using another call to Revise.add_callback with key=key.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.remove_callback","page":"Developer reference","title":"Revise.remove_callback","text":"Revise.remove_callback(key)\n\nRemove a callback previously installed by a call to Revise.add_callback(...). See its docstring for details.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Evaluating-changes-(revising)-and-computing-diffs","page":"Developer reference","title":"Evaluating changes (revising) and computing diffs","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"revise is the primary entry point for implementing changes. Additionally,","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.revise_file_now","category":"page"},{"location":"dev_reference/#Revise.revise_file_now","page":"Developer reference","title":"Revise.revise_file_now","text":"Revise.revise_file_now(pkgdata::PkgData, file)\n\nProcess revisions to file. This parses file and computes an expression-level diff between the current state of the file and its most recently evaluated state. It then deletes any removed methods and re-evaluates any changed expressions. Note that generally it is better to use revise as it properly handles methods that move from one file to another.\n\nid must be a key in Revise.pkgdatas, and file a key in Revise.pkgdatas[id].fileinfos.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Caching-the-definition-of-methods","page":"Developer reference","title":"Caching the definition of methods","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.get_def","category":"page"},{"location":"dev_reference/#Revise.get_def","page":"Developer reference","title":"Revise.get_def","text":"success = get_def(method::Method)\n\nAs needed, load the source file necessary for extracting the code defining method. The source-file defining method must be tracked. If it is in Base, this will execute track(Base) if necessary.\n\nThis is a callback function used by CodeTracking.jl's definition.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Parsing-source-code","page":"Developer reference","title":"Parsing source code","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.parse_source\nRevise.parse_source!","category":"page"},{"location":"dev_reference/#Revise.parse_source","page":"Developer reference","title":"Revise.parse_source","text":"mexs = parse_source(filename::AbstractString, mod::Module)\n\nParse the source filename, returning a ModuleExprsSigs mexs. mod is the \"parent\" module for the file (i.e., the one that included the file); if filename defines more module(s) then these will all have separate entries in mexs.\n\nIf parsing filename fails, nothing is returned.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.parse_source!","page":"Developer reference","title":"Revise.parse_source!","text":"parse_source!(mexs::ModuleExprsSigs, filename, mod::Module)\n\nTop-level parsing of filename as included into module mod. Successfully-parsed expressions will be added to mexs. Returns mexs if parsing finished successfully, otherwise nothing is returned.\n\nSee also Revise.parse_source.\n\n\n\n\n\nsuccess = parse_source!(mod_exprs_sigs::ModuleExprsSigs, src::AbstractString, filename::AbstractString, mod::Module)\n\nParse a string src obtained by reading file as a single string. pos is the 1-based byte offset from which to begin parsing src.\n\nSee also Revise.parse_source.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Lowered-source-code","page":"Developer reference","title":"Lowered source code","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Much of the \"brains\" of Revise comes from doing analysis on lowered code. This part of the package is not as well documented.","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.minimal_evaluation!\nRevise.methods_by_execution!\nRevise.CodeTrackingMethodInfo","category":"page"},{"location":"dev_reference/#Revise.minimal_evaluation!","page":"Developer reference","title":"Revise.minimal_evaluation!","text":"isrequired, evalassign = minimal_evaluation!([predicate,] methodinfo, src::Core.CodeInfo, mode::Symbol)\n\nMark required statements in src: isrequired[i] is true if src.code[i] should be evaluated. Statements are analyzed by isreq, haseval = predicate(stmt), and predicate defaults to Revise.is_method_or_eval. haseval is true if the statement came from @eval or eval(...) call. Since the contents of such expression are difficult to analyze, it is generally safest to execute all such evals.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.methods_by_execution!","page":"Developer reference","title":"Revise.methods_by_execution!","text":"methods_by_execution!(recurse=JuliaInterpreter.Compiled(), methodinfo, docexprs, mod::Module, ex::Expr;\n mode=:eval, disablebp=true, skip_include=mode!==:eval, always_rethrow=false)\n\nEvaluate or analyze ex in the context of mod. Depending on the setting of mode (see the Extended help), it supports full evaluation or just the minimal evaluation needed to extract method signatures. recurse controls JuliaInterpreter's evaluation of any non-intercepted statement; likely choices are JuliaInterpreter.Compiled() or JuliaInterpreter.finish_and_return!. methodinfo is a cache for storing information about any method definitions (see CodeTrackingMethodInfo). docexprs is a cache for storing documentation expressions; obtain an empty one with Revise.DocExprs().\n\nExtended help\n\nThe action depends on mode:\n\n:eval evaluates the expression in mod, similar to Core.eval(mod, ex) except that methodinfo and docexprs will be populated with information about any signatures or docstrings. This mode is used to implement includet.\n:sigs analyzes ex and extracts signatures of methods and docstrings (specifically, statements flagged by Revise.minimal_evaluation!), but does not evaluate ex in the traditional sense. It will selectively execute statements needed to form the signatures of defined methods. It will also expand any @evaled expressions, since these might contain method definitions.\n:evalmeth analyzes ex and extracts signatures and docstrings like :sigs, but takes the additional step of evaluating any :method statements.\n:evalassign acts similarly to :evalmeth, and also evaluates assignment statements.\n\nWhen selectively evaluating an expression, Revise will incorporate required dependencies, even for minimal-evaluation modes like :sigs. For example, the method definition\n\nmax_values(T::Union{map(X -> Type{X}, Base.BitIntegerSmall_types)...}) = 1 << (8*sizeof(T))\n\nfound in base/abstractset.jl requires that it create the anonymous function in order to compute the signature.\n\nThe other keyword arguments are more straightforward:\n\ndisablebp controls whether JuliaInterpreter's breakpoints are disabled before stepping through the code. They are restored on exit.\nskip_include prevents execution of include statements, instead inserting them into methodinfo's cache. This defaults to true unless mode is :eval.\nalways_rethrow, if true, causes an error to be thrown if evaluating ex triggered an error. If false, the error is logged with @error. InterruptExceptions are always rethrown. This is primarily useful for debugging.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.CodeTrackingMethodInfo","page":"Developer reference","title":"Revise.CodeTrackingMethodInfo","text":"CodeTrackingMethodInfo(ex::Expr)\n\nCreate a cache for storing information about method definitions. Adding signatures to such an object inserts them into CodeTracking.method_info, which maps signature Tuple-types to (lnn::LineNumberNode, ex::Expr) pairs. Because method signatures are unique within a module, this is the foundation for identifying methods in a manner independent of source-code location.\n\nIt also has the following fields:\n\nexprstack: used when descending into @eval statements (via push_expr and pop_expr!) ex (used in creating the CodeTrackingMethodInfo object) is the first entry in the stack.\nallsigs: a list of all method signatures defined by a given expression\ndeps: list of top-level named objects (Symbols and GlobalRefs) that method definitions in this block depend on. For example, if Sys.iswindows() f() = 1 else f() = 2 end would store Sys.iswindows here.\nincludes: a list of module=>filename for any include statements encountered while the expression was parsed.\n\n\n\n\n\n","category":"type"},{"location":"dev_reference/#Modules-and-paths","page":"Developer reference","title":"Modules and paths","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.modulefiles","category":"page"},{"location":"dev_reference/#Revise.modulefiles","page":"Developer reference","title":"Revise.modulefiles","text":"parentfile, included_files = modulefiles(mod::Module)\n\nReturn the parentfile in which mod was defined, as well as a list of any other files that were included to define mod. If this operation is unsuccessful, (nothing, nothing) is returned.\n\nAll files are returned as absolute paths.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Handling-errors","page":"Developer reference","title":"Handling errors","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.trim_toplevel!","category":"page"},{"location":"dev_reference/#Revise.trim_toplevel!","page":"Developer reference","title":"Revise.trim_toplevel!","text":"trim_toplevel!(bt)\n\nTruncate a list of instruction pointers, as obtained from backtrace() or catch_backtrace(), at the first \"top-level\" call (e.g., as executed from the REPL prompt) or the first entry corresponding to a method in Revise or its dependencies.\n\nThis is used to make stacktraces obtained with Revise more similar to those obtained without Revise, while retaining one entry to reveal Revise's involvement.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"In current releases of Julia, hitting Ctrl-C from the REPL can stop tasks running in the background. This risks stopping Revise's ability to watch for changes in files and directories. Revise has a work-around for this problem.","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.throwto_repl","category":"page"},{"location":"dev_reference/#Revise.throwto_repl","page":"Developer reference","title":"Revise.throwto_repl","text":"success = throwto_repl(e::Exception)\n\nTry throwing e from the REPL's backend task. Returns true if the necessary conditions were met and the throw can be expected to succeed. The throw is generated from another task, so a yield will need to occur before it happens.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Git-integration","page":"Developer reference","title":"Git integration","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.git_source\nRevise.git_files\nRevise.git_repo","category":"page"},{"location":"dev_reference/#Revise.git_source","page":"Developer reference","title":"Revise.git_source","text":"Revise.git_source(file::AbstractString, reference)\n\nRead the source-text for file from a git commit reference. The reference may be a string, Symbol, or LibGit2.Tree.\n\nExample:\n\nRevise.git_source(\"/path/to/myfile.jl\", \"HEAD\")\nRevise.git_source(\"/path/to/myfile.jl\", :abcd1234) # by commit SHA\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.git_files","page":"Developer reference","title":"Revise.git_files","text":"files = git_files(repo)\n\nReturn the list of files checked into repo.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Revise.git_repo","page":"Developer reference","title":"Revise.git_repo","text":"repo, repo_path = git_repo(path::AbstractString)\n\nReturn the repo::LibGit2.GitRepo containing the file or directory path. path does not necessarily need to be the top-level directory of the repository. Also returns the repo_path of the top-level directory for the repository.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Distributed-computing","page":"Developer reference","title":"Distributed computing","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise.init_worker","category":"page"},{"location":"dev_reference/#Revise.init_worker","page":"Developer reference","title":"Revise.init_worker","text":"Revise.init_worker(p)\n\nDefine methods on worker p that Revise needs in order to perform revisions on p. Revise itself does not need to be running on p.\n\n\n\n\n\n","category":"function"},{"location":"dev_reference/#Teaching-Revise-about-non-julia-source-codes","page":"Developer reference","title":"Teaching Revise about non-julia source codes","text":"","category":"section"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"Revise can be made to work for transpilers from non-Julia languages to Julia with a little effort. For example, if you wrote a transpiler from C to Julia, you can define a struct CFile which overrides enough of the common String methods (abspath,isabspath, joinpath, normpath,isfile,findfirst, and String), it will be supported by Revise if you define a method like","category":"page"},{"location":"dev_reference/","page":"Developer reference","title":"Developer reference","text":"function Revise.parse_source!(mod_exprs_sigs::Revise.ModuleExprsSigs, file::CFile, mod::Module; kwargs...)\n ex = # julia Expr returned from running transpiler\n Revise.process_source!(mod_exprs_sigs, ex, file, mod; kwargs...)\nend\n","category":"page"},{"location":"user_reference/#User-reference","page":"User reference","title":"User reference","text":"","category":"section"},{"location":"user_reference/","page":"User reference","title":"User reference","text":"There are really only six functions that most users would be expected to call manually: revise, includet, Revise.track, entr, Revise.retry, and Revise.errors. Other user-level constructs might apply if you want to debug Revise or prevent it from watching specific packages, or for fine-grained handling of callbacks.","category":"page"},{"location":"user_reference/","page":"User reference","title":"User reference","text":"revise\nRevise.track\nincludet\nentr\nRevise.retry\nRevise.errors","category":"page"},{"location":"user_reference/#Revise.revise","page":"User reference","title":"Revise.revise","text":"revise(; throw=false)\n\neval any changes in the revision queue. See Revise.revision_queue. If throw is true, throw any errors that occur during revision or callback; otherwise these are only logged.\n\n\n\n\n\nrevise(mod::Module; force::Bool=true)\n\nRevise all files that define mod.\n\nIf force=true, reevaluate every definition in mod, whether it was changed or not. This is useful to propagate an updated macro definition, or to force recompiling generated functions. Be warned, however, that this invalidates all the compiled code in your session that depends on mod, and can lead to long recompilation times.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.track","page":"User reference","title":"Revise.track","text":"Revise.track(Base)\nRevise.track(Core.Compiler)\nRevise.track(stdlib)\n\nTrack updates to the code in Julia's base directory, base/compiler, or one of its standard libraries.\n\n\n\n\n\nRevise.track(mod::Module, file::AbstractString)\nRevise.track(file::AbstractString)\n\nWatch file for updates and revise loaded code with any changes. mod is the module into which file is evaluated; if omitted, it defaults to Main.\n\nIf this produces many errors, check that you specified mod correctly.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.includet","page":"User reference","title":"Revise.includet","text":"includet(filename)\n\nLoad filename and track future changes. includet is intended for quick \"user scripts\"; larger or more established projects are encouraged to put the code in one or more packages loaded with using or import instead of using includet. See https://timholy.github.io/Revise.jl/stable/cookbook/ for tips about setting up the package workflow.\n\nBy default, includet only tracks modifications to methods, not data. See the extended help for details. Note that this differs from packages, which evaluate all changes by default. This default behavior can be overridden; see Configuring the revise mode.\n\nExtended help\n\nBehavior and justification for the default revision mode (:evalmeth)\n\nincludet uses a default __revise_mode__ = :evalmeth. The consequence is that if you change\n\na = [1]\nf() = 1\n\nto\n\na = [2]\nf() = 2\n\nthen Revise will update f but not a.\n\nThis is the default choice for includet because such files typically mix method definitions and data-handling. Data often has many untracked dependencies; later in the same file you might push!(a, 22), but Revise cannot determine whether you wish it to re-run that line after redefining a. Consequently, the safest default choice is to leave the user in charge of data.\n\nWorkflow tips\n\nIf you have a series of computations that you want to run when you redefine your methods, consider separating your method definitions from your computations:\n\nmethod definitions go in a package, or a file that you includet once\nthe computations go in a separate file, that you re-include (no \"t\" at the end) each time you want to rerun your computations.\n\nThis can be automated using entr.\n\nInternals\n\nincludet is essentially shorthand for\n\nRevise.track(Main, filename; mode=:includet, skip_include=true)\n\nDo not use includet for packages, as those should be handled by using or import. If using and import aren't working, you may have packages in a non-standard location; try fixing it with something like push!(LOAD_PATH, \"/path/to/my/private/repos\"). (If you're working with code in Base or one of Julia's standard libraries, use Revise.track(mod) instead, where mod is the module.)\n\nincludet is deliberately non-recursive, so if filename loads any other files, they will not be automatically tracked. (See Revise.track to set it up manually.)\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.entr","page":"User reference","title":"Revise.entr","text":"entr(f, files; all=false, postpone=false, pause=0.02)\nentr(f, files, modules; all=false, postpone=false, pause=0.02)\n\nExecute f() whenever files or directories listed in files, or code in modules, updates. If all is true, also execute f() as soon as code updates are detected in any module tracked by Revise.\n\nentr will process updates (and block your command line) until you press Ctrl-C. Unless postpone is true, f() will be executed also when calling entr, regardless of file changes. The pause is the period (in seconds) that entr will wait between being triggered and actually calling f(), to handle clusters of modifications, such as those produced by saving files in certain text editors.\n\nExample\n\nentr([\"/tmp/watched.txt\"], [Pkg1, Pkg2]) do\n println(\"update\")\nend\n\nThis will print \"update\" every time \"/tmp/watched.txt\" or any of the code defining Pkg1 or Pkg2 gets updated.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.retry","page":"User reference","title":"Revise.retry","text":"Revise.retry()\n\nAttempt to perform previously-failed revisions. This can be useful in cases of order-dependent errors.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.errors","page":"User reference","title":"Revise.errors","text":"Revise.errors()\n\nReport the errors represented in Revise.queue_errors. Errors are automatically reported the first time they are encountered, but this function can be used to report errors again.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise-logs-(debugging-Revise)","page":"User reference","title":"Revise logs (debugging Revise)","text":"","category":"section"},{"location":"user_reference/","page":"User reference","title":"User reference","text":"Revise.debug_logger\nRevise.actions\nRevise.diffs","category":"page"},{"location":"user_reference/#Revise.debug_logger","page":"User reference","title":"Revise.debug_logger","text":"logger = Revise.debug_logger(; min_level=Debug)\n\nTurn on debug logging (if min_level is set to Debug or better) and return the logger object. logger.logs contains a list of the logged events. The items in this list are of type Revise.LogRecord, with the following relevant fields:\n\ngroup: the event category. Revise currently uses the following groups:\n\"Action\": a change was implemented, of type described in the message field.\n\"Parsing\": a \"significant\" event in parsing. For these, examine the message field for more information.\n\"Watching\": an indication that Revise determined that a particular file needed to be examined for possible code changes. This is typically done on the basis of mtime, the modification time of the file, and does not necessarily indicate that there were any changes.\nmessage: a string containing more information. Some examples:\nFor entries in the \"Action\" group, message can be \"Eval\" when modifying old methods or defining new ones, \"DeleteMethod\" when deleting a method, and \"LineOffset\" to indicate that the line offset for a method was updated (the last only affects the printing of stacktraces upon error, it does not change how code runs)\nItems with group \"Parsing\" and message \"Diff\" contain sets :newexprs and :oldexprs that contain the expression unique to post- or pre-revision, respectively.\nkwargs: a pairs list of any other data. This is usually specific to particular group/message combinations.\n\nSee also Revise.actions and Revise.diffs.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.actions","page":"User reference","title":"Revise.actions","text":"actions(logger; line=false)\n\nReturn a vector of all log events in the \"Action\" group. \"LineOffset\" events are returned only if line=true; by default the returned items are the events that modified methods in your session.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise.diffs","page":"User reference","title":"Revise.diffs","text":"diffs(logger)\n\nReturn a vector of all log events that encode a (non-empty) diff between two versions of a file.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Prevent-Revise-from-watching-specific-packages","page":"User reference","title":"Prevent Revise from watching specific packages","text":"","category":"section"},{"location":"user_reference/","page":"User reference","title":"User reference","text":"Revise.dont_watch_pkgs\nRevise.silence","category":"page"},{"location":"user_reference/#Revise.dont_watch_pkgs","page":"User reference","title":"Revise.dont_watch_pkgs","text":"Revise.dont_watch_pkgs\n\nGlobal variable, use push!(Revise.dont_watch_pkgs, :MyPackage) to prevent Revise from tracking changes to MyPackage. You can do this from the REPL or from your .julia/config/startup.jl file.\n\nSee also Revise.silence.\n\n\n\n\n\n","category":"constant"},{"location":"user_reference/#Revise.silence","page":"User reference","title":"Revise.silence","text":"Revise.silence(pkg)\n\nSilence warnings about not tracking changes to package pkg.\n\n\n\n\n\n","category":"function"},{"location":"user_reference/#Revise-module","page":"User reference","title":"Revise module","text":"","category":"section"},{"location":"user_reference/","page":"User reference","title":"User reference","text":"Revise","category":"page"},{"location":"user_reference/#Revise","page":"User reference","title":"Revise","text":"Revise.jl tracks source code changes and incorporates the changes to a running Julia session.\n\nRevise.jl works behind-the-scenes. To track a package, e.g. Example:\n\n(@v1.6) pkg> dev Example # make a development copy of the package\n[...pkg output omitted...]\n\njulia> using Revise # this must come before the package under development\n\njulia> using Example\n\n[...develop the package...] # Revise.jl will automatically update package functionality to match code changes\n\n\nFunctions in Revise.jl that may come handy in special circumstances:\n\nRevise.track: track updates to Base Julia itself or Core.Compiler\nincludet: load a file and track future changes. Intended for small, quick works\nentr: call an additional function whenever code updates\nrevise: evaluate any changes in Revise.revision_queue or every definition in a module\nRevise.retry: perform previously-failed revisions. Useful in cases of order-dependent errors\nRevise.errors: report the errors represented in Revise.queue_errors\n\n\n\n\n\n","category":"module"},{"location":"limitations/#Limitations","page":"Limitations","title":"Limitations","text":"","category":"section"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"There are some kinds of changes that Revise (or often, Julia itself) cannot incorporate into a running Julia session:","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"changes to type definitions or consts\nconflicts between variables and functions sharing the same name\nremoval of exports","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"These kinds of changes require that you restart your Julia session.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"During early stages of development, it's quite common to want to change type definitions. You can work around Julia's/Revise's limitations by temporary renaming. We'll illustrate this below, using write to be explicit about when updates to the file happen. But in ordinary usage, these are changes you'd likely make with your editor.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"julia> using Pkg, Revise\n\njulia> Pkg.generate(\"MyPkg\")\n Generating project MyPkg:\n MyPkg/Project.toml\n MyPkg/src/MyPkg.jl\nDict{String, Base.UUID} with 1 entry:\n \"MyPkg\" => UUID(\"69940cda-0c72-4a1a-ae0b-fd3109336fe8\")\n\njulia> cd(\"MyPkg\")\n\njulia> write(\"src/MyPkg.jl\",\"\"\"\n module MyPkg\n\n export FooStruct, processFoo\n\n abstract type AbstractFooStruct end\n struct FooStruct1 <: AbstractFooStruct\n bar::Int\n end\n FooStruct = FooStruct1\n function processFoo(foo::AbstractFooStruct)\n @info foo.bar\n end\n\n end\n \"\"\")\n230\n\njulia> Pkg.activate(\".\")\n Activating project at `~/blah/MyPkg`\n\njulia> using MyPkg\n No Changes to `~/blah/MyPkg/Project.toml`\n No Changes to `~/blah/MyPkg/Manifest.toml`\nPrecompiling MyPkg\n 1 dependency successfully precompiled in 2 seconds\n\njulia> processFoo(FooStruct(1))\n[ Info: 1\n\njulia> write(\"src/MyPkg.jl\",\"\"\"\n module MyPkg\n\n export FooStruct, processFoo\n\n abstract type AbstractFooStruct end\n struct FooStruct2 <: AbstractFooStruct # change version nuumber\n bar::Float64 # change type of the field\n end\n FooStruct = FooStruct2 # update alias reference\n function processFoo(foo::AbstractFooStruct)\n @info foo.bar\n end\n\n end\n \"\"\")\n234\n\njulia> FooStruct # make sure FooStruct refers to FooStruct2\nMyPkg.FooStruct2\n\njulia> processFoo(FooStruct(3.5))\n[ Info: 3.5","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"Here, note that we made two changes: we updated the \"version number\" of FooStruct when we changed something about its fields, and we also re-assigned FooStruct to alias the new version. We did not change the definition of any methods that have been typed AbstractFooStruct.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"This works as long as the new type name doesn't conflict with an existing name; within a session you need to change the name each time you change the definition.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"Once your development has converged on a solution, it's best to switch to the \"permanent\" name: in the example above, FooStruct is a non-constant global variable, and if used internally in a function there will be consequent performance penalties. Switching to the permanent name will force you to restart your session.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"julia> isconst(MyPkg, :FooStruct)\ntrue\n\njulia> write(\"src/MyPkg.jl\",\"\"\"\n module MyPkg\n\n export FooStruct, processFoo\n\n abstract type AbstractFooStruct end # this could be removed\n struct FooStruct <: AbstractFooStruct # change to just FooStruct\n bar::Float64\n end\n\n function processFoo(foo::AbstractFooStruct) # consider changing to FooStruct\n @info foo.bar\n end\n\n end\n \"\"\")\n\njulia> run(Base.julia_cmd()) # start a new Julia session, alternatively exit() and restart julia\n\n\njulia> using Pkg, Revise # NEW Julia Session\n\njulia> Pkg.activate(\".\")\n Activating project at `~/blah/MyPkg`\n\njulia> using MyPkg\nPrecompiling MyPkg\n 1 dependency successfully precompiled in 2 seconds\n\njulia> isconst(MyPkg, :FooStruct)\ntrue\n","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"In addition, some situations may require special handling:","category":"page"},{"location":"limitations/#Macros-and-generated-functions","page":"Limitations","title":"Macros and generated functions","text":"","category":"section"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"If you change a macro definition or methods that get called by @generated functions outside their quote block, these changes will not be propagated to functions that have already evaluated the macro or generated function.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"You may explicitly call revise(MyModule) to force reevaluating every definition in module MyModule. Note that when a macro changes, you have to revise all of the modules that use it.","category":"page"},{"location":"limitations/#Distributed-computing-(multiple-workers)-and-anonymous-functions","page":"Limitations","title":"Distributed computing (multiple workers) and anonymous functions","text":"","category":"section"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"Revise supports changes to code in worker processes. The code must be loaded in the main process in which Revise is running.","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"Revise cannot handle changes in anonymous functions used in remotecalls. Consider the following module definition:","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"module ParReviseExample\nusing Distributed\n\ngreet(x) = println(\"Hello, \", x)\n\nfoo() = for p in workers()\n remotecall_fetch(() -> greet(\"Bar\"), p)\nend\n\nend # module","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"Changing the remotecall to remotecall_fetch((x) -> greet(\"Bar\"), p, 1) will fail, because the new anonymous function is not defined on all workers. The workaround is to write the code to use named functions, e.g.,","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"module ParReviseExample\nusing Distributed\n\ngreet(x) = println(\"Hello, \", x)\ngreetcaller() = greet(\"Bar\")\n\nfoo() = for p in workers()\n remotecall_fetch(greetcaller, p)\nend\n\nend # module","category":"page"},{"location":"limitations/","page":"Limitations","title":"Limitations","text":"and the corresponding edit to the code would be to modify it to greetcaller(x) = greet(\"Bar\") and remotecall_fetch(greetcaller, p, 1).","category":"page"},{"location":"#Introduction-to-Revise","page":"Home","title":"Introduction to Revise","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Revise.jl may help you keep your Julia sessions running longer, reducing the need to restart when you make changes to code. With Revise, you can be in the middle of a session and then edit source code, update packages, switch git branches, and/or stash/unstash code; typically, the changes will be incorporated into the very next command you issue from the REPL. This can save you the overhead of restarting, loading packages, and waiting for code to JIT-compile.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Using Revise also improves your experience when using the debuggers. Revise will keep track of changed locations of your methods in file, and ensure that the debugger displays the source code of what you're actually debugging.","category":"page"},{"location":"","page":"Home","title":"Home","text":"note: Automatically loading Revise\nMany users automatically load Revise on startup. On versions of Julia older than 1.5, this is slightly more involved than just adding using Revise to .julia/config/startup.jl: see Using Revise by default for details.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"You can obtain Revise using Julia's Pkg REPL-mode (hitting ] as the first character of the command prompt):","category":"page"},{"location":"","page":"Home","title":"Home","text":"(v1.0) pkg> add Revise","category":"page"},{"location":"","page":"Home","title":"Home","text":"or with using Pkg; Pkg.add(\"Revise\").","category":"page"},{"location":"#Usage-example","page":"Home","title":"Usage example","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"We'll make changes to Julia's \"Example\" package (a trivial package designed to illustrate the file and directory organization of typical packages). We have to \"develop\" it in order to make changes:","category":"page"},{"location":"","page":"Home","title":"Home","text":"(v1.0) pkg> dev Example\n[...output related to installation...]\n","category":"page"},{"location":"","page":"Home","title":"Home","text":"Now we load Revise (if we haven't already done so) and Example:","category":"page"},{"location":"","page":"Home","title":"Home","text":"julia> using Revise # importantly, this must come before `using Example`\n\njulia> using Example\n\njulia> hello(\"world\")\n\"Hello, world\"","category":"page"},{"location":"","page":"Home","title":"Home","text":"Now we're going to check that the Example module currently lacks a function named f:","category":"page"},{"location":"","page":"Home","title":"Home","text":"julia> Example.f()\nERROR: UndefVarError: f not defined","category":"page"},{"location":"","page":"Home","title":"Home","text":"But say we really want f, so let's add it. You can either navigate to the source code (at .julia/dev/Example/src/Example.jl) in an editor manually, or you can use Julia to open it for you:","category":"page"},{"location":"","page":"Home","title":"Home","text":"julia> edit(hello) # opens Example.jl in the editor you have configured","category":"page"},{"location":"","page":"Home","title":"Home","text":"Now, add a function f() = π and save the file. Go back to the REPL (the same REPL, don't restart Julia) and try this:","category":"page"},{"location":"","page":"Home","title":"Home","text":"julia> Example.f()\nπ = 3.1415926535897...","category":"page"},{"location":"","page":"Home","title":"Home","text":"Voila! Even though we'd loaded Example before adding this function, Revise noticed the change and inserted it into our running session.","category":"page"},{"location":"","page":"Home","title":"Home","text":"warning: Warning\nRevise's first revision has latency of several seconds–it's compiling all of its internal code, which includes a complete Julia interpreter and all of Revise's parse/diff/patch/cache machinery. After your first revision, future revisions will generally be fast enough that they will seem nearly instantaneous. (There are exceptions, but they occur only in specific circumstances, for example when Revise's own code gets invalidated by your changes.)","category":"page"},{"location":"","page":"Home","title":"Home","text":"Now suppose we realize we've made a horrible mistake: that f method will mess up everything, because it's part of a more complicated dispatch process and incorrectly intercepts certain f calls. No problem, just delete f in your editor, save the file, and you're back to this:","category":"page"},{"location":"","page":"Home","title":"Home","text":"julia> Example.f()\nERROR: UndefVarError: f not defined","category":"page"},{"location":"","page":"Home","title":"Home","text":"all without restarting Julia. While you can evaluate new methods without Revise using inline evaluation through your IDE, method deletion is just one example of a change that can only be made easily by Revise.","category":"page"},{"location":"","page":"Home","title":"Home","text":"If you need more examples, see Revise usage: a cookbook.","category":"page"},{"location":"#Other-key-features-of-Revise","page":"Home","title":"Other key features of Revise","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Revise updates its internal paths when you change versions of a package. To try this yourself, first re-insert that definition of f in the dev version of Example and save the file. Now try toggling back and forth between the dev and released versions of Example:","category":"page"},{"location":"","page":"Home","title":"Home","text":"(v1.0) pkg> free Example # switch to the released version of Example\n\njulia> Example.f()\nERROR: UndefVarError: f not defined\n\n(v1.0) pkg> dev Example\n\njulia> Example.f()\nπ = 3.1415926535897...","category":"page"},{"location":"","page":"Home","title":"Home","text":"Revise is not tied to any particular editor. (The EDITOR or JULIA_EDITOR environment variables can be used to specify your preference for which editor gets launched by Julia's edit function.)","category":"page"},{"location":"","page":"Home","title":"Home","text":"If you don't want to have to remember to say using Revise each time you start Julia, see Using Revise by default.","category":"page"},{"location":"#What-Revise-can-track","page":"Home","title":"What Revise can track","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Revise is fairly ambitious: if all is working, subject to a few Limitations you should be able to track changes to","category":"page"},{"location":"","page":"Home","title":"Home","text":"any package that you load with import or using\nany script you load with includet (see Configuring the revise mode for important default restrictions on includet)\nany file defining Base julia itself (with Revise.track(Base))\nany of Julia's standard libraries (with, e.g., using Unicode; Revise.track(Unicode))\nany file defining Core.Compiler (with Revise.track(Core.Compiler))","category":"page"},{"location":"","page":"Home","title":"Home","text":"The last one requires that you clone Julia and build it yourself from source.","category":"page"},{"location":"#Secrets-of-Revise-\"wizards\"","page":"Home","title":"Secrets of Revise \"wizards\"","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Revise can assist with methodologies like test-driven development. While it's often desirable to write the test first, sometimes when fixing a bug it's very difficult to write a good test until you understand the bug better. Often that means basically fixing the bug before your write the test. With Revise, you can","category":"page"},{"location":"","page":"Home","title":"Home","text":"fix the bug while simultaneously developing a high-quality test\nverify that your test passes with the fixed code\ngit stash your fix and check that your new test fails on the old code, thus verifying that your test captures the essence of the former bug (if it doesn't fail, you need a better test!)\ngit stash pop, test again, commit, and submit","category":"page"},{"location":"","page":"Home","title":"Home","text":"all without restarting your Julia session.","category":"page"},{"location":"#Other-Revise-workflows","page":"Home","title":"Other Revise workflows","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Revise can be used to perform work when files update. For example, let's say you want to regenerate a set of web pages whenever your code changes. Suppose you've placed your Julia code in a package called MyWebCode, and the pages depend on \"file.js\" and all files in the \"assets/\" directory; then","category":"page"},{"location":"","page":"Home","title":"Home","text":"entr([\"file.js\", \"assets\"], [MyWebCode]) do\n build_webpages(args...)\nend","category":"page"},{"location":"","page":"Home","title":"Home","text":"will execute build_webpages(args...) whenever you save updates to the listed files or MyWebCode.","category":"page"},{"location":"","page":"Home","title":"Home","text":"If you want to regenerate the web page as soon as any change is detected, not only in MyWebCode but also in any package tracked by Revise, you can provide the all keyword argument to entr:","category":"page"},{"location":"","page":"Home","title":"Home","text":"entr([\"file.js\", \"assets\"]; all=true) do\n build_webpages(args...)\nend","category":"page"},{"location":"#Taking-advantage-of-Revise-in-other-packages","page":"Home","title":"Taking advantage of Revise in other packages","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"To make it easier for other packages to benefit from Revise without needing to add it as a dependency or understand Revise's internals, Revise interfaces with CodeTracking, which is a small package acting as Revise's \"query\" interface.","category":"page"},{"location":"#What-else-do-I-need-to-know?","page":"Home","title":"What else do I need to know?","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Except in cases of problems (see below), that's it! Revise is a tool that runs in the background, and when all is well it should be essentially invisible, except that you don't have to restart Julia so often.","category":"page"},{"location":"","page":"Home","title":"Home","text":"Revise can also be used as a \"library\" by developers who want to add other new capabilities to Julia; the sections How Revise works and Developer reference are particularly relevant for them.","category":"page"},{"location":"#If-Revise-doesn't-work-as-expected","page":"Home","title":"If Revise doesn't work as expected","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"If Revise isn't working for you, here are some steps to try:","category":"page"},{"location":"","page":"Home","title":"Home","text":"See Configuration for information on customization options. In particular, some file systems (like NFS) and current users of WSL2 might require special options.\nRevise can't handle all kinds of code changes; for more information, see the section on Limitations.\nTry running test Revise from the Pkg REPL-mode. If tests pass, check the documentation to make sure you understand how Revise should work. If they fail (especially if it mirrors functionality that you need and isn't working), see Debugging problems with paths for one set of suggestions.","category":"page"},{"location":"","page":"Home","title":"Home","text":"If you still encounter problems, please file an issue. Especially if you think Revise is making mistakes in adding or deleting methods, please see the page on Debugging Revise for information about how to attach logs to your bug report.","category":"page"},{"location":"internals/#How-Revise-works","page":"How Revise works","title":"How Revise works","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"In addition to the material below, see these talks:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"JuliaCon 2018\nJuliaCon 2019","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Revise is based on the fact that you can change functions even when they are defined in other modules. Here's an example showing how you do that manually (without using Revise):","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"julia> convert(Float64, π)\n3.141592653589793\n\njulia> # That's too hard, let's make life easier for students\n\njulia> @eval Base convert(::Type{Float64}, x::Irrational{:π}) = 3.0\nconvert (generic function with 714 methods)\n\njulia> convert(Float64, π)\n3.0","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Revise removes some of the tedium of manually copying and pasting code into @eval statements. To decrease the amount of re-JITting required, Revise avoids reloading entire modules; instead, it takes care to eval only the changes in your package(s), much as you would if you were doing it manually. Importantly, changes are detected in a manner that is independent of the specific line numbers in your code, so that you don't have to re-evaluate just because code moves around within the same file. (One unfortunate side effect is that line numbers may become inaccurate in backtraces, but Revise takes pains to correct these, see below.)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Conceptually, Revise implements diff and patch for a running Julia session. Schematically, Revise's inner loop (revise()) looks like this:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"for def in setdiff(oldexprs, newexprs)\n # `def` is an expression that defines a method.\n # It was in `oldexprs`, but is no longer present in `newexprs`--delete the method.\n delete_methods_corresponding_to_defexpr(mod, def)\nend\nfor def in setdiff(newexprs, oldexprs)\n # `def` is an expression for a new or modified method. Instantiate it.\n Core.eval(mod, def)\nend","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"In somewhat greater detail, Revise uses the following overall strategy:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"add callbacks to Base so that Revise gets notified when new packages are loaded or new files included\nprepare source-code caches for every new file. These caches will allow Revise to detect changes when files are updated. For precompiled packages this happens on an as-needed basis, using the cached source in the *.ji file. For non-precompiled packages, Revise parses the source for each included file immediately so that the initial state is known and changes can be detected.\nmonitor the file system for changes to any of the dependent files; it immediately appends any updates to a list of file names that need future processing\nintercept the REPL's backend to ensure that the list of files-to-be-revised gets processed each time you execute a new command at the REPL\nwhen a revision is triggered, the source file(s) are re-parsed, and a diff between the cached version and the new version is created. eval the diff in the appropriate module(s).\nreplace the cached version of each source file with the new version, so that further changes are diffed against the most recent update.","category":"page"},{"location":"internals/#The-structure-of-Revise's-internal-representation","page":"How Revise works","title":"The structure of Revise's internal representation","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"(Image: diagram)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Figure notes: Nodes represent primary objects in Julia's compilation pipeline. Arrows and their labels represent functions or data structures that allow you to move from one node to another. Red (\"destructive\") paths force recompilation of dependent functions.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Revise bridges between text files (your source code) and compiled code. Revise consequently maintains data structures that parallel Julia's own internal processing of code. When dealing with a source-code file, you start with strings, parse them to obtain Julia expressions, evaluate them to obtain Julia objects, and (where appropriate, e.g., for methods) compile them to machine code. This will be called the forward workflow. Revise sets up a few key structures that allow it to progress from files to modules to Julia expressions and types.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Revise also sets up a backward workflow, proceeding from compiled code to Julia types back to Julia expressions. This workflow is useful, for example, when dealing with errors: the stack traces displayed by Julia link from the compiled code back to the source files. To make this possible, Julia builds \"breadcrumbs\" into compiled code that store the filename and line number at which each expression was found. However, these links are static, meaning they are set up once (when the code is compiled) and are not updated when the source file changes. Because trivial manipulations to source files (e.g., the insertion of blank lines and/or comments) can change the line number of an expression without necessitating its recompilation, Revise implements a way of correcting these line numbers before they are displayed to the user. The same problem presents when using a debugger, in that one wants the debugger to display the correct code (at the correct line number) even after modifications have been made to the file. This capability requires that Revise proceed backward from the compiled objects to something resembling the original text file.","category":"page"},{"location":"internals/#Terminology","page":"How Revise works","title":"Terminology","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"A few convenience terms are used throughout: definition, signature-expression, and signature-type. These terms are illustrated using the following example:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"

function print_item(io::IO, item, ntimes::Integer=1, pre::String=\"\")\n    print(io, pre)\n    for i = 1:ntimes\n        print(io, item)\n    end\nend

","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"This represents the definition of a method. Definitions are stored as expressions, using a Revise.RelocatableExpr. The highlighted portion is the signature-expression, specifying the name, argument names and their types, and (if applicable) type-parameters of the method.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"From the signature-expression we can generate one or more signature-types. Since this function has two default arguments, this signature-expression generates three signature-types, each corresponding to a different valid way of calling this method:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Tuple{typeof(print_item),IO,Any} # print_item(io, item)\nTuple{typeof(print_item),IO,Any,Integer} # print_item(io, item, 2)\nTuple{typeof(print_item),IO,Any,Integer,String} # print_item(io, item, 2, \" \")","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"In Revise's internal code, a definition is often represented with a variable def, and a signature-type with sigt. Recent versions of Revise do not make extensive use of signature expressions.","category":"page"},{"location":"internals/#Computing-signatures","page":"How Revise works","title":"Computing signatures","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Since version 2.0, Revise works primarily with lowered-code representations, specifically using the lowered code to compute method signatures (if you don't know about lowered code, see this tutorial). There are several reasons that make this an attractive approach, of which the most important are:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"keyword-argument methods get \"expanded\" to multiple methods handling various ways of populating the arguments. The lowered code lists all of them, which ensures that Revise knows about them all. (There are some challenges regarding \"gensymmed\" names, see LoweredCodeUtils and julia#30908, but in short LoweredCodeUtils \"fixes\" those difficulties.)\nfor methods generated by code, the only really reliable mechanism to compute all the signatures is to step through the code that generates the methods. That is performed using JuliaInterpreter.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"As an example, suppose the following code is part of your module definition:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"for T in (Float16, Float32, Float64)\n @eval sizefloat(x::$T) = sizeof($T)\nend","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"clarification: Clarification\nThis is equivalent to the following explicit definitions:sizefloat(x::Float16) = 2\nsizefloat(x::Float32) = 4\nsizefloat(x::Float64) = 8","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"If you replace the loop with for T in (Float32, Float64), then Revise should delete the method for Float16. But this implies that Revise can deduce all the method-signatures created by this block, which essentially requires \"simulating\" the block that defines the methods. (In simple cases there are other approaches, but for complex cases stepping through the code seems to be the only viable answer.)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Because lowered code is far simpler than ordinary Julia code, it is much easier to interpret. Let's look briefly at a method definition:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"floatwins(x::AbstractFloat, y::Integer) = x","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"which has lowered representation approximately equal to","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"CodeInfo(\n│ $(Expr(:method, :floatwins))\n│ %2 = Core.Typeof(floatwins)\n│ %3 = Core.svec(%2, AbstractFloat, Integer)\n│ %4 = Core.svec()\n│ %5 = Core.svec(%3, %4)\n│ $(Expr(:method, :floatwins, :(%5), CodeInfo(quote\n return x\nend)))\n└── return floatwins\n)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"(I've edited this lightly for clarity.) As one steps through this, the first line tells us we're about to define a method for the function floatwins. Lines 2-5 compute the signature, in the representation svec(sig, params), where here sig = svec(typeof(floatwins), AbstractFloat, Integer) and params = svec(). (This example has no type parameters, which is why params is empty.)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"What Revise does is steps through the first 5 of these lines, and when it encounters the Expr(:method, :floatwins, :(%5), CodeInfo(...)) statement, it pulls out the signature (the %5, which refers to the result computed on the 5th line) and records this as a method generated by this block of code. (It does not, however, evaluate the Expr(:method, ...) expression as a whole, because that would force it to be recompiled.) Stepping through this code ensures that Revise can compute the exact signature, no matter how this method is defined at the level of ordinary Julia code.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Unfortunately, modules sometimes contain code blocks that perhaps shouldn't be interpreted:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"init_c_library() # library crashes if we call this twice","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Starting with version 2.3, Revise attempts to avoid interpreting any code not necessary for signature computation. If you are just tracking changes, Revise will skip over such blocks; if you're loading a file with includet for the first time, Revise will execute such blocks in compiled mode.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Revise achieves this by computing backedges, essentially a set of links encoding the dependencies among different lines of the lowered code. For the floatwins example above, the backedges would represent the fact that line 2 has one direct dependant, line 3 (which uses %2), that lines 3 and 4 both have line 5 as their dependents, and line 5 has line 6 as a dependent. As a consequence, to (nearly) execute line 6, we have to execute lines 2-5, because they set up the signature. If an interdependent block doesn't contain any :method or related (:struct_type, :eval) expressions, then it doesn't need to interpret the block at all.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"As should be evident, the lowered code makes it much easier to analyze the graph of these dependencies. There are, however, a few tricky cases. For example, any code inside an @eval might, or might not, expand into lowered code that contains a :method expression. Because Revise can't reliably predict what it will look like after expansion, Revise will execute any code in (or needed for) an @eval block. As a consequence, even after version 2.3 Revise may sometimes interpret more code than is strictly necessary.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"note: Note\nIf Revise executes code that still shouldn't be run twice, one good solution is to put all initialization inside your module's __init__ function. For files that you track with includet, you can also split \"code that defines methods\" into a separate file from \"code that does work,\" and have Revise track only the method-defining file. However, starting with version 2.3 Revise should be fairly good at doing this on its own; such manual interventions should not be necessary in most cases.","category":"page"},{"location":"internals/#Core-data-structures-and-representations","page":"How Revise works","title":"Core data structures and representations","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Most of Revise's magic comes down to just three internal variables:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Revise.watched_files: encodes information used by the filesystem (FileWatching) to detect changes in source files.\nRevise.revision_queue: a list of \"work to do,\" containing the files that have been modified since the last code update.\nRevise.pkgdatas: the central repository of parsed code, used to \"diff\" for changes and then \"patch\" the running session.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Two \"maps\" are central to Revise's inner workings: ExprsSigs maps link definition=>signature-types (the forward workflow), while CodeTracking (specifically, its internal variable method_info) links from signature-type=>definition (the backward workflow). Concretely, CodeTracking.method_info is just an IdDict mapping sigt=>(locationinfo, def). Of note, a stack frame typically contains a link to a method, which stores the equivalent of sigt; consequently, this information allows one to look up the corresponding locationinfo and def. (When methods move, the location information stored by CodeTracking gets updated by Revise.)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Some additional notes about Revise's ExprsSigs maps:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"For expressions that do not define a method, it is just def=>nothing\nFor expressions that do define a method, it is def=>[sigt1, ...]. [sigt1, ...] is the list of signature-types generated from def (often just one, but more in the case of methods with default arguments or keyword arguments).\nThey are represented as an OrderedDict so as to preserve the sequence in which expressions occur in the file. This can be important particularly for updating macro definitions, which affect the expansion of later code. The order is maintained so as to match the current ordering of the source-file, which is not necessarily the same as the ordering when these expressions were last evaled.\nEach key in the map (the definition RelocatableExpr) is the most recently evaled version of the expression. This has an important consequence: the line numbers in the def (which are still present, even though not used for equality comparisons) correspond to the ones in compiled code. Any discrepancy with the current line numbers in the file is handled through updates to the location information stored by CodeTracking.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"ExprsSigs are organized by module and then file, so that one can map filename=>module=>def=>sigts. Importantly, single-file modules can be \"reconstructed\" from the keys of the corresponding ExprsSigs (and multi-file modules from a collection of such items), since they hold the complete ordered set of expressions that would be evaled to define the module.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"The global variable that holds all this information is Revise.pkgdatas, organized into a dictionary of Revise.PkgData objects indexed by Base Julia's PkgId (a unique identifier for packages).","category":"page"},{"location":"internals/#An-example","page":"How Revise works","title":"An example","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Consider a module, Items, defined by the following two source files:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Items.jl:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"__precompile__(false)\n\nmodule Items\n\ninclude(\"indents.jl\")\n\nfunction print_item(io::IO, item, ntimes::Integer=1, pre::String=indent(item))\n print(io, pre)\n for i = 1:ntimes\n print(io, item)\n end\nend\n\nend","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"indents.jl:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"indent(::UInt16) = 2\nindent(::UInt8) = 4","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"If you create this as a mini-package and then say using Revise, Items, you can start examining internal variables in the following manner:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"julia> id = Base.PkgId(Items)\nItems [b24a5932-55ed-11e9-2a88-e52f99e65a0d]\n\njulia> pkgdata = Revise.pkgdatas[id]\nPkgData(Items [b24a5932-55ed-11e9-2a88-e52f99e65a0d]:\n \"src/Items.jl\": FileInfo(Main=>ExprsSigs(<1 expressions>, <0 signatures>), Items=>ExprsSigs(<2 expressions>, <3 signatures>), )\n \"src/indents.jl\": FileInfo(Items=>ExprsSigs(<2 expressions>, <2 signatures>), )","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"(Your specific UUID may differ.)","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Path information is stored in pkgdata.info:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"julia> pkgdata.info\nPkgFiles(Items [b24a5932-55ed-11e9-2a88-e52f99e65a0d]):\n basedir: \"/tmp/pkgs/Items\"\n files: [\"src/Items.jl\", \"src/indents.jl\"]","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"basedir is the only part using absolute paths; everything else is encoded relative to that location. This facilitates, e.g., switching between develop and add mode in the package manager.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"src/indents.jl is particularly simple:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"julia> pkgdata.fileinfos[2]\nFileInfo(Items=>ExprsSigs with the following expressions:\n :(indent(::UInt16) = begin\n 2\n end)\n :(indent(::UInt8) = begin\n 4\n end), )","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"This is just a summary; to see the actual def=>sigts map, do the following:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"julia> pkgdata.fileinfos[2].modexsigs[Items]\nOrderedCollections.OrderedDict{Revise.RelocatableExpr,Union{Nothing, Array{Any,1}}} with 2 entries:\n :(indent(::UInt16) = begin… => Any[Tuple{typeof(indent),UInt16}]\n :(indent(::UInt8) = begin… => Any[Tuple{typeof(indent),UInt8}]","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"These are populated now because we specified __precompile__(false), which forces Revise to defensively parse all expressions in the package in case revisions are made at some future point. For precompiled packages, each pkgdata.fileinfos[i] can instead rely on the cachefile (another field stored in the Revise.FileInfo) as a record of the state of the file at the time the package was loaded; as a consequence, Revise can defer parsing the source file(s) until they are updated.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Items.jl is represented with a bit more complexity, \"Items.jl\"=>Dict(Main=>map1, Items=>map2). This is because Items.jl contains one expression (the __precompile__ statement) that is evaled in Main, and other expressions that are evaled in Items.","category":"page"},{"location":"internals/#Revisions-and-computing-diffs","page":"How Revise works","title":"Revisions and computing diffs","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"When the file system notifies Revise that a file has been modified, Revise re-parses the file and assigns the expressions to the appropriate modules, creating a Revise.ModuleExprsSigs mexsnew. It then compares mexsnew against mexsref, the reference object that is synchronized to code as it was evaled. The following actions are taken:","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"if a def entry in mexsref is equal to one in mexsnew, the expression is \"unchanged\" except possibly for line number. The locationinfo in CodeTracking is updated as needed.\nif a def entry in mexsref is not present in mexsnew, that entry is deleted and any corresponding methods are also deleted.\nif a def entry in mexsnew is not present in mexsref, it is evaled and then added to mexsref.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Technically, a new mexsref is generated every time to ensure that the expressions are ordered as in mexsnew; however, conceptually this is better thought of as an updating of mexsref, after which mexsnew is discarded.","category":"page"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"Note that one consequence is that modifying a method causes two actions, the deletion of the original followed by evaling a new version. During revision, all method deletions are performed first, followed by all the new evaled methods. This ensures that if a method gets moved from fileB.jl to fileA.jl, Revise doesn't mistakenly redefine and then delete the method simply because fileA.jl got processed before fileB.jl.","category":"page"},{"location":"internals/#Internal-API","page":"How Revise works","title":"Internal API","text":"","category":"section"},{"location":"internals/","page":"How Revise works","title":"How Revise works","text":"You can find more detail about Revise's inner workings in the Developer reference.","category":"page"},{"location":"config/#Configuration","page":"Configuration","title":"Configuration","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"compat: Compat\nThese instructions are applicable only for Julia 1.5 and higher. If you are running an older version of Julia, upgrading to at least 1.6 is recommended. If you cannot upgrade, see the documentation for Revise 3.2.x or earlier.","category":"page"},{"location":"config/#Using-Revise-by-default","page":"Configuration","title":"Using Revise by default","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"If you like Revise, you can ensure that every Julia session uses it by launching it from your ~/.julia/config/startup.jl file. Note that using Revise adds a small latency at Julia startup, generally about 0.7s when you first launch Julia and another 0.25s for your first package load. Users should weigh this penalty against whatever benefit they may derive from not having to restart their entire session.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"This can be as simple as adding","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"using Revise","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"as the first line in your startup.jl. If you have a Unix terminal available, simply run","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"mkdir -p ~/.julia/config/ && echo \"using Revise\" >> ~/.julia/config/startup.jl","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"If you use different package environments and do not always have Revise available,","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"try\n using Revise\ncatch e\n @warn \"Error initializing Revise\" exception=(e, catch_backtrace())\nend","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"is recommended instead.","category":"page"},{"location":"config/#Using-Revise-automatically-within-Jupyter/IJulia","page":"Configuration","title":"Using Revise automatically within Jupyter/IJulia","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"If you want Revise to launch automatically within IJulia, then you should also create a .julia/config/startup_ijulia.jl file with the contents","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"try\n @eval using Revise\ncatch e\n @warn \"Error initializing Revise\" exception=(e, catch_backtrace())\nend","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"or simply run","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"mkdir -p ~/.julia/config/ && tee -a ~/.julia/config/startup_ijulia.jl << END\ntry\n @eval using Revise\ncatch e\n @warn \"Error initializing Revise\" exception=(e, catch_backtrace())\nend\nEND","category":"page"},{"location":"config/#Configuring-the-revise-mode","page":"Configuration","title":"Configuring the revise mode","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"By default, in packages all changes are tracked, but with includet only method definitions are tracked. This behavior can be overridden by defining a variable __revise_mode__ in the module(s) containing your methods and/or data. __revise_mode__ must be a Symbol taking one of the following values:","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":":eval: evaluate everything (the default for packages)\n:evalmeth: evaluate changes to method definitions (the default for includet) This should work even for quite complicated method definitions, such as those that might be made within a for-loop and @eval block.\n:evalassign: evaluate method definitions and assignment statements. A top-level expression a = Int[] would be evaluated, but push!(a, 1) would not because the latter is not an assignment.\n:sigs: do not implement any changes, only scan method definitions for their signatures so that their location can be updated as changes to the file(s) are made.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"If you're using includet from the REPL, you can enter __revise_mode__ = :eval to set it throughout Main. __revise_mode__ can be set independently in each module.","category":"page"},{"location":"config/#Optional-global-configuration","page":"Configuration","title":"Optional global configuration","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"Revise can be configured by setting environment variables. These variables have to be set before you execute using Revise, because these environment variables are parsed only during execution of Revise's __init__ function.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"There are several ways to set these environment variables:","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"If you are Using Revise by default then you can include statements like ENV[\"JULIA_REVISE\"] = \"manual\" in your .julia/config/startup.jl file prior to the line containing using Revise.\nOn Unix systems, you can set variables in your shell initialization script (e.g., put lines like export JULIA_REVISE=manual in your .bashrc file if you use bash).\nOn Unix systems, you can launch Julia from the Unix prompt as $ JULIA_REVISE=manual julia to set options for just that session.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"The function of specific environment variables is described below.","category":"page"},{"location":"config/#Manual-revision:-JULIA_REVISE","page":"Configuration","title":"Manual revision: JULIA_REVISE","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"By default, Revise processes any modified source files every time you enter a command at the REPL. However, there might be times where you'd prefer to exert manual control over the timing of revisions. Revise looks for an environment variable JULIA_REVISE, and if it is set to anything other than \"auto\" it will require that you manually call revise() to update code.","category":"page"},{"location":"config/#User-scripts:-JULIA_REVISE_INCLUDE","page":"Configuration","title":"User scripts: JULIA_REVISE_INCLUDE","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"By default, Revise only tracks files that have been required as a consequence of a using or import statement; files loaded by include are not tracked, unless you explicitly use includet or Revise.track(filename). However, you can turn on automatic tracking by setting the environment variable JULIA_REVISE_INCLUDE to the string \"1\" (e.g., JULIA_REVISE_INCLUDE=1 in a bash script).","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"note: Note\nMost users should avoid setting JULIA_REVISE_INCLUDE. Try includet instead.","category":"page"},{"location":"config/#Configurations-for-fixing-errors","page":"Configuration","title":"Configurations for fixing errors","text":"","category":"section"},{"location":"config/#No-space-left-on-device","page":"Configuration","title":"No space left on device","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"note: Note\nThis applies only to Linux","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"Revise needs to be notified by your filesystem about changes to your code, which means that the files that define your modules need to be watched for updates. Some systems impose limits on the number of files and directories that can be watched simultaneously; if such a limit is hit, on Linux this can result in Revise silently ceasing to work (albeit with unit tests failing) or in a fairly cryptic error like","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"ERROR: start_watching (File Monitor): no space left on device (ENOSPC)","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"The cure is to investigate and possibly increase the number of files that can be watched.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"Invoking","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"$ sysctl fs.inotify","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"at the linux prompt may e.g. result in","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"fs.inotify.max_queued_events = 16384\nfs.inotify.max_user_instances = 128\nfs.inotify.max_user_watches = 524288","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"For Revise usage, max_user_watches >= 65536 is recommended, and more can be helpful; the value of 524288 above is common on modern systems. One can set higher values as needed, e.g.,","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"$ sudo sysctl fs.inotify.max_user_instances=2048","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"After changing these values, it is advised to run Revise's unit tests to see if they pass.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"This change can be made permanent.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"For more information see issues #26 and #778.","category":"page"},{"location":"config/#Polling-and-NFS-mounted-code-directories:-JULIA_REVISE_POLL","page":"Configuration","title":"Polling and NFS-mounted code directories: JULIA_REVISE_POLL","text":"","category":"section"},{"location":"config/","page":"Configuration","title":"Configuration","text":"note: Note\nThis applies only to Unix systems with code on network-mounted drives","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"Revise works by monitoring your filesystem for changes to the files that define your code. On most operating systems, Revise can work \"passively\" and wait to be signaled that one or more watched directories has changed.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"Unfortunately, a few file systems (notably, the Unix-based Network File System NFS) don't support this approach. In such cases, Revise needs to \"actively\" check each file periodically to see whether it has changed since the last check. This active process is called polling. You turn on polling by setting the environment variable JULIA_REVISE_POLL to the string \"1\" (e.g., JULIA_REVISE_POLL=1 in a bash script).","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"warning: Warning\nIf you're using polling, you may have to wait several seconds before changes take effect. Polling is not recommended unless you have no other alternative.","category":"page"},{"location":"config/","page":"Configuration","title":"Configuration","text":"note: Note\nNFS stands for Network File System and is typically only used to mount shared network drives on Unix file systems. Despite similarities in the acronym, NTFS, the standard filesystem on Windows, is completely different from NFS; Revise's default configuration should work fine on Windows without polling. However, WSL2 users currently need polling due to this bug.","category":"page"}] } diff --git a/previews/PR846/user_reference/index.html b/previews/PR846/user_reference/index.html index cb67de4f..21bc05e1 100644 --- a/previews/PR846/user_reference/index.html +++ b/previews/PR846/user_reference/index.html @@ -1,13 +1,13 @@ -User reference · Revise.jl

User reference

There are really only six functions that most users would be expected to call manually: revise, includet, Revise.track, entr, Revise.retry, and Revise.errors. Other user-level constructs might apply if you want to debug Revise or prevent it from watching specific packages, or for fine-grained handling of callbacks.

Revise.reviseFunction
revise(; throw=false)

eval any changes in the revision queue. See Revise.revision_queue. If throw is true, throw any errors that occur during revision or callback; otherwise these are only logged.

source
revise(mod::Module)

Reevaluate every definition in mod, whether it was changed or not. This is useful to propagate an updated macro definition, or to force recompiling generated functions.

source
Revise.trackFunction
Revise.track(Base)
+User reference · Revise.jl

User reference

There are really only six functions that most users would be expected to call manually: revise, includet, Revise.track, entr, Revise.retry, and Revise.errors. Other user-level constructs might apply if you want to debug Revise or prevent it from watching specific packages, or for fine-grained handling of callbacks.

Revise.reviseFunction
revise(; throw=false)

eval any changes in the revision queue. See Revise.revision_queue. If throw is true, throw any errors that occur during revision or callback; otherwise these are only logged.

source
revise(mod::Module; force::Bool=true)

Revise all files that define mod.

If force=true, reevaluate every definition in mod, whether it was changed or not. This is useful to propagate an updated macro definition, or to force recompiling generated functions. Be warned, however, that this invalidates all the compiled code in your session that depends on mod, and can lead to long recompilation times.

source
Revise.trackFunction
Revise.track(Base)
 Revise.track(Core.Compiler)
-Revise.track(stdlib)

Track updates to the code in Julia's base directory, base/compiler, or one of its standard libraries.

source
Revise.track(mod::Module, file::AbstractString)
-Revise.track(file::AbstractString)

Watch file for updates and revise loaded code with any changes. mod is the module into which file is evaluated; if omitted, it defaults to Main.

If this produces many errors, check that you specified mod correctly.

source
Revise.includetFunction
includet(filename)

Load filename and track future changes. includet is intended for quick "user scripts"; larger or more established projects are encouraged to put the code in one or more packages loaded with using or import instead of using includet. See https://timholy.github.io/Revise.jl/stable/cookbook/ for tips about setting up the package workflow.

By default, includet only tracks modifications to methods, not data. See the extended help for details. Note that this differs from packages, which evaluate all changes by default. This default behavior can be overridden; see Configuring the revise mode.

Extended help

Behavior and justification for the default revision mode (:evalmeth)

includet uses a default __revise_mode__ = :evalmeth. The consequence is that if you change

a = [1]
+Revise.track(stdlib)

Track updates to the code in Julia's base directory, base/compiler, or one of its standard libraries.

source
Revise.track(mod::Module, file::AbstractString)
+Revise.track(file::AbstractString)

Watch file for updates and revise loaded code with any changes. mod is the module into which file is evaluated; if omitted, it defaults to Main.

If this produces many errors, check that you specified mod correctly.

source
Revise.includetFunction
includet(filename)

Load filename and track future changes. includet is intended for quick "user scripts"; larger or more established projects are encouraged to put the code in one or more packages loaded with using or import instead of using includet. See https://timholy.github.io/Revise.jl/stable/cookbook/ for tips about setting up the package workflow.

By default, includet only tracks modifications to methods, not data. See the extended help for details. Note that this differs from packages, which evaluate all changes by default. This default behavior can be overridden; see Configuring the revise mode.

Extended help

Behavior and justification for the default revision mode (:evalmeth)

includet uses a default __revise_mode__ = :evalmeth. The consequence is that if you change

a = [1]
 f() = 1

to

a = [2]
-f() = 2

then Revise will update f but not a.

This is the default choice for includet because such files typically mix method definitions and data-handling. Data often has many untracked dependencies; later in the same file you might push!(a, 22), but Revise cannot determine whether you wish it to re-run that line after redefining a. Consequently, the safest default choice is to leave the user in charge of data.

Workflow tips

If you have a series of computations that you want to run when you redefine your methods, consider separating your method definitions from your computations:

  • method definitions go in a package, or a file that you includet once
  • the computations go in a separate file, that you re-include (no "t" at the end) each time you want to rerun your computations.

This can be automated using entr.

Internals

includet is essentially shorthand for

Revise.track(Main, filename; mode=:includet, skip_include=true)

Do not use includet for packages, as those should be handled by using or import. If using and import aren't working, you may have packages in a non-standard location; try fixing it with something like push!(LOAD_PATH, "/path/to/my/private/repos"). (If you're working with code in Base or one of Julia's standard libraries, use Revise.track(mod) instead, where mod is the module.)

includet is deliberately non-recursive, so if filename loads any other files, they will not be automatically tracked. (See Revise.track to set it up manually.)

source
Revise.entrFunction
entr(f, files; all=false, postpone=false, pause=0.02)
+f() = 2

then Revise will update f but not a.

This is the default choice for includet because such files typically mix method definitions and data-handling. Data often has many untracked dependencies; later in the same file you might push!(a, 22), but Revise cannot determine whether you wish it to re-run that line after redefining a. Consequently, the safest default choice is to leave the user in charge of data.

Workflow tips

If you have a series of computations that you want to run when you redefine your methods, consider separating your method definitions from your computations:

  • method definitions go in a package, or a file that you includet once
  • the computations go in a separate file, that you re-include (no "t" at the end) each time you want to rerun your computations.

This can be automated using entr.

Internals

includet is essentially shorthand for

Revise.track(Main, filename; mode=:includet, skip_include=true)

Do not use includet for packages, as those should be handled by using or import. If using and import aren't working, you may have packages in a non-standard location; try fixing it with something like push!(LOAD_PATH, "/path/to/my/private/repos"). (If you're working with code in Base or one of Julia's standard libraries, use Revise.track(mod) instead, where mod is the module.)

includet is deliberately non-recursive, so if filename loads any other files, they will not be automatically tracked. (See Revise.track to set it up manually.)

source
Revise.entrFunction
entr(f, files; all=false, postpone=false, pause=0.02)
 entr(f, files, modules; all=false, postpone=false, pause=0.02)

Execute f() whenever files or directories listed in files, or code in modules, updates. If all is true, also execute f() as soon as code updates are detected in any module tracked by Revise.

entr will process updates (and block your command line) until you press Ctrl-C. Unless postpone is true, f() will be executed also when calling entr, regardless of file changes. The pause is the period (in seconds) that entr will wait between being triggered and actually calling f(), to handle clusters of modifications, such as those produced by saving files in certain text editors.

Example

entr(["/tmp/watched.txt"], [Pkg1, Pkg2]) do
     println("update")
-end

This will print "update" every time "/tmp/watched.txt" or any of the code defining Pkg1 or Pkg2 gets updated.

source
Revise.retryFunction
Revise.retry()

Attempt to perform previously-failed revisions. This can be useful in cases of order-dependent errors.

source
Revise.errorsFunction
Revise.errors()

Report the errors represented in Revise.queue_errors. Errors are automatically reported the first time they are encountered, but this function can be used to report errors again.

source

Revise logs (debugging Revise)

Revise.debug_loggerFunction
logger = Revise.debug_logger(; min_level=Debug)

Turn on debug logging (if min_level is set to Debug or better) and return the logger object. logger.logs contains a list of the logged events. The items in this list are of type Revise.LogRecord, with the following relevant fields:

  • group: the event category. Revise currently uses the following groups:
    • "Action": a change was implemented, of type described in the message field.
    • "Parsing": a "significant" event in parsing. For these, examine the message field for more information.
    • "Watching": an indication that Revise determined that a particular file needed to be examined for possible code changes. This is typically done on the basis of mtime, the modification time of the file, and does not necessarily indicate that there were any changes.
  • message: a string containing more information. Some examples:
    • For entries in the "Action" group, message can be "Eval" when modifying old methods or defining new ones, "DeleteMethod" when deleting a method, and "LineOffset" to indicate that the line offset for a method was updated (the last only affects the printing of stacktraces upon error, it does not change how code runs)
    • Items with group "Parsing" and message "Diff" contain sets :newexprs and :oldexprs that contain the expression unique to post- or pre-revision, respectively.
  • kwargs: a pairs list of any other data. This is usually specific to particular group/message combinations.

See also Revise.actions and Revise.diffs.

source
Revise.actionsFunction
actions(logger; line=false)

Return a vector of all log events in the "Action" group. "LineOffset" events are returned only if line=true; by default the returned items are the events that modified methods in your session.

source
Revise.diffsFunction
diffs(logger)

Return a vector of all log events that encode a (non-empty) diff between two versions of a file.

source

Prevent Revise from watching specific packages

Revise.dont_watch_pkgsConstant
Revise.dont_watch_pkgs

Global variable, use push!(Revise.dont_watch_pkgs, :MyPackage) to prevent Revise from tracking changes to MyPackage. You can do this from the REPL or from your .julia/config/startup.jl file.

See also Revise.silence.

source
Revise.silenceFunction
Revise.silence(pkg)

Silence warnings about not tracking changes to package pkg.

source

Revise module

ReviseModule

Revise.jl tracks source code changes and incorporates the changes to a running Julia session.

Revise.jl works behind-the-scenes. To track a package, e.g. Example:

(@v1.6) pkg> dev Example        # make a development copy of the package
+end

This will print "update" every time "/tmp/watched.txt" or any of the code defining Pkg1 or Pkg2 gets updated.

source
Revise.retryFunction
Revise.retry()

Attempt to perform previously-failed revisions. This can be useful in cases of order-dependent errors.

source
Revise.errorsFunction
Revise.errors()

Report the errors represented in Revise.queue_errors. Errors are automatically reported the first time they are encountered, but this function can be used to report errors again.

source

Revise logs (debugging Revise)

Revise.debug_loggerFunction
logger = Revise.debug_logger(; min_level=Debug)

Turn on debug logging (if min_level is set to Debug or better) and return the logger object. logger.logs contains a list of the logged events. The items in this list are of type Revise.LogRecord, with the following relevant fields:

  • group: the event category. Revise currently uses the following groups:
    • "Action": a change was implemented, of type described in the message field.
    • "Parsing": a "significant" event in parsing. For these, examine the message field for more information.
    • "Watching": an indication that Revise determined that a particular file needed to be examined for possible code changes. This is typically done on the basis of mtime, the modification time of the file, and does not necessarily indicate that there were any changes.
  • message: a string containing more information. Some examples:
    • For entries in the "Action" group, message can be "Eval" when modifying old methods or defining new ones, "DeleteMethod" when deleting a method, and "LineOffset" to indicate that the line offset for a method was updated (the last only affects the printing of stacktraces upon error, it does not change how code runs)
    • Items with group "Parsing" and message "Diff" contain sets :newexprs and :oldexprs that contain the expression unique to post- or pre-revision, respectively.
  • kwargs: a pairs list of any other data. This is usually specific to particular group/message combinations.

See also Revise.actions and Revise.diffs.

source
Revise.actionsFunction
actions(logger; line=false)

Return a vector of all log events in the "Action" group. "LineOffset" events are returned only if line=true; by default the returned items are the events that modified methods in your session.

source
Revise.diffsFunction
diffs(logger)

Return a vector of all log events that encode a (non-empty) diff between two versions of a file.

source

Prevent Revise from watching specific packages

Revise.dont_watch_pkgsConstant
Revise.dont_watch_pkgs

Global variable, use push!(Revise.dont_watch_pkgs, :MyPackage) to prevent Revise from tracking changes to MyPackage. You can do this from the REPL or from your .julia/config/startup.jl file.

See also Revise.silence.

source
Revise.silenceFunction
Revise.silence(pkg)

Silence warnings about not tracking changes to package pkg.

source

Revise module

ReviseModule

Revise.jl tracks source code changes and incorporates the changes to a running Julia session.

Revise.jl works behind-the-scenes. To track a package, e.g. Example:

(@v1.6) pkg> dev Example        # make a development copy of the package
 [...pkg output omitted...]
 
 julia> using Revise             # this must come before the package under development
@@ -15,4 +15,4 @@
 julia> using Example
 
 [...develop the package...]     # Revise.jl will automatically update package functionality to match code changes
-

Functions in Revise.jl that may come handy in special circumstances:

  • Revise.track: track updates to Base Julia itself or Core.Compiler
  • includet: load a file and track future changes. Intended for small, quick works
  • entr: call an additional function whenever code updates
  • revise: evaluate any changes in Revise.revision_queue or every definition in a module
  • Revise.retry: perform previously-failed revisions. Useful in cases of order-dependent errors
  • Revise.errors: report the errors represented in Revise.queue_errors
source
+

Functions in Revise.jl that may come handy in special circumstances:

  • Revise.track: track updates to Base Julia itself or Core.Compiler
  • includet: load a file and track future changes. Intended for small, quick works
  • entr: call an additional function whenever code updates
  • revise: evaluate any changes in Revise.revision_queue or every definition in a module
  • Revise.retry: perform previously-failed revisions. Useful in cases of order-dependent errors
  • Revise.errors: report the errors represented in Revise.queue_errors
source