Урок 8 Написание документации к функциям пакета


Этим уроком мы начинаем обширную тему документации вашего пакета. В этом видео мы разберёмся с тем, как добавить документацию к функциям пакета с помощью специальных комментариев и пакета roxygen2.


Данный урок основан на главе "Function documentation" книги "R Packages (2e)", под авторством Хедли Викхема и Дженни Брайан.


8.1 Видео

8.1.1 Тайм коды

00:00 Вступление
00:43 Какие компоненты пакета отвечают за документацию функций
01:31 Рабочий процесс
04:47 Структура roxygen комментариев
05:34 Особенности разметки roxygen комментариев
07:10 Из каких разделов состоит документация к функциям пакета
09:06 Title, Description и Details
12:07 Документирование аргументов функции
14:55 Возвращаемое функцией значение
16:42 Примеры использования функции
20:12 Повторное использование документации
22:23 Документация уровня пакета
23:50 Заключение

8.2 Презентация

8.3 Конспект

8.3.1 Рабочий процесс

Рабочий процесс по добавлению документации к функциям состоит из следующих этапов:

  1. Добавляете roxygen2 комментарии над объектами, к которым хотите сгенерировать документацию (Ctrl+Shift+Alt+R когда курсор находится внутри кода функции или кода генерирующего объект)
  2. Запускаете devtools::document() (Ctrl/Cmd + Shift + D), чтобы преобразовать комментарии roxygen2 в .Rd файлы.
  3. Запускаете предварительный просмотрт сгенерированной документации с помощью ?function_name.
  4. Исправляете ошибки, повторяете процесс до тех пор, пока не получите нужный результат.

8.3.2 Структура roxygen комментариев

Например, для нашего оператора %+%, который мы с вами добавили в пакет ещё в первом уроке, можно добавить следующие roxygen комментарии для генерации документации:

#' Concatenate Strings
#'
#' Operator for concatenate two strings.
#'
#' Use `%+%` operator for join two or more strings
#' @section Main:
#' The main ......
#' @param x Character, first string
#' @param y Character, second string
#'
#' @returns A character vector with lenght 1
#' @export
#'
#' @examples
#' # two values
#' "first" %+% "second"
#'
#' # three values
#' "first" %+% "second" %+% "three"

roxygen комментарии имеют следующие структуру:

  • Комментарии roxygen2 начинаются с #'
  • Все комментарии roxygen2, предшествующие функции называются блоком
  • Блоки разбиваются на теги , которые выглядят как @tagName tagValue
  • По умолчанию каждый блок генерирует один топик документации , т.е. один .Rd файл в man/ каталоге.

8.3.3 Особенности разметки .Rd файлов

  • Используйте апострофы для выделения блока текста как кода: #' I like \thisfunction()`, because it's great`.
  • Для добавление ссылки на документацию к другой функции вашего или стороннего пакета используйте квадратные скобки:
#' It's obvious that `thisfunction()` is better than [otherpkg::otherfunction()]
#' or even our own [olderfunction()].
  • Ссылайтесь на виньетки с помощью кода: vignette("rd-formatting", package = "roxygen2")
  • Маркированные списки прописываются так же, как и в обычном markdown:
#' Best features of `thisfunction()`:
#' * Smells nice
#' * Has good vibes

8.3.4 Разделы документации

  • Заголовок (@title) – Обычно задаётся позиционно в первой строке roxygen комментария
  • Описание (@description) – Более подробное описание, что делает ваша функция, можно задать позиционно, второй строкой roxygen комментария
  • Детали (@details) – Тут описывают важные детали использования вашей функций, можно задать позиционно третей строкой комментария
  • Аргументы функции (@param) – Описание каждого аргумента функции
  • Возвращаемое значение (@return) – Какой объект возвращает ваша функция
  • Примеры (@examples) – Примеры использования вашей функции

8.3.4.1 Title, description и details

Первые 3 тега roxygen можно использовать позиционно, без указания их названий:

  • Title – заголовок должен быть написан в регистре заголовков, не заканчиваться точкой и после него должна идти пустая строка.
  • Description – более подробное описание того, что делает функция, как правило занимает один абзац. Если ваше описание требует более чем один абзац вам придётся явно использовать тег.1
  • Details – описание деталей работы вашей функции, в осном этот блок не используется в документации. Хорошей практикой является создание пользовательских разделов подробностей с помощью тега @section.

8.3.4.2 Аргументы

  • Для документирования аргументов функции используйте тег @param
  • Если использование нескольких аргументов тесно связано между собой можете объединить их через запятую, без указания пробела: #' @param x,y A pair of character vectors.
  • Тег @inheritParams позволяет наследовать описание аргументов из других функций вашего или даже стороннего пакета. При этом будут унаследованы только те аргументы, которые присутвуют в новой функции, и которые в ней отдельно не были задокументированы.

8.3.4.3 Возвращаемое значение

За возвращаемое значение отвечает тег @returns. Для возвращаемого значения вам необходимо описать класс, тип и размер возвращаемого функцией значения.

8.3.4.4 Примеры

  • Примеры описываются с использованием тега @examples
  • Все прописанные примеры должны выполняться без ошибок, поскольку они регулярно проверяются:
    • В интерактивном режиме с помощью команды example()
    • Во время работы R CMD check на вашем компьютере
    • Во время проверки R CMD check на CRAN.
    • Когда генерируется веб-сайт вашего пакета с помощью pkgdown
  • При использовании нестабильных ресурсов в ваших примерах, например веб-сайтах используйте тег @examplesIf, который позволяет пропускать такие примеры, так же можно завернуть ваши примеры в \dontrun{}, для избегания запуска примеров, которые заведомо заканчиваются ошибками.

Примеры использования \dontrun{} и @examplesIf можно подсмотреть в коде пакета googledrive:

Пример \dontrun{} используется в функции drive_find:

#' @examples
#' \dontrun{
#' # list "My Drive" w/o regard for folder hierarchy
#' drive_find()
#'
#' # filter for folders, the easy way and the hard way
#' drive_find(type = "folder")
#' drive_find(q = "mimeType = 'application/vnd.google-apps.folder'")
#'
#' # filter for Google Sheets, the easy way and the hard way
#' drive_find(type = "spreadsheet")
#' drive_find(q = "mimeType='application/vnd.google-apps.spreadsheet'")
#'
#' }
#'

@examplesIf используется в функции drive_publish:

#' @examplesIf drive_has_token()
#' # Create a file to publish
#' file <- drive_example_remote("chicken_sheet") %>%
#'   drive_cp()
#'
#' # Publish file
#' file <- drive_publish(file)
#' file$published
#'
#' # Unpublish file
#' file <- drive_unpublish(file)
#' file$published
#'
#' # Clean up
#' drive_rm(file)

В данном случае с помощью функции drive_has_token() мы проверяем есть ли кеш авторизационных данных, для доступа к данным, если учётные данные доступны то примеры выполняются, если не доступны то нет.

8.3.5 Объединить документацию к нескольким функциям в один файл

Документацию к нескольким близким по функционалу функциям имеет смысл объединить в один файл документации, сделать это можно с помощью тега @rdname. Например в пакете stringr в один файл объеденена документация к функциям str_length() и str_width():

Изначально общее описание документации обеих функций мы привязываем к функции str_length():

#' The length/width of a string
#'
#' @description
#' `str_length()` returns the number of codepoints in a string. These are
#' the individual elements (which are often, but not always letters) that
#' can be extracted with [str_sub()].
#'
#' `str_width()` returns how much space the string will occupy when printed
#' in a fixed width font (i.e. when printed in the console).
#'
#' ...
str_length <- function(string) {
  ...
}

Далее используем в функции str_width() тег @rdname для её объединения с str_length():

#' @rdname str_length
str_width <- function(string) {
  ...
}

8.3.6 Повторное использование документации

При необходимости объединить документацию нескольких функций в один файл документации используйте тег @rdname, и передайте в него название функции, в которой подробно описана документация всех функций.

Теги отвечающие за наследование частей документации:

  • @inherit source_function - унаследует все поддерживаемые компоненты от source_function().
  • @inheritSection source_function Section title - унаследует один раздел с заголовком «Section title» от source_function().
  • @inheritDotParams - автоматически генерирует документацию по параметрам для ... общего случая, когда вы переходите ... к другой функции.

Пример из пакета stringr, в котором практически в каждой функции доступны оноимённые аргументы string и pattern. Прописывать их отдельно для каждой функции не удобно, поскльку при необходимости внести в описание этих аргументов изменения, нам бы пришлось их руками вносить практически во все фукции пакета, поэтому в одной функции, в нашем случае str_length() мы прописали описание этих аргументов, а в других функциях мы их можем переиспользовать:

Описание аргументов в функции str_length():

#' @param string Input vector. Either a character vector, or something
#'  coercible to one.
#' @param pattern Pattern to look for.
#'
#'   The default interpretation is a regular expression, as described in
#'   `vignette("regular-expressions")`. Use [regex()] for finer control of the
#'   matching behaviour.
#'
#'   Match a fixed string (i.e. by comparing only bytes), using
#'   [fixed()]. This is fast, but approximate. Generally,
#'   for matching human text, you'll want [coll()] which
#'   respects character matching rules for the specified locale.
#'
#'   Match character, word, line and sentence boundaries with
#'   [boundary()]. An empty pattern, "", is equivalent to
#'   `boundary("character")`.

Далее с помощью тега @inheritParams в других функциях, например str_match()

#' @inheritParams str_detect
#' @param pattern Unlike other stringr functions, `str_match()` only supports
#'   regular expressions, as described `vignette("regular-expressions")`.
#'   The pattern should contain at least one capturing group.

При этом унаследованы будут только те аргументы, которые присутвуют в новой функции, и не задокументированы в ней. В примере выше мы унаследуем описание аргумента string, но переопределяем описание аргумента pattern.

8.3.7 Раздел справки по пакету

Используйте функцию usethis::use_package_doc() для генерации файла R/{pkgname}-package.R, который содердит документацию уровня пакета. Так же это хорошее место для хранения импорта, т.е. тегов @import и @importFrom. Данный файл по умолчанию будет иметь следующий вид:

#' @keywords internal 
"_PACKAGE"

# The following block is used by usethis to automatically manage
# roxygen namespace tags. Modify with care!
## usethis namespace: start
#' @importFrom glue glue_collapse
## usethis namespace: end
NULL

8.4 Тест