Giter VIP home page Giter VIP logo

burglr's Introduction

burglr

Experimental!

{burglr} provides a way to copy functions from other packages without adding them as dependencies.

It fetches the code of the required function along with all other functions that it depends on, whether they’re in the same package or not.

Use ethically! (see notes)

Installation

Install with :

remotes::install_github("moodymudskipper/burglr")

Example

If you run burglr::burgle(Hmisc::ballocation, Hmisc::as.discrete), a "burgled.R" file will be created in the "R" folder of your package.

It will contain everything necessary to make the functions work as in the original package.

burglr::burgle(Hmisc::ballocation, Hmisc::as.discrete)
#> Copying Hmisc:::ballocation and its dependencies
#> Hmisc:::ballocation
#> Hmisc:::bpower
#> scales:::alpha
#> Copying Hmisc:::as.discrete and its dependencies
#> Hmisc:::as.discrete
#> Hmisc:::as.discrete.default
#> Hmisc:::is.discrete
#> Hmisc:::discrete
#> Please consider giving credit to the authors by adding them as contributors in your package's DESCRIPTION file!

Content of "R/burgled.R" :

# generated by {burglr}

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ballocation (copied from Hmisc:::ballocation)

# from Hmisc 4.4-2
`ballocation` <- function(p1, p2, n, alpha = 0.05) {
  q1 <- 1 - p1
  q2 <- 1 - p2
  f.minvar.diff <- 1 / (1 + sqrt(p2 * q2 / (p1 * q1)))
  f.minvar.ratio <- 1 / (1 + sqrt(p1 * q2 / p2 / q1))
  z <- c(
    fraction.group1.min.var.diff = f.minvar.diff, fraction.group1.min.var.ratio = f.minvar.ratio,
    fraction.group1.min.var.logodds = 1 - f.minvar.diff
  )
  if (!missing(n)) {
    possf <- seq(0.001, 0.999, length = 1000)
    pow <- bpower(p1, p2,
      n1 = n * possf, n2 = n * (1 - possf),
      alpha = alpha
    )
    f <- possf[pow == max(pow)]
    f <- f[abs(f - 0.5) == min(abs(f - 0.5))]
    z <- c(z, fraction.group1.max.power = f[1])
  }
  z
}

# from Hmisc 4.4-2
`bpower` <- function(p1, p2, odds.ratio, percent.reduction, n, n1, n2, alpha = 0.05) {
  if (!missing(odds.ratio)) {
    p2 <- p1 * odds.ratio / (1 - p1 + p1 * odds.ratio)
  } else if (!missing(percent.reduction)) {
    p2 <- p1 * (1 - percent.reduction / 100)
  }
  if (!missing(n)) {
    n1 <- n2 <- n / 2
  }
  z <- qnorm(1 - alpha / 2)
  q1 <- 1 - p1
  q2 <- 1 - p2
  pm <- (n1 * p1 + n2 * p2) / (n1 + n2)
  ds <- z * sqrt((1 / n1 + 1 / n2) * pm * (1 - pm))
  ex <- abs(p1 - p2)
  sd <- sqrt(p1 * q1 / n1 + p2 * q2 / n2)
  c(Power = 1 - pnorm((ds - ex) / sd) + pnorm((-ds - ex) / sd))
}

# from scales 1.1.1
`alpha` <- function(colour, alpha = NA) {
  if (length(colour) != length(alpha)) {
    if (length(colour) > 1 && length(alpha) > 1) {
      stop("Only one of colour and alpha can be vectorised")
    }
    if (length(colour) > 1) {
      alpha <- rep(alpha, length.out = length(colour))
    }
    else {
      colour <- rep(colour, length.out = length(alpha))
    }
  }
  rgb <- farver::decode_colour(colour, alpha = TRUE)
  rgb[!is.na(alpha), 4] <- alpha[!is.na(alpha)]
  farver::encode_colour(rgb, rgb[, 4])
}
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# as.discrete (copied from Hmisc:::as.discrete)

# from Hmisc 4.4-2
`as.discrete` <- function(x, ...) {
  UseMethod("as.discrete")
}

# from Hmisc 4.4-2
#' @export
`as.discrete.default` <- function(x, ...) {
  if (is.discrete(x)) {
    x
  } else {
    discrete(x)
  }
}

# from Hmisc 4.4-2
`is.discrete` <- function(x) {
  inherits(x, "discrete")
}

# from Hmisc 4.4-2
`discrete` <- function(x, levels = sort(unique.default(x), na.last = TRUE),
                       exclude = NA) {
  if (!is.numeric(x)) {
    stop("x must be a numeric vairable")
  }
  exclude <- as.vector(exclude, typeof(x))
  levels <- levels[is.na(match(levels, exclude))]
  f <- x[!(x %in% exclude)]
  attr(f, "levels") <- levels
  class(f) <- "discrete"
  f
}

We see that :

  • the package and its version are mentioned.
  • When importing S3 generics, all available methods are imported too, and are tagged with #' @export so they can be registered when you devtools::document()

Technical notes

  • burgle() makes sure to check which packages are imported so we avoid unnecessary copies of functions, so for instance in the example above if your package imports {scales} scales::alpha won’t be copied
  • You can name arguments if you wish to rename the function in your package. This is not recommended in general as recursive functions and other corner cases won’t work.
  • You cannot (yet ?) copy functions that use code in C, C++ or Fortran, in their own body or through their unimported dependencies.
  • A convenient use case is to use {burgler} on your own packages of misc functions.
  • This is not 100% robust but seems to work ok in general. Please report issues.

Ethical and legal notes

  • Use dependencies when you can!
  • The name is tongue in cheek and should serve as a reminder that copying a substantial amount of code without giving credit is unethical and possibly illegal.
  • If you copy functions from packages you don’t own, consider editing the DESCRIPTION file to give credit where it’s due.
  • Pay attention to licenses

burglr's People

Contributors

moodymudskipper avatar jimr1603 avatar

Stargazers

Robin Penfold avatar  avatar Anatolii Tsyplenkov avatar Josep Pueyo-Ros avatar Jacob Scott avatar Zhonghui Gai avatar jpbranson avatar Robin Lovelace avatar Thomas Gorman avatar Justin Millar avatar Trevor L. Davis avatar Eli Pousson avatar Garrick Aden-Buie avatar Andrew Allen Bruce avatar Brady Johnston avatar M. Amalan avatar Allan Irvine avatar frankfanslc avatar Andy avatar  avatar Pierre-Yves Berrard avatar Alain Quartier-la-Tente avatar Brancen Gregory avatar Patrick Howard avatar AM avatar  avatar cswaters avatar Markus Lang avatar Ben Johnson avatar Philipp Bayer avatar Serdar Balcı avatar Hongyuan Jia avatar Tim Taylor avatar Federico Marini avatar Ben Marwick avatar Adam H. Sparks avatar Micah K. Wilson avatar Brodie Gaslam avatar Liz Roten avatar Clemens Schmid avatar Will Bonnell avatar Anh N Tran avatar Johannes Breuer avatar  avatar Wendy Wong avatar Ljupcho Naumov avatar Alex Ho avatar Francisco Rodriguez-Sanchez avatar Victor Perrier avatar Eyayaw Beze avatar Matt Dray avatar Dan Chaltiel avatar InfinityLoop avatar Dony Indiarto avatar Ryo Nakagawara avatar Jimmy Briggs avatar Bai Feng avatar Devin Pastoor avatar

Watchers

James Cloos avatar  avatar  avatar

burglr's Issues

Improve output when the package is already in Imports

Hi Antoine,

If you are trying to burgle a function, chances are that this function is in your Imports at the moment.

In your sub-function on line 27, you explicitly write that you want to do nothing and return if the package name is in the DESCRIPTION file.

However, the main function still outputs "Copying xxx::xxx and its dependencies, xxx is licensed..." in this case, which is a bit misleading since it is not copied.

How about copying it anyway, along with warning the user that the package is still cited in DESCRIPTION?

Error if a variable is named `x`

Hi Antoine,

Thanks again for this very nice package.

There is a small but very surprising bug that occurs when a variable x is already declared in the global environment and you want to burgle a function that has an x argument itself. Removing this variable saves the day.

Here is a reprex:

> x=1
> burglr::burgle(base::mean)
Copying base:::mean and its dependencies

 base  is licensed:  Part of R 4.1.0 
 and is by:
  R Core Team and contributors worldwide 
base:::mean
base:::mean.Date
Error in burgle1(nm, pkg, export = TRUE, first = FALSE) : 
  can't make sense of environment of `x`
In addition: Warning message:
In stop(e, call. = FALSE) : additional arguments ignored in stop()
> burglr::burgle(scales:::alpha)
Copying scales:::alpha and its dependencies

 scales  is licensed:  MIT + file LICENSE 
 and is by:
  Hadley Wickham [aut, cre],
  Dana Seidel [aut],
  RStudio [cph] 
Remember to comply with the terms of the license of the code you are using.

          See https://r-pkgs.org/license.html for more info.
> rm(x)
> burglr::burgle(base::mean)
Copying base:::mean and its dependencies

 base  is licensed:  Part of R 4.1.0 
 and is by:
  R Core Team and contributors worldwide 
base:::mean
base:::mean.Date
base:::mean.default
base:::mean.difftime
base:::mean.POSIXct
base:::mean.POSIXlt
Remember to comply with the terms of the license of the code you are using.

          See https://r-pkgs.org/license.html for more info.

self contained functions

Define a function's dependencies in the function itself at the top.
This might be more convenient that the general approach here in some cases.
We can either inline only the functions of the target package or go further, we should provide a list of packages to use or not use or this would get out of hand
This won't produce beautiful code (not DRY for sure) but performance should not suffer much and we can get a self contained function easily.

Maybe not in this package since it's cursed now

Licencing

Off the top of my head, I think we can throw a warning for the common licences as a prompt for what attribution is needed.

Then we can extend that to "here's what should be adequate attribution, do you want to include it?".

I'll try to get a PR for the former this long weekend.

Dependency diagram

This would be handy, to see which branch makes the dependency tree go wild.

Just fill up as we recourse a 2 column data frame with pkg1::fun and pkg2::dependency, then on.exit translate to nomnoml and view.

Have blocks colored by package.

If failure the diagram is displayed too, and failing function is highlighted (bright red).

We could also highlight name conflicts but I think we'll solve this with our pseudo namespaces.

Feature suggestion: support custom filename for copied functions

I totally understand that the filename burgled.R is light-hearted, it also seems a bit inaccurate when I'm using burglr to copy functions from my own packages into my own packages. Would you be interested in a pull request to allow burglr to support custom filenames for the file for copied functions?

I added a filename parameter to a fork of burglr just now and would be happy to open a pull request if you think this would be a welcome feature!

namespaces ?

We could in principle implement them, I think.

It would be quite complex, but would also unclutter the main package's namespace.

We'd have a .burgled environment containing other environments, only target functions would be in our namespace and their closure env would be a leaf of a tree we'd build in .burgled.

The burgled.R might be a bit less readable with the environment fun stuff, but it doesn't really matter.

copying C/C++ stuff

I don't know if it's possible, because I think C/C++ dependency are declared all over the place so it's tough to isolate a function.

And then we'd need to touch other files than just burgled.R of course.

It would be great though to be able to copy pryr:::is_promise2 for instance, but IIRC even manually this is hell, so not sure how to automate it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.