Streamlined the behavior of ggtrace(use_names)
for assigning names to the tracedump, where:
If a list of expression is provided in trace_exprs
, uses the names of that list if TRUE
, and uses deparsed expressions from the method steps as names if FALSE
.
If trace_exprs
is empty, uses deparsed expressions from the method steps as names if TRUE
, and returns unnamed output if FALSE
.
New special value ggtrace(trace_steps = "all")
which evaluates to all steps in the method body.
Bug fix in the default behavior of ggtrace(trace_exprs)
doubly-evaluating an existing step from the method body, which sometimes caused unexpected side effects. This behavior is now prevented by cloning the method environment in such cases.
New helper function layer_is()
, primarily for internal use by sublayer data inspection functions (e.g., layer_before_stat()
). These functions now become more robust to changes in the internals, by specifically targetting the downstream of by_layer()
.
Improved formatting of long-form calls generated by sublayer data functions when verbose = TRUE
.
More visible documentation of shorthand aliases for workflow functions introduced in last release.
Exported shorthand aliases for all workflow functions. E.g., inspect_n()
complement to ggtrace_inspect_n()
Cleaned up examples and tests to align with {ggplot2}
version 3.5.0
Upkeep release for the JSM 2023 talk "Sub-layer modularity in the Grammar of Graphics".
layer_*()
snapshot functions gain a verbose
argument.
getOption("ggtrace.rethrow_error")
To control re-printing of the error by workflow functions when error = TRUE
. Set to FALSE
on load.
get_method_inheritance()
now works correctly for top-level ggprotos - ex: get_method_inheritance(Geom)
(#99; thanks @yjing14 for bug report).Significant usability improvements, including {cli}
integration.
Convenience functions layer_before_stat()
, layer_after_stat()
, layer_before_geom()
, and layer_after_scale()
that returns a snapshot of layer data in the internals. Inspired by ggplot2::layer_data()
with a similar interface. (#97; thanks @JoFrhwld for suggestion)
Interactive debugging functions last_layer_errorcontext()
and last_sublayer_errorcontext()
which return the internal context of layer errors at the level of the Layer
and sub-Layer
(e.g., Stat
or Geom
) ggproto methods, respectively. last_sublayer_errorcontext()
is still in experimental phase (error-prone, may be removed in future).
New catch-all Inspect workflow function ggtrace_inspect_on_error()
which dumps information about the method
that errors while rendering x
.
error = TRUE
argument, which allows inspection of an earlier intermediate step even when the ggplot rendering process fails down the line (#89)is.ggtrace_placeholder()
returns scalar logical (#94)ggtrace_inspect_vars()
simplifies the output when at
is length 1.ggtrace_capture_env()
removes ggtrace-internal variables before snapshotting the environment (#88)cond
argument of workflow functions now support integer shorthand for conditioning on the counter. E.g., cond = 1L
is converted to cond = quote(._counter_ == 1L)
. Multi-length integer vector is supported for highjack workflows. (#84)Added complements to the base::debug()
family of functions that are compatible with ggproto methods - ggdebug()
, ggdebugonce()
, ggundebug()
get_method_inheritance()
to get the list of methods from self and parent ggprotos
ggtrace_inspect_n()
to get the number of times a method was called in the evaluation of a ggplot
ggtrace_inspect_which()
to get the indices when a condition cond
evaluated to true inside a method
ggtrace_inspect_vars()
to get the value of variables at specified steps of a method's execution
ggtrace_inspect_args()
to get the value of arguments passed to a method
ggtrace_highjack_args()
to modify formals of a method at its execution
with_ggtrace()
gets a out
argument which can take 1 of three options:
method
as the ggplot x
is evaluated<gtable>
grob after evaluating x
with injected expressions in method
.grid::grid.draw()
) as a side effect.Low-level functions ggtrace()
/gguntrace()
and wrapper with_ggtrace()
can now take quosures in the method
argument, which allows them to be used more programmatically.
ggtrace_capture_env()
default value of at
is changed to -1L
, which captures a snapshot of the runtime environment right before the method returns. Only the first element is used if at
is length > 1ggtrace_highjack_return()
) to reflect the fact that they always return the graphical output (gtable grob) (#78)ggtrace(once = TRUE)
if it was triggered by a copy of the traced function (#59)with_ggtrace()
for a functional interface to ggtrace()
ggtrace_capture_fn()
and ggtrace_capture_env()
, which return a snapshot of the function/environment of the ggproto method at execution timeggtrace_inspect_return()
and ggtrace_modify_return()
to grab and swap return values at method's executionggformals()
which returns the formals()
of functions and ggproto methodsggbody()
gains a as.list
argument to control whether output of body()
is turned into a listset_last_ggtrace()
and set_global_ggtrace()
for the tracedumpsget_method()
which returns ggproto methods as functionsggdebugonce()
and ggtraceback()
verbose
argument of ggtrace()
is changed to FALSE
.ggedit()
now only works when isTRUE(interactive())
(#62)global_ggtrace_on/off()
which are aliases for global_ggtrace_state(TRUE/FALSE)
(#63)clear_(last|global)_ggtrace()
functions now print a message saying that the trace dump has been cleared.global_ggtrace_state()
is FALSE
on load) and must be explicitly activated with global_ggtrace_state(TRUE)
.global_ggtrace_state()
print_output = TRUE
would evaluate the expression twice (problematic for Inject workflows and causing general slowdowns)global_ggtrace_state()
. It is still active by default but should memory become a concern, it can be turned off with global_ggtrace_state(state = FALSE)
.ggplo2:::ggplot_build.ggplot
and ggplot2:::ggplot_add.Layer
. Some other examples of what's now possible.ggtrace()
gains a ...
for extensibility in future updates.ggedit()
gains a remove_trace
argument. When TRUE
, it untraces first before editing.method
argument of ggbody()
ggtrace()
fails to evaluate the first line when it's the only reachable line (#44)ggtrace()
now throws a warning when the actual number of traced steps does not match the expected number of traced steps (i.e., length of trace_steps
) and logs incomplete tracedumps (#44)ggtrace(..., once = TRUE)
is less noisy about there being a persistent trace. It now only sends a line of message saying so when the persistent trace is created. When it's triggered, it'll tell you that there is a persistent trace on the methodbut will not remind you to untrace it later nor print the corresponding gguntrace()
code to do so.trace_exprs
evaluating to NULL
would be removed from the tracedump (#38)is_traced()
checks whether a method is currently being tracedggtrace()
now breaks early with a more informative message if the method is not a function (ex: Stat$extra_params
, which is a 1-length character vector "na.rm"
)
ggbody()
now warns if it's returning the body of a method that's currently being traced (#35)
ggedit()
now informs you if you're editing on top of an existing trace/edit.
Fixed bug where trace_exprs
would fail to be recycled if it is a list of length-1. Now a 1-length list of an expression as well as an expression by itself will get recycled to match the number of steps passed to trace_steps
.
Fixed bug where ggtrace()
wouldn't loop over trace_exprs
after the first time it's triggered with persistent tracing on (once = TRUE
). Issue was due to failing to reset the internal counter after each time the trace is triggered.
.print
argument to ggtrace()
is renamed to print_output
to make its functionality more transparent.Several options for finer control over printing and formatting of output from ggtrace()
, in addition to the existing .print
argument:
ggtrace()
gets a use_names
argument. When TRUE
, it uses the names of the list of expressions passed to trace_exprs
as the names for the tracedump set to last_ggtrace()
and added to global_ggtrace()
.
ggtrace()
gets a verbose
argument. When FALSE
, it suppresses the display of all non-message()
information, including information about which expression is evaluated where, as well as the output of those expressions when evaluated (which can be selectively suppressed with .print
for finer control). verbose
is TRUE
by default.
Setting options(ggtrace.suppressMessages = TRUE)
will also suppress messages()
s about what method is being traced, whether a trace has been triggered on a method, whether there exists a persistent trace, etc. This information is very important so using this option is not recommended, but it has been made available. This option is set to FALSE
on package load.
Setting options(ggtrace.as_tibble = TRUE)
will return evaluated expressions as tibbles if the output is a data frame. Using this option may be convenient for interactive inspections but it is not recommended for testing or debugging (see related {ggplot2} Github issue). This option is set to FALSE
on package load.
Tracedumps accumulated in global_ggtrace()
are named after the method (+ hexadecimal ID) for ease of searching. (#31)
Triggering of a trace is now informed via message()
instead of cat()
(#29)
ggtrace()
correctly throws an error when trace_steps
is not ordered. This is checked after the negative index conversion, so something like trace_steps = c(1, -1)
still works fine).New function global_ggtrace()
which returns accumulated tracedumps from ggtrace()
. This is useful in conjunction with ggtrace(once = FALSE)
for tracing a method that you expect will be called multiple times (ex: Stat$compute_group
gets triggered the same number of times as the number of groups in a panel).
New function clear_global_ggtrace()
which clears global_ggtrace()
by setting it to NULL
.
trace_exprs
were allowed to be different from the number of trace_steps
, causing ggtrace()
to silently fail. This now throws an error.trace_exprs
argument can now take a named list of expressions (#21)
Improved documentation for ggbody()
More informative messages for ggtrace()
and gguntrace()
once = FALSE
The unary functions ggedit()
and gguntrace()
gain dynamic dots ...
as the second argument, which gets ignored. This now makes it easy to call these function by modifying the call to an earlier{ggtrace}
function from the console or in other interactive contexts.
Significant re-write of ggbody()
for better error handling (#23)
Aborts if method is not a call or is not of the accepted form, with specific error messages for each.
If a method doesn't exist in a parent, directs users to call ggbody(, inherit = TRUE)
If the recursive search with inherit = TRUE
fails, directs users to load all relevant packages
Notifies if inherit = TRUE
but method is defined for the object, not inherited
Better error handling for gguntrace()
when the method is no longer being traced (#24)
Uses the re-written ggbody()
to validate the method
Unlike base::untrace()
, no longer errors if given a method not currently being traced. It now prints a message saying so instead
Standardization of messages printed by all {ggtrace}
functions. Messages are now more informative and refer to the ggproto method in the callable format ggproto$method
.
The obj
argument is completely removed from all functions in the package. The constraint on supplying methods as expressions forces users to be intentional about tracing ggproto methods by having to provide them as code. This also allows the functions to return more informative messages, which was the main motivation for this breaking change.
Because the `obj` argument was only designed for compatibility with the `get("method", ggproto)` syntax for retrieving the function body of ggproto methods, it should not affect interactive workflows. In fact, the shortform `method = ggproto$method` is more convenient and has always been recommended for passing a ggproto method to `{ggtrace}` functions.
As a reminder, all functions that take the ggproto method in the method
argument expects an expression in the following forms (this part hasn't changed):
ggproto$method
namespace::ggproto$method
namespace:::ggproto$method
Accurate string conversion for ggproto objects (#9), made possible by the breaking change.
trace_steps
argument can now take negative indices (#22) and has better error handling for out of range indices.
Better deparsing for the split_ggproto_method()
internal.
Added a Tips & Tricks section to the documentation for ggtrace()
.
.store
renamed to .ggtrace_storage
to prevent overriding ggplot2::.store
(#18)The ~line
keyword for ggtrace()
is renamed to ~step
for consistency with the argument name trace_steps
(#14)
For safety reasons, the ~step
keyword will now only be substituted for the expression at the current step only if ~step
is by itself (i.e., is an exact match) (#16, #11).
For example, `~step` will not be substituted if `quote(head(~step))` is passed to `trace_exprs`. Users are encouraged to return the method's environment with `quote(environment())` or interactively debug with `ggedit()` if they want to manipulate the expression.
The position of the obj
argument of ggtrace()
has been moved from second to fourth, to allow for shortcuts like ggtrace(method = ..., 2:3, quote(data))
, which will evaluate and store the output of the data
variable at the second and third steps of the method body. (#15)
trace_exprs
argument of ggtrace()
is now optional. If not provided, defaults to ~step
(#13)
You can now tell ggbody()
to (recursively) search for the method from its parents with inherit = TRUE
(#12)
New function gguntrace()
with the same syntax for specifying the ggproto method.
ggtrace()
gains a once = TRUE
argument, which can be set to FALSE
for persistent tracing
ggtrace()
where step_deparsed
was being returned as a multi-length vectorggedit()
for interactive debugging via directly editing the source code.Refactored ggtrace()
. The package now only depends on {rlang}
.
Significant re-write to the readme / documentation
pkgdown site: https://yjunechoe.github.io/ggtrace
Functions: