Detecting `package_name::function_name()` with static code analysis - r

I am trying to dive into the internals of static code analysis packages like codetools and CodeDepends, and my immediate goal is to understand how to detect function calls written as package_name::function_name() or package_name:::function_name(). I would have liked to just use findGlobals() from codetools, but this is not so simple.
Example function to analyze:
f <- function(n){
tmp <- digest::digest(n)
stats::rnorm(n)
}
Desired functionality:
analyze_function(f)
## [1] "digest::digest" "stats::rnorm"
Attempt with codetools:
library(codetools)
f = function(n) stats::rnorm(n)
findGlobals(f, merge = FALSE)
## $functions
## [1] "::"
##
## $variables
## character(0)
CodeDepends comes closer, but I am not sure I can always use the output to match functions to packages. I am looking for an automatic rule that connects rnorm() to stats and digest() to digest.
library(CodeDepends)
getInputs(body(f)
## An object of class "ScriptNodeInfo"
## Slot "files":
## character(0)
##
## Slot "strings":
## character(0)
##
## Slot "libraries":
## [1] "digest" "stats"
##
## Slot "inputs":
## [1] "n"
##
## Slot "outputs":
## [1] "tmp"
##
## Slot "updates":
## character(0)
##
## Slot "functions":
## { :: digest rnorm
## NA NA NA NA
##
## Slot "removes":
## character(0)
##
## Slot "nsevalVars":
## character(0)
##
## Slot "sideEffects":
## character(0)
##
## Slot "code":
## {
## tmp <- digest::digest(n)
## stats::rnorm(n)
## }
EDIT To be fair to CodeDepends, there is so much customizability and power for those who understand the internals. At the moment, I am just trying to wrap my head around collectors, handlers, walkers, etc. Apparently, it is possible to modify the standard :: collector to make special note of each namespaced call. For now, here is a naive attempt at something similar.
col <- inputCollector(`::` = function(e, collector, ...){
collector$call(paste0(e[[2]], "::", e[[3]]))
})
getInputs(quote(stats::rnorm(x)), collector = col)#functions
Browse[1]> getInputs(quote(stats::rnorm(x)), collector = col)#functions
stats::rnorm rnorm
NA NA

If you want to extract namespaced functions from a function, try something like this
find_ns_functions <- function(f, found=c()) {
if( is.function(f) ) {
# function, begin search on body
return(find_ns_functions(body(f), found))
} else if (is.call(f) && deparse(f[[1]]) %in% c("::", ":::")) {
found <- c(found, deparse(f))
} else if (is.recursive(f)) {
# compound object, iterate through sub-parts
v <- lapply(as.list(f), find_ns_functions, found)
found <- unique( c(found, unlist(v) ))
}
found
}
And we can test with
f <- function(n){
tmp <- digest::digest(n)
stats::rnorm(n)
}
find_ns_functions(f)
# [1] "digest::digest" "stats::rnorm"

Ok, so this was possible with CodeDepends previously, but a bit harder than it should have been. I've just committed version 0.5-4 to github, which now makes this really "easy". Essentially you just need to modify the default colonshandlers ("::" and/or ":::") as follows:
library(CodeDepends) # version >= 0.5-4
handler = function(e, collector, ..., iscall = FALSE) {
collector$library(asVarName(e[[2]]))
## :: or ::: name, remove if you don't want to count those as functions called
collector$call(asVarName(e[[1]]))
if(iscall)
collector$call(deparse((e))) #whole expr ie stats::norm
else
collector$vars(deparse((e)), input=TRUE) #whole expr ie stats::norm
}
getInputs(quote(stats::rnorm(x,y,z)), collector = inputCollector("::" = handler))
getInputs(quote(lapply( 1:10, stats::rnorm)), collector = inputCollector("::" = handler))
The first getInputs call above gives the result:
An object of class "ScriptNodeInfo"
Slot "files":
character(0)
Slot "strings":
character(0)
Slot "libraries":
[1] "stats"
Slot "inputs":
[1] "x" "y" "z"
Slot "outputs":
character(0)
Slot "updates":
character(0)
Slot "functions":
:: stats::rnorm
NA NA
Slot "removes":
character(0)
Slot "nsevalVars":
character(0)
Slot "sideEffects":
character(0)
Slot "code":
stats::rnorm(x, y, z)
As, I believe, desired.
One thing to note here is the iscall argument I've added to the colons handler. The default handler and applyhandlerfactory now have special logic so that when they invoke one of the colons handlers in a situation where it is a function being called, that is set to TRUE.
I haven't done extensive testing yet of what will happen when "stats::rnorm" appears in lieu of symbols, particularly in the inputs slot when calculating dependencies, but I'm hopeful that should all continue to work as well. If it doesn't let me know.
~G

Related

How to make Ops method compatible between two objects of a non base class

Let's imagine that I have a class "my" and I want to trigger certain behaviour when it is added to an object that has units (i.e. from units package):
library(units)
my1 = structure(2, class="my")
Ops.my <- function(e1, e2=NULL) {
ok <-
switch(
.Generic,
`-` = ,
`*` = ,
`+` = ,
'<=' = TRUE,
FALSE
)
if (!ok) {
stop(gettextf("%s not meaningful", sQuote(.Generic)))
}
get(.Generic)(as.integer(e1), as.integer(e2))
}
my1+set_units(5,nm)
Currently, it gives me the following warning:
Warning message:
Incompatible methods ("Ops.my", "Ops.units") for "+"
But I actually want to handle "my" and "units" addition in a certain way, how do I do it?
I tried with something like Ops.my.units <- but it doesn't seem to work.
There doesn't seem to be a way to do this with Ops. From the docs:
The classes of both arguments are considered in dispatching any member of this group. For each argument its vector of classes is examined to see if there is a matching specific (preferred) or Ops method. If a method is found for just one argument or the same method is found for both, it is used. If different methods are found, there is a warning about ‘incompatible methods’
This is probably a good thing. Part of the benefit of an object-oriented system in a non-compiled language like R is that it helps preserve type safety. This stops you from accidentally adding apples to oranges, as we can see in the following example:
apples <- structure(2, class = "apples")
oranges <- structure(2, class = "oranges")
Ops.apples <- function(e1, e2) {
value <- do.call(.Generic, list(as.integer(e1), as.integer(e2)))
class(value) <- "apples"
value
}
Ops.oranges <- function(e1, e2) {
value <- do.call(.Generic, list(as.integer(e1), as.integer(e2)))
class(value) <- "oranges"
value
}
apples + apples
#> [1] 4
#> attr(,"class")
#> [1] "apples"
oranges + oranges
#> [1] 4
#> attr(,"class")
#> [1] "oranges"
apples + oranges
#> [1] 4
#> attr(,"class")
#> [1] "apples"
#> Warning message:
#> Incompatible methods ("Ops.apples", "Ops.oranges") for "+"
You can see that even here, we could just ignore the warning.
suppressWarnings(apples + oranges)
#> [1] 4
#> attr(,"class")
#> [1] "apples"
But hopefully you can see why this may not be good - we have added 2 apples and 2 oranges, and have returned 4 apples.
Throughout R and its extension packages, there are numerous type-conversion functions such as as.integer, as.numeric, as.logical, as.character, as.difftime etc. These allow for some element of control when converting between types and performing operations on different types.
The "right" way to do this kind of thing is specifically convert one of the object types to the other in order to perform the operation:
as.my <- function(x) UseMethod("as.my")
as.my.default <- function(x) {
value <- as.integer(x)
class(value) <- 'my'
value
}
my1 + as.my(set_units(5,nm))
#> [1] 7

Environments in RStudio when sourcing

I have found that the usual way of finding the WD of a script sourced in RStudio is "dirname(parent.frame(2)$ofile)". I tried to research the meaning of this, read lots of explanations on environments, but I am still no closer to understanding this command. I ran this script:
print(environment())
print(parent.frame(1))
print(parent.frame(2))
print(parent.frame(3))
print(parent.frame(4))
f <- function() {
print('Do:')
print(environment())
print(parent.frame(1))
print(parent.frame(2))
print(parent.frame(3))
print(parent.frame(4))
}
f()
print("Parent 1:")
print(ls(parent.frame(1)))
print("Parent 2:")
print(ls(parent.frame(2)))
print(identical(environment(),parent.frame(1)))
print(identical(environment(),parent.frame(2)))
print(identical(environment(),parent.frame(3)))
print(identical(environment(),parent.frame(4)))
I got the output:
<environment: R_GlobalEnv>
<environment: 0x111987ab0>
<environment: 0x107fef700>
<environment: R_GlobalEnv>
<environment: R_GlobalEnv>
[1] "Do:"
<environment: 0x1119957d8>
<environment: R_GlobalEnv>
<environment: 0x111995998>
<environment: 0x107fef700>
<environment: R_GlobalEnv>
[1] "Parent 1:"
[1] "enclos" "envir" "expr"
[1] "Parent 2:"
[1] "chdir" "continue.echo" "curr.fun" "deparseCtrl"
[5] "echo" "ei" "enc" "encoding"
[9] "envir" "exprs" "file" "filename"
[13] "from_file" "have_encoding" "i" "i.symbol"
[17] "keep.source" "lastshown" "lines" "loc"
[21] "local" "max.deparse.length" "Ne" "ofile"
[25] "print.eval" "prompt.echo" "skip.echo" "spaced"
[29] "srcfile" "srcrefs" "tail" "use_file"
[33] "verbose" "width.cutoff" "yy"
[1] FALSE
[1] FALSE
[1] TRUE
[1] TRUE
I am not sure I understand the output.
1) What exactly are parents 1 and 2 of the Global Environment? Where can I read more on their attributes, including the used $ofile?
2) Why is parent.frame(1) in not equivalent to parent.frame(2) from within the function? Aren't they identical - the parent of Global Env?
3) Why does parent.frame start returning global environment when numbers get sufficiently big? Is this just how the function is written or is there some logic to this hierarchy?
When you go up the parent.frame, you can see the environments of the functions that are calling your function. The source() function has a lot of code that makes it work. It doesn't just dump the commands into your console. Basically it's running something like
source <- function(...) {
...
eval(ei, envir)
...
}
where ei is one of the expressions in your file. Then eval looks like this
eval <- function (expr, envir , enclos = ) {
.Internal(eval(expr, envir, enclos))
}
So when you call the first parent.frame() from a function that you call in a file that's sourced, it's going to see the eval() call first. If you look at formals(eval) you can see that it has those three variables that are in your first parent. The second parent lists all the variables that are created in the source() function itself, Including the ei variable we just saw. So heres where those values are
# parent.frame(4)
# parent.frame(3)
source <- function(...) {
# parent.frame(2)
eval(ei, envir)
}
eval <- function (expr, envir , enclos = ) {
# parent.frame(1)
.Internal(eval(expr, envir, enclos))
# ^^ your code
}
But variable resolution in R doesn't look in environments where functions are called from. Rather it uses lexical scoping so it looks where a function is defined (not called). If you want to get that environment, you can call parent.env(environment()) from inside the function instead. With a simple function, you should get the global environment. So really this means that parent.frame is just an unfortunate name because that's not "really" what it is.

Why aren't these two R objects identical?

I was reading the book 'Data Mining with R' and came across this code:
library(DMwR)
clean.algae <- knnImputation(algae, k = 10)
x <- sapply(names(clean.algae)[12:18],
function(x,names.attrs) {
f <- as.formula(paste(x,"~ ."))
dataset(f,clean.algae[,c(names.attrs,x)],x)
},
names(clean.algae)[1:11])
I thought x could be rewritten as:
y <- sapply(names(clean.algae)[12:18],
function(x) {
f <- as.formula(paste(x,"~ ."))
dataset(f,clean.algae[,c(names(clean.algae)[1:11],x)],x)
}
)
However, identical(x,y) returns FALSE.
I decided to investigate why by restricting my attention to just the first element these lists.
I found that:
identical(attributes(x[[1]])$data,
attributes(y[[1]])$data)
[1] FALSE
However:
which(!(attributes(x[[1]])$data == attributes(y[[1]])$data))
integer(0)
Which to me means all elements in the data frame are equal, hence the two data frames must be identical. Why is this not the case?
I also have similar question for the object's formula attribute:
> identical(attributes(x[[1]])$formula,
+ attributes(y[[1]])$formula)
[1] FALSE
>
> attributes(x[[1]])$formula == attributes(y[[1]])$formula
[1] TRUE
tl;dr the source of the non-identicality is indeed in differences in associated environments, both of the #formula slots of the components of the objects, and in the terms attributes of the #data slots. As #ThomasK points out in comments above, for most comparison purposes all.equal() is good enough/preferred ...
Formulas are equal but not identical:
identical(x$a1#formula,y$a1#formula)
## [1] FALSE
all.equal(x$a1#formula,y$a1#formula)
## TRUE
Environments differ:
environment(x$a1#formula)
## <environment: 0x9a408dc>
environment(y$a1#formula)
## <environment: 0x9564aa4>
Setting the environments to be identical makes the formulae identical:
environment(x$a1#formula) <- .GlobalEnv
environment(y$a1#formula) <- .GlobalEnv
identical(x$a1#formula,y$a1#formula)
## TRUE
However, there's more stuff that's different: identical(x$a1,y$a1) is still FALSE.
Digging some more:
for (i in slotNames(x$a1)) {
print(i)
print(identical(slot(x$a1,i),slot(y$a1,i)))
}
## [1] "data"
## [1] FALSE
## [1] "name"
## [1] TRUE
## [1] "formula"
## [1] TRUE
Digging deeper into the data slot (also with judicious use of str()) finds more environments -- associated with terms (closely related to formulae) this time:
dx <- x$a1#data
dy <- y$a1#data
environment(attr(dx,"terms"))
## <environment: 0x9a408dc>
environment(attr(dy,"terms"))
## <environment: 0x9564aa4>
Setting these equal to each other should lead to identicality between x$a1 and y$a1, but I haven't tested.

What is "{" class in R?

Here is the code:
mf = function(..., expr) {
expr = substitute(expr)
print(class(expr))
print(str(expr))
expr
}
mf(a = 1, b = 2, expr = {matrix(NA, 4, 4)})
Output:
[1] "{"
length 2 { matrix(NA, 4, 4) }
- attr(*, "srcref")=List of 2
..$ :Class 'srcref' atomic [1:8] 1 25 1 25 25 25 1 1
.. .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x7fbcdbce3860>
..$ :Class 'srcref' atomic [1:8] 1 26 1 41 26 41 1 1
.. .. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x7fbcdbce3860>
- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x7fbcdbce3860>
- attr(*, "wholeSrcref")=Class 'srcref' atomic [1:8] 1 0 1 42 0 42 1 1
.. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x7fbcdbce3860>
NULL
{
matrix(NA, 4, 4)
}
Apparently the result of substitute(expr) produces something of the class "{". What is this class exactly? Why is {matrix(NA, 4, 4)} of length 2? What do these strange attrs mean?
The { is the class for a block of code. Just looking at the classes, note the difference between these
mf(a = 1, b = 2, expr = {matrix(NA, 4, 4)})
# [1] "{"
mf(a = 1, b = 2, expr = matrix(NA, 4, 4))
# [1] "call"
A class of { can hold multiple statements. The length() indicates how many statements are in the block (including the start of the block). For example
length(quote({matrix(NA, 4, 4)}))
# [1] 2
length(quote({matrix(NA, 4, 4); matrix(NA,3,3)}))
# [1] 3
length(quote({}))
# [1] 1
The attributes "srcref" and "srcfile" are how R tracks where functions are defined for trying to give informative error messages. You can see the ?srcfile help page for more information about that.
What you're seeing is a reflection of the way R exposes its internal language structure through its own data structures.
The substitute() function returns the parse tree of an R expression. The parse tree is a tree of language elements. These can include literal values, symbols (basically variable names), function calls, and braced blocks. Here's a demonstration of all the R language elements as returned by substitute(), showing their types in all of R's type classification schemes:
tmc <- function(x) c(typeof(x),mode(x),class(x));
tmc(substitute(TRUE));
## [1] "logical" "logical" "logical"
tmc(substitute(4e5L));
## [1] "integer" "numeric" "integer"
tmc(substitute(4e5));
## [1] "double" "numeric" "numeric"
tmc(substitute(4e5i));
## [1] "complex" "complex" "complex"
tmc(substitute('a'));
## [1] "character" "character" "character"
tmc(substitute(somevar));
## [1] "symbol" "name" "name"
tmc(substitute(T));
## [1] "symbol" "name" "name"
tmc(substitute(sum(somevar)));
## [1] "language" "call" "call"
tmc(substitute(somevec[1]));
## [1] "language" "call" "call"
tmc(substitute(somelist[[1]]));
## [1] "language" "call" "call"
tmc(substitute(somelist$x));
## [1] "language" "call" "call"
tmc(substitute({blah}));
## [1] "language" "call" "{"
Notes:
Note how all three type classification schemes are very similar, but subtly different. This can be a source of confusion. typeof() gives the storage type of the object, sometimes called the "internal" type (to be honest, it probably shouldn't be called "internal" because it is frequently exposed very directly to the user at the R level, but it is often described that way; I would call it the "fundamental" or "underlying" type), mode() gives a similar classification scheme that everyone should probably ignore, and class() gives the implicit (if there's no class attribute) or explicit (if there is) class of the object, which is used for S3 method lookup (and, it should be said, is sometimes examined directly by R code, independent of the S3 lookup process).
Note how TRUE is a logical literal, but T is a symbol, just like any other variable name, and just happens to be assigned to TRUE by default (and ditto for F and FALSE). This is why sometimes people recommend against using T and F in favor of using TRUE and FALSE, because T and F can be reassigned (but personally I prefer to use T and F for the concision; no one should ever reassign those!).
The astute reader will notice that in my demonstration of literals, I've omitted the raw type. This is because there's no such thing as a raw literal in R. In fact, there are very few ways to get a hold of raw vectors in R; raw(), as.raw(), charToRaw(), and rawConnectionValue() are the only ways that I'm aware of, and if I used those functions in a substitute() call, they would be returned as "call" objects, just like in the sum(somevar) example, not literal raw values. The same can be said for the list type; there's no such thing as a list literal (although there are many ways to acquire a list via function calls). Plain raw vectors return 'raw' for all three type classifications, and plain lists return 'list' for all three type classifications.
Now, when you have a parse tree that is more complicated than a simple literal value or symbol (meaning it must be a function call or braced expression), you can generally examine the contents of that parse tree by coercing to list. This is how R exposes its internal language structure through its own data structures.
Diving into your example:
pt <- as.list(substitute({matrix(NA,4,4)}));
pt;
## [[1]]
## `{`
##
## [[2]]
## matrix(NA, 4, 4)
This makes it clear why length() returns 2: that's the length of the list that represents the parse tree. In general, the bracing of the expression is translated into the first list component, and the remaining list components are built from the semicolon-separated statements within the braces:
as.list(substitute({}));
## [[1]]
## `{`
##
as.list(substitute({a}));
## [[1]]
## `{`
##
## [[2]]
## a
##
as.list(substitute({a;b}));
## [[1]]
## `{`
##
## [[2]]
## a
##
## [[3]]
## b
##
as.list(substitute({a;b;c}));
## [[1]]
## `{`
##
## [[2]]
## a
##
## [[3]]
## b
##
## [[4]]
## c
Note that this is identical to how function calls work, except with the difference that, for function calls, the list components are formed from the comma-separated arguments to the function call:
as.list(substitute(sum()));
## [[1]]
## sum
##
as.list(substitute(sum(1)));
## [[1]]
## sum
##
## [[2]]
## [1] 1
##
as.list(substitute(sum(1,3)));
## [[1]]
## sum
##
## [[2]]
## [1] 1
##
## [[3]]
## [1] 3
##
as.list(substitute(sum(1,3,5)));
## [[1]]
## sum
##
## [[2]]
## [1] 1
##
## [[3]]
## [1] 3
##
## [[4]]
## [1] 5
From the above it becomes clear that the first list component is actually a symbol representing the name of a function, for both braced expressions and function calls. In other words, the open brace is a function call, one which simply returns its final argument. Just as square brackets are normal function calls with a convenient syntax built on top of them, the open brace is a normal function call with a convenient syntax built on top of it:
a <- 4:6;
a[2];
## [1] 5
`[`(a,2);
## [1] 5
{1;2};
## [1] 2
`{`(1,2);
## [1] 2
Returning to your example, we can fully explore the parse tree by traversing the list structure that represents the parse tree. I just wrote a nice little recursive function that can do this very easily:
unwrap <- function(x) if (typeof(x) == 'language') lapply(as.list(x),unwrap) else x;
unwrap(substitute(3));
## [1] 3
unwrap(substitute(a));
## a
unwrap(substitute(a+3));
## [[1]]
## `+`
##
## [[2]]
## a
##
## [[3]]
## [1] 3
##
unwrap(substitute({matrix(NA,4,4)}));
## [[1]]
## `{`
##
## [[2]]
## [[2]][[1]]
## matrix
##
## [[2]][[2]]
## [1] NA
##
## [[2]][[3]]
## [1] 4
##
## [[2]][[4]]
## [1] 4
As you can see, the braced expression turns into a normal function call of the function `{`(), taking one argument, which is the single statement you coded into it. That statement consists of a single function call to matrix(), taking three arguments, each of which being a literal value: NA, 4, and 4. And that's the entire parse tree.
So now we can understand the meaning of the "{" class on a deep level: it represents an element of a parse tree that is a function call to the `{`() function. It happens to be classed differently from other function calls ("{" instead of "call"), but as far as I can tell, that has no significance anywhere. Also observe that the typeof() and mode() are identical ("language" and "call", respectively) between all parse tree elements representing function calls, for both `{`() and others alike.

Insert "" instead of NA when adding rows in gdf [gWidgets2RGtk2]

Is it possible to insert "" instead of NA when creating a new row in gdf?
EDIT: Here's some sample code that I tried
require(gWidgets2RGtk2)
df <- data.frame(x=1:5,y=6:10) #Sample data frame
w2 <- gwindow("keyfile editor")
h <- gdf(df,cont=w2)
addHandlerChanged(h, handler = function(h,...){ #Handler to remove NA
h<<-apply(h[1:nrow(h),1:ncol(h)], 2, function(x) gsub("NA","",x))
})
svalue(h$obj, drop = FALSE)
gives you the new value for the updated row. So in theory,
addHandlerChanged(h, handler = function(h,...) {
svalue(h$obj, drop = FALSE)[] <- lapply(
svalue(h$obj, drop = FALSE),
function(x) {
x[is.na(x)] <- ""
}
)
}
should replace all the NAs with "". There are two problems:
Firstly, replacing the missing values with an empty string converts the whole column to be a character vector, which you probably don't want, and secondly, there seems to be a problem with svalue<- that means the values aren't updating.
I think that the problem is this:
methods(`svalue<-`)
## [1] svalue<-.default* svalue<-.GCheckbox* svalue<-.GFormLayout* svalue<-.GGroup*
## [5] svalue<-.GHtml* svalue<-.GLabel* svalue<-.GMenuBar* svalue<-.GRadio*
## [9] svalue<-.GToolBar* svalue<-.GTree*
shows that there is no GDf-specific method for setting the svalue, so svalue<-.default will be called.
gWidgets2:::`svalue<-.default`
## function (obj, index = NULL, ..., value)
## {
## if (!isExtant(obj)) {
## return(obj)
## }
## if (getWithDefault(index, FALSE))
## obj$set_index(value, ...)
## else obj$set_value(value, ...)
## obj
## }
This calls the object's set_value method.
ls(attr(h, ".xData"))
## [1] "add_cell_popup" "add_popup_to_view_col" "add_to_parent"
## [4] "add_view_columns" "block" "block_editable_column"
## [7] "cell_popup_id" "change_signal" "clear_stack"
## [10] "clear_view_columns" "cmd_coerce_column" "cmd_insert_column"
## [13] "cmd_remove_column" "cmd_replace_column" "cmd_set_column_name"
## [16] "cmd_set_column_names" "cmd_stack" "coerce_with"
## [19] "connected_signals" "default_cell_popup_menu" "default_expand"
## [22] "default_fill" "default_popup_menu" "freeze_attributes"
## [25] "get_column_index" "get_column_value" "get_dim"
## [28] "get_name" "get_view_column" "handler_id"
## [31] "initFields" "initialize" "initialize#GComponent"
## [34] "initialize#GWidget" "invoke_change_handler" "invoke_handler"
## [37] "is_editable" "map_j" "model"
## [40] "not_deleted" "notify_observers" "parent"
## [43] "set_editable" "set_frame" "set_name"
## [46] "set_names" "set_parent" "store"
## [49] "toolkit" "unblock_editable_column" "widget"
but there doesn't seem to be one implemented yet.
Well, Richie did his usual thorough job. This question has a few problems: One you use the variable h as a global variable (for the gdf object) and as the argument to the handler, so within the handler h does not refer to the object, but h$obj would. Second To set values for selection in the gdf object uses the [<- method (h[i,j] <- "" calls the h object's set_items method). You tried to modify the object, not call a method on it. As for NA values, underlying the items to select from is an RGtk2DataFrame, which like a data frame in R will coerce values to character if you try to put a character value into a numeric value. Best, to use R as it is intended. If you really want to get rid of NA values you can do so when you go to use the values that the user has edited, modifying h[,] as you want.
Now, if you really wanted to do this, I think you could at the RGtk2 level by writing an appropriate cell renderer.

Resources