Skip to content

Commit 557d0e0

Browse files
committed
Avoid non-API R lookup symbols under R-devel
1 parent d5df5b7 commit 557d0e0

File tree

4 files changed

+70
-41
lines changed

4 files changed

+70
-41
lines changed

src/python.cpp

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,20 @@ using namespace Rcpp;
2828

2929
using namespace reticulate::libpython;
3030

31+
SEXP ns_reticulate;
32+
33+
inline SEXP reticulate_get_ns_var(const char* name) {
34+
return reticulate_get_var(Rf_install(name), ns_reticulate);
35+
}
36+
3137
int _Py_Check(PyObject* o) {
3238
// default impl we assign to some Python api functions until we've initialized Python;
3339
return 0;
3440
}
3541

3642

3743
PyGILState_STATE _initialize_python_and_PyGILState_Ensure() {
38-
Function initialize_python = Environment::namespace_env("reticulate")["ensure_python_initialized"];
44+
Function initialize_python(reticulate_get_ns_var("ensure_python_initialized"));
3945
initialize_python();
4046
return PyGILState_Ensure();
4147
}
@@ -45,14 +51,14 @@ SEXP sym_py_object;
4551
SEXP sym_simple;
4652
SEXP sym_convert;
4753

48-
SEXP ns_reticulate;
49-
5054
SEXP r_func_py_filter_classes;
5155
SEXP r_func_get_r_trace;
5256
SEXP r_func_py_callable_as_function;
5357
SEXP r_func_r_to_py;
5458
SEXP r_func_py_to_r;
5559
SEXP r_func_py_to_r_wrapper;
60+
SEXP r_func_r_convert_dataframe_column;
61+
SEXP r_func_py_resolve_module_proxy;
5662

5763
tthread::thread::id s_main_thread = 0;
5864

@@ -69,14 +75,16 @@ void reticulate_init(DllInfo *dll) {
6975
sym_convert = Rf_install("convert");
7076
sym_pyobj = Rf_install("pyobj");
7177

72-
ns_reticulate = reticulate_get_var_in_frame(R_NamespaceRegistry, Rf_install("reticulate"));
78+
ns_reticulate = reticulate_find_namespace("reticulate");
7379

74-
r_func_py_filter_classes = reticulate_get_var(Rf_install("py_filter_classes"), ns_reticulate);
75-
r_func_py_callable_as_function = reticulate_get_var(Rf_install("py_callable_as_function"), ns_reticulate);
76-
r_func_r_to_py = reticulate_get_var(Rf_install("r_to_py"), ns_reticulate);
77-
r_func_py_to_r = reticulate_get_var(Rf_install("py_to_r"), ns_reticulate);
78-
r_func_py_to_r_wrapper = reticulate_get_var(Rf_install("py_to_r_wrapper"), ns_reticulate);
79-
r_func_get_r_trace = reticulate_get_var(Rf_install("get_r_trace"), ns_reticulate);
80+
r_func_py_filter_classes = reticulate_get_ns_var("py_filter_classes");
81+
r_func_py_callable_as_function = reticulate_get_ns_var("py_callable_as_function");
82+
r_func_r_to_py = reticulate_get_ns_var("r_to_py");
83+
r_func_py_to_r = reticulate_get_ns_var("py_to_r");
84+
r_func_py_to_r_wrapper = reticulate_get_ns_var("py_to_r_wrapper");
85+
r_func_get_r_trace = reticulate_get_ns_var("get_r_trace");
86+
r_func_r_convert_dataframe_column = reticulate_get_ns_var("r_convert_dataframe_column");
87+
r_func_py_resolve_module_proxy = reticulate_get_ns_var("py_resolve_module_proxy");
8088

8189
s_main_thread = tthread::this_thread::get_id();
8290
}
@@ -924,8 +932,7 @@ bool option_is_true(const std::string& name) {
924932
}
925933

926934
bool traceback_enabled() {
927-
Environment pkgEnv = Environment::namespace_env("reticulate");
928-
Function func = pkgEnv["traceback_enabled"];
935+
Function func(reticulate_get_ns_var("traceback_enabled"));
929936
return as<bool>(func());
930937
}
931938

@@ -1150,8 +1157,7 @@ std::string conditionMessage_from_py_exception(PyObject* exc) {
11501157
oss << as_std_string(PyList_GetItem(formatted, i));
11511158

11521159
static std::string hint = []() {
1153-
Environment pkg_env(Environment::namespace_env("reticulate"));
1154-
Function hint_fn = pkg_env[".py_last_error_hint"];
1160+
Function hint_fn(reticulate_get_ns_var(".py_last_error_hint"));
11551161
CharacterVector r_result = hint_fn();
11561162
return Rcpp::as<std::string>(r_result[0]);
11571163
}();
@@ -3898,16 +3904,17 @@ PyObjectRef py_module_import(const std::string& module, bool convert) {
38983904

38993905
// [[Rcpp::export]]
39003906
void py_module_proxy_import(PyObjectRef proxy) {
3901-
Rcpp::Environment refenv = proxy.get_refenv();
3902-
if (refenv.exists("module")) {
3907+
SEXP refenv = proxy.get_refenv();
3908+
SEXP module_sym = Rf_install("module");
3909+
SEXP r_module = reticulate_get_var_or_null(refenv, module_sym);
3910+
if (r_module != NULL) {
39033911
GILScope _gil;
3904-
Rcpp::RObject r_module = refenv.get("module");
3905-
std::string module = as<std::string>(r_module);
3912+
std::string module = as<std::string>(Rcpp::RObject(r_module));
39063913
PyObject* pModule = py_import(module);
39073914
if (pModule == NULL)
39083915
throw PythonException(py_fetch_error());
39093916
proxy.set(pModule);
3910-
refenv.remove("module");
3917+
R_removeVarFromFrame(module_sym, refenv);
39113918
}// else, if !exists("module", <refenv>),
39123919
// then we're unwinding a recursive py_resolve_module_proxy() call, e.g.:
39133920
// -> py_resolve_module_proxy() -> import() -> py_module_onload() ->
@@ -4417,8 +4424,7 @@ PyObject* r_to_py_pandas_nullable_series (const RObject& column, const bool conv
44174424
// [[Rcpp::export]]
44184425
PyObjectRef r_convert_dataframe(RObject dataframe, bool convert) {
44194426
GILScope _gil;
4420-
Function r_convert_dataframe_column =
4421-
Environment::namespace_env("reticulate")["r_convert_dataframe_column"];
4427+
Function r_convert_dataframe_column(r_func_r_convert_dataframe_column);
44224428

44234429
PyObjectPtr dict(PyDict_New());
44244430

@@ -4871,8 +4877,7 @@ SEXP py_iterate(PyObjectRef x, Function f, bool simplify = true) {
48714877

48724878

48734879
bool try_py_resolve_module_proxy(SEXP proxy) {
4874-
Rcpp::Environment pkgEnv = Rcpp::Environment::namespace_env("reticulate");
4875-
Rcpp::Function py_resolve_module_proxy = pkgEnv["py_resolve_module_proxy"];
4880+
Rcpp::Function py_resolve_module_proxy(r_func_py_resolve_module_proxy);
48764881
return py_resolve_module_proxy(proxy);
48774882
}
48784883

src/r_api.h

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
11
#ifndef RETICULATE_R_API_H
22
#define RETICULATE_R_API_H
33

4+
inline SEXP reticulate_find_namespace(const char* name) {
5+
SEXP ns = R_NilValue;
6+
SEXP name_sexp = PROTECT(Rf_mkString(name));
7+
ns = R_FindNamespace(name_sexp);
8+
UNPROTECT(1);
9+
return ns;
10+
}
11+
412
// R_getVarEx() is the supported replacement for Rf_findVar* on R >= 4.5.
513
// It is not identical: it forces promises and errors on R_MissingArg.
614
// Use these wrappers only where those differences are acceptable.
7-
inline SEXP reticulate_get_var_in_frame(SEXP env, SEXP sym) {
15+
inline SEXP reticulate_get_var_or_null(SEXP env, SEXP sym) {
816
#if defined(R_VERSION) && R_VERSION >= R_Version(4, 5, 0)
9-
return R_getVarEx(sym, env, FALSE, R_UnboundValue);
17+
return R_getVarEx(sym, env, FALSE, NULL);
1018
#else
11-
return Rf_findVarInFrame(env, sym);
19+
SEXP value = Rf_findVarInFrame(env, sym);
20+
return value == R_UnboundValue ? NULL : value;
1221
#endif
1322
}
1423

1524
inline SEXP reticulate_get_var(SEXP sym, SEXP env) {
1625
#if defined(R_VERSION) && R_VERSION >= R_Version(4, 5, 0)
17-
return R_getVarEx(sym, env, TRUE, R_UnboundValue);
26+
SEXP value = R_getVarEx(sym, env, FALSE, NULL);
1827
#else
19-
return Rf_findVar(sym, env);
28+
SEXP value = Rf_findVarInFrame(env, sym);
29+
if (value == R_UnboundValue)
30+
value = NULL;
2031
#endif
32+
if (value == NULL)
33+
Rf_error("object '%s' not found", CHAR(PRINTNAME(sym)));
34+
return value;
2135
}
2236

2337
#endif

src/reticulate_types.h

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,14 @@ class PyObjectRef: public Rcpp::RObject {
8383
// or throw an exception if it can't return a valid PyObject*
8484
PyObject* get() const {
8585

86-
SEXP xptr = reticulate_get_var_in_frame(get_refenv(), sym_pyobj);
86+
SEXP refenv = get_refenv();
87+
SEXP xptr = reticulate_get_var_or_null(refenv, sym_pyobj);
88+
if (xptr == NULL) {
89+
if (try_py_resolve_module_proxy(refenv)) {
90+
return get();
91+
}
92+
Rcpp::stop("malformed pyobj");
93+
}
8794

8895
if(TYPEOF(xptr) == EXTPTRSXP) {
8996
PyObject* pyobj = (PyObject*) R_ExternalPtrAddr(xptr);
@@ -92,13 +99,6 @@ class PyObjectRef: public Rcpp::RObject {
9299
return pyobj;
93100
}
94101

95-
// might be a (lazy) module_proxy
96-
if(xptr == R_UnboundValue) {
97-
if(try_py_resolve_module_proxy(get_refenv())) {
98-
return get();
99-
}
100-
}
101-
102102
Rcpp::stop("malformed pyobj");
103103
return NULL; // unreachable, for compiler
104104
}
@@ -127,18 +127,23 @@ class PyObjectRef: public Rcpp::RObject {
127127

128128
// This will *not* initialize Python or resolve module proxies
129129
bool is_null_xptr() const {
130-
SEXP xptr = reticulate_get_var_in_frame(get_refenv(), sym_pyobj);
130+
SEXP refenv = get_refenv();
131+
SEXP xptr = reticulate_get_var_or_null(refenv, sym_pyobj);
132+
if (xptr == NULL)
133+
return false; // return false for lazy module proxy
134+
131135
if(TYPEOF(xptr) == EXTPTRSXP)
132136
return ((PyObject*) R_ExternalPtrAddr(xptr) == NULL);
133-
if(xptr == R_UnboundValue)
134-
return false; // return false for lazy module proxy
135137
if(xptr == R_NilValue)
136138
return true; // ??? manually finalized obj? ??? should never happen ???
137139
return false; // should never happen
138140
}
139141

140142
bool convert() const {
141-
SEXP sexp = reticulate_get_var_in_frame(get_refenv(), sym_convert);
143+
SEXP refenv = get_refenv();
144+
SEXP sexp = reticulate_get_var_or_null(refenv, sym_convert);
145+
if (sexp == NULL)
146+
return true;
142147

143148
if(TYPEOF(sexp) == LGLSXP)
144149
return (bool) Rf_asLogical(sexp);
@@ -147,7 +152,10 @@ class PyObjectRef: public Rcpp::RObject {
147152
}
148153

149154
bool simple() const {
150-
SEXP sexp = reticulate_get_var_in_frame(get_refenv(), sym_simple);
155+
SEXP refenv = get_refenv();
156+
SEXP sexp = reticulate_get_var_or_null(refenv, sym_simple);
157+
if (sexp == NULL)
158+
return true;
151159

152160
if(TYPEOF(sexp) == LGLSXP)
153161
return (bool) Rf_asLogical(sexp);

tests/testthat/test-compiled-code.R

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ test_that("compiled code avoids deprecated variable lookup entry points", {
1111
symbols <- system2(nm, c("-u", dylib), stdout = TRUE, stderr = TRUE)
1212

1313
expect_false(any(grepl("\\b_?Rf_findVar(InFrame)?$", symbols)))
14+
expect_false(any(grepl("\\b_?R_NamespaceRegistry$", symbols)))
15+
expect_false(any(grepl("\\b_?R_UnboundValue$", symbols)))
1416
})

0 commit comments

Comments
 (0)