【问题标题】:How to avoid argparser type error when omitting argument?省略参数时如何避免argparser类型错误?
【发布时间】:2020-11-18 05:20:06
【问题描述】:

在 R 中使用 argparser 时,在对 add_argument 的调用中指定参数的类型时出现错误,但未在 MacOSX 命令行中将参数传递给脚本。例如,给定这个 R 脚本:

library(argparser)
p <- arg_parser(description = "A test parser")
p <- add_argument(p, "--alpha", type = "double", help = "alpha for p-value")
p <- add_argument(p, "--sig-digits", type = "integer", help="number of significant digits")
args <- parse_args(p)
print(str(args))

并在命令行调用它:

Rscript argparser-test.R --alpha 0.1

返回错误:

Error in (function (object, class, nargs)  :
Invalid argument value: expecting integer but got: (NA).
Calls: parse_args -> mapply -> <Anonymous>

有趣的是,如果您让--alpha 使用它的默认值,则不会出现错误:

Rscript argparser-test.R

返回:

List of 5
 $           : logi FALSE
 $ help      : logi FALSE
 $ opts      : logi NA
 $ alpha     : logi NA
 $ sig_digits: logi NA
NULL

注意这里sig_digitsNA 值是logical 类型,而不是integer,正如add_argument 函数中定义的那样。

我在这里做错了吗?同时,我想我会通过设置默认的--sig-digits = -1 来解决这个问题,然后将其作为异常处理,但我不想这样做。

更新:实际上,-1 会引发相同的错误,这非常令人沮丧,因为我想对没有意义的异常使用一个数字。 9999有效,用户输入的可能性不大,但实际上是有效的。

【问题讨论】:

    标签: r command-line-arguments argparse


    【解决方案1】:

    大约一个月前我遇到了这个错误。这是argparser 包如何解析可选参数的问题。基本上,它确实尊重可选参数的顺序,因为它在每种情况下都应如此,因此有时它会期望错误的参数类型。

    我在包裹bitbucket 页面上打开了一个issue。我强烈建议对此进行投票并添加评论以帮助增加对该问题的关注。

    在我的问题中,我提供了一个可能的解决方案,相当于将parse_args 更改为以下定义(可以使用此函数提取并重新创建包,此时它将[应该]按预期工作)

    parse_args <- function (parser, argv = commandArgs(trailingOnly = TRUE)) 
    {
        stopifnot(is(parser, "arg.parser"))
        values <- list()
        argv <- preprocess_argv(argv, parser)
        arg.flags <- parser$args[parser$is.flag]
        x <- as.logical(parser$defaults[parser$is.flag])
        x[is.na(x)] <- FALSE
        names(x) <- sub("^-+", "", arg.flags)
        flag.idx <- match(arg.flags, argv)
        flag.idx <- flag.idx[!is.na(flag.idx)]
        if (length(flag.idx) > 0) {
            x[match(argv[flag.idx], arg.flags)] <- TRUE
            argv <- argv[-flag.idx]
        }
        values <- c(values, x)
        if (values$help) {
            print(parser)
            quit()
        }
        x <- parser$defaults[parser$is.opt.arg]
        arg.opt <- parser$args[parser$is.opt.arg]
        names(x) <- sub("^-+", "", arg.opt)
        i <- match("--opts", argv)
        if (!is.na(i)) {
            opts <- readRDS(argv[i + 1])
            opts <- opts[!names(opts) %in% c("opts", "help")]
            idx <- match(sanitize_arg_names(names(opts)), sanitize_arg_names(names(x)))
            if (any(is.na(idx))) {
                stop("Extra arguments supplied in OPTS file: (", 
                    paste(setdiff(names(opts), names(x)), collapse = ", "), 
                    ").")
            }
            x[idx] <- opts
        }
        arg.idx <- match(arg.opt, argv)
        arg.idx <- arg.idx[!is.na(arg.idx)]
        arg.opt.types <- parser$types[parser$is.opt.arg]
        arg.opt.nargs <- parser$nargs[parser$is.opt.arg]
        ###               ###
        ## Altered section ##
        ###               ###
        if (length(arg.idx) > 0) {
            # extract values following the optional argument label
            x[ind <- match(argv[arg.idx], arg.opt)] <- argv[arg.idx+1];
            # convert type of extraced values; x is now a list
            x[ind] <- mapply(convert_type,                                
                             object = x[ind], 
                             class = arg.opt.types[ind], 
                             nargs = arg.opt.nargs[ind], 
                             SIMPLIFY = FALSE);                                    
            # remove extracted arguments
            to.remove <- c(arg.idx, arg.idx+1);
            argv <- argv[-to.remove];
        }
        ###               ###
        ## Altered section ##
        ###               ###
        values <- c(values, x)
        x <- argv
        args.req <- parser$args[parser$is.req.arg]
        args.req.types <- parser$types[parser$is.req.arg]
        args.req.nargs <- parser$nargs[parser$is.req.arg]
        if (length(x) < length(args.req)) {
            print(parser)
            stop(sprintf("Missing required arguments: expecting %d values but got %d values: (%s).", 
                length(args.req), length(x), paste(x, collapse = ", ")))
        }
        else if (length(x) > length(args.req)) {
            print(parser)
            stop(sprintf("Extra arguments supplied: expecting %d values but got %d values: (%s).", 
                length(args.req), length(x), paste(x, collapse = ", ")))
        }
        else if (length(args.req) > 0) {
            names(x) <- args.req
            x <- mapply(convert_type, object = x, class = args.req.types, 
                nargs = args.req.nargs, SIMPLIFY = FALSE)
        }
        values <- c(values, x)
        names(values) <- sanitize_arg_names(names(values))
        values
    }
    

    【讨论】:

    • Error in preprocess_argv(argv, parser) : could not find function "preprocess_argv"
    • 是的,这是有道理的。该函数调用argparser 环境中的几个函数,这些函数可能没有“附加”(例如:您可以使用argparser:::preprocess_argv 查看它们。要使用它而无需下载和重新打包该函数,请执行以下操作: 1)执行该函数,以得到如上的错误。 2) 对于每个错误,对错误中提到的函数使用搜索和替换,并用argparser:::[name of function that gives error] 替换。在某些时候,它希望停止给出错误。我只是做了我的改动并没有破坏功能来测试它。
    • 宾果游戏!一切都已修复,非常感谢您完成和分享这项工作。
    • 我的荣幸。看起来包的作者对这个包不是很感兴趣。我考虑过将 argparser2020 包扩展并推送到 cran,但我决定在这样做之前给作者 djhshih 一些时间。
    • 另一件事,argparser 将多值参数解析为逗号分隔的字符串,而不是 argparse 中的列表。不太好,对吧?
    猜你喜欢
    • 2020-04-23
    • 1970-01-01
    • 2018-06-18
    • 1970-01-01
    • 1970-01-01
    • 2015-12-31
    • 1970-01-01
    • 1970-01-01
    • 2018-10-18
    相关资源
    最近更新 更多