trevorld/r-optparse

If "callback" `option` with "character" `type` then logical `default` converted into a character value

trevorld opened this issue · 2 comments

Example by @hs3434 in #46

library("optparse")
str2bool <- function(option, flag, option_value, parser) {
    print(option_value)
    s <- tolower(as.character(option_value))
    if (s %in% c("0", "false", "f", "no", "n")) {
        FALSE
    } else {
        TRUE
    }
}
parser <- OptionParser()
parser <- add_option(parser, "--test", type = "character", default = FALSE, callback = str2bool)
parse_args(parser) |> print()
$help
[1] FALSE

$test
[1] "FALSE"
parse_args(parser, "--test=f") |> print()
$help
[1] FALSE

$test
[1] FALSE

This isn't something that the inspiration python module ever did:

import optparse


def str2bool(option, opt, value, parser):
    setattr(parser.values, option.dest, bool(value))


parser = optparse.OptionParser()
parser.add_option("--test", action="callback", type="string",
                  default=False, callback=str2bool)
opts, args = parser.parse_args()
print(opts)
~/tmp$ python3 optparse_bug2.py 
{'test': False}
~/tmp$ python3 optparse_bug2.py --test=True
{'test': True}

I do observe for the python module that for the "store" action and the "int" type that a character default would be converted to an int:

import optparse

parser = optparse.OptionParser()
parser.add_option("--int", type="int", default="2")
opts, args = parser.parse_args()
print(opts)
~/tmp$ python3 optparse_bug.py 
{'int': 2}

Because the python only handle the numeric and choise,
Here is the code in optparse.py lines 543-548:

TYPE_CHECKER = { "int"    : check_builtin,
                     "long"   : check_builtin,
                     "float"  : check_builtin,
                     "complex": check_builtin,
                     "choice" : check_choice,
                   }

The check_builtin only include numeric , here is the code in optparse.py lines 422- 433:

_builtin_cvt = { "int" : (_parse_int, _("integer")),
                 "long" : (_parse_int, _("integer")),
                 "float" : (float, _("floating-point")),
                 "complex" : (complex, _("complex")) }

def check_builtin(option, opt, value):
    (cvt, what) = _builtin_cvt[option.type]
    try:
        return cvt(value)
    except ValueError:
        raise OptionValueError(
            _("option %s: invalid %s value: %r") % (opt, what, value))

Maybe you can copy this and develop some new features.