conflicted/0000755000176200001440000000000014366420267012376 5ustar liggesusersconflicted/NAMESPACE0000644000176200001440000000037714361532435013620 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method("[",conflict_report) S3method(print,conflict_report) export(conflict_prefer) export(conflict_prefer_all) export(conflict_prefer_matching) export(conflict_scout) export(conflicts_prefer) import(rlang) conflicted/LICENSE0000644000176200001440000000006014144726750013377 0ustar liggesusersYEAR: 2020 COPYRIGHT HOLDER: conflicted authors conflicted/README.md0000644000176200001440000001116614366231126013654 0ustar liggesusers # conflicted [![R-CMD-check](https://github.com/r-lib/conflicted/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/r-lib/conflicted/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/r-lib/conflicted/branch/main/graph/badge.svg)](https://app.codecov.io/gh/r-lib/conflicted?branch=main) The goal of conflicted is to provide an alternative conflict resolution strategy. R’s default conflict resolution system gives precedence to the most recently loaded package. This can make it hard to detect conflicts, particularly when introduced by an update to an existing package. conflicted takes a different approach, making every conflict an error and forcing you to choose which function to use. Thanks to [@krlmlr](https://github.com/krlmlr) for this neat idea! This code was previously part of the experimental [strict](https://github.com/hadley/strict) package, but I decided improved conflict resolution is useful by itself and worth its own package. ## Installation ``` r # install.packages("devtools") devtools::install_github("r-lib/conflicted") ``` ## Usage To use conflicted, all you need to do is load it: ``` r library(conflicted) library(dplyr) filter(mtcars, cyl == 8) #> Error: #> ! [conflicted] filter found in 2 packages. #> Either pick the one you want with `::`: #> • dplyr::filter #> • stats::filter #> Or declare a preference with `conflicts_prefer()`: #> • `conflicts_prefer(dplyr::filter)` #> • `conflicts_prefer(stats::filter)` ``` As suggested, you can either namespace individual calls: ``` r dplyr::filter(mtcars, am & cyl == 8) #> mpg cyl disp hp drat wt qsec vs am gear carb #> Ford Pantera L 15.8 8 351 264 4.22 3.17 14.5 0 1 5 4 #> Maserati Bora 15.0 8 301 335 3.54 3.57 14.6 0 1 5 8 ``` Or declare a session-wide preference: ``` r conflicts_prefer(dplyr::filter()) #> [conflicted] Will prefer dplyr::filter over any other package. filter(mtcars, am & cyl == 8) #> mpg cyl disp hp drat wt qsec vs am gear carb #> Ford Pantera L 15.8 8 351 264 4.22 3.17 14.5 0 1 5 4 #> Maserati Bora 15.0 8 301 335 3.54 3.57 14.6 0 1 5 8 ``` I recommend declaring preferences directly underneath the corresponding library call: ``` r library(dplyr) conflicts_prefer(dplyr::filter) ``` You can ask conflicted to report any conflicts in the current session: ``` r conflict_scout() #> 2 conflicts #> • `filter()`: dplyr #> • `lag()`: dplyr and stats ``` Functions surrounded by `[]` have been chosen using one of the built-in rules. Here `filter()` has been selected because of the preference declared above; the set operations have been selected because they follow the superset principle and extend the API of the base equivalents. ### How it works Loading conflicted creates a new “conflicted” environment that is attached just after the global environment. This environment contains an active binding for any object that is exported by multiple packages; the active binding will throw an error message describing how to disambiguate the name. The conflicted environment also contains bindings for `library()` and `require()` that suppress conflict reporting and update the conflicted environment with any new conflicts. ## Alternative approaches It is worth comparing conflicted to [modules](https://github.com/klmr/modules) and [import](https://github.com/rticulate/import). Both packages provide strict alternatives to `library()`, giving much finer control over what functions are added to the search path. ``` r # modules expects you to namespace all package functions dplyr <- modules::import_package('dplyr') dplyr$filter(mtcars, cyl == 8) # import expects you to explicit load functions import::from(dplyr, select, arrange, dplyr_filter = filter) dplyr_filter(mtcars, cyl == 8) ``` These require more upfront work than conflicted, in return for greater precision and control. Since conflicted was created base R also improved its tools for managing search path conflicts. See [the blog post](https://developer.r-project.org/Blog/public/2019/03/19/managing-search-path-conflicts/) by Luke Tierney for details. The main difference is that base R requires up front conflict resolution of all functions when loading a package; conflicted only reports problems as you use conflicted functions. ## Code of Conduct Please note that the conflicted project is released with a [Contributor Code of Conduct](https://conflicted.r-lib.org/CODE_OF_CONDUCT.html). By contributing to this project, you agree to abide by its terms. conflicted/man/0000755000176200001440000000000014366022572013146 5ustar liggesusersconflicted/man/conflict_scout.Rd0000644000176200001440000000156413337555204016461 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/find.R \name{conflict_scout} \alias{conflict_scout} \title{Find conflicts amongst a set of packages} \usage{ conflict_scout(pkgs = NULL) } \arguments{ \item{pkgs}{Set of packages for which to report conflicts. If \code{NULL}, the default, will report conflicts for all loaded packages} } \value{ A named list of character vectors. The names are functions and the values are the packages where they appear. If there is only a single package listed, it means that an automated disambiguation has selected that function. A user friendly print method displays the result as bulleted list. } \description{ \code{conflict_scout()} is the workhorse behind the conflicted package. You can call it directly yourself if you want to see all conflicts before hitting them in practice. } \examples{ conflict_scout() } conflicted/man/conflicts_prefer.Rd0000644000176200001440000000161514361536630016767 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/favor.R \name{conflicts_prefer} \alias{conflicts_prefer} \title{Declare many preferences at once} \usage{ conflicts_prefer(..., .quiet = FALSE) } \arguments{ \item{...}{Functions to prefer in form \code{pkg::fun} or \code{pkg::fun()}.} \item{.quiet}{If \code{TRUE}, all output will be suppressed} } \description{ \code{conflicts_prefer()} allows you to declare "winners" of conflicts, declaring one or many winners at once. See \code{\link[=conflict_prefer]{conflict_prefer()}} for more precise control. } \section{Best practices}{ I recommend placing a single call to \code{conflicts_prefer()} at the top of your script, immediately after loading all needed packages with calls to \code{library()}. } \examples{ conflicts_prefer( dplyr::filter(), dplyr::lag(), ) # or conflicts_prefer( dplyr::filter, dplyr::lag, ) } conflicted/man/conflict_prefer.Rd0000644000176200001440000000356014366022572016605 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/prefer.R \name{conflict_prefer} \alias{conflict_prefer} \alias{conflict_prefer_matching} \alias{conflict_prefer_all} \title{Persistently prefer one function over another} \usage{ conflict_prefer(name, winner, losers = NULL, quiet = FALSE) conflict_prefer_matching(pattern, winner, losers = NULL, quiet = FALSE) conflict_prefer_all(winner, losers = NULL, quiet = FALSE) } \arguments{ \item{name}{Name of function.} \item{winner}{Name of package that should win the conflict.} \item{losers}{Optional vector of packages that should lose the conflict. If omitted, \code{winner} will beat all comers.} \item{quiet}{If \code{TRUE}, all output will be suppressed} \item{pattern}{Regular expression used to select objects from the \code{winner} package.} } \description{ \code{conflict_prefer()} allows you to declare "winners" of conflicts. You can either declare a specific pairing (i.e. \code{dplyr::filter()} beats \code{base::filter()}), or an overall winner (i.e. \code{dplyr::filter()} beats all comers). As of conflicted 1.2.0, in most case you should use \code{\link[=conflicts_prefer]{conflicts_prefer()}} instead as it's both faster and easier to use. Use \code{conflicted_prefer_all()} to prefer all functions in a package, or \code{conflicted_prefer_matching()} to prefer functions that match a regular expression. } \examples{ # Prefer over all other packages conflict_prefer("filter", "dplyr") # Prefer over specified package or packages conflict_prefer("filter", "dplyr", "base") conflict_prefer("filter", "dplyr", c("base", "filtration")) # Prefer many functions that match a pattern \dontrun{ # Prefer col_* from vroom conflict_prefer_matching("^col_", "vroom") } # Or all functions from a package: \dontrun{ # Prefer all tidylog functions over dtplyr functions conflict_prefer_all("tidylog", "dtplyr") } } conflicted/man/conflicted-package.Rd0000644000176200001440000000222714317650051017136 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/conflicted.R \docType{package} \name{conflicted-package} \alias{conflicted} \alias{conflicted-package} \title{conflicted: An Alternative Conflict Resolution Strategy} \description{ R's default conflict management system gives the most recently loaded package precedence. This can make it hard to detect conflicts, particularly when they arise because a package update creates ambiguity that did not previously exist. 'conflicted' takes a different approach, making every conflict an error and forcing you to choose which function to use. } \section{Resolving conflicts}{ To permanently resolve a conflict within a session, use assignment: \preformatted{ library(conflicted) library(dplyr) filter <- dplyr::filter } } \seealso{ Useful links: \itemize{ \item \url{https://conflicted.r-lib.org/} \item \url{https://github.com/r-lib/conflicted} \item Report bugs at \url{https://github.com/r-lib/conflicted/issues} } } \author{ \strong{Maintainer}: Hadley Wickham \email{hadley@rstudio.com} Other contributors: \itemize{ \item RStudio [copyright holder, funder] } } \keyword{internal} conflicted/DESCRIPTION0000644000176200001440000000237614366420267014114 0ustar liggesusersPackage: conflicted Title: An Alternative Conflict Resolution Strategy Version: 1.2.0 Authors@R: c( person("Hadley", "Wickham", , "hadley@rstudio.com", role = c("aut", "cre")), person("RStudio", role = c("cph", "fnd")) ) Description: R's default conflict management system gives the most recently loaded package precedence. This can make it hard to detect conflicts, particularly when they arise because a package update creates ambiguity that did not previously exist. 'conflicted' takes a different approach, making every conflict an error and forcing you to choose which function to use. License: MIT + file LICENSE URL: https://conflicted.r-lib.org/, https://github.com/r-lib/conflicted BugReports: https://github.com/r-lib/conflicted/issues Depends: R (>= 3.2) Imports: cli (>= 3.4.0), memoise, rlang (>= 1.0.0) Suggests: callr, covr, dplyr, Matrix, methods, pkgload, testthat (>= 3.0.0), withr Config/Needs/website: tidyverse/tidytemplate Config/testthat/edition: 3 Encoding: UTF-8 RoxygenNote: 7.2.3 NeedsCompilation: no Packaged: 2023-01-31 19:50:23 UTC; hadleywickham Author: Hadley Wickham [aut, cre], RStudio [cph, fnd] Maintainer: Hadley Wickham Repository: CRAN Date/Publication: 2023-02-01 08:20:06 UTC conflicted/tests/0000755000176200001440000000000014366270377013545 5ustar liggesusersconflicted/tests/testthat/0000755000176200001440000000000014366420266015377 5ustar liggesusersconflicted/tests/testthat/funmatch/0000755000176200001440000000000014366270377017212 5ustar liggesusersconflicted/tests/testthat/funmatch/NAMESPACE0000644000176200001440000000003214366022572020414 0ustar liggesusersexport(pi) export(median) conflicted/tests/testthat/funmatch/DESCRIPTION0000644000176200001440000000066514150006510020700 0ustar liggesusersPackage: funmatch Title: What the Package Does (One Line, Title Case) Version: 0.0.0.9000 Authors@R: c( person("Hadley", "Wickham", , "hadley@rstudio.com", role = c("aut", "cre")), person("RStudio", role = c("cph", "fnd")) ) Description: What the package does (one paragraph). License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a license Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.1.2 conflicted/tests/testthat/funmatch/R/0000755000176200001440000000000014366022572017403 5ustar liggesusersconflicted/tests/testthat/funmatch/R/test.R0000644000176200001440000000004114366022572020500 0ustar liggesusersmedian <- 10 pi <- function() 3 conflicted/tests/testthat/test-prefer.R0000644000176200001440000000336114366230556017766 0ustar liggesuserstest_that("can register preference over all or selected packages", { on.exit(prefs_reset()) conflict_prefer("x1", "pkga", quiet = TRUE) conflict_prefer("x2", "pkga", "pkgb", quiet = TRUE) conflict_prefer("x3", "pkga", c("pkgb", "pkgc"), quiet = TRUE) expect_setequal(prefs_ls(), c("x1", "x2", "x3")) }) # resolution -------------------------------------------------------------- test_that("length 1 vector beats all comers", { on.exit(prefs_reset()) conflict_prefer("x1", "pkga", quiet = TRUE) expect_equal(prefs_resolve("x1"), "pkga") }) test_that("length n vector beats listed others", { on.exit(prefs_reset()) conflict_prefer("x1", "pkga", "pkgb", quiet = TRUE) expect_equal(prefs_resolve("x1", c("pkga", "pkgb")), "pkga") expect_equal(prefs_resolve("x1", c("pkga", "pkgb", "pkgc")), c("pkga", "pkgc")) }) # messaging ---------------------------------------------------------------- test_that("useful messages for specific preferences", { withr::defer(prefs_reset()) expect_snapshot({ conflict_prefer("mean", "canoodle", c("noodle", "doodle")) conflict_prefer("mean", "canoodle", "boodle") conflict_prefer("+", "canoodle") }) }) test_that("can register preference for multiple functions", { pkgload::load_all(test_path("funmatch"), quiet = TRUE, export_all = FALSE) withr::defer({ pkgload::unload("funmatch") prefs_reset() }) expect_snapshot({ conflict_prefer_all("funmatch") }) expect_setequal(prefs_ls(), c("median", "pi")) prefs_reset() expect_snapshot({ conflict_prefer_all("funmatch", "base") }) expect_setequal(prefs_ls(), "pi") prefs_reset() expect_snapshot({ conflict_prefer_matching("m", "funmatch") }) expect_setequal(prefs_ls(), "median") prefs_reset() }) conflicted/tests/testthat/test-favor.R0000644000176200001440000000052614361536266017622 0ustar liggesuserstest_that("trailing () is optional", { withr::defer(prefs_reset()) expect_snapshot({ conflicts_prefer( dplyr::lag, dplyr::filter() ) }) }) test_that("errors if invalid form", { expect_snapshot(error = TRUE, { conflicts_prefer(1) conflicts_prefer(foo()) conflicts_prefer(dplyr::filter(a = 1)) }) }) conflicted/tests/testthat/data/0000755000176200001440000000000014366270377016316 5ustar liggesusersconflicted/tests/testthat/data/NAMESPACE0000644000176200001440000000000014150013633017500 0ustar liggesusersconflicted/tests/testthat/data/data/0000755000176200001440000000000014366270377017227 5ustar liggesusersconflicted/tests/testthat/data/data/mtcars.rda0000644000176200001440000000013114150014220021151 0ustar liggesusersBZh91AY&SY9 <-߀1D@( !d=F?T(i&&kIˆػnA&A {?QK|R_"(Hconflicted/tests/testthat/data/DESCRIPTION0000644000176200001440000000073214150013710017777 0ustar liggesusersPackage: data Title: What the Package Does (One Line, Title Case) Version: 0.0.0.9000 Authors@R: c( person("Hadley", "Wickham", , "hadley@rstudio.com", role = c("aut", "cre")), person("RStudio", role = c("cph", "fnd")) ) Description: What the package does (one paragraph). License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a license Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.1.2 Depends: R (>= 2.10) LazyData: true conflicted/tests/testthat/test-package.R0000644000176200001440000000041414366230556020072 0ustar liggesuserstest_that("pkg_ls() respects exclude", { skip_if_not(getRversion() >= "3.6") library(callr) expect_true("r" %in% pkg_ls("callr")) pkgload::unload("callr") library(callr, exclude = "r") expect_false("r" %in% pkg_ls("callr")) pkgload::unload("callr") }) conflicted/tests/testthat/test-shim.R0000644000176200001440000000362714357277473017461 0ustar liggesuserstest_that("shimmed arguments match unshimmed", { shims_bind() expect_equal(formals(require), formals(base::require)) expect_equal(formals(library), formals(base::library)) }) test_that("shims load package with conflicts silently", { col_red <- function() {} shims_bind() expect_message(library(cli), NA) detach("package:cli") expect_message(require(cli, quietly = TRUE), NA) detach("package:cli") }) test_that("detaching package removes shims", { conflict <- "ns_env" skip_if_not(conflict %in% pkg_ls("pkgload") && conflict %in% pkg_ls("rlang")) shims_bind() library(pkgload) library(rlang) expect_true(exists(conflict, ".conflicts", inherits = FALSE)) detach("package:pkgload") detach("package:rlang") expect_false(exists(conflict, ".conflicts", inherits = FALSE)) }) test_that("shimmed help returns same as unshimmed", { shims_bind() expect_equal( library(help = "rlang"), base::library(help = "rlang") ) expect_equal( library(help = rlang), base::library(help = rlang) ) }) test_that("shimmed library() returns same as unshimmed", { # skip on CRAN because library() returns list of all installed packages # which might be changing in the background as other packages are installed skip_on_cran() shims_bind() expect_equal(library(), base::library()) }) # package_name ------------------------------------------------------------ test_that("package_name mimics library", { expect_equal(package_name(quo(ggplot2)), "ggplot2") x <- "ggplot2" expect_equal(package_name(quo(x), TRUE), "ggplot2") }) test_that("package_name throws errors with invalid names" ,{ x <- c("x", "y") expect_error(package_name(quo(x), TRUE), "character vector") x <- 1:10 expect_error(package_name(quo(x), TRUE), "character vector") x <- NA_character_ expect_error(package_name(quo(x), TRUE), "NA") x <- "" expect_error(package_name(quo(x), TRUE), "''") }) conflicted/tests/testthat/primitive/0000755000176200001440000000000014366270377017415 5ustar liggesusersconflicted/tests/testthat/primitive/NAMESPACE0000644000176200001440000000001414144733754020624 0ustar liggesusersexport(sum) conflicted/tests/testthat/primitive/DESCRIPTION0000644000176200001440000000066614144733754021130 0ustar liggesusersPackage: primitive Title: What the Package Does (One Line, Title Case) Version: 0.0.0.9000 Authors@R: c( person("Hadley", "Wickham", , "hadley@rstudio.com", role = c("aut", "cre")), person("RStudio", role = c("cph", "fnd")) ) Description: What the package does (one paragraph). License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a license Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.1.2 conflicted/tests/testthat/primitive/R/0000755000176200001440000000000014144733754017613 5ustar liggesusersconflicted/tests/testthat/primitive/R/sum.R0000644000176200001440000000002614144733754020540 0ustar liggesuserssum <- function(x) 10 conflicted/tests/testthat/_snaps/0000755000176200001440000000000014366022572016660 5ustar liggesusersconflicted/tests/testthat/_snaps/favor.md0000644000176200001440000000134614366022611020315 0ustar liggesusers# trailing () is optional Code conflicts_prefer(dplyr::lag, dplyr::filter()) Message [conflicted] Will prefer dplyr::lag over any other package. [conflicted] Will prefer dplyr::filter over any other package. # errors if invalid form Code conflicts_prefer(1) Condition Error in `conflicts_prefer()`: ! All arguments must be in form `pkg::fun` or `pkg::fun()`. Code conflicts_prefer(foo()) Condition Error in `conflicts_prefer()`: ! All arguments must be in form `pkg::fun` or `pkg::fun()`. Code conflicts_prefer(dplyr::filter(a = 1)) Condition Error in `conflicts_prefer()`: ! All arguments must be in form `pkg::fun` or `pkg::fun()`. conflicted/tests/testthat/_snaps/prefer.md0000644000176200001440000000204214366022612020456 0ustar liggesusers# useful messages for specific preferences Code conflict_prefer("mean", "canoodle", c("noodle", "doodle")) Message [conflicted] Will prefer canoodle::mean over noodle::mean and doodle::mean. Code conflict_prefer("mean", "canoodle", "boodle") Message [conflicted] Removing existing preference. [conflicted] Will prefer canoodle::mean over boodle::mean. Code conflict_prefer("+", "canoodle") Message [conflicted] Will prefer canoodle::`+` over any other package. # can register preference for multiple functions Code conflict_prefer_all("funmatch") Message [conflicted] Will prefer funmatch::median over any other package. [conflicted] Will prefer funmatch::pi over any other package. --- Code conflict_prefer_all("funmatch", "base") Message [conflicted] Will prefer funmatch::pi over base::pi. --- Code conflict_prefer_matching("m", "funmatch") Message [conflicted] Will prefer funmatch::median over any other package. conflicted/tests/testthat/_snaps/disambiguate.md0000644000176200001440000000360414366022610021634 0ustar liggesusers# error message is informative Code disambiguate_prefix("x", c("a", "b", "c"))() Condition Error: ! [conflicted] x found in 3 packages. Either pick the one you want with `::`: * a::x * b::x * c::x Or declare a preference with `conflicts_prefer()`: * `conflicts_prefer(a::x)` * `conflicts_prefer(b::x)` * `conflicts_prefer(c::x)` Code disambiguate_prefix("if", c("a", "b", "c"))() Condition Error: ! [conflicted] if found in 3 packages. Either pick the one you want with `::`: * a::`if` * b::`if` * c::`if` Or declare a preference with `conflicts_prefer()`: * `` conflicts_prefer(a::`if`) `` * `` conflicts_prefer(b::`if`) `` * `` conflicts_prefer(c::`if`) `` Code disambiguate_infix("%in%", c("a", "b", "c"))() Condition Error: ! [conflicted] %in% found in 3 packages. Declare a preference with `conflicts_prefer()`: * `` conflicts_prefer(a::`%in%`) `` * `` conflicts_prefer(b::`%in%`) `` * `` conflicts_prefer(c::`%in%`) `` # display namespace if not attached Code cnds$prefix Output Error: ! [conflicted] x found in 3 packages. Either pick the one you want with `::`: * a::x * b::x * c::x Or declare a preference with `conflicted::conflicts_prefer()`: * `conflicted::conflicts_prefer(a::x)` * `conflicted::conflicts_prefer(b::x)` * `conflicted::conflicts_prefer(c::x)` Code cnds$infix Output Error: ! [conflicted] %in% found in 3 packages. Declare a preference with `conflicted::conflicts_prefer()`: * `` conflicted::conflicts_prefer(a::`%in%`) `` * `` conflicted::conflicts_prefer(b::`%in%`) `` * `` conflicted::conflicts_prefer(c::`%in%`) `` conflicted/tests/testthat/_snaps/find.md0000644000176200001440000000041014366022611020107 0ustar liggesusers# conflict_scout prints usefully Code conflict_scout("dplyr") Message 0 conflicts Code conflict_scout(c("rlang", "pkgload")) Message 2 conflicts * `ns_env()`: rlang and pkgload * `pkg_env()`: rlang and pkgload conflicted/tests/testthat/test-zzz.R0000644000176200001440000000040314150212424017313 0ustar liggesuserstest_that("unloading removes shims", { # Do in separate process to avoid interferring with tests in_search <- callr::r(function() { library(conflicted) pkgload::unload("conflicted") ".conflicts" %in% search() }) expect_false(in_search) }) conflicted/tests/testthat/test-disambiguate.R0000644000176200001440000000202414361532435021130 0ustar liggesuserstest_that("error message is informative", { expect_snapshot(error = TRUE, { disambiguate_prefix("x", c("a", "b", "c"))() disambiguate_prefix("if", c("a", "b", "c"))() disambiguate_infix("%in%", c("a", "b", "c"))() }) }) test_that("display namespace if not attached", { cnds <- callr::r(function(is_devel) { if (is_devel) { pkgload::load_all(attach = FALSE) } list( prefix = rlang::catch_cnd( conflicted:::disambiguate_prefix("x", c("a", "b", "c"))() ), infix = rlang::catch_cnd( conflicted:::disambiguate_infix("%in%", c("a", "b", "c"))() ) ) }, list(is_devel = pkgload::is_dev_package("conflicted"))) expect_snapshot({ cnds$prefix cnds$infix }) }) test_that("can save active binding without error", { env <- env( a = disambiguate_prefix("x", c("a", "b", "c")), b = disambiguate_infix("y", c("a", "b", "c")) ) expect_error(save(a, envir = env, file = tempfile()), NA) expect_error(save(b, envir = env, file = tempfile()), NA) }) conflicted/tests/testthat/test-find.R0000644000176200001440000000376614366022572017431 0ustar liggesuserstest_that("conflict_scout prints usefully", { expect_snapshot({ conflict_scout("dplyr") conflict_scout(c("rlang", "pkgload")) }) }) test_that("primitive functions are never supersets", { pkgload::load_all(test_path("primitive"), quiet = TRUE) on.exit(pkgload::unload("primitive")) expect_false(is_superset("sum", "primitive", "base")) expect_equal( superset_principle("sum", c("primitive", "base")), c("primitive", "base") ) }) test_that("superset", { # by definition/design, there are no real conflicts in base functions expect_equal(superset_principle("cbind", c("base", "methods")), character()) # Automatically created S4 generics obey the superset principle expect_equal(superset_principle("print", c("base", "Matrix")), character()) # Even if the arguments have been customised expect_equal(superset_principle("rcond", c("base", "Matrix")), character()) }) test_that("functions aren't conflicts with non-functions", { pkgload::load_all(test_path("funmatch"), quiet = TRUE) on.exit(pkgload::unload("funmatch")) expect_equal(function_lookup("pi", c("base", "funmatch")), character()) expect_equal(function_lookup("median", c("stats", "funmatch")), character()) }) test_that("can find conflicts with data", { pkgload::load_all(test_path("data"), quiet = TRUE) on.exit(pkgload::unload("data")) expect_named(conflict_scout(c("datasets", "data")), "mtcars") }) # moved functions ---------------------------------------------------- test_that(".Deprecated call contains function name", { f <- function() { .Deprecated("pkg::x") } expect_false(has_moved("pkg", "foo", f)) expect_true(has_moved("pkg", "x", f)) }) test_that("returns FALSE for weird inputs", { expect_false(has_moved(obj = 20)) expect_false(has_moved(obj = mean)) f <- function() {} expect_false(has_moved(obj = mean)) f <- function() { .Deprecated() } expect_false(has_moved(obj = mean)) f <- function() { .Deprecated(1) } expect_false(has_moved(obj = mean)) }) conflicted/tests/testthat.R0000644000176200001440000000010013300400260015464 0ustar liggesuserslibrary(testthat) library(conflicted) test_check("conflicted") conflicted/R/0000755000176200001440000000000014366270377012604 5ustar liggesusersconflicted/R/conflicts.R0000644000176200001440000000164014366022572014704 0ustar liggesusersconflicts_register <- function(pkgs = pkgs_attached()) { conflicts <- conflict_scout(pkgs) env <- conflicts_init() map2(names(conflicts), conflicts, conflict_disambiguate, env = env) # Shim library() and require() so we can rebuild shims_bind(env) env } conflicts_register_if_needed <- function(package) { if (pkg_attached(package)) { conflicts_register() } invisible() } conflicts_remove <- function(pkg) { # The detach hook is called before the package is removed from the search path conflicts_register(setdiff(pkgs_attached(), pkg)) } # Environment management ------------------------------------------------- conflicts_init <- function() { conflicts_reset() get("attach")(env(), name = ".conflicts") } conflicts_attached <- function() { ".conflicts" %in% search() } conflicts_reset <- function() { if (conflicts_attached()) { detach(".conflicts", character.only = TRUE) } } conflicted/R/utils.R0000644000176200001440000000241114357277473014071 0ustar liggesusersinvert <- function(x) { if (length(x) == 0) return() stacked <- utils::stack(x) tapply(as.character(stacked$ind), stacked$values, list) } pkg_devtools <- function(name) { ns <- .getNamespace(name) if (is.null(ns)) { return(FALSE) } !is.null(ns$.__DEVTOOLS__) } on_detach <- function(pkg, fun) { force(fun) done <- FALSE call_once <- function(...) { if (done) return() done <<- TRUE fun() } setHook(packageEvent(pkg, "detach"), call_once) } map2 <- function(.x, .y, .f, ...) { mapply(.f, .x, .y, MoreArgs = list(...), SIMPLIFY = FALSE) } map_chr <- function(.x, .f, ...) { vapply(.x, .f, ..., FUN.VALUE = character(1)) } unique_obj <- function(name, pkgs) { objs <- lapply(pkgs, getExportedValue, name) names(objs) <- pkgs pkgs[!duplicated(objs)] } style_object <- function(pkg, name, winner = FALSE) { paste0( if (winner) cli::style_bold(cli::col_blue(pkg)) else cli::col_blue(pkg), "::", backtick(name) ) } label_conflicted <- function() { cli::col_grey("[conflicted]") } backtick <- function(x) { ifelse(x == make.names(x), x, paste0("`", x, "`")) } compact <- function(x) { empty <- vapply(x, is_empty, logical(1)) x[!empty] } cat_line <- function(...) { cat(paste0(..., "\n", collapse = "")) } conflicted/R/zzz.R0000644000176200001440000000056114150212252013541 0ustar liggesusers.onLoad <- function(...) { has_moved <<- memoise::memoise(has_moved) } .onAttach <- function(...) { conflicts_register() } # This should really be done on .onDetach(), but because it's called inside of # detach() it messes up the computation of `pos` inside of `detach()` and it # detaches the wrong environment .onUnload <- function(...) { conflicts_reset() } conflicted/R/favor.R0000644000176200001440000000267214361536627014051 0ustar liggesusers#' Declare many preferences at once #' #' @description #' `conflicts_prefer()` allows you to declare "winners" of conflicts, #' declaring one or many winners at once. #' #' See [conflict_prefer()] for more precise control. #' #' @section Best practices: #' I recommend placing a single call to `conflicts_prefer()` at the top of #' your script, immediately after loading all needed packages with calls to #' `library()`. #' #' @export #' @param ... Functions to prefer in form `pkg::fun` or `pkg::fun()`. #' @inheritParams conflict_prefer #' @examples #' conflicts_prefer( #' dplyr::filter(), #' dplyr::lag(), #' ) #' #' # or #' conflicts_prefer( #' dplyr::filter, #' dplyr::lag, #' ) conflicts_prefer <- function(..., .quiet = FALSE) { calls <- exprs(...) # accept pkg::fun() or pkg::fun calls <- lapply(calls, standardise_call) is_ok <- vapply(calls, is_ns_call, logical(1)) if (any(!is_ok)) { cli::cli_abort( "All arguments must be in form {.code pkg::fun} or {.fn pkg::fun}." ) } pkgs <- vapply(calls, function(x) as.character(x[[2]]), character(1)) funs <- vapply(calls, function(x) as.character(x[[3]]), character(1)) for (i in seq_along(calls)) { conflict_preference_register(funs[[i]], pkgs[[i]], quiet = .quiet) } conflicts_register() invisible() } standardise_call <- function(x) { if (is_call(x, n = 0)) { x[[1]] } else { x } } is_ns_call <- function(x) { is_call(x, "::", n = 2) } conflicted/R/shim.R0000644000176200001440000001230413503251737013656 0ustar liggesusersshims_bind <- function(env = caller_env()) { if (getRversion() >= "3.6.0") { env_bind(env, library = shim_library_3_6, require = shim_require_3_6 ) } else { env_bind(env, library = shim_library_3_1, require = shim_require_3_1 ) } } # library ----------------------------------------------------------------- shim_library_3_1 <- function(package, help, pos = 2, lib.loc = NULL, character.only = FALSE, logical.return = FALSE, warn.conflicts = TRUE, quietly = FALSE, verbose = getOption("verbose") ) { if (!missing(package)) { package <- package_name(enquo(package), character.only = character.only) conflicts_reset() on.exit(conflicts_register()) on_detach(package, function() conflicts_remove(package)) library( package, pos = pos, lib.loc = lib.loc, character.only = TRUE, logical.return = logical.return, warn.conflicts = FALSE, quietly = quietly, verbose = verbose ) } else if (!missing(help)) { help <- package_name(enquo(help), character.only = character.only) library( help = help, character.only = TRUE ) } else { library( lib.loc = lib.loc, logical.return = logical.return ) } } if (getRversion() >= "3.6.0") { shim_library_3_6 <- function(package, help, pos = 2, lib.loc = NULL, character.only = FALSE, logical.return = FALSE, warn.conflicts, quietly = FALSE, verbose = getOption("verbose"), mask.ok, exclude, include.only, attach.required = missing(include.only) ) { if (!missing(package)) { package <- package_name(enquo(package), character.only = character.only) conflicts_reset() on.exit(conflicts_register()) on_detach(package, function() conflicts_remove(package)) library( package, pos = pos, lib.loc = lib.loc, character.only = TRUE, logical.return = logical.return, warn.conflicts = FALSE, quietly = quietly, verbose = verbose, mask.ok = mask.ok, exclude = exclude, include.only = include.only, attach.required = attach.required ) } else if (!missing(help)) { help <- package_name(enquo(help), character.only = character.only) library( help = help, character.only = TRUE ) } else { library( lib.loc = lib.loc, logical.return = logical.return ) } } } else { shim_library_3_6 <- function(...) {} } # require ----------------------------------------------------------------- shim_require_3_1 <- function(package, lib.loc = NULL, quietly = FALSE, warn.conflicts = TRUE, character.only = FALSE) { package <- package_name(enquo(package), character.only = character.only) conflicts_reset() on.exit(conflicts_register()) on_detach(package, function() conflicts_remove(package)) require( package, lib.loc = lib.loc, quietly = quietly, warn.conflicts = FALSE, character.only = TRUE ) } if (getRversion() >= "3.6.0") { shim_require_3_6 <- function(package, lib.loc = NULL, quietly = FALSE, warn.conflicts, character.only = FALSE, mask.ok, exclude, include.only, attach.required = missing(include.only) ) { package <- package_name(enquo(package), character.only = character.only) conflicts_reset() on.exit(conflicts_register()) on_detach(package, function() conflicts_remove(package)) require( package, lib.loc = lib.loc, quietly = quietly, warn.conflicts = FALSE, character.only = TRUE, mask.ok = mask.ok, exclude = exclude, include.only = include.only, attach.required = attach.required ) } } else { shim_require_3_6 <- function(...) {} } # Helpers ----------------------------------------------------------------- package_name <- function(package, character.only = FALSE) { if (!character.only) { package <- as.character(quo_squash(package)) } else { package <- eval_tidy(package) } if (!is.character(package) || length(package) != 1L) { abort("`package` must be character vector of length 1.") } if (is.na(package) || (package == "")) { abort("`package` must not be NA or ''.") } package } conflicted/R/prefer.R0000644000176200001440000000752714366022572014215 0ustar liggesusers#' Persistently prefer one function over another #' #' @description #' `conflict_prefer()` allows you to declare "winners" of conflicts. #' You can either declare a specific pairing (i.e. `dplyr::filter()` beats #' `base::filter()`), or an overall winner (i.e. `dplyr::filter()` beats #' all comers). As of conflicted 1.2.0, in most case you should use #' [conflicts_prefer()] instead as it's both faster and easier to use. #' #' Use `conflicted_prefer_all()` to prefer all functions in a package, or #' `conflicted_prefer_matching()` to prefer functions that match a regular #' expression. #' #' @param name Name of function. #' @param winner Name of package that should win the conflict. #' @param losers Optional vector of packages that should lose the conflict. #' If omitted, `winner` will beat all comers. #' @param quiet If `TRUE`, all output will be suppressed #' @export #' @examples #' # Prefer over all other packages #' conflict_prefer("filter", "dplyr") #' #' # Prefer over specified package or packages #' conflict_prefer("filter", "dplyr", "base") #' conflict_prefer("filter", "dplyr", c("base", "filtration")) #' #' # Prefer many functions that match a pattern #' \dontrun{ #' # Prefer col_* from vroom #' conflict_prefer_matching("^col_", "vroom") #' } #' # Or all functions from a package: #' \dontrun{ #' # Prefer all tidylog functions over dtplyr functions #' conflict_prefer_all("tidylog", "dtplyr") #' } conflict_prefer <- function(name, winner, losers = NULL, quiet = FALSE) { conflict_preference_register(name, winner, losers = losers, quiet = quiet) conflicts_register_if_needed(winner) } conflict_preference_register <- function(name, winner, losers = NULL, quiet = FALSE) { stopifnot(is.character(name), length(name) == 1) stopifnot(is.character(winner), length(winner) == 1) stopifnot(is.null(losers) || is.character(losers)) if (env_has(prefs, name)) { if (!quiet) { cli::cli_inform( "{label_conflicted()} Removing existing preference." ) } } if (!quiet) { full <- style_object(winner, name, winner = TRUE) if (is.null(losers)) { cli::cli_inform( "{label_conflicted()} Will prefer {full} over any other package." ) } else { alt <- style_object(losers, name) cli::cli_inform( "{label_conflicted()} Will prefer {full} over {alt}." ) } } env_bind(prefs, !!name := c(winner, losers)) invisible() } #' @export #' @param pattern Regular expression used to select objects from the `winner` #' package. #' @rdname conflict_prefer conflict_prefer_matching <- function(pattern, winner, losers = NULL, quiet = FALSE) { names <- grep(pattern, sort(pkg_ls(winner)), value = TRUE) names <- losers_intersect(names, losers) for (name in names) { conflict_preference_register(name, winner, losers = losers, quiet = quiet) } conflicts_register_if_needed(winner) } #' @export #' @rdname conflict_prefer conflict_prefer_all <- function(winner, losers = NULL, quiet = FALSE) { names <- sort(pkg_ls(winner)) names <- losers_intersect(names, losers) for (name in names) { conflict_preference_register(name, winner, losers = losers, quiet = quiet) } conflicts_register_if_needed(winner) } losers_intersect <- function(names, losers) { if (is.null(losers)) { names } else { loser_names <- unlist(lapply(losers, pkg_ls)) names <- intersect(names, loser_names) } } prefs_resolve <- function(fun, conflicts) { pkgs <- prefs[[fun]] if (length(pkgs) == 1) { pkgs[[1]] } else { c(pkgs[[1]], setdiff(conflicts, pkgs)) } } # Environment management -------------------------------------------------- # contains character vectors - first element gives winner, # subsequent elements (if present) gives losers prefs <- env() prefs_ls <- function() { env_names(prefs) } prefs_reset <- function() { env_unbind(prefs, env_names(prefs)) } conflicted/R/find.R0000644000176200001440000001122214357277473013651 0ustar liggesusers#' Find conflicts amongst a set of packages #' #' `conflict_scout()` is the workhorse behind the conflicted package. You can #' call it directly yourself if you want to see all conflicts before hitting #' them in practice. # #' @param pkgs Set of packages for which to report conflicts. If `NULL`, #' the default, will report conflicts for all loaded packages #' @return A named list of character vectors. The names are functions and #' the values are the packages where they appear. If there is only a single #' package listed, it means that an automated disambiguation has selected #' that function. #' #' A user friendly print method displays the result as bulleted list. #' @export #' @examples #' conflict_scout() conflict_scout <- function(pkgs = NULL) { pkgs <- pkgs %||% pkgs_attached() objs <- lapply(pkgs, pkg_ls) names(objs) <- pkgs index <- invert(objs) potential <- Filter(function(x) length(x) > 1, index) # Only consider it a conflict if the objects are actually different unique <- Map(unique_obj, names(potential), potential) conflicts <- Filter(function(x) length(x) > 1, unique) # superset principle: ignore single conflict with base packages # unless attr(f, "conflicted_superset") is FALSE conflicts <- map2(names(conflicts), conflicts, superset_principle) # a function that has moved packages should never conflict conflicts <- map2(names(conflicts), conflicts, drop_moved) # a function doesn't conflict with a non-dataset conflicts <- map2(names(conflicts), conflicts, function_lookup) # apply declared user preferences for (fun in ls(prefs)) { if (!has_name(conflicts, fun)) next conflicts[[fun]] <- prefs_resolve(fun, conflicts[[fun]]) } conflicts <- compact(conflicts) new_conflict_report(conflicts) } new_conflict_report <- function(conflicts, n = length(conflicts)) { structure(conflicts, n = n, class = "conflict_report") } #' @export `[.conflict_report` <- function(x, ...) { new_conflict_report(NextMethod(), n = attr(x, "n")) } #' @export print.conflict_report <- function(x, ...) { n <- attr(x, "n") cli::cli_text("{n} conflict{?s}") if (length(x) == 0) { return(invisible(x)) } x <- x[order(names(x))] fun <- map_chr(names(x), function(x) sprintf("{.fn %s}", x)) pkgs <- map_chr(x, function(x) cli::format_inline("{.pkg {x}}")) bullets <- paste0(fun, ": ", pkgs) names(bullets) <- rep("*", length(bullets)) cli::cli_bullets(bullets) if (n > length(x)) { cat_line("...") } invisible(x) } superset_principle <- function(fun, pkgs) { base <- intersect(pkgs, base_packages) non_base <- setdiff(pkgs, base_packages) if (length(non_base) == 0) { # all base, so no conflicts character() } else if (length(non_base) == 1) { # only one so see if it assumes superset principle if (is_superset(fun, non_base, base = base)) { character() } else { pkgs } } else { pkgs } } function_lookup <- function(fun, pkgs) { # Only apply this rule if exaclty two conflicts if (length(pkgs) != 2) { return(pkgs) } pkg1 <- getExportedValue(pkgs[[1]], fun) pkg2 <- getExportedValue(pkgs[[2]], fun) if (is.function(pkg1) != is.function(pkg2)) { character() } else { pkgs } } is_superset <- function(fun, pkg, base) { # Special case dplyr::lag() which looks like it should agree if (pkg == "dplyr" && fun == "lag") return(FALSE) pkg_obj <- getExportedValue(pkg, fun) # If it's a standardGeneric, assume the author know's what they're doing if (methods::is(pkg_obj, "standardGeneric")) { return(TRUE) } base_obj <- getExportedValue(base, fun) if (!is.function(pkg_obj) || !is.function(base_obj)) return(FALSE) # Assume any function that just takes ... passes them on appropriately, # like BiocGenerics::table() args_pkg <- names(fn_fmls(pkg_obj)) if (identical(args_pkg, "...")) return(TRUE) if (is.primitive(base_obj)){ return(FALSE) } args_base <- names(fn_fmls(base_obj)) # To be a superset, all base arguments must be included in the pkg funtion all(args_base %in% args_pkg) } drop_moved <- function(fun, pkgs) { is_dep <- vapply(pkgs, has_moved, fun = fun, FUN.VALUE = logical(1)) pkgs[!is_dep] } # memoised onLoad has_moved <- function(pkg, fun, obj = NULL) { if (is.null(obj)) { obj <- getExportedValue(pkg, fun) } if (!is.function(obj)) { return(FALSE) } body <- body(obj) if (length(body) < 2 || !is_call(body, "{")) return(FALSE) if (!is_call(body[[2]], ".Deprecated")) return(FALSE) if (length(body[[2]]) < 2) return(FALSE) new <- body[[2]][[2]] if (!is.character(new)) return(FALSE) grepl(paste0("::", fun), new) } conflicted/R/conflicted.R0000644000176200001440000000040114357315551015026 0ustar liggesusers#' @section Resolving conflicts: #' #' To permanently resolve a conflict within a session, use assignment: #' #' \preformatted{ #' library(conflicted) #' library(dplyr) #' #' filter <- dplyr::filter #' } #' #' @keywords internal #' @import rlang "_PACKAGE" conflicted/R/disambiguate.R0000644000176200001440000000523614361536422015362 0ustar liggesusersconflict_disambiguate <- function(fun, pkgs, env) { if (length(pkgs) == 0) { # no conflict, so no action needed } else if (length(pkgs) == 1) { # No ambiguity, but need to make sure this choice wins, not version # from search path (which might be in wrong order) env_bind(env, !!fun := getExportedValue(pkgs, fun)) } else { if (is_infix_fun(fun)) { env_bind_active(env, !!fun := disambiguate_infix(fun, pkgs)) } else { env_bind_active(env, !!fun := disambiguate_prefix(fun, pkgs)) } } } disambiguate_infix <- function(name, pkgs) { force(name) force(pkgs) function(value) { if (from_save(sys.calls())) return(NULL) prefer <- prefer_bullets(pkgs, name) cli::cli_abort(c( "{label_conflicted()} {.strong {name}} found in {length(pkgs)} packages.", "Declare a preference with {.fn {add_ns('conflicts_prefer')}}:", prefer )) } } disambiguate_prefix <- function(name, pkgs) { force(name) force(pkgs) function(value) { if (from_save(sys.calls())) return(NULL) bt_name <- backtick(name) namespace <- map_chr(pkgs, function(pkg) { style_object(pkg, name) }) names(namespace) <- rep("*", length(namespace)) prefer <- prefer_bullets(pkgs, name) cli::cli_abort(c( "{label_conflicted()} {.strong {name}} found in {length(pkgs)} packages.", "Either pick the one you want with {.code ::}:", namespace, "Or declare a preference with {.fn {add_ns('conflicts_prefer')}}:", prefer )) } } add_ns <- function(fun = "") { paste0(if (!pkg_attached("conflicted")) "conflicted::", fun) } # Helpers ----------------------------------------------------------------- prefer_bullets <- function(pkgs, name) { if (make.names(name) != name) { name <- backtick(name) } ns <- add_ns() prefer <- map_chr(pkgs, function(pkg) { sprintf( "{.run [%sconflicts_prefer(%s::%s)](conflicted::conflicts_prefer(%s::%s))}", ns, pkg, name, pkg, name ) }) names(prefer) <- rep("*", length(prefer)) prefer } is_infix_fun <- function(name) { base <- c( ":", "::", ":::", "$", "@", "^", "*", "/", "+", "-", ">", ">=", "<", "<=", "==", "!=", "!", "&", "&&", "|", "||", "~", "<-", "<<-" ) name %in% base || grepl("^%.*%$", name) } # This is a hack needed for RStudio, which saves and restores environments # when you "install and restart", because save() evaluates active bindings. from_save <- function(calls) { is_save <- function(call) { is_call(call) && length(call) > 1 && is_symbol(call[[1]], "save") } for (call in calls) { if (is_save(call)) return(TRUE) } FALSE } conflicted/R/package.R0000644000176200001440000000215514366230556014320 0ustar liggesuserspkgs_attached <- function() { pkgs <- gsub("package:", "", grep("package:", search(), value = TRUE)) # Ignore any packages loaded by devtools since these contain # export all imported functions by default is_dev <- vapply(pkgs, pkg_devtools, logical(1)) pkgs[!is_dev] } pkg_attached <- function(x) { paste0("package:", x) %in% search() } # Use attached package if possible, because that is affected by the # `exclude` and `include.only` arguments to library(). Otherwise fall back # to a NAMESPACE based approach pkg_ls <- function(pkg) { if (pkg_attached(pkg)) { ls(pkg_env_name(pkg)) } else { ns <- getNamespace(pkg) exports <- getNamespaceExports(ns) names <- intersect(exports, env_names(ns)) int <- grepl("^.__", names) c(names[!int], pkg_data(pkg)) } } pkg_data <- function(x) { ns <- ns_env(x) lazy_data <- .getNamespaceInfo(ns, "lazydata") if (is.null(lazy_data)) return(character()) env_names(lazy_data) } base_packages <- c( "base", "datasets", "grDevices", "graphics", "methods", "stats", "utils" ) pkgs_base <- function(x) { all(x %in% base_packages) } conflicted/NEWS.md0000644000176200001440000000575014366270366013506 0ustar liggesusers# conflicted 1.2.0 * New `conflicts_prefer()` to easily declare multiple preferences at once: `conflicts_prefer(dplyr::filter, lubridate::week, ...)` (#82). * Disambiguation message now provides clickable preferences (#74). * Conflicts now take into account the `include.only` and `exclude` arguments that you might have specified in `library()` (#84). * `conflict_prefer_all()` and `conflict_prefer_matching()` are now much faster. And when `losers` is supplied, they only register the minimal necessary number of conflicts. # conflicted 1.1.0 * New `conflicted_prefer_all()` and `conflicted_prefer_matching()` to prefer functions en masse (#51). * Improvements to conflict detection and resolution: * Reports conflicts involving lazy loaded datasets (#54). * Don't report conflicts involving a `standardGeneric` (#47). * Better handling of conflicts cleared by superset principle: if there is a conflict all functions (including any base functions) are reported, and if there isn't a conflict, no packages are reported (instead of 1) (#47). * Don't report conflict between a function and a non-function (#30). * Conflicts involving a primitive function no longer error (@nerskin, #46, #48). # conflicted 1.0.4 * Fixes for dev rlang # conflicted 1.0.3 * Fix > vs >= mistake # conflicted 1.0.2 * Align with changes to R 3.6 # conflicted 1.0.1 * Internal `has_moved()` function no longer fails when it encounters a call to `.Deprecated()` with no arguments (#29). * `.conflicts` environment is correctly removed and replaced each time a new package is loaded (#28). # conflicted 1.0.0 ### New functions * `conflict_scout()` reports all conflicts found with a set of packages. * `conflict_prefer()` allows you to declare a persistent preference (within a session) for one function over another (#4). ### Improve conflict resolution * conflicts now generally expects packages that override functions in base packages to obey the "superset principle", i.e. that `foo::bar(...)` must return the same value of `base::bar(...)` whenever the input is not an error. In other words, if you override a base function you can only extend the API, not change or reduce it. There are two exceptions. If the arguments of the two functions are not compatible (i.e. the function in the package doesn't include all arguments of the base package), conflicts can tell it doesn't follow the superset principle. Additionally, `dplyr::lag()` fails to follow the superset principle, and is marked as a special case (#2). * Functions that have moved between packages (i.e. functions with a call to `.Deprecated("pkg::foo")`) as the first element of the function body) will never generate conflicts. * conflicted now listens for `detach()` events and removes conflicts that are removed by detaching a package (#5) ## Minor improvements and bug fixes * Error messages for infix functions and non-syntactic function names are improved (#14) conflicted/MD50000644000176200001440000000434214366420267012711 0ustar liggesusers9607b56bed70ca57761ae5f3aabc7855 *DESCRIPTION 19aadfe1af9deffb4714bde0baccbb1a *LICENSE a0a175689dfc4b519bd3bad6fbe8bc4c *NAMESPACE f3424d3763aafcf0b9026ddad0313631 *NEWS.md 8312551536ece26cf46804f3a315db7f *R/conflicted.R 1cf9ddd72dbb28830e37c5f342e07b61 *R/conflicts.R 24aab097675983e291184bd54a258506 *R/disambiguate.R e182881c4910db1cd1f683f6b7c7f69a *R/favor.R 2983f50e896fbaefc4d8d99fb73ef579 *R/find.R 33f833dc97315ece302cfa63e6d65560 *R/package.R 528ea3668ca00762f342166a05e7f9f5 *R/prefer.R 33c81769c843007ee0749e717304f66d *R/shim.R 0da33727a968c627869b3eec98a943e3 *R/utils.R 448aeff492e09676c01449db59035128 *R/zzz.R 512f436261775eaa05664ff432f189ea *README.md d9ba499957b13dfc1e38c08c608769db *man/conflict_prefer.Rd e7a0a76452844a0877bf00f165e85617 *man/conflict_scout.Rd 0b1a4363efd79266e5446d00f82e967c *man/conflicted-package.Rd 7c239b82517df9f8b96783a4fdf07266 *man/conflicts_prefer.Rd 283584d5afb5d58ed352841dc857683b *tests/testthat.R 190bc8e59ae7bd30182fc02567d17a8d *tests/testthat/_snaps/disambiguate.md 4ff40a4e059a8b47f8a761d0c69d2687 *tests/testthat/_snaps/favor.md dc9f9a81820d1483ae1e24ab3c155bee *tests/testthat/_snaps/find.md 264e3391609bdba656702598741e408b *tests/testthat/_snaps/prefer.md a371f3bc1ec11e1490eaa60b3e5374cf *tests/testthat/data/DESCRIPTION d41d8cd98f00b204e9800998ecf8427e *tests/testthat/data/NAMESPACE 2f0d8baa1649b67e4305bc4f8015a9d5 *tests/testthat/data/data/mtcars.rda b2fe99574447e64ffad0acf2eb9dba11 *tests/testthat/funmatch/DESCRIPTION d020ac245025e2c977c3ec0f2b8366f4 *tests/testthat/funmatch/NAMESPACE f2f31e7249a50ad752b6c443415d1e93 *tests/testthat/funmatch/R/test.R 58e993c21481846aa675b11a721c2077 *tests/testthat/primitive/DESCRIPTION fbbb0550527b2ab46e2a1a097107f110 *tests/testthat/primitive/NAMESPACE 557fdc0af47d6d3b10028fe11410910c *tests/testthat/primitive/R/sum.R 0953cbdb3c8eb96d16946ff59c9d51f0 *tests/testthat/test-disambiguate.R f13e9f47a84c811a0c1a23120d5cd87b *tests/testthat/test-favor.R 5673f90d9b647e98f20a999c431a3632 *tests/testthat/test-find.R 8bd2dfa7e2d241261a3a9b44f325f27d *tests/testthat/test-package.R 0aa6eeeb0f933618de5a67b13c29627a *tests/testthat/test-prefer.R 07b15d805dd1469fb002b5c1ebddba03 *tests/testthat/test-shim.R a7fb5c7eec5b5dd4a57cced6ea1f8de6 *tests/testthat/test-zzz.R