From c2175821cfd36a154dddf4615c868b704322d847 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Thu, 5 Dec 2024 21:17:17 +0000 Subject: [PATCH] build based on d21c32f --- dev/.documenter-siteinfo.json | 2 +- dev/config/index.html | 2 +- dev/cookbook/index.html | 2 +- dev/debugging/index.html | 2 +- dev/dev_reference/index.html | 10 +++++----- dev/index.html | 2 +- dev/internals/index.html | 2 +- dev/limitations/index.html | 2 +- dev/tricks/index.html | 2 +- dev/user_reference/index.html | 12 ++++++------ 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 45756967..68d7304c 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-11-19T07:04:47","documenter_version":"1.8.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-12-05T21:17:10","documenter_version":"1.8.0"}} \ No newline at end of file diff --git a/dev/config/index.html b/dev/config/index.html index 5e4c5818..6e456512 100644 --- a/dev/config/index.html +++ b/dev/config/index.html @@ -15,4 +15,4 @@ end END

Editor configuration

At present, vim is the only editor known to benefit from configuration: if needed, you should modify your settings file so that backupcopy=yes.

More generally, Revise uses file-watching, and any editor that saves files with the following sequence:

  1. rename the old version to a backup
  2. write the new file

will generate two triggers per save; if Revise reacts to the "disappearance" of the file after step 1, it may end up needlessly deleting methods, and even if they get redefined after step 2, you'll need to recompile all the code the next time you execute any dependent methods.

Your best option is to change your editor settings, if possible, to ensure that Revise is triggered only after the new file is finished writing. Either of:

  1. write the new file to a temporary name
  2. rename the temporary to the real file name

or

  1. save the old file to a backup name
  2. write the new data to the real file name

should work fine. In vim, backupcopy=yes forces the latter of these two behaviors.

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/dev/cookbook/index.html b/dev/cookbook/index.html index bdf8d562..0ca69e79 100644 --- a/dev/cookbook/index.html +++ b/dev/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/dev/debugging/index.html b/dev/debugging/index.html index affce09a..3084572d 100644 --- a/dev/debugging/index.html +++ b/dev/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/dev/dev_reference/index.html b/dev/dev_reference/index.html index 2e90a9e7..9e92a523 100644 --- a/dev/dev_reference/index.html +++ b/dev/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/dev/index.html b/dev/index.html index d375632e..9522d4bd 100644 --- a/dev/index.html +++ b/dev/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/dev/internals/index.html b/dev/internals/index.html index 8eced07e..b976acc3 100644 --- a/dev/internals/index.html +++ b/dev/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/dev/limitations/index.html b/dev/limitations/index.html index 08d61b52..53c298c0 100644 --- a/dev/limitations/index.html +++ b/dev/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/dev/tricks/index.html b/dev/tricks/index.html index 527d65fa..da4b1053 100644 --- a/dev/tricks/index.html +++ b/dev/tricks/index.html @@ -11,4 +11,4 @@ @info("Launching sub-REPL, use `^D` to reload, `exit()` to quit.") REPL.run_repl(repl) Revise.retry() -end

Many thanks to staticfloat for contributing this suggestion.

+end

Many thanks to staticfloat for contributing this suggestion.

diff --git a/dev/user_reference/index.html b/dev/user_reference/index.html index 6ba000fd..6784f401 100644 --- a/dev/user_reference/index.html +++ b/dev/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; 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)
+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. (Call Revise.track manually on each file, if you've already includedd all the code you need.)

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. (Call Revise.track manually on each file, if you've already includedd all the code you need.)

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