load/remove user-defined unit on package load/unload: best practise?
Asked Answered
M

1

11

In a package I am developing I need to define a new unit: flight level (FL) equivalent to 100 ft.

The units package provides the following possibility:

units::install_conversion_constant("FL", "ft", 100)

In order to make package test (devtools::test()) and package check (devtools::test()) both work for my unit tests using this user-defined unit, I discovered that I need to register it in the package load phase.

Here is what I did:

In zzz.R (a new files as per "When you do need side-effects" section):

# register flight levels (FL) as a unit when loading this package
.onLoad <- function(libname, pkgname) {
  # install user-define unit for flight level
  units::install_conversion_constant("FL", "ft", 100)

  invisible()
}

# register flight levels (FL) as a unit when loading this package
.onUnload <- function(libname, pkgname) {
  # uninstall user-define unit for flight level
  units::remove_symbolic_unit("FL")

  invisible()
}

Failing to do that and putting the unit registration code in some R/unit-conversion.R file makes devtools::test() succeed but devtools::check() fail.

Is the solution above the correct approach to register (and remove [should this be done too?]) a new unit in a package?

Misguidance answered 6/8, 2018 at 14:4 Comment(3)
IMO you are doing the right thing. You need to be sure that things work properly when someone else uses and imports just one of your functions, so onLoad is the way to goPhantasmal
@Phantasmal what about .onUnload, any opinions?Misguidance
I wouldn’t call you out for not doing it, but personally, I like that you do it. I also think that the answer provided below doesn‘t really touch your case. At the same time, I couldn’t provide an answer since I don‘t have any modification suggestions :)Phantasmal
P
3

This is almost absolutely the place to do it for your package. I say Almost because there is an exception to every rule. Read the section below for some more detail and the good practice reccomendation from the base R manual

https://stat.ethz.ch/R-manual/R-devel/library/base/html/ns-hooks.html

Good practice Loading a namespace should where possible be silent, with startup messages given by .onAttach. These messages (and any essential ones from .onLoad) should use packageStartupMessage so they can be silenced where they would be a distraction.

There should be no calls to library nor require in these hooks. The way for a package to load other packages is via the Depends field in the ‘DESCRIPTION’ file: this ensures that the dependence is documented and packages are loaded in the correct order. Loading a namespace should not change the search path, so rather than attach a package, dependence of a namespace on another package should be achieved by (selectively) importing from the other package's namespace.

Uses of library with argument help to display basic information about the package should use format on the computed package information object and pass this to packageStartupMessage.

There should be no calls to installed.packages in startup code: it is potentially very slow and may fail in versions of R before 2.14.2 if package installation is going on in parallel. See its help page for alternatives.

Compiled code should be loaded (e.g., via library.dynam) in .onLoad or a useDynLib directive in the ‘NAMESPACE’ file, and not in .onAttach. Similarly, compiled code should not be unloaded (e.g., via library.dynam.unload) in .Last.lib nor .onDetach, only in .onUnload.

Peashooter answered 13/8, 2018 at 12:58 Comment(4)
Thanks @adam-wheeler, I did read the R manual page you mentioned, but it is still not clear to me. I was fishing for some examples of package kind-of initialization code from a package user perspective...Misguidance
I dont understand what you are asking for. what do you mean by "package kind-of initialization code from a package user perspective"Peashooter
Adam, I am developing a package that makes use of flight levels. I want to have a unit of measurement for them and use it throughout the package code. I then use the units package code as in my question to define a custom unit, FL. My question is where to put the definition of FL in my package code, i.e. whether .onLoad and .onUnload are the right places.Misguidance
.onLoad and .onUnload in a file called zzz.R is the right place to do it. Use the load functions because this is what they are meant to do and this is how packages should be built; use zzz.R because you want to carry on the tradition.Peashooter

© 2022 - 2024 — McMap. All rights reserved.