snitch

The experimental snitch πŸ“¦ spies on functions and packages. The name and logo are inspired from the Harry Potter world.
R
snitch
Author
Affiliation

Romain François

Published

February 4, 2024

The snitch πŸ“¦ is one of those weekend experiments. In this particular case, it comes from I don’t know how to use base::trace() and I want a thing that tells me how a particular function is called, so I want to spy on a function.

The name and logo of the package might be because I’ve started to read the first Harry Potter book to my 6 years old daughter πŸ§™.

DALL-E gave a decent logo once I removed explicit HP references πŸ€·β€β™‚οΈ, i.e. β€œa small gold colored spherical object with wings” instead of β€œthe golden snitch”:

Can you make an hexagon logo for an R package called snitch, inspired by a small gold colored spherical object with wings. The package goal is to spy on function calls.

Anyway πŸ€–, let’s dive in, you can install it with pak::pak("tadascience/snitch") I’m not sure at this point this will make its way to CRAN.

The package has snitch::fun() to spy on a function … (I don’t know how to get the colors in quarto so let’s go with a screenshot). You first get a rule with breadcrumbs of functions that are spyed on, and then their arguments, courtesy of ls.str() :

… and snitch::pkg() to spy on functions from a package.

suppressPackageStartupMessages(library(dplyr))
snitch::pkg("dplyr", pattern = "^summarise_")

mtcars %>%
  summarise(mean = mean(disp), n = n())
── summarise_cols ──────────────────────────────────────────────────────────────
   by : List of 3
    $ type : chr "ungrouped"
    $ names: chr(0) 
    $ data : tibble [1 Γ— 1] (S3: tbl_df/tbl/data.frame)
   data : 'data.frame': 32 obs. of  11 variables:
    $ mpg : num  21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
    $ cyl : num  6 6 4 6 8 6 8 4 4 6 ...
    $ disp: num  160 160 108 258 360 ...
    $ hp  : num  110 110 93 110 175 105 245 62 95 123 ...
    $ drat: num  3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
    $ wt  : num  2.62 2.88 2.32 3.21 3.44 ...
    $ qsec: num  16.5 17 18.6 19.4 17 ...
    $ vs  : num  0 0 1 1 0 1 0 1 1 1 ...
    $ am  : num  1 1 1 0 0 0 0 0 0 0 ...
    $ gear: num  4 4 4 3 3 3 3 4 4 4 ...
    $ carb: num  4 4 1 1 2 1 4 2 2 4 ...
   dots : List of 2
    $ mean: language ~mean(disp)
    $ n   : language ~n()
   error_call : <environment: 0x107937698> 
   verb :  chr "summarise"
── summarise_cols β¬’ FUN ────────────────────────────────────────────────────────
   mask : Classes 'DataMask', 'R6' <DataMask>
     Public:
       add_one: function (name, chunks, result) 
       clone: function (deep = FALSE) 
       current_cols: function (vars) 
       current_key: function () 
       current_non_group_vars: function () 
       current_rows: function () 
       current_vars: function () 
       eval_all: function (quo) 
       eval_all_filter: function (quos, env_filter) 
       eval_all_mutate: function (quo) 
       eval_all_summarise: function (quo) 
       forget: function () 
       get_current_data: function (groups = TRUE) 
       get_current_group_id: function () 
       get_current_group_id_mutable: function () 
       get_current_group_size: function () 
       get_current_group_size_mutable: function () 
       get_keys: function () 
       get_rlang_mask: function () 
       get_rows: function () 
       get_size: function () 
       get_used: function () 
       initialize: function (data, by, verb, error_call) 
       is_grouped: function () 
       is_rowwise: function () 
       pick_current: function (vars) 
       remove: function (name) 
       resolve: function (name) 
       set_current_group: function (group) 
       unused_vars: function () 
     Private:
       by_names: 
       chops: environment
       current_data: list
       env_current_group_info: environment
       env_mask_bindings: environment
       grouped: FALSE
       keys: tbl_df, tbl, data.frame
       rows: vctrs_list_of, vctrs_vctr, list
       rowwise: FALSE
       size: 32
       verb: summarise 
   quo :  language ~mean(disp)
── summarise_cols β¬’ FUN ────────────────────────────────────────────────────────
   mask : Classes 'DataMask', 'R6' <DataMask>
     Public:
       add_one: function (name, chunks, result) 
       clone: function (deep = FALSE) 
       current_cols: function (vars) 
       current_key: function () 
       current_non_group_vars: function () 
       current_rows: function () 
       current_vars: function () 
       eval_all: function (quo) 
       eval_all_filter: function (quos, env_filter) 
       eval_all_mutate: function (quo) 
       eval_all_summarise: function (quo) 
       forget: function () 
       get_current_data: function (groups = TRUE) 
       get_current_group_id: function () 
       get_current_group_id_mutable: function () 
       get_current_group_size: function () 
       get_current_group_size_mutable: function () 
       get_keys: function () 
       get_rlang_mask: function () 
       get_rows: function () 
       get_size: function () 
       get_used: function () 
       initialize: function (data, by, verb, error_call) 
       is_grouped: function () 
       is_rowwise: function () 
       pick_current: function (vars) 
       remove: function (name) 
       resolve: function (name) 
       set_current_group: function (group) 
       unused_vars: function () 
     Private:
       by_names: 
       chops: environment
       current_data: list
       env_current_group_info: environment
       env_mask_bindings: environment
       grouped: FALSE
       keys: tbl_df, tbl, data.frame
       rows: vctrs_list_of, vctrs_vctr, list
       rowwise: FALSE
       size: 32
       verb: summarise 
   quo :  language ~n()
── summarise_build ─────────────────────────────────────────────────────────────
   by : List of 3
    $ type : chr "ungrouped"
    $ names: chr(0) 
    $ data : tibble [1 Γ— 1] (S3: tbl_df/tbl/data.frame)
   cols : List of 3
    $ new    :List of 2
    $ sizes  : int 1
    $ all_one: logi TRUE
      mean  n
1 230.7219 32

Is it useful, I don’t know, but I like it, and I prefer typing snitch::fun() than remembering how to use trace().