How to Make Variable Available to Namespace at Loading Time

How to make variable available to namespace at loading time

Following the approach in this answer to a related question, you can change your .onLoad() function like so:

.onLoad <- function(libname, pkgname) {
variable <- some_function()
assign("variable", variable, envir = parent.env(environment()))
}

Then you can access variable without attaching the package using MyRPackage:::variable. I don't know what you do with some_function(), so I tried the following with a dummy package:

.onLoad <- function(libname, pkgname) {
variable <- 42
assign("variable", variable, envir = parent.env(environment()))
}

And in a fresh R session the result was

> MyRPackage:::variable
[1] 42

Further explanation

From Hadley Wickham's Advanced R:

There are four special environments:

...

  • The environment() is the current environment.

...

You can list the bindings in the environment’s frame with ls() and
see its parent with parent.env().

So, if we modify the .onLoad() function further, we can see this in action:

.onLoad <- function(libname, pkgname) {
print(environment()) # For demonstration purposes only;
print(parent.env(environment())) # Don't really do this.
variable <- 42
assign("variable", variable, envir = parent.env(environment()))
}

Then starting an R session results in

<environment: 0x483da88>
<environment: namespace:MyRPackage>

being printed to the console at the start of the session. This allows you to assign variable in the environment namespace:MyRPackage even though trying assign("variable", variable, envir = namespace:MyRPackage) would result in the error

Error: package or namespace load failed for ‘MyRPackage’:

  .onLoad failed in loadNamespace() for 'MyRPackage', details:

  call: get(name, envir = ns, inherits = FALSE)

  error: object 'namespace' not found

when installing the package.

How do I assign variables to be available when an R package is loaded?

Although .onLoad is one option, I think that it wouldn't be the recommended way to go about this (we can read here for what .onLoad is mainly used).

The question is, do you want my_colors to be available to the users? Or do you just want it to be available as internal data for your functions. From looking at your github repo I think the latter is the case, but lets go through both options. They are both explained in the link above from @Waldi's answer.

  1. Make the colors and palettes available as external data to the user. You would do this with usethis::use_data(my_colors) and then you'd just need to document the data, but do not use @export in the documentation. In the description file you could set LazyData: true so the data is only loaded into memory when actually used. In this case your functions can use the data, but it would also be available to the users.

  2. Alternatively, you could make the colors and palettes available as internal data for your functions. This can be done with the same function from {usethis}, the only difference is we set the internal argument to TRUE: usethis::use_data(my_colors, internal = TRUE). Now you can refer inside your function to the data my_palettes and the function argument can be used to subset my_palettes.

For example:

scale_color_mine(palette = "federal")

would internally do:

scale_color_mine  <- function(palette) {
# do stuff here
cur_palette <- my_palettes[[palette]]
# do stuff here
}

In this case the users won't be able to access the data, but the good thing is that my_colors and my_palettes wouldn't populate the namespace.

Python: load variables in a dict into namespace

Consider the Bunch alternative:

class Bunch(object):
def __init__(self, adict):
self.__dict__.update(adict)

so if you have a dictionary d and want to access (read) its values with the syntax x.foo instead of the clumsier d['foo'], just do

x = Bunch(d)

this works both inside and outside functions -- and it's enormously cleaner and safer than injecting d into globals()! Remember the last line from the Zen of Python...:

>>> import this
The Zen of Python, by Tim Peters
...
Namespaces are one honking great idea -- let's do more of those!

C++ How do I make a function/variable local to a namespace?

AFAIK there is no access restriction in namespaces in C++.

namespace foo {
void local_func() {}

// local_func can be accessed inside the namespace

void global_func() { local_func(); }
}

// ...

foo::local_func(); // local_func can be accessed outside the namespace

You can achieve something close with static member functions inside a class definition:

class foo {
private:
static void local_func() {}

public:
// local_func can be accessed inside the "namespace" of the class

void global_func() { local_func(); }
};

// ...

foo::local_func(); // error : local_func is private to the class

Change variable in package namespace

The problem seems to have been with the format of the assign statements. Instead of

assign(".myvar", "bar", pos = "package:mypackage")

I used,

assign(".myvar", "bar", pos = asNamespace("mypackage"))

and this resolved the issue.

Injecting variables into an import namespace

I came up with a solution based on this answer and the importlib docs. Basically, I have access to the module object before it is loaded by using the correct sequence of calls to importlib:

from importlib.util import spec_from_file_location, module_from_spec
from os.path import splitext, basename

def loadConfig(fileName):
test = 'This is a test'
name = splitext(basename(fileName))[0]
spec = spec_from_file_location(name, fileName)
config = module_from_spec(spec)
config.test = test
spec.loader.exec_module(config)
return config

testmod = loadConfig('./testmod.py')

This is a bit better than modifying builtins, which may have unintended consequences in other parts of the program, and may also restrict the names I can pass in to the module.

I decided to put all the configuration items into a single field accessible at load time, which I named config. This allows me to do the following in testmod:

if 'test' in config:
x = config['test']

The loader now looks like this:

from importlib.util import spec_from_file_location, module_from_spec
from os.path import splitext, basename

def loadConfig(fileName, **kwargs):
name = splitext(basename(fileName))[0]
spec = spec_from_file_location(name, fileName)
config = module_from_spec(spec)
config.config = kwargs
spec.loader.exec_module(config)
return config

testmod = loadConfig('./testmod.py', test='This is a test')

After finding myself using this a bunch of times, I finally ended up adding this functionality to the utility library I maintain, haggis. haggis.load.load_module loads a text file as a module with injection, while haggis.load.module_as_dict does a more advanced version of the same that loads it as a potentially nested configuration file into a dict.

Accessing a global and namespace variable

Let's start with another example.

const int x = 10;

namespace e {
const int y = 5;
}

int main()
{
std::cout << e::y;
using namespace e;
std::cout << y;
}

There is variable with value 10 and name x in global namespace (which can be referred to as x simply) and variable with value 5 with name y in namespace e (which must be referred to as e::y).

By adding using namespace e;, you inject all names from namespace e into global namespace. This means global namespace now contains names x and y, and namespace e contains name y. You can now refer to variable with value 5 using both y and e::y.

Now, back to your example. If we change y to x:

const int x = 10;

namespace e {
const int x = 5;
}

int main()
{
std::cout << e::x;
using namespace e;
std::cout << x;
}

There is x in global namespace and x in namespace e. By adding using namespace e; you inject all the names from namespace e to global namespace, so now global namespace contains names x and x, and namespace e contains name x. See the problem? Global namespace contains two names x, which confuses the compiler. When you try to print variable under the name x, compiler looks up names in global namespace and finds two x. It cannot choose which one you meant, so it throws error.

This is the main reason why using namespace (particularly using namespace std;) is considered evil. One can easily break working code by updating a library or introducing a new function. Compiler error is best outcome in such a case, but sometimes it's possible that compiler will silently replace one function by another, because it matches better.

You can still access both variables using fully qualified names:

int main()
{
using namespace e;
std::cout << ::x << " "; //x from global with fully quafilied name
std::cout << ::e::x << " "; //x from namespace e with fully qualified name
std::cout << e::x; //not fully qualified, but not ambiguous either - only one x in namespace e
}

How do you access a procedure's local variables in a namespace eval {} inside that same procedure?

You can just use set with a fully qualified variable name that uses the desired namespace:

proc getNS {name} {
namespace eval ns::$name {} ;# Create namespace if it doesn't already exist
set ns::${name}::n $name
}

getNS foo
puts $ns::foo::n ;# foo

Another way is to use uplevel to refer to the scope of the proc that calls namespace eval:

proc getNS {name} {
namespace eval ns::$name {
set n [uplevel 1 {set name}]
}
}


Related Topics



Leave a reply



Submit