Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Мой мадригал тем инструментам разработки, которые изменили мою жизнь
Программирование: срез за много лет
Программирование стало гораздо более многогранным ремеслом с тех пор, как в середине 1990-х я впервые попробовал AmigaBASIC. В те времена еще можно было купить один большой том о компьютере, на котором вы программируете – и там бы нашлось 99% всей нужной информации. Эта книга, где на множестве страниц уголки загнуты в качестве закладок, обклеенная стикерами, лежала бы у вас под рукой, пока вы вбивали бы команды в монохромный текстовый редактор.
Современная книга по клиентскому веб-фреймворку может быть толще, чем во времена программирования под C64 бывали мануалы, достаточные для создания полноценных игр. С другой стороны, сегодня информация по любым платформам, для которых требуется писать код, находится буквально в одном клике.
Сегодня никто бы больше и не подумал покупать документацию по разработке – и Microsoft, и Apple свободно выкладывают свою документацию в Интернете для всех желающих. А что говорить о проектах с открытым исходным кодом!
Во времена npm, PyPI и GitHub сложно объяснить, насколько неоднозначным решением (которое требовалось всесторонне обдумывать) раньше считалось потребовать хоть какие-нибудь возможности, которые выходили бы за рамки функционала операционной системы. Часто вместе с продуктом приходилось сдавать и все его зависимости.
Нынешнее изобилие и разнообразие инструментов – это благо, но в таких условиях фрагментируется информация, необходимая вам для продуктивной работы.
У вас могут быть открыты десятки вкладок с документацией по пакетам, с которыми вы работаете в настоящий момент. Вы бешено перещелкиваете вкладки, чтобы найти нужную. Поскольку мне доводилось работать из лучшего в мире города для занятий лонгбордингом, где несколько десятков тысяч человек делят одну точку присутствия на всех, могу вас заверить, что чисто онлайновые документы – не единственная проблема, когда Интернет намертво отрубается. Скажу вам, что поиск при сбоящем Интернете – это хуже, чем вообще никакого поиска.
Если вы, как и я, работаете сразу со множеством языков программирования, вокруг каждого из которых сформировались колоссальные субкультуры (даже в пределах Python есть Flask + SQLAlchemy + Postgres, которая кардинально отличается от программирования сетевых серверов на основе asyncio), то поймете, что мне просто дурно помыслить, может ли кто-нибудь вспомнить все аргументы к каждому из используемых мной методов. В основном потому, что я сам едва вспоминаю собственный номер телефона.
Вот почему моя жизнь в корне изменилась в 2012 году, когда я открыл для себя Dash.
Обозреватели документации по API
Разум нужен, чтобы придумывать идеи, а не хранить их.
— Дэвид Аллен
Dash наделяет меня суперсилой – иметь все актуальные API под рукой, только один раз на клавишу нажать:
Нажимаю «Пробел» - и всплывает окно со строкой поиска, в которой уже стоит курсор,
Начинаю вводить приблизительное название API или темы
Выбираю из предложенных вариантов – и выхожу на символ, по которому открывается официальная документация к проекту.
Нажимаю Esc – и всплывающее окно исчезает, а я могу сразу снова писать код, потому что мой редактор снова в фокусе.
Если я забуду, о чем только что читал – то просто вновь нажимаю «Пробел» - и окно всплывает в том же месте, где находилось ранее.
Все это происходит молниеносно – я хочу, чтобы на путь в оба конца уходило менее 2 секунд. Такая скорость и нужна, чтобы не терять концентрацию и не забывать, чем занимаешься. Я уже научился делать все это на автомате. Давно забытая роскошь нативных приложений – да, я знаю о https://devdocs.io.
Когда вся документация по API лежит на расстоянии одного клика – какую силу тебе это придаёт!
Чем меньше сил я трачу на припоминание аргумента функции или на то, чтобы вспомнить путь, по которому будет импортироваться класс – тем больше могу сосредоточиться на обдумывании решаемой задачи.
Не считаю себя особенно умным, поэтому не упускаю возможности разгрузить мозг.
Притом, что приложение Dash для Mac стоит $ 30 Mac (также доступно в рамках подписки Setapp!), имеются бесплатные аналоги под Windows и под Linux, называются Zeal, а также приложение за $ 20 под Windows, называется Velocity. Естественно, существует как минимум один пакет Emacs для той же цели: helm-dash.
Мораль: такую благодать с API можно организовать на любой платформе! Далее я напишу конкретно о Dash, поскольку работаю именно с ним, но, если не указано иное, то все сказанное применимо к любым инструментам.
Одна штука, общая для всех этих инструментов – общий формат для создания локальной документации.
Наборы документации
Все они используют пакеты с наборами документации (docsets) от Apple – это каталоги с документацией в формате HTML, метаданные в списке свойств, основанном на формате XML, а также индекс для поиска в базе данных SQLite:
some.docset
└── Contents
├── Info.plist # ← метаданные
└── Resources
├── Documents # ← корневой каталог с HTML-документами
│ └── index.html
└── docSet.dsidx # ← База данных SQLite с поисковым индексом
Если у вас на диске есть куча HTML-файлов, то их можно преобразовать в набор docset, который уже может потребляться Dash. Это просто HTML-файлы с метаданными. А поскольку эти HTML-файлы лежат у вас на диске, все работает оффлайн.
Следовательно, наборы docsets могут заменить документацию, которые вы и так держите на локальном компьютере, чтобы иметь к информации быстрый и/или оффлайновый доступ, ничего особенного не делая. Просто упакуйте этот материал в нужную структуру каталогов, добавьте пустой индекс и внесите простые метаданные.
Сезам! Теперь их можно поднять, нажав всего одну клавишу, а убрать их с глаз другой клавишей.
Давайте вернемся к тому скучному историческому экскурсу, с которого начиналась эта статья: я работаю с мириадами проектов на бесчисленных платформах, изо дня в день. Причем, я в данном случае имею в виду не только API для программирования, а еще и роли Ansible, классы CSS, конфигурацию HAProxy, тонкости Postgres (и SQL!)…много всего.
Хотя базовая документация по Python и Go поставляется с Dash, а документацию Godoc можно добавить прямо по URL, но, как бы Dash ни старался – все тщетно. Современный мир разработки ПО настолько фрагментирован, что ни один инструмент не предоставит мне всего, что мне нужно.
Sphinx
Серьезнейшим недостающим звеном для меня стали документы на основе Sphinx, доминирующие во всей экосистеме Python (и не только в ней).
Sphinx – это фреймворк для написания документации, от конкретного языка он не зависит. Это не просто документация по API, равно как и не повествовательные документы, а все сразу, причем, сильно перелинкованное. Sphinx в свое время снискал дурную славу, так как вынуждал пользователей работать с reStructuredText, но теперь становится все больше таких проектов, которые опираются на чудесный пакет MyST, позволяющий все сделать в разметке. Если у вас есть какие-либо предубеждения по поводу того, как выглядит документация Sphinx, настоятельно рекомендую посетить галерею с темами Sphinx и посмотреть, насколько красивы могут быть ваши документы. Инструмент написан на Python, но используется широко, в том числе, в ядре Linux, в Swift от Apple, в проекте LLVM (Clang!) или в бешено популярных PHP-проектах.
Кроме того, он предлагает то самое недостающее звено: индекс записей API, разделов, терминов из глоссария, конфигурационных опций, аргументов командной строки и пр. Все это распределяется по вашей документации таким образом, как вам угодно, но всегда поддается перелинковке, туда и обратно. Я считаю, это просто чудесно, особенно, если вы придерживаетесь систематического фреймворка, например, Diátaxis.
Ключевой компонент, обеспечивающий все это, строго говоря – просто расширение: intersphinx. Исходно он создавался для внутрипроектной перелинковки (отсюда и название) и предлагает к нашим услугам машинно-читаемый индекс. Этот индекс стал настолько популярен, что сейчас даже поддерживается через специальное расширение для MkDocs под названием mkdocstrings и через pydoctor. Документация, совместимая с intersphinx, узнается именно по такому файлу индекса: objects.inv
.
Именно поэтому я 10 лет назад начал и до сих пор веду проект doc2dash.
doc2dash
Это инструмент командной строки, который можно скачать у меня на Homebrew tap, скачать один из заранее подготовленных бинарников для Linux, macOS и Windows со страницы с релизами или установить из PyPI.
Затем, все, что вам останется сделать – это указать данному инструменту путь к каталогу с intersphinx-совместимой документацией, и он сделает все нужное, чтобы предоставить вам docset.
Обратите внимание: имя здесь doc2dash, а не sphinx2dash. Инструмент всегда позиционировался как фреймворк для написания высококачественных конвертеров, и первые в их ряду — Sphinx и pydoctor. К сожалению, этой надежде было не суждено сбыться, так как – что логично – в каждом сообществе предпочитали работать со своим языком и собственным инструментарием.
Но мне эти инструменты всегда казались какими-то одноразовыми, поэтому лишний раз подчеркну, что мне хотелось бы работать с другими инструментами, обеспечивающими поддержку для других форматов документации. Не нужно изобретать велосипед, весь фреймворк уже готов! Это просто файл с кодом! Вам даже не требуется открывать ваш парсер ни мне, ни всему миру.
Сам факт, что и Dash, и doc2dash уже давно разменяли второй десяток, а среди моих друзей по-прежнему есть ребята, у которых при работе открыт дохреллион вкладок с API-документацией. У меня от этого сердце сжимается, в хорошем смысле. Продолжаю показывать людям, как работает Dash, настаиваю, что он крут, и они заносят его в свои списки «когда-нибудь разберусь». Но без лишних напоминаний это «когда-нибудь» так и не наступает.
На этом заканчивается вводная часть статьи (про память как у золотой рыбки), и я попробую вас растормошить, чтобы это «когда-нибудь» наступило «сегодня»!
Учимся преобразовывать и отправлять документацию
Цель этого руководства – научить вас преобразовывать intersphinx-совместимую документацию в docset и как отправлять полученный результат в генерируемый пользователем реестр docset, поддерживаемый в Dash – так, чтобы другим не требовалось дублировать уже проделанную вами работу.
Исходим из того, что вы уже выбрали и установили ваш любимый обозреватель API. Не важно, какой именно вы используете, но в данном руководстве вся работа рассмотрена на примере Dash. Если вы хотите опционально отправлять docset по результатам работы, то вам также потребуется базовое понимание GitHub – в частности, нужно знать, как в нем построена работа с пул-реквестами.
Когда я взялся работать над этим руководством, мне наконец-то представился случай приступить к публикации docsets по моим собственным проектам, начиная со structlog. Предлагаю вам самим подобрать проект, совместимый с intersphinx, который пока не поддерживается в Dash, и вкладку с документацией по которому вы открываете чаще всего.
Приступим же!
Обзаводимся doc2dash
Если вы уже пользуетесь Homebrew, то проще всего взять doc2dash прямо у меня, отсюда:
$ brew install hynek/tap/doc2dash
Там есть заготовленные бутылки для Linux x86-64 и macOS как на x86-64, так и на Apple silicon, поэтому установка должна пойти очень быстро.
Если вы сами не придумали обходной маневр, помогающий оптимизировать установку пакетов Python, то лучше всего воспользоваться заранее собранными бинарниками со страницы релизов. В настоящее время предлагаются бинарники для Linux, Windows и macOS – все для архитектур x86-64. Надеюсь, в будущем появятся и другие предложения, если этот подход окажется популярным. Наконец, пакеты можно взять с
PyPI. Категорически рекомендую пользоваться pipx, а запускать doc2dash проще всего такой командой:
$ pipx run doc2dash --help
Есть здесь и продвинутый совет #дляпрофи: если вы разбираетесь в PyPy, умеете им пользоваться, а также планируете сконвертировать огромные деревья с документацией. Итак, совет: doc2dash более чем вдвое прибавляет в скорости, если работать с PyPy, а не с CPython.
Если вы ничего из этого раздела не поняли – просто забудьте о нем и возьмите чашечку горячего кофе.
Собираем документацию
Далее переходим к самой серьезной проблеме, из-за которой поступают новые запросы на доработку doc2dash: вся документация нам нужна в полной, собранной форме. Как правило, это означает, что вам нужно скачать репозиторий и самостоятельно разобраться, как скомпоновать документацию – и только затем переходить к установке doc2dash. Дело в том, что на большинстве сайтов с документацией отсутствует возможность скачать всю документацию целиком.
Вот что я придумал: сначала поискать tox.ini
или noxfile.py
и посмотреть, собирает ли он документацию. Если нет – поискать readthedocs.yml
, а если и это не удастся – искать конкретные файлы с названиями вроде docs-requirements.txt
или опциональные цели для установки, например, docs
. Последняя надежда – пройтись по страницам YAML и проверить конфигурации CI.
Как только вы справитесь с установкой всех зависимостей, вам, скорее всего, останется сделать только make html
в каталоге с документацией.
Когда справитесь со всем этим, у вас должен получиться каталог _build/html
для Sphinx или site
для MkDocs.
Обратите внимание: в случае с MkDocs, если в проекте не используется расширение mkdocstrings – увы, сегодня это можно сказать практически про все популярные проекты – у вас не окажется файла objects.inv
и, следовательно, не будет данных API, которые можно было бы потреблять.
Искренне надеюсь, что появится больше проектов на основе MkDocs, в которых добавится поддержка mkdocstrings! Этот инструмент, как Sphinx, также не зависит от языка.
Преобразование
За самым сложным этапом следует самый простой: преобразуем в docset ту документацию, которую только что собрали.
Все, что требуется для этого сделать – указать doc2dash на каталог с HTML-документацией и ждать:
$ doc2dash _build/html
Вот и все!
doc2dash умеет извлекать имя из индекса intersphinx и использует это имя по умолчанию (его можно переопределить при помощи --name
). У вас должно получиться добавить этот docset в обозреватель API на ваше усмотрение – после этого все должно работать.
Если передать --add-to-dash
или -a
, то финальная версия docset автоматически добавится в Dash, когда все будет готово. Если передать --add-to-global
или -A
, то готовый docset будет перенесен в глобальный каталог (~/Library/Application Support/doc2dash/DocSets
) и добавлен оттуда. При создании docset для собственных нужд я редко использую doc2dash без -A
.
Улучшаем набор документации
По документации к Dash есть ряд рекомендаций по улучшению того docset, что был собран на предыдущем этапе. Здесь важно отметить, что следующие несколько шагов строго опциональны, и я часто ими пренебрегаю, так как ленив.
Но в данном случае я хочу отправить docset в предоставленный пользователем реестр Dash, так что давайте пройдем этот путь от начала и до конца!
Готовим главную страницу
В Dash всегда можно поискать все установленные docsets, но иногда поле поиска хочется ограничить. Например, при вводе p3:
(двоеточие здесь значимое), то Dash начинает искать только по docset от Python 3. Еще до того, как вы начнете вводить текст, под поисковым полем откроется меню, первым элементом которого будет «Main Page».
При преобразовании документов structlog эта главная страница содержит индекс, который может быть полезен, но мне он обычно не нужен. Выходя на главную страницу, я обычно собираюсь полистать повествовательную документацию.
В doc2dash опция для установки главной страницы называется --index-page
или -I
, здесь (относительно корня документа) принимает имя того файла, страница которого вас интересует.
Здесь можно запутаться, так как имя файла с индексом — genindex.html
, а имя файла с главной страницей — index.html
, как обычно в HTML. Следовательно, добавляем в командную строку --index-page index.html
.
Добавляем иконку
Наборы с документацией могут сопровождаться иконками, которые расставлены по всему Dash рядом с именами и символами docsets. Это не только красиво, но и помогает быстрее узнавать нужный docset. Если же вы ищете информацию сразу по многим docsets, то понятно, откуда берется символ.
У structlog на логотипе милый бобёр, так что давайте в ImageMagick изменим размер этого логотипа, чтобы он составлял 16x16 пикселов:
$ magick \
docs/_static/structlog_logo_transparent.png \
-resize 16x16 \
docs/_static/docset-icon.png
Теперь его можно добавить в docset, воспользовавшись опцией --icon docset-icon.png
.
Поддержка онлайновой переадресации
Оффлайновые документы великолепны, но иногда бывает полезно перейти к онлайновой версии той страницы с документацией, которую вы сейчас читаете. Как правило, это делается, чтобы свериться с более новой или более старой версией.
В меню Dash для этого есть элемент «Open Online Page ⇧⌘B», но инструменту нужно сообщить базовый URL к документации. Это можно задать при помощи --online-redirect-url
or -u
.
Что касается пакетов Python, лежащих на Read the Docs, там можно выбирать между версиями stable
(отмечена как наиболее актуальная в системе контроля версий) и latest
(актуальная главная ветка).
Думаю, с latest
целесообразнее в случаях, если вы не хотите расставаться с комфортом, присущим оффлайновой документации. Поэтому добавлю:
--online-redirect-url https://www.structlog.org/en/latest/
Всё вместе
Готово! Давайте выполним все, что записано в командной строке и посмотрим, как это выглядит в Dash:
$ doc2dash \
--index-page index.html \
--icon docs/_static/docset-icon.png \
--online-redirect-url https://www.structlog.org/en/latest/ \
docs/_build/html
Converting intersphinx docs from '/Users/hynek/FOSS/structlog/docs/_build/html' to 'structlog.docset'.
Parsing documentation...
Added 238 index entries.
Patching for TOCs... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
Чудесно:
Обратите внимание на иконку в поисковой строке. Если я нажму ⇧⌘B на любой странице с якорем, то перескочу к тому же месту в новейшей версии онлайновой документации.
Автоматизация
Поскольку я хочу создавать новую версию docsets для каждого нового релиза, подготовку этих версий нужно автоматизировать. В structlog «Действия» (Actions) GitHub уже используются как средство непрерывной интеграции, поэтому целесообразно воспользоваться тем же механизмом и для сборки docset.
Применительно к локальному тестированию мне очень пригодится, что проект doc2dash написан на Python, и я могу работать в среде tox, повторно использующей все те же зависимости, что были задействованы и при тестировании самой документации.
tox – это комбинация make и менеджера виртуальной среды. tox основан на файловом формате ini
. Исходно он предназначался для тестирования программ на Python сразу для многих версий Python, но в итоге превратился в гораздо более мощный инструмент.
Он сильно выигрывает по сравнению с Makefile
, поскольку отлично портируется, а также в нем встроена функция подготовки пакетов для Python (так или иначе, она необходима для сборки документации).
Среда устанавливает structlog[docs]
– т.e., пакет с опциональными зависимостями docs
, плюс doc2dash. Затем по порядку выполняет следующие команды:
[testenv:docset]
extras = docs
deps = doc2dash
allowlist_externals =
rm
cp
tar
commands =
rm -rf structlog.docset docs/_build
sphinx-build -n -T -W -b html -d {envtmpdir}/doctrees docs docs/_build/html
doc2dash --index-page index.html --icon docs/_static/docset-icon.png --online-redirect-url https://www.structlog.org/en/latest/ docs/_build/html
cp docs/_static/docset-icon@2x.png structlog.docset/icon@2x.png
tar --exclude='.DS_Store' -cvzf structlog.tgz structlog.docset
Далее я могу собрать docset, просто вызвав tox -e docset
. Пока в doc2dash сохранится поддержка иконок в высоком разрешении, он также копирует крупную версию логотипа, 32x32 пикселя, непосредственно в docset.
Сама эта операция тривиальна, но для нее требуются простыни шаблонного кода, так что я просто оставлю ссылку, иллюстрирующую рабочий поток. Обратите внимание на действие upload-artifact
в конце, позволяющее мне скачать собранные docsets из сводок отдельных прогонов.
На данном этапе у нас уже есть отличный docset, собранный автоматически. Давайте выставим его на всеобщее обозрение!
Выкладка
На заключительном этапе отправим наш docset в репозиторий Dash, который заполняют пользователи, так, чтобы другие специалисты могли с удобством скачать его через графический пользовательский интерфейс Dash. В данном случае удобно, что в Dash используется концепция целого процесса, такого, с которым знакомы, пожалуй, все фанаты опенсорса: речь идет о таких пул-реквестах, как в GitHub.
Первым делом сверяемся со списком Docset Contribution Checklist. К счастью, мы – или, в некоторых случаях, doc2dash – уже обо всем позаботились!
Итак, идем дальше, делаем форк репозитория https://github.com/Kapeli/Dash-User-Contributions и клонируем его на наш компьютер.
Сначала копируем директорию Sample_Docset
в docsets
, переименовывая ее при этом. У меня командная строка принимает следующий вид:
$ cp -a Sample_Docset docsets/structlog
Давайте зайдем в каталог через cd docsets/structlog
и отсюда двинемся далее.
Самое главное в данном случае – добавить сам docset, но в виде tar-файла, упакованного в архиве gzip. В руководстве для контрибьюторов даже предоставляется шаблон для этой цели. В моем случае командная строка примет вид:
$ tar --exclude='.DS_Store' -cvzf structlog.tgz structlog.docset
Возможно, вы успели заметить, что я уже упаковал в архив tar мой файл tox, поэтому мне остается просто его скопировать:
$ cp ~/FOSS/structlog/structlog.tgz .
Кроме того, он хочет иконок сверх тех, что лежат в docset, поэтому я копирую их из docset:
$ cp ~/FOSS/structlog/structlog.docset/icon* .
Далее нам следовало бы заполнить метаданные в файле docset.html
, и в моем случае это делается очень просто:
{
"name": "structlog",
"version": "22.1.0",
"archive": "structlog.tgz",
"author": {
"name": "Hynek Schlawack",
"link": "https://github.com/hynek"
},
"aliases": []
}
Наконец, от нас требуется коротко написать, кто мы такие, и как собирать docset. Ознакомившись с другими примерами, я остановился на следующем варианте:
# structlog
<https://www.structlog.org/>
Maintained by [Hynek Schlawack](https://github.com/hynek/).
## Сборка Docset
### Требования
- Python 3.10
- [*tox*](https://tox.wiki/)
### Сборка
1. Clone the [*structlog* repository](https://github.com/hynek/structlog).
2. Check out the tag you want to build.
3. `tox -e docset` will build the documentation and convert it into `structlog.docset` in one step.
Фокус с tox оправдался - теперь мне никому не придется объяснять, как упаковываются пакеты в Python!
Не забудьте удалить остатки «рыбы» об образце docset, эта информация нам не нужна:
$ rm -r versions Sample_Docset.tgz
Готово! Давайте проверим, что у нас с изменениями:
$ git checkout -b structlog
$ git add docsets/structlog
$ git commit -m "Add structlog docset"
[structlog 33478f9] Add structlog docset
5 files changed, 30 insertions(+)
create mode 100644 docsets/structlog/README.md
create mode 100644 docsets/structlog/docset.json
create mode 100644 docsets/structlog/icon.png
create mode 100644 docsets/structlog/icon@2x.png
create mode 100644 docsets/structlog/structlog.tgz
$ git push -u
Выглядит хорошо – время делать пул-реквест!
Через несколько часов:
Определённо, это успех: теперь любой может скачать structlog Documentation Set, и на этом наше краткое руководство завершается!
Заключение
Надеюсь, мне удалось и немного подогреть ваш интерес к документации API, и разоблачить некоторые заблуждения, касающиеся создания собственных наборов документации. Я ставил перед собой цель прокачать навыки тем программистам, кто – как и я – закапываются в бесчисленных пакетах, которые необходимо держать в уме, чтобы справиться с задачей.
Но превыше всего я надеюсь, что моя статья вдохновит кого-нибудь помочь мне с добавлением новых форматов в doc2dash, чтобы еще больше стало таких программистов, которые могут позволить себе роскошь иметь всю документацию по API прямо на кончиках пальцев.
Кроме того, закончив эту статью, я стал надеяться, что Zeal удастся оживить. Говорят, он давно застопорился, и последнему релизу уже четыре года. Поскольку это опенсорсный проект, было бы круто, если бы к нему подключились новые руки, и мы располагали бы хорошими обозревателями API на всех платформах.