Giter Club home page Giter Club logo

import's Introduction

import

CRAN status CRAN status shields R build status

An Import Mechanism For R

The import package is intended to simplify the way in which functions from external packages or modules are made available for use in R scripts. Learn more on the package website, by reading vignette("import"), or using the help (?import::from).

Introduction

The typical way of using functionality exposed by a package in R scripts is to load (and attach) the entire package with library() (or require()). This can have the undesirable effect of masking objects in the user’s search path and can also make it difficult and confusing to identify what functionality comes from which package when using several library statements.

The import package provides a simple alternative, allowing the user specify in a concise way exactly which objects. For example, the Hmisc package exposes over four hundred functions. Instead of exposing all of those functions, someone who only needs access to, say the impute() and the nomiss() functions, can import those functions only:

import::from(Hmisc, impute, nomiss)

For more on the motivation behind the package, see vignette(“import”)

Installation

To install import from CRAN:

install.packages("import")

You can also install the development version of import from GitHub using devtools:

devtools::install_github("rticulate/import")

Usage

Importing functions from R packages

The most basic use case is to import a few functions from package (here the psych package):

import::from(psych, geometric.mean, harmonic.mean)
geometric.mean(trees$Volume)

If one of the function names conflicts with an existing function (such as filter from the dplyr package) it is simple to rename it:

import::from(dplyr, select, arrange, keep_when = filter)
keep_when(mtcars, hp>250)

Use .all=TRUE to import all functions from a package. If you want to rename one of them, you can still do that:

import::from(dplyr, keep_when = filter, .all=TRUE)

To omit a function from the import, use .except (which takes a character vector):

import::from(dplyr, .except=c("filter", "lag"))

Note that import tries to be smart about this and assumes that if you are using the .except parameter, you probably want to import everything you are not explicitly omitting, and sets the .all parameter to TRUE. You can still override this in exceptional cases, but you seldom need to.

These and other examples are discussed in more detail in the Importing from Packages section of the package vignette.

Importing Functions from “Module” Scripts

The import package allows R files to be used as “modules” from which functions are loaded. For example, the file sequence_module.R contains several functions calculating terms of mathematical sequences. It is possible to import from such files, just as one imports from packages:

import::from(sequence_module.R, fibonacci, square, triangular)

Renaming, as well as the .all and .except parameters, work in the same way as for packages:

import::from(sequence_module.R, fib=fibonacci, .except="square")

These and other examples are discussed in more detail in the Importing from Modules section of the package vignette.

Choosing where import looks for packages or modules

The import package will by default use the current set of library paths, i.e. the result of .libPaths(). It is, however, possible to specify a different set of library paths using the .library argument in any of the import functions, for example to import packages installed in a custom location, or to remove any ambiguity as to where imports come from.

Note that in versions up to and including 1.3.0 this defaulted to use only the first entry in the library paths, i.e. .library=.libPaths()[1L]. We believe the new default is applicable in a broader set of circumstances, but if this change causes any issues, we would very much appreciate hearing about it.

When importing from a module (.R file), the directory where import looks for the module script can be specified with the with .directory parameter. The default is . (the current working directory).

Choosing where the imported functions are placed

By default, imported objects are placed in a separate entity in the search path called “imports”. One can also specify which names to use in the search path and use several to group imports:

import::from(magrittr, "%>%", "%$%", .into = "operators") 
import::from(dplyr, arrange, .into = "datatools")

If using custom search path entities actively, one might prefer the alternative syntax (which does the same but reverses the argument order):

import::into("operators", "%>%", "%$%", .from = magrittr)
import::into("datatools", arrange, .from = dplyr)

If it is desired to place imported objects in the current environment, use import::here():

More advanced usage

The import package is designed to be simple to use for basic cases, so it uses symbolic evaluation to allow the names of packages, modules and functions to be entered without quotes (except for operators, such as "%>%" which must be quoted). However, this means that it calling a variable containing the name of a module, or a vector of functions to import, will not work. For this use case, you can use the .character_only parameter:

module_name <- "../utils/my_module.R"

# Will not work (import will look for a package called "module_name")
import::from(module_name, foo, bar)

# This will correctly import the foo() and bar() functions from "../utils/my_module.R"
import::from(module_name, foo, bar, .character_only=TRUE)

The .character_only parameter is covered in more detail in the Advanced Usage section of the package vignette, which also describes how you can import from module scripts stored online with the help of the pins package, or achieve python-like imports with the help of {} notation for environments in the .into parameter.

Contributing

Contributions to this project are welcome. Please start by opening an issue or discussion thread. New features are added conservatively based on supply (is anyone willing to contribute an implementation of the feature?), demand (how many people seem to need a new feature?), and last, but not least, by whether a feature can be implemented without breaking backwards compatibility.

(Did we forget to add you? If so, please let us know!)

See also:

  • Some of the use cases for import can now be handled directly in base R using the new exclude and include.only arguments of library() and require()
  • For an interesting but slightly different idea of Python-like modules for R, see the box package by @klmr (previously called modules).
  • Another approach, focused on treating the use of functions with naming conflicts as explicit errors is the conflicted package by @hadley.

import's People

Contributors

awong234 avatar brshallo avatar flying-sheep avatar hutch3232 avatar j-moravec avatar klmr avatar smbache avatar torfason avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

import's Issues

importing S3 methods

Not entirely related to the #16 so raising a new issue so we can discuss this problem.

Relevant posts from the old thread:
#16 (comment)
#16 (comment)

Found a potential solution. The problem is that the S3 method needs to be registered. From UseMethod documentation (here on page 629):

Namespaces can register methods for generic functions. To support this, UseMethod and
NextMethod search for methods in two places: in the environment in which the generic function is
called, and in the registration data base for the environment in which the generic is defined (typi-
cally a namespace). So methods for a generic function need to be available in the environment of
the call to the generic, or they must be registered. (It does not matter whether they are visible in the
environment in which the generic is defined.) As from R 3.5.0, the registration data base is searched
after the top level environment (see topenv) of the calling environment (but before the parents of
the top level environment).

If you look at packages with S3, you will see in NAMESPACE:

S3method(generic, class)

S3method is not available, but there is a related function with a signature: .S3method(generic, class, function).

As a local hotfix, modifying the module_catter.R with:

# module_catter.R

catter <- function(x) {
  class(x) <- "catter"
  x
}

print.catter <- function(x) {
  cat(x)
  invisible(x)
}

.S3method("print", "catter", print.catter) # It looks like methods are not limited to generic.class calls

and then running:

import::from(module_catter.R, catter)
x <- catter(letters[1:3]) # Construct catter
x                                  # Uses print.catter()
#> a b c

As a hotfix, adding .S3method call after every definition of S3 method will fix the issue.

Modifying import to look at function names and constructing individual calls of .S3method for function names with x.y pattern should do the trick. Or maybe use the registerS3method which is the internal workhorse of the .S3method, if importing to a particular environment is required, instead of the default parent.frame()

Alternatively, just add to documentation that registering S3 methods is required.

Making the import package work with the drake/targets package

I've recently discovered both the drake/targets and the import package which I see as vital additions to the R ecosystem, and it would be great to make them work together.

If I'm not mistaken, drake relies on all user scripts being loaded by source. drake can detect any changes made to the functions used and invalidate the targets, so that on a subsequent run these targets will be newly calculated.

Using the import package with drake poses the problem that changes to module scripts imported with import (or recursive imports of such module scripts) won't be detected, so drake would still see the same.

Ideally for a middle sized analysis I would have some helper/constants files and module scripts where each function imported from a module scripts would take care of a target. This would lead to very nice isolation, testability and reproducibility. I'm aware that the recommended approach is to use a package structure, but so far I haven't seen a solution that seems satisfying. A single package that has very different functionalities seems to get out of hand quickly along with limitations like not having subfolders in R/ and so on, while having many small packages for a middle sized task poses other structuring issues in a single repo like having different renvs, R projects and so on which I do not want in a case where everything happens in the same context and only there.

Do you see a possibility to expose something that would notify drake that it needs to invalidate affected targets? I already asked the drake creator about this, but he suggested that this would need to be solved on the side of import.

Thank you all for your great work!

Use pkgdown to create a web site for the import package

Now that import has CI implemented, wouldn't it be nice to have a website for the package using pkgdown? It's almost free (in terms of time, absolutely free in terms of money) and allows better access to vignettes and other documentation for people who have not installed the package (and for more up-to-date documentation for the times when master gets a bit ahead of CRAN).

Proof-of-concept branch here:
https://github.com/torfason/import/tree/docs

And the end result here (the proof-of-concept is hosted using my own account for now):
https://torfason.github.io/import/

The proof-of-concept ended up being more like a final product, so I can create a pull request from the branch if that sounds appealing to you @smbache.

import all objects from package for modules

In the 'modules' use case, it'd be useful to import all objects from a package, effectively just like library(), but only have those exposed objects be visible to the current module.
For example, in module.R:

library(tibble)
library(dplyr)
x <- function() {
  as_tibble(iris) %>%
    group_by(Sepal.Length) %>%
    summarize(mean_petal_width = mean(Petal.Width))
}

Then in some higher-level script, one would import the module:

import::from("module.R", x)
x()  ## doesn't work because the attached packages have been detached

Rather than library(), as you've pointed out in the docs, we can use:

import::here(as_tibble, .from = "tibble")
import::here(group_by, summarize, .from = "dplyr")

... but that can get cumbersome if the module uses LOTS of objects.
Is there the possibility of an import::from(packagename, .all = TRUE) option, that acts similarly to library but helps with this module use case?

equivalent to source(somepackage, local = TRUE)

I really like the idea of bringing python's import functionality to R. Thanks!

Question: Does the import function have an option to evaluate functions in a particular environment? I would like to use it in a Shiny app where some functions need to be evaluated in the 'shiny' environment.

Also, python import allows wildcards. Is that an option?

support for hidden functions/objects

Say I have a script, functions.R:

.script_version <- "v1.0"

message("you have loaded version: ", .script_version)

foo_a <- function(){

}

When I run:

import::from(.from = "functions.R", .all = TRUE, .character_only = TRUE)

It finds the hidden variable .script_version, prints the message, but then somewhere along the way discards it. I would really like it if the hidden object stayed hidden in the .into environment.

you have loaded version: v1.0
> ls(envir = as.environment("imports"))
[1] "?"     "foo_a"
> ls(envir = as.environment("imports"), all.names = TRUE)
[1] "?"     "foo_a"
> .script_version
Error: object '.script_version' not found

I have tried to identify where it may be getting lost, and I think perhaps here:

import/R/from.R

Line 205 in 169346c

all_objects <- ls(scripts[[from]])

From ?ls:

all.names
a logical value. If TRUE, all object names are returned. If FALSE, names which begin with a . are omitted.

I do have a work-around, where I assign it to the environment I want within in the script, but it's a little messier.

Thanks!

Join tidyverse

This packages aims to keep the namespace as clean as possible, making it possible to only import functions that will later be used by the user explicitly. This reduces name clashes in the namespace, and keeps the program namepsace tidy rather than messy. Would it be possible to apply to join tidyverse?

Setup CI

We should setup some Github actions for CI

Retain documentation linkage for aliased functions

I'm assuming you've seen this SO question?

My answer there led me to realize that maintaining accessible documentation when creating a function alias is really hard. I'd love to see a solution for this inside this package so as to avoid the following:

> import::from(dplyr, select, arrange, keep_when = filter)
> ? keep_when
No documentation forkeep_whenin specified packages and libraries:
you could try ‘??keep_when

Time for a new release (1.3.0)?

It's now been around 18 months since the last release of import and I've been thinking about whether it is getting to be time for a new one?

I am opening this issue for any interested parties to chime in, with suggestions on which issues would be most important to get into such a release (and of course for anyone who would like to offer assistance). Here is a first stab at a list (to be updated):

  • An annoying release blocker, #60, now fixed in dev

  • Some of the issues are genuine bugs. Even if none of them seems to impact too many use cases individually, it would be very nice to fix them. (some of them have either PRs or clear approaches to fixing).

  • Others are enhancements. Getting them into a release depends both on whether there is interest in implementing, and on whether we can find a way to do it without breaking existing usage.

  • Postponed for a later release

    • #56, fixed in dev (through PR #55), thanks to @awong234, but caused an unexpected regression. Fix probably identified, but aim is to release this separately.
    • #63, proposed by myself, based on comments including by @J-Moravec on other issues. Might be nice to have?

Dev process

Update: Staging area has been moved to the main repo

dev refers to rticulate/import@dev. This branch will be the staging area, with a final omnibus PR to rticulate/import@master.

There is a preference for a relatively clean linear commit history in the rticulate@master branch, so it it great to keep each issue fix to 1-3 commits unless it is that much bigger. No worries though about submitting incremental pull requests, I can rebase them (or people can rebase themselves) to get back to a few logical commits with an end result for which all tests and checks pass. A recommended default commit structure is:

  • Tests first in a single commit, allowing examination of how they fail before a feature/fix is implemented
  • The implementation code in a second commit
  • Documentation, including updates to NEWS.md in a third commit

Governance

For anyone interested in getting involved, it might be good to give some background on the governance of this project:

The package was written by @smbache and I consider him to be the "benevolent dictator" of this project. I became involved around the last release, and because of his time constraints he assigned me as the lead maintainer for the purposes of CRAN submissions. The philosophy that I think the two of us share regarding import includes among other things:

  • We do not have the resources to "move fast and break things" so slow and steady is the way to go
  • Therefore:
    • New features should be backwards compatible unless there is an extremely compelling reason
    • New features should generally be documented as well or better than existing features
    • New features should generally have as good or better test coverage as existing features
  • When it comes to choosing features both demand and supply are important
    • Demand in terms of the number of people who comment on an issue
    • Supply in terms of help with implementation

Within this philosophy, I would very much like to make any decisions on consensus, and would be most interested in hearing from people who have submitted or commented on bugs already, since they clearly care about this project in one way or another.

The floor is open :-)

Towards 1.4.0

Targeted issues

As of September 2023, v1.3.1 has been released, with all items originally targeted for that.

User visible features targeted for v1.4.0 now only include #63, (config function), which has not been implemented. No target date has been set for that release. Keeping this issue open since it contains important documentation about the development process and governance of the project.

Development process

Development happens on the dev. Contributions are merged from forked repos to dev through pull requests. Stable changes are then either migrated to the main branch for wider testing before a release, or are migrated upon the next release.

There is a preference for a relatively clean linear commit history in the main branch, so it it great to keep each issue fix to 1-3 commits unless it is that much bigger. No worries though about submitting incremental pull requests, they can be rebased at the end, by either submitter or maintainer, to get back to a few logical commits with an end result for which all tests and checks pass. A recommended default commit structure is:

  • Tests first in a single commit, allowing examination of how they fail before a feature/fix is implemented
  • The implementation code in a second commit
  • Documentation, including updates to NEWS.md and a version bump, in a third commit

The project now includes a build/build_and_release_process.R file. Contributors are kindly asked to run the code in this file prior to pushing any commits, and make sure that it (a) runs with out error and (b) does not result in unexpected modifications of project files. Among other things, the script regenerates autogenerated files, such as the *.Rd and README.md files, to correctly reflect changes in primary source files.

Governance

For anyone interested in getting involved, it might be good to give some background on the governance of this project:

The package was written by @smbache, but the package is currently maintained by @torfason. The philosophy that they share regarding import includes among other things:

  • We do not have the resources to "move fast and break things" so slow and steady is the way to go
  • Therefore:
    • New features should be backwards compatible unless there is an extremely compelling reason
    • New features should generally be documented as well or better than existing features
    • New features should generally have as good or better test coverage as existing features
  • When it comes to choosing features both demand and supply are important
    • Demand in terms of the number of people who comment on an issue
    • Supply in terms of help with implementation

Within this philosophy, I would very much like to make any decisions on consensus, and would be most interested in hearing from people who have submitted or commented on bugs already, since they clearly care about this project in one way or another.

avoiding the use of `library` in Shiny apps

Shiny apps require the use of library or require. It seems that the import package would be a good option when an app requires several packages. My shiny app is an R-package with a namespace. I have been trying to figure out how to import the various functions from different packages (see attempt below) but haven't gotten things working yet. Any ideas / suggestions? Related to #4

copy_imported <- function(.from) {

  from <- as.character(substitute(.from))

  import_list <- getNamespaceImports(from)
  parent  <- parent.frame()
  import_names <- names(import_list)

  for (i in unique(import_names)) {
    if (i %in% c("base","shiny","magrittr")) next

    symbols <- unlist(import_list[which(i == import_names)])

    for (j in symbols) {
      # do.call(import::from, list(i = as.symbol(i), j = as.symbol(j)))
      fn <- get(j, envir = asNamespace(i), inherits = TRUE)
      assign(j, eval.parent(call("function", formals(fn), body(fn))), parent)
    }
  }

  invisible(NULL)
}

Recursive calls to import::from() do not work

Calling import::*() recursively (that is, importing functions from a module that itself imports functions from another module) does not work if unless the inner import statement uses import::here(). Specifically, it works if the inner import statement imports into {environment()}, which is what happens when using import::here()

This is the root cause of several other issues that have been marked as fixed-in-master.

It would be preferable if all discussions about this issue take place here, rather than in other issues that are mostly a symptom of this issue.

Using library() inside imported module should be an error

If a module loads a package through a library() statement, this allows functions to be defined relying on the package, but then they won't work outside the module, with an error that can be cryptic to users.

This behavior is as documented in the readme (https://import.rticulate.org/), but it seems better to throw an error immediately on the import::from() call if this occurs.

Implementing this seems trivial by checking whether .packages() changes during the sourcing of the module. I expect to prepare a PR shortly that does this. Comments/pros/cons/OK appreciated.

Import module .from=`variable`

I am trying to import a function for a module, where the the module path is in a variable. This does not work, as the variable is interpreted as a package instead of a string pointing to the module's file. In particular, doing this does not work:
path_to_module <- "~/repos/R/my_repo/my_module.R"
import::from(.from=path_to_module, my_function, .into="")

Is there an easy way to go around this restriction?

Use case for "import::from(<pkg>, '<func>', .into = '<group>')"?

I came across the import package recently
and finally saw some hope with solving the namespace chaos
in R compared to Python.
Thank you for the great work!
I saw the following example on your website:

# Choosing where the imported functions are placed
import::from(magrittr, "%>%", "%$%", .into = "operators")

Intuitively, it is a very appealing idea, to get things organized.
However, due to my limited knowledge about the inner-workings of R,
I am not sure when & why I will be using this function.
as opposed to importing everything into the same global environment.

Do you mind giving me some example about the use case of this particular function?
Thank you very much!

New CRAN issue?

Just wondering if the new import functionality will make its way to CRAN? I really like the feature of being able to use R scripts as imports.

My use case: I am using checkpoint for package management, so the dependence is on CRAN; but precisely because of this dependence, working with the universe outside CRAN (/MRAN) is somewhat frustrating. The solution I've been using is https://github.com/wahani/modules which is perfectly fine, but I like the all in one solution offered by this package.

chdir option?

Hello,

Thanks for this package, lately it's become an integral part of my workflow.

Usually, I use import to import some specific objects from R scripts that otherwise would pollute my global environment. One issue I've run into is that if I do import::here(my_obj, .from = 'path/to/script.R'), the script is actually sourced in working directory path/to/. Would it be possible to give an option to disable this behavior? I'm currently organizing all the scripts within a project to assume the same working directory, but organize the scripts in nested folders as appropriate. Having an ability to set chdir = FALSE would be very usefully. Ideally the default could be something like getOption("import.chdir", TRUE).

Similar functionality for R scripts

Is it possible to have a similar functionality for R scripts e.g. say I had a shiny app with directory structure:

global.R
ui.R
server.R
utils/
  connect.R

and connect.R had the template:

# function to connect to a data base
connect <- function(...) {
  # make some calls to the utility functions defined in connect.R
}

# utility functions
...

would it be possible to have something like (in global.R):

import::from("utils/connect.R", "connect")

so I get connect() without all the noise from the utility functions.

I'm guessing this might be pushing it, but it'd be super neat if possible.

Duplicate imports

Currently, import happily allows importing the same symbol from more than one package, the last import wins:

> import::from("plyr", "arrange")
> environment(arrange)
<environment: namespace:plyr>
> import::from("dplyr", "arrange")
> environment(arrange)
<environment: namespace:dplyr>
> import::from("plyr", "arrange")
> environment(arrange)
<environment: namespace:plyr>

Would you consider a "safe mode" where this usage triggers an error?

import::here() parameter order is not consistent with import::from()

import::here() parameter is not consistent with import::from(). Because .from comes after ..., one must always spell out .from= explicitly.

I propose to move .from to the start of the parameter list in import::here(), just as it is in import::from(). This means that the following will work:

import::here(my_module.R, my_function)

Rationale:

  • This makes it consistent with import::from() and saves some keystrokes.
  • The proposed fix to #34 will cause some people to start seeing warnings asking them to switch from import::from() to import::here(). This will be less of a headache if the parameter order for the most used parameters is consistent.
  • No prior usage of import::here() will become invalid. Because any legal call to import::here() currently must specify .from= explicitly, those calls will continue to work.

Import modules from URLs

It occurred to me from our twitter discussion that one could get some compact "single script reproducibility" by allowing import::from() and import::here() to take URLs or gist URLs as arguments so that your modules can be placed anywhere on the web, e.g.,

import::from('https://gist.github.com/noamross/378884a472b5ef58c6cf", myfunc)

recent github version breaks import::* within functions

Unfortunately, I don't know exactly which change corresponds to this behavior, but on the current github version (nominally 1.1.1), this fails:

 > tempf <- function() { import::here("%>%", .from = "magrittr"); 1:10 %>% rev }
 > tempf()
 Error in 1:10 %>% rev (from #1) : could not find function "%>%"
 > packageVersion("import")
 [1] ‘1.1.1

When I revert to 1.1.0 via CRAN, that basic test succeeds:

 > tempf <- function() { import::here("%>%", .from = "magrittr"); 1:10 %>% rev }
 > tempf()
  [1] 10  9  8  7  6  5  4  3  2  1
 > packageVersion("import")
 [1] ‘1.1.0

No other package or R version (3.5.1) changes between these two tests.

I suppose I could walk back along each commit to try to find the point at which this behavior changed ... just let me know if you'd like that help and I can do some commit-based testing.

Cheers!

import as

One of the frequent patterns in python looks like this:

import pandas as pd
import numpy as np

Would it be possible to have something like this in the import package?

Ideally, something like this:

import::from("magrittr", "%>%")
import::as(dp="dplyr", tr="tidyr", pr="purrr")

cars %>%
  dp::select(...) %>%
  tr::pivot_wider(...)

In theory this might be possible in case :: operator is extended.

Or maybe there's an easier way to do this?

Docs don’t work when using import::from('pkg (>=1.0)', ...)

Using the versioned form of import::from, upon trying to fetch the help of an item, import tries to fetch the docs for `pkg (>=1.0)`::item instead of pkg::item:

> import::from('pkg (>=1.0)', item)
> ?item
import: showing documentation for `pkg (>=1.0)`::item

Error in find.package(if (is.null(package)) loadedNamespaces() else package, :
there is no package called ‘pkg (>=1.0)’
...

README (as opposed to README.md) is out of date

README looks like it was a duplicate of README.md at some point in time, but is out of date. Wouldn't it be better to have a minimal README, which directs people to look at README.md and perhaps the GitHub repo, CRAN (and the perhaps upcoming pkgdown site). Or is there a reason there needs to be a copy of README.md in README?

In either case, I can update on my docs branch and corporate in a PR.

Feature request: Load package functions into an existing environment even if it is not attached to the search path

I normally source my util functions into a new environment and call the util functions by specifying the environment variable name to avoid flooding my global environment with util functions:

util <- new.env()
source("util.R", local=util)
util$hello()   # call the helper function

If the util code is stable I want to create a package.

To avoid changing all calls of the util functions I would like to use your package to load all package functions into a new enviornment:

util <- new.env()
import::from(util, .into = util)  # this does not work of course since "into" requires a string, not an environment

Would it be possible to add such a functionality?

PS: See also this question at SO: https://stackoverflow.com/questions/40075236/load-library-into-existing-environment-equivalent-to-local-parameter-of-sour

if __name__ == “__main__”

Hi! Great package. One thing i'm missing or haven't understand how to do is to avoid import code from one file to another one. In python I can use the if __name__ == “__main__” construct. Is something similar available here? Thanks.

support symbols in .into

Say I have a script, functions.R:

foo_a <- function(){
  
}

Now I want to import the contents of that script, perhaps using a wrapper function, and to use a dynamic .into. The current from function doesn't like being supplied a symbol.

env_name <- "custom"
import::from(.from = "C:/Users/Public/functions.R", .into = env_name, .character_only = TRUE)
Error in into_expr[[1]] : object of type 'symbol' is not subsettable

I couldn't find a nice work-around to this. Is this possible to incorporate?

Thanks!

Inherits, TRUE or FALSE

Should import use inherits = TRUE?

Currently

import::from(dplyr, '%>%')

will fail, as

get("%>%", envir = asNamespace("dplyr"), inherits = FALSE)

fails. Using inherits = TRUE will fix that.

Documentation doesn't match behavior for current github version

In the "modules" section of the documentation, the behavior doesn't match the description.
I explicitly copied the some_module.R example (and swapped out the library(ggplot2) line with the suggested import::here(qplot, .from = ggplot2) line, then tried to import::from that module like so:

 > import::from(some_module.R, a, b, p, plot_it)
 Error in qplot(Sepal.Length, Sepal.Width, data = iris, color = Species) :
   could not find function "qplot"

I'm using the most recent commit on the master branch (8ad48cc).

Add import::config() function or similar?

As the number of dot-prefixed parameters to import::from() has increased considerably, I have been thinking that an import::config() function might be a good addition to the package. The formals (the declared function signature) of import::from() would not be changed, but any missing() parameters would be overridden by options set with something like:

import::config(.directory=here::here("utils"))
# or assuming import gets a new .S3 parameter
import::config(.character_only=TRUE, .S3=TRUE)

The option values themselves could be stored using base::options, but the rationale for having a separate function is that both findability and the ability to perform sanity checking on the parameter values would be improved.

Thoughts?

import::from doesn't seem to work from vignettes

unless package is already imported (e.g., via requireNamespace).

See my test repo. It contains a vignette that uses import::from. Test: R CMD build ..

For package knitr, this works (tag knitr-works). For package tidyr, this doesn't (tag tidyr-breaks), unless the package is loaded via requireNamespace before (tag with-requireNamespace).

Would anything break if requireNamespace is called in import::from et al.?

How to treat non-exported objects

Currently, there is no difference between importing objects that are exported and those that are not, but maybe there should be.

It could be possible to check e.g. if the call is:

import::from(pkg, x, y)

# or

import:::from(pkg, x, y)

and only accept imports of non-exported functions in the latter.

Import all but one (or a few) function in a package

I love the import package when it comes to pulling a single function from sometimes very large packages without polluting the namespace.

However, a pretty common use case (which i'm not the only one facing) is that I'm using a package I like, but it happens to have one function that conflicts with a very important other function.

So, what I would love to have is something like:

import::from_excluding(nicepackage,conflictingfunction)

Workarounds exist of course, including creating elaborate orderings of package import statements, but all solutions I've found, including those presented in the link above, seem very inferior to the solution I propose, which I'm kind of hoping would not be a big extension to the import package.

Importing methods

Hi!

I’m a fan of https://github.com/thomasp85/patchwork

It defines functions like -.ggplot which I’d like to import. I need to use import:::from for this, which seems unintuitive: when the patchwork package is attached, I can use those functions, so why would I need to use private accession syntax?

`import::from()` not working inside package function

I'm getting the following error when running import::from() from the debugger after loading a personal package.

import::from("purrr", "map", .into = "explictpackage:purrr", .character_only = TRUE)
#> Error in as.environment(where) : 
#>   no item called "explictpackage:purrr" on the search list

Strangely I don't get any error when running that line from the debugger after doing something like debugonce("mean"); mean(1:10). But I had to revert back to using the approach mentioned in #53 for attaching functions here: https://github.com/brshallo/funspotr/blob/issue-5/R/spot-funs.R#L104

Version specification in import::from

As mentioned on twitter by @noamross specifying a version for a package function could improve script reproducibility.

Looking at your source for import::from, https://github.com/smbache/import/blame/master/R/from.R#L108 implementing seems straightforward using the versionCheck argument of loadNamespace (which I've only just discovered).

PS: a brief look on Google indicates actually using versionCheck may be a bit tricky. See this thread:
http://r.789695.n4.nabble.com/Problem-understanding-behaviour-of-versionCheck-for-loadNamespace-and-when-versions-for-Imports-pack-td4700099.html

CI on windows seems like more trouble than it is worth, propose to remove it

Most recently it failed because of problems with installing vctrs on which dplyr depends(!). Both mac and linux are passing. Having a CI that keeps failing for non-material reasons slows the workflow and forces repeated workarounds that might end up breaking things that matter.

I'll update the latest pull request to remove it unless there are very strong objections.

The package will nevertheless be checked on Windows as part of release to CRAN.

Add ability to load packages from any library directory

Assuming 'lib' is a directory that is not defined in the .libPaths(), and package foo exists in 'lib', and function bar exists in foo, I expect:

import::from(foo, bar, .library='lib')

To import function bar from foo. At the moment this causes an error, due to getNamespaceExports being called before the namespace of foo is made available. The solution appears to be a quick reordering of the operations, as loadNamespace comes right after that, and since that uses the .library parameter it can successfully load the namespace for getNamespaceExports to reference.

Open PR in #55

Workaround currently is to explicitly add lib to the .libPaths

Switch default branch to main

The new default branch on GitHub is now main and there are a number of good reasons to use that name. And now seems like a pretty good time to make the switch for the import repo.

`get` being overridden by other packages

Problem

library(config)
#> 
#> Attaching package: 'config'
#> The following objects are masked from 'package:base':
#> 
#>     get, merge
import::from('R/make_import_call.R', make_import_call)
#> Error: unused arguments (envir = <environment>, inherits = FALSE)

Created on 2022-03-30 by the reprex package (v2.0.1)

Any package that loads a function called get is used instead of base::get (I understand that config is meant to be used much like import, without calling library, but this is an example)

Expected output

Expected no error on load

Proposed Solution

In make_import_call, namespace get as base::get.

#' Make an import call object.
#'
#' The import call constructed by this function can be evaluated with
#' \code{eval} to perform the actual import.
#'
#' @param params list of parameters to substitute in the call.
#' @param exports_only logical indicating whether only exported objects are
#'   allowed.
#'
#' @return A call object.
#'
#' @noRd
make_import_call <- function(params, exports_only)
{
  cl <-
    if (exports_only)
       substitute(safe_assign(new, getExportedValue(nm, ns = ns), pos = pos),
                  params)
    else
       substitute(safe_assign(new, base::get(nm, envir = ns, inherits = inh), pos = pos),
                  params)

  cl[[1]] <- safe_assign

  cl
}

GitHub commit checks have started to fail without discernible reason

The commit checks triggered by pull requests such as #55 currently fail even though they passed at the time of the last release, and nothing seems to have changed in the package that should cause such the failure. See for example the results here:

https://github.com/rticulate/import/runs/5777825787?check_suite_focus=true

This is kind of a blocker for a new release of import, but I'm personally not too proficient with GitHub actions, so I would very much appreciate assistance with this.

Unclear documentation on .character_only = TRUE affect on .into

(This may be more my own misunderstanding but thought I'd open issue as found a bit unclear when reading through).

I was a bit confused by the documentation of .character_only impact on .into.

For example in the help file under the argument for .character_only it states:

(Note that this parameter does not apply to how the .into parameter is handled).

Which seems in-line with how the package works. However in the advanced usage section of the vignette it reads:

By setting .character_only=TRUE, all non-standard evaluation of the .from, .into and the ... parameters is disabled (other parameters are always evaluated in a standard way). Instead, the parameters are processed as character vectors containing the relevant values.

Which I felt like had contradicted documentation point above...?

Context

What I want to be able to do is something like:

pkg <- "tidyr"
pkg_env <- "package:tidyr"
fun <- "nest"
import::from(pkg, fun, .into = pkg_env , .character_only = TRUE)

Goal is that if I run above and then do find("nest") it will return "package:tidyr" but won't load other functions... but need to do programmatically for any pkg::fun()` pairing.)

The current approach I am using is what @torfason suggested as option at #53

pkg <- "tidyr"
env <- new.env()
env_nm <- "package:tidyr"
fun <- "nest"

import::from(pkg, fun, .into = {env}, .character_only = TRUE)
attach(env, name = env_nm)

Placing to-be-imported operators inside double vs. single quote?

I really like the philosophy behind import and introduced it to students in my class.
Recently, a couple of students mentioned to me that
when they tried to import the pipe operator %>% from magrittr package,
placing the pipe operator inside a pair of single vs. double quotation marks made a difference.
Only import::from(magrittr, "%>%") worked for them,
whereas import::from(magrittr, '%>%') did not.

I could not replicate this problem myself either on a Mac or on a Windows.
Both single and double quotes worked properly for me.
I even tried a pair of backticks ` `, which also worked.
For the record, both students who had trouble with single quotes were on Windows.

I am aware of the single vs. double quotes subtleties
and that double quotes are the preferred method.
However, I still want to know whether anyone is aware of such differences for import method
and if so, why.

Thank you very much!

import function only for a specific environment

I expected that in the code below the first and second count would reference dplyr but the third would show count to be part of the plyr package. Instead, the second count already refers to plyr.

Is my intended/expected import process possible with import?

env <- new.env(emptyenv())
attr(env, "name") <- "env"
library(dplyr)
count
import::from(plyr, count, .into = "env")
count
attach(env)
count

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.