How to import sf to package to run a function that depends on lwgeom?

133 views Asked by At

I'm building a package that imports {sf}, and more specifically I use st_length() in one of my functions.

I initially added only {sf} to my package "Imports", but when I checked it I got a few {lwgeom} related errors:

Running examples in 'gtfstools-Ex.R' failed
   The error most likely occurred in:
   
   > base::assign(".ptime", proc.time(), pos = "CheckExEnv")
   > ### Name: get_trip_speed
   > ### Title: Get trip speed
   > ### Aliases: get_trip_speed
   > 
   > ### ** Examples
   > 
   > data_path <- system.file("extdata/spo_gtfs.zip", package = "gtfstools")
   > 
   > gtfs <- read_gtfs(data_path)
   > 
   > trip_speed <- get_trip_speed(gtfs)
   Error in sf::st_length(trips_geometries) : 
     package lwgeom required, please install it first

This error happens when the examples are running, but some similar errors happen with the tests.

Then I added {lwgeom} to Imports. The check runs fine, but in the end I get a note: NOTE: Namespaces in Imports field not imported from: 'lwgeom'

What's the best practice when dealing with cases like this? Should I just keep track of this note and send it as a comment to CRAN during the package submission process?

2

There are 2 answers

2
dhersz On BEST ANSWER

The Suggests != Depends article by Dirk Eddelbuettel refers to a relevant bit of Writing R Extensions (WRE) that might be useful to this case.

Section 1.1.3.1 (suggested packages) reads (as of 2021-03-12):

Note that someone wanting to run the examples/tests/vignettes may not have a suggested package available (and it may not even be possible to install it for that platform). The recommendation used to be to make their use conditional via if(require("pkgname")): this is OK if that conditioning is done in examples/tests/vignettes, although using if(requireNamespace("pkgname")) is preferred, if possible.

However, using require for conditioning in package code is not good practice as it alters the search path for the rest of the session and relies on functions in that package not being masked by other require or library calls. It is better practice to use code like

   if (requireNamespace("rgl", quietly = TRUE)) {
      rgl::plot3d(...)
   } else {
      ## do something else not involving rgl.
   }

So while just adding {lwgeom} to Suggests works, we may stumble upon the issue where someone that runs a "lean installation" (i.e. without suggested packages) of my package won't be able to use the functions that rely on {lwgeom}.

More importantly, if an author of a package that I am importing decides to run a reverse dependency check on my package while not installing suggested packages, the check would fail because I'd have a few examples, tests and vignettes bits failing due to not having {lwgeom} available.

Thus, in addition to listing it in Suggests, I added some checks on examples and vignettes like suggested by WRE:

*examples/vignette context*

# the examples below require the 'lwgeom' package to be installed
if (requireNamespace("lwgeom", quietly = TRUE)) {

  ... do something ...

}

In the functions that require {lwgeom} I added:

if (!requireNamespace("lwgeom", quietly = TRUE))
    stop(
      "The 'lwgeom' package is required to run this function. ",
      "Please install it first."
    )

And added this bit to the tests of such functions (using {testthat}):

if (!requireNamespace("lwgeom", quietly = TRUE)) {

  expect_error(
    set_trip_speed(gtfs, "CPTM L07-0", 50),
    regexp = paste0(
      "The \\'lwgeom\\' package is required to run this function\\. ",
      "Please install it first\\."
    )
  )

  skip("'lwgeom' package required to run set_trip_speed() tests.")

}
0
Jindra Lacko On

You can consider adding the {lwgeom} package in Suggests field of your package DESCRIPTION file. It should do the trick.