diff --git a/DESCRIPTION b/DESCRIPTION index fb2e862..ede054c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: argparse Type: Package Title: Command Line Optional and Positional Argument Parser -Version: 2.0.4 +Version: 2.1.0 Authors@R: c(person("Trevor L", "Davis", role=c("aut", "cre"), email="trevor.l.davis@gmail.com"), person("Allen", "Day", role="ctb", comment="Some documentation and examples ported from the getopt package."), person("Python Software Foundation", role="ctb", comment="Some documentation from the optparse Python module."), diff --git a/NEWS.md b/NEWS.md index dd94e53..fa72e5a 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,9 @@ +argparse 2.1.0 +============== + +* Parsers now support ``parse_known_args()`` (#34). + Suggestion of David Lacalle Castillo (@WaterKnight1998). + argparse 2.0.4 ============== @@ -69,12 +75,15 @@ argparse 1.0.4 * You can now pass in a character vector to the ``metavar`` argument of ``add_argument``. Thanks Paul Newell for bug report and a patch. * `add_argument` now throws a warning recommending using action "store_true" or "store_false" - if type set to "logical" and action set to "store". Suggestion of Martí Duran Ferrer + if type set to "logical" and action set to "store". + Suggestion of Martí Duran Ferrer * You can now explicitly set a `NULL` default in `add_argument`. Previously one could only implicitly set a `NULL` default by not setting any default at all. Suggestion of Hyunsoo Kim. -* Fixes parsing bug when using a very large argument list. Thanks Taylor Pospisil for bug report. -* Parse error usage message now prints to standard error. User requested help message quit status is now zero. +* Fixes parsing bug when using a very large argument list. + Thanks Taylor Pospisil for bug report. +* Parse error usage message now prints to standard error. + User requested help message quit status is now zero. Thanks to PlasmaBinturong for report/request. * If Python script fails with error pass on error message to user. Thanks to Martí Duran Ferrer for report/request. @@ -95,7 +104,6 @@ argparse 1.0.0 but will instead throw an error. ``argparse`` will continue to ``quit(status=1)`` after printing a help message for non-interactive Rscripts. - argparse 0.5.3 ============== diff --git a/R/argparse.R b/R/argparse.R index fed321d..3194934 100644 --- a/R/argparse.R +++ b/R/argparse.R @@ -39,8 +39,6 @@ #' #' @references Python's \code{argparse} library, which this package is based on, #' is described here: \url{https://docs.python.org/3/library/argparse.html} -#' @section Acknowledgement: -#' A big thanks to Martin Diehl for a bug report. #' #' @import jsonlite #' @import R6 @@ -134,32 +132,14 @@ Parser <- R6Class("Parser", # nolint paste(sprintf("'%s'", args), collapse = ", ")), "print(json.dumps(args.__dict__, sort_keys=True))") output <- private$python_code$run(new_code) - if (grepl("^usage:", output[1])) { - has_positional_arguments <- any(grepl("^positional arguments:", output)) - has_optional_arguments <- any(grepl("^optional arguments:", output)) - if (has_positional_arguments || has_optional_arguments) { - .print_message_and_exit(output, "help requested:") - } else { - .stop(output, "parse error:") - } - } else if (grepl("^Traceback", output[1])) { - .stop(output, "Error: python error") - } else if (any(grepl("^SyntaxError: Non-ASCII character", output))) { - message <- paste("Non-ASCII character detected.", - "If you wish to use Unicode arguments/options", - "please upgrade to Python 3.2+", - "Please see file INSTALL for more details.") - .stop(message, "non-ascii character error:") - } else if (any(grepl("^SyntaxError: positional argument follows keyword argument", output)) || - grepl("^SyntaxError: non-keyword arg after keyword arg", output[2])) { - message <- "Positional argument following keyword argument." - .stop(message, "syntax error:") - } else if (grepl("^\\{", output)) { - args <- jsonlite::fromJSON(paste(output, collapse = "")) - return(args) - } else { # presumably version number request - .print_message_and_exit(output, "version requested:") - } + parse_args_output(output) + }, + parse_known_args = function(args = commandArgs(TRUE)) { + new_code <- c(sprintf("args_remainder = %s.parse_known_args([%s])", private$name, + paste(sprintf("'%s'", args), collapse = ", ")), + "print(json.dumps((args_remainder[0].__dict__, args_remainder[1])))") + output <- private$python_code$run(new_code) + parse_args_output(output) }, print_help = function() { cat(private$python_code$run(sprintf("%s.print_help()", private$name)), sep = "\n") @@ -207,6 +187,34 @@ Parser <- R6Class("Parser", # nolint n_mutually_exclusive_groups = 0, n_groups = 0) ) +parse_args_output <- function(output) { + if (grepl("^usage:", output[1])) { + has_positional_arguments <- any(grepl("^positional arguments:", output)) + has_optional_arguments <- any(grepl("^optional arguments:", output)) + if (has_positional_arguments || has_optional_arguments) { + .print_message_and_exit(output, "help requested:") + } else { + .stop(output, "parse error:") + } + } else if (grepl("^Traceback", output[1])) { + .stop(output, "Error: python error") + } else if (any(grepl("^SyntaxError: Non-ASCII character", output))) { + message <- paste("Non-ASCII character detected.", + "If you wish to use Unicode arguments/options", + "please upgrade to Python 3.2+", + "Please see file INSTALL for more details.") + .stop(message, "non-ascii character error:") + } else if (any(grepl("^SyntaxError: positional argument follows keyword argument", output)) || + grepl("^SyntaxError: non-keyword arg after keyword arg", output[2])) { + message <- "Positional argument following keyword argument." + .stop(message, "syntax error:") + } else if (grepl("^\\{|^\\[", output)) { + args <- jsonlite::fromJSON(paste(output, collapse = "")) + return(args) + } else { # presumably version number request + .print_message_and_exit(output, "version requested:") + } +} # @param argument argument to be converted from R to Python convert_argument <- function(argument) { diff --git a/cran-comments.rst b/cran-comments.rst index c70513e..719a95b 100644 --- a/cran-comments.rst +++ b/cran-comments.rst @@ -1,9 +1,5 @@ ## Notes -* The examples and unit tests are now skipped if an appropriate - version of Python is not found as with the 'r-devel-windows-x86_64-gcc10-UCRT' - CRAN flavor. - * As in previous uploads while in a non-interactive session (i.e. in an Rscript) if ``parse_args()`` observes a help flag it will print a usage message and then call ``quit()``. Additionally if a user specifically adds diff --git a/man/ArgumentParser.Rd b/man/ArgumentParser.Rd index eec89f8..e939d4f 100644 --- a/man/ArgumentParser.Rd +++ b/man/ArgumentParser.Rd @@ -26,11 +26,6 @@ Default will be to use \code{findpython} package to find suitable Python binary. \code{ArgumentParser} creates a parser object that acts as a wrapper to Python's argparse module } -\section{Acknowledgement}{ - - A big thanks to Martin Diehl for a bug report. -} - \examples{ if (argparse:::detects_python()) { diff --git a/tests/testthat/test-argparse.R b/tests/testthat/test-argparse.R index 2ad58c3..f09d185 100644 --- a/tests/testthat/test-argparse.R +++ b/tests/testthat/test-argparse.R @@ -137,6 +137,17 @@ test_that("version flags works as expected", { expect_equal(length(el), 0) }) +context("parse_known_args()") +test_that("parse_known_args() works as expected", { + skip_if_not(detects_python()) + parser <- ArgumentParser() + parser$add_argument("-o", "--output_filename", default = "outfile.txt") + a_r <- parser$parse_known_args(c("-o", "foobar.txt", "-n", "4")) + expect_equal(a_r[[1]]$output_filename, "foobar.txt") + expect_equal(a_r[[2]], c("-n", "4")) + +}) + context("ArgumentParser") test_that("ArgumentParser works as expected", { skip_if_not(detects_python())