Jupyter Notebooks + VSCode Dev Container с поддержкой Puppeteer

У меня с Python отношения любви и ненависти: разрабатывать на Python очень просто (обилие примеров и пакетов, кроссплатформенность), но настройка среды разработки (версии Python, версии пакетов) слишком громоздка. Это возрастает, когда вам нужно поделиться этой настройкой с другим разработчиком на другой платформе. Я работаю на Windows, мои коллеги — на Mac и Linux.

Поэтому сейчас я использую Visual Studio Code и «dev-контейнеры»:

Расширение Visual Studio Code Remote — Containers позволяет использовать контейнер Docker в качестве полнофункциональной среды разработки. Оно позволяет вам открыть любую папку внутри контейнера (или смонтированную в него) и воспользоваться всеми возможностями Visual Studio Code.

Источник: Разработка внутри контейнера

У меня есть несколько причин для перехода на эту конфигурацию:

  • Visual Studio Code уже является моим инструментом для многих языков (Node.js, bash, Terraform). Многие мои коллеги также используют его для различных задач.
  • Запуск всего dev setup в Docker помогает поддерживать чистоту хост-системы. Это не первый случай, когда кто-то «окирпичил» свою установку Python из-за требования новой версии.
  • Docker дает вам хорошую, тиражируемую, кроссплатформенную установку: на каждой машине разработки она должна работать одинаково.

Теперь главный вопрос: будет ли это работать для Jupyter Notebooks? Visual Studio Code уже предоставляет отличный пользовательский интерфейс, но сможет ли он запустить Jupyter в контейнере разработки? Давайте узнаем! Мой вариант использования — скрепер, поэтому мне нужна поддержка Puppeteer.

  1. Настройка контейнера разработки Python по умолчанию
  2. Установка IPython & Pandas в контейнер разработки
    1. Нужно ли еще устанавливать пакеты в блокнот?
  3. Как насчет Puppeteer?
  4. Не фиксировать вывод Jupyter Notebook
    1. Автоматическое сохранение?
  5. Повышение производительности
  6. Расширения VSCode
  7. Заключительные мысли
  8. Changelog

Настройка контейнера разработки Python по умолчанию

Начнем с контейнера разработки Python 3:

  1. Откройте палитру команд (F1 в Windows)
  2. Найдите: Remote-Containers: Add Development Container Configuration files... Откроется небольшой мастер.
  3. Выберите Python 3 в качестве языка.
  4. Выберите 3 в качестве версии (в любом случае будет добавлена последняя).
  5. Нам не нужен Node.js, поэтому выберем None (можно включить позже, но эта опция ускорит сборку контейнера).

Установка создаст конфигурационные файлы в папке .devcontainer вашего проекта.

Установка IPython & Pandas в Dev-контейнер

Теперь откройте Dockerfile из папки .devcontainer и откомментируйте следующие строки:

# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
COPY requirements.txt /tmp/pip-tmp/
RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt 
   && rm -rf /tmp/pip-tmp
Войти в полноэкранный режим Выйти из полноэкранного режима

Это позволяет начать использовать файл requirements.txt, для установки пакетов с PIP на уровне контейнера.

Далее, добавьте requirements.txt в корень вашего проекта и введите следующие строки:

ipython
ipykernel
pandas
Войти в полноэкранный режим Выйти из полноэкранного режима

Это гарантирует, что контейнер установит пакеты, необходимые для вашего ноутбука.

Нужно ли еще устанавливать пакеты в ноутбук?

Не обязательно, но это может облегчить работу, если вы это сделаете, и это поможет людям, которые не используют вашу установку. С помощью этой простой строки вы установите все пакеты, указанные в вашем файле requirements.txt:

%pip install --quiet --exists-action i --disable-pip-version-check -r ../requirements.txt --user
Войти в полноэкранный режим Выйти из полноэкранного режима

Все будет выполнено быстро, так как все уже установлено в вашем dev-контейнере. Это также упрощает добавление новых пакетов, так как вам не придется перезапускать ваш dev-контейнер.

Как насчет Puppeteer?

Мы будем использовать Python-версию Puppeteer под названием Pyppeteer. Запуск Puppeteer из контейнера не так прост. Давайте перенесем код из этой статьи в нашу установку.

Добавьте следующие строки в Dockerfile:

# Install Google Chrome Stable and fonts
# Note: this installs the necessary libs to make the browser work with Puppeteer.
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && apt-get install gnupg wget -y && 
  wget --quiet --output-document=- https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor > /etc/apt/trusted.gpg.d/google-archive.gpg && 
  sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' && 
  apt-get update && 
  apt-get install google-chrome-stable -y --no-install-recommends && 
  rm -rf /var/lib/apt/lists/*
Войти в полноэкранный режим Выйти из полноэкранного режима

Это загрузит и установит Chrome в контейнер. Теперь нам нужно добавить пакеты Puppeteer в наш файл requirements.txt:

ipython
ipykernel
pandas
pyppeteer
Войти в полноэкранный режим Выйти из полноэкранного режима

Добавьте следующую строку в свой блокнот:

%pip install --quiet --exists-action i --disable-pip-version-check pyppeteer
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь, когда мы запускаем браузер в блокноте, нам нужно только проверить, находимся ли мы в нашем контейнере разработки:

browser_options = {
    'headless': True,
    'args': ["--no-sandbox"]
}

if os.getenv('PUPPETEER_SKIP_CHROMIUM_DOWNLOAD', '') != '':
    browser_options["executablePath"] = '/usr/bin/google-chrome'

browser = await launch(browser_options)
Войти в полноэкранный режим Выйти из полноэкранного режима

Это гарантирует, что в контейнере используется предустановленная версия Chrome.

Не фиксируйте вывод Jupyter Notebook

Мы хотим улучшить то, что мы фиксируем в Git: давайте не будем фиксировать вывод блокнота, реализовав код из этой статьи. Во-первых, добавим пакет nbconvert в ваш requirements.txt:

ipython
ipykernel
nbconvert
pandas
pyppeteer
pyppeteer_stealth
Вход в полноэкранный режим Выход из полноэкранного режима

Давайте настроим Git так, чтобы он мог его использовать; добавьте файл .gitconfig в корень вашего проекта:

[filter "strip-notebook-output"]
    clean = "jupyter nbconvert --ClearOutputPreprocessor.enabled=True --to=notebook --stdin --stdout --log-level=ERROR"

Вход в полноэкранный режим Выход из полноэкранного режима

Теперь добавьте файл под названием .gitattributes:

*.ipynb filter=strip-notebook-output

Вход в полноэкранный режим Выйти из полноэкранного режима

Последний файл, который вам нужно добавить, это repo_init.sh:

#!/usr/bin/env bash

git config --local include.path ../.gitconfig
git add --renormalize .
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь запустите свой проект в контейнере dev и подключите все с помощью bash repo_init.sh. Каждый раз, когда пользователь клонирует ваш репозиторий в первый раз, этот скрипт должен быть выполнен.

Теперь Git сможет понять, что изменилось, и больше не будет показывать изменения, когда вы только что запустили скрипт. Примечание: VSCode по-прежнему будет думать, что блокнот изменился, но сам Git не будет фиксировать изменения.

Автоматическое сохранение?

Когда вы запускаете блокнот, файл изменяется. Блокноты сохраняют и код, и вывод. Меня очень раздражает, что VSCode показывает несохраненный файл в моей IDE (и пытается восстановить его, если я закрываю редактор). Чтобы смягчить эту проблему, вы можете включить автоматическое сохранение в настройках вашего контейнера dev:

  1. Откройте .devcontainer/devcontainer.json.
  2. Перейдите к customizations > vscode > settings.
  3. Добавить: "files.autoSave": "afterDelay",.
  4. Добавить "files.autoSaveDelay": 1000

Проблема устранена.

Улучшение производительности

В настоящее время наша установка является наивной, поскольку она не полностью использует кэширование Docker. Давайте посмотрим на наш финальный Dockerfile:

# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.245.0/containers/python-3/.devcontainer/base.Dockerfile

# [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster
ARG VARIANT="3.10-bullseye"
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}

# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10
ARG NODE_VERSION="none"
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi

# Install Google Chrome Stable and fonts
# Note: this installs the necessary libs to make the browser work with Puppeteer.
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
RUN apt-get update && apt-get install gnupg wget -y && 
  wget --quiet --output-document=- https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor > /etc/apt/trusted.gpg.d/google-archive.gpg && 
  sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' && 
  apt-get update && 
  apt-get install google-chrome-stable -y --no-install-recommends && 
  rm -rf /var/lib/apt/lists/*

# [Optional] If your pip requirements rarely change, uncomment this section to add them to the image.
COPY requirements.txt /tmp/pip-tmp/
RUN pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt 
   && rm -rf /tmp/pip-tmp

Вход в полноэкранный режим Выход из полноэкранного режима

Поменяв местами установку пакетов, вам не нужно переустанавливать Chrome каждый раз, когда меняются пакеты Python.

Расширения VSCode

Приятным моментом в этой установке является возможность совместного использования расширений Visual Studio Code. Они хранятся в devcontainer.json, как и настройки. Я использую эти расширения:

  • Jupyter
  • Jupyter Keymap
  • Jupyter Notebook Renderers
  • vscode-icons — для обеспечения лучших иконок в проводнике проекта
  • TODO Highlight — для более четкого выделения того, что вам еще нужно сделать
  • ShellCheck — линтер для написания bash-скриптов — почему-то я всегда заканчиваю тем, что пишу скрипты в своих проектах.

Я заменил узел customizations > vscode > extensions этим:

      "extensions": [
        "ms-python.python",
        "ms-python.vscode-pylance",
        "ms-toolsai.jupyter",
        "ms-toolsai.jupyter-keymap",
        "ms-toolsai.jupyter-renderers",
        "vscode-icons-team.vscode-icons",
        "wayou.vscode-todo-highlight",
        "timonwong.shellcheck"
      ]
Войти в полноэкранный режим Выйти из полноэкранного режима

Когда кто-то открывает проекты, он получает уведомление о расширениях.

Заключительные мысли

Мне нравится, что эта установка работает на любой машине. Подумайте о блокировке версий, так как и Chrome, и ваши пакеты не версионированы (поэтому вы можете загрузить неработающие изменения).

Мне не нравится тот факт, что Visual Studio Code считает, что блокнот изменился, в то время как Git знает, что это не так. Согласно выпуску #9514, это то, что должно быть исправлено в ядре Visual Studio Code. Поэтому я не совсем понимаю, почему вопрос #83232 (или #24883) закрыт.

Changelog

2022-08-09 Изменена строка %pip install для использования файла requirements.txt.

Оцените статью
devanswers.ru
Добавить комментарий