Защитить «частные» объекты в моем пакете R

Мотивация

Я разрабатываю пакет R (назовите его ), который собирает в работающем кэше некоторые объекты, созданные вызовами его функций. Достаточно просто реализовать кеш как list объект (назовите его) в пространство имен. Однако я хочу, чтобы пользователь не вмешивался напрямую в кеш, который всегда можно открыть через ::: синтаксис: pkg:::.cache.

То есть я хочу защитить аналогично частным полям в объектно-ориентированном программировании. Все операции должны выполняться внутренними вспомогательными функциями, которые вызываются исключительно@exported функции с "ароматом API".

Эта проблема

Таким образом, у меня была идея определить environment объект (назовите его) в пространстве имен, чтобы я мог ... поместить свой в моем .

3

Мое затруднение заключается в том, что подобное окружение - одна из немногих вещей в 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, достаточно опытных, чтобы дать канонический ответ (если таковой имеется).

0 ответов

Другие вопросы по тегам