Защитить «частные» объекты в моем пакете R
Мотивация
Я разрабатываю пакет R (назовите его ), который собирает в работающем кэше некоторые объекты, созданные вызовами его функций. Достаточно просто реализовать кеш как
list
объект (назовите его) в пространство имен. Однако я хочу, чтобы пользователь не вмешивался напрямую в кеш, который всегда можно открыть через
:::
синтаксис:
pkg:::.cache
.
То есть я хочу защитить аналогично частным полям в объектно-ориентированном программировании. Все операции должны выполняться внутренними вспомогательными функциями, которые вызываются исключительно@export
ed функции с "ароматом API".
Эта проблема
Таким образом, у меня была идея определить environment
объект (назовите его) в пространстве имен, чтобы я мог ... поместить свой в моем .
Мое затруднение заключается в том, что подобное окружение - одна из немногих вещей в R, которые работают по ссылке . Так что я боюсь
pkg:::.vault
будет подвергать его содержимое как модификации, так и просмотру. Я могу изменить свои функции доступа так, чтобы они не допускали такого воздействия, но все эти усилия напрасны, если их можно напрямую изменить на месте.
🚨 НЕ ЗАПУСКАТЬ 🚨
В качестве примера рассмотрим этот опасный (?) Код ниже. Сначала я исследую внутреннюю среду из, чтобы «идентифицировать цель»:
dplyr:::.S3MethodsClasses
#> <environment: 0x7fab9c6c5310>
ls(dplyr:::.S3MethodsClasses)
#> [1] "grouped_df" "rowwise_df"
Затем я исследую
rowwise_df
объект и считать его достойной «мишенью»:
dplyr:::.S3MethodsClasses$rowwise_df
#> Virtual Class "rowwise_df" [package "dplyr"]
#>
#> Slots:
#>
#> Name: .Data names row.names
#> Class: list character data.frameRowLabels
#>
#> Name: .S3Class
#> Class: character
#>
#> Extends:
#> Class "tbl_df", directly
#> Class "tbl", by class "tbl_df", distance 2
# ...
Наконец, я использую тот факт, что в отличие от большинства объектов из
dplyr
пространство имен ...
library(dplyr)
mutate <- NULL
dplyr::mutate
#> function (.data, ...)
#> {
#> UseMethod("mutate")
#> }
#> <bytecode: 0x7fab9c2f6120>
#> <environment: namespace:dplyr>
... такая среда, как
.S3MethodsClasses
указывает по ссылке , и поэтому его содержимое можно легко изменить на месте:
# "Copy" the pointer to allow `<-` assignment.
same_pointer <- dplyr:::.S3MethodsClasses
same_pointer
#> <environment: 0x7fab9c6c5310>
# Modify in place.
same_pointer$rowwise_df <- NULL
dplyr:::.S3MethodsClasses$rowwise_df
#> NULL
И вот так грабят «хранилище»!
Подозрения
Я подозреваю, что ответ может лежать здесь, с lockEnvironment()
и друзья, но приложение немного выше меня. Возможно, я мог бы что-нибудь сделать
.onLoad
для настройки среды, которая затем будет
lock*()
ред - как
.vault
сам и привязки внутри него - но не раньше, чем сделать активную привязку между
.cache
и функцию доступа, которая будет заполняться внутри пространство имен.
Примечание
Я уже разрабатываю функцию завершения (вспомогательных) функций, например, когда они вызываются вне другой функции из
pkg
пространство имен. Таким образом, экспонирование
pkg:::.get_cache()
не позволит пользователю работать
.get_cache()
либо вручную, либо в собственной пользовательской функции.
Каноничность
Я был бы особенно признателен за советы разработчиков R, достаточно опытных, чтобы дать канонический ответ (если таковой имеется).