django

Python Дайджест: как обновиться с Python 3.4 до Python 3.11, если pip уже сломан

  • среда, 1 февраля 2023 г. в 00:54:47
https://habr.com/ru/company/first/blog/709068/
  • Блог компании FirstVDS
  • Open source
  • Python
  • Django




Python Дайджест собирает IT-новости уже 9 лет, рассказывает о концепциях, проектах, релизах. Кодовая база за это время мало изменилась и уже деградировала. Более 5 лет не хватало сил и времени, чтобы привести проект в актуальное состояние. Django с 1.9 обновилась уже до 4.1 версии, Python 3.4 не актуален, да даже обновить пакет через pip не получается, потому что сломан.


В 4 частях расскажу от первого лица, как 9-летний проект из состояния outdated вернулся в actual состояние и снова набрал 100 баллов в PageSpeed.


Начну с обновления до актуального Python и Django.


Содержание цикла статей


  • 1 часть: Как обновиться с Python 3.4 до Python 3.11, если pip уже сломан
    • — вы здесь —
  • 2 часть: Как актуализировать всю кодовую базу с помощью pre-commit
  • 3 часть: Как сделать CI для OpenSource проекта с Github Actions
  • 4 часть: Как ускорить Django проект до (почти) максимума

О Python Дайджест




Python Дайджест — это open source проект про Python, написанный на Python. В следующем году исполнится 10 лет как собираются, вычищаются, изучаются материалы и собираются дайджесты.


Проект был создан разработчиками и для разработчиков. Хотелось знать актуальные подходы к разработке, библиотеки, обновления важных библиотек, узнавать, что теперь стало стандартом. И с того времени мало что поменялось. И все также я использую этот ресурс, чтобы быть «в курсе», только теперь не для себя, а для команд, с которыми работаю.


Состояние на конец ноября 2022:


  • Есть сервер на Ubuntu 14.04 с запущенным через uwsgi и supervisor Django приложением.
  • Основное virtualenv окружение с версией Python 3.4, но когда-то pip был обновлен руками на основе 3.6 версии и теперь не работает pip freeze.
  • Дополнительный virtualenv с версией Python 3.6, в котором работает часть приложения, но зависимости не совпадают с requirements.txt.
  • Как сконфигурирован сервер, приложение, переменные окружения и прочее — никто уже не помнит, а в bash history ничего нет.
  • Изменения в код вносились прямо на сервере и не дублировались в git.
  • Импорт новостей сбоит — системные SSL сертификаты устарели и уже не получается их обновить.

Задача (часть 1) — обновиться минимум до Python 3.6


3.6 — реперная точка, потому что там появились f-string. Эту функциональность нельзя включить через какой-нибудь __future__ flag на более старых версиях. При этом свежие версии библиотек почти всегда используют f-string.

План работ


  • Объединить код сервиса с сервера и git ветки.
  • Развернуть окружение на Python 3.4, чтобы запустить тесты локально.
  • Обновить зависимости и перейти на poetry для управления ими.
  • Постепенно обновлять зависимости и интерпретатор, чтобы получить Python 3.11 и Django 4.1.

Как синхронизировал кодовую базу


Для начала объединил текущую мастер-ветку с актуальным серверным кодом:


  • Заархивировал в tar.gz и перенес код с сервера на локальную машину с помощью scp.
  • Через meld (инструмент для визуального diff) просмотрел изменения и объединил их.
  • Сохранил в отдельной ветке результат объединения.

При объединении двух версий кодовых баз стоит внимательнее смотреть на:


  • Тесты. Как изменились тесты из репозитория, что теперь проверяют.
  • Миграции. Были ли изменения базы данных и какие. Бэкапом их повторим, а миграции нужно добавить в репозиторий.
  • Настройки и переменные окружения, в том числе и как обрабатываются типы переменных.
  • Как используются новые версии библиотек, чтобы учесть изменения их API.

Как НЕ запустил Python 3.4 на Ubuntu 22.04


У меня в системе стоит Python 3.10, что много выше, чем Python 3.4, который требуется для запуска приложения.


К счастью, есть pyenv, который позволяет установить любую версию python рядом с основной.


Это так я думал, но при выполнении команды pyenv intall 3.4 получил ошибку Missing the OpenSSL lib?. Даже инструкция про это есть.


Из инструкции и Pull Request становится ясно, что за время с Python 3.4 OpenSSL обновился с 1.11 до 3.0 (перепрыгнув несколько версий) и просто так на современном дистрибутиве его не поставить. Инструкция предлагает попробовать с системным OpenSSL версии 3.0, вместо 1.11, сделать downgrade версии, через brew на Ubuntu поставить нужную версию OpenSSL.


Ничего не дало плодов. Мне нужен был 3.4 и 3.6 Python, только пока зависимости обновляю, и поднимать полноценную виртуалку не хотелось. Поэтому решил пойти по другому пути.


Как запустил Python 3.4 в Docker


Если нужен какой-то софт, который тяжело поставить/не хочется ставить в систему, то что берем? Конечно, же контейнеры. В моем случае это Docker.


Контейнеры предоставляют условно изолированную среду внутри родительской операционной системы. Можно описать образ контейнера в файле Dockerfile со всем необходимым, загрузить на Docker Hub, а затем запускать единообразным способом на разных машинах.


В Docker Hub хранятся настолько древние образы, что можно удивляться для кого они. Вот для таких проектов, которые много лет работали и не требовали критических изменений.

Составил Dockerfile с Python 3.4, который умеет стягивать зависимости из папки проекта и устанавливать их. Вуаля! Теперь можно работать с зависимостями. А если указывать команду для запуска контейнера через docker compose (внутри dev ужас), будет совсем приятно и не надо будет пересобирать слишком часто.


На этом шаге стало ясно, что проект получится актуализировать.


Как получил версии пакетов со сломанным pip


Есть контейнер с Python 3.4. Теперь нужно получить актуальный серверный requirements.txt. Как писал в начале — pip был сломан из-за ручного обновления (python setup.py install для pip), но не беда — можно из интерпретатора достать.


import module_name

help(module_name)

И оттуда вычитать версию пакета (или просто grep'ом парсить из venv папки).
Это нужно было сделать для всех критических пакетов, чтобы, когда будет обновление зависимостей через poetry, получили рабочее приложение.


Какие использовал менеджеры зависимостей


Немного про менеджеры зависимостей.


На практике я проходил по пути от простого pip install, затем перешел к requirements.txt, где были описаны пакеты, затем для управления зависимостей использовал pip-tools (тогда poetry еще тестировался), а уже затем стал использовать poetry:


  • requirements.txt — текстовый файл, в котором по строчкам перечисляются пакеты и версии для них. Первый общеупотребимый подход по описанию зависимостей.
  • pip-tools — набор утилит, которые позволяют задать для основных зависимостей версии, а версии для остальных зависимостей вычислить автоматически. Это такой переходный инструмент от состояния «не фиксируем версии» к «фиксируем основное, а остальное не знаем надо ли». Зачастую работает, но бывает получается битое окружение.
  • poetry — это текущий стандарт управления зависимостями. Позволяет уже не бояться за окружение (зачастую) и вычислять версии зависимости так, чтобы все пакеты были точно согласованы.

Вероятно следующий будет pdm, а пока «стандарт» это poetry.


Pipenv с Pipfile.lock тоже использовал для управления зависимостями, хотя его мощь именно в создании всего dev окружения, а не только установка пакетов.

Как обновил Python зависимости с Docker



Image: Alice Lang, alicelang-creations@outlook.fr


Запуская через docker compose контейнер с Python 3.4, я начал приводить в порядок requirements.txt: указал актуальные версии с сервера, дописал недостающие в файл.


Это позволило запустить pip-tools (а точнее, pip-compile) и актуализировать все остальные зависимости.


Поставил poetry и последовательно добавил все пакеты в проект. Получил lock файл с хэшами пакетов. Фуф, теперь можно обновлять зависимости дальше — через poetry add "package_name<=version"


Обычно для пакетов назначают версию: «не обновляй выше текущей версии», однако, для массового обновления, где ручным образом проверяются, стоит ставить «меньше, равно». Уже после обновления можно заменить >= на ^

Конечно же, за 5 лет без обновлений ряд пакетов устарел, что-то умерло, какие-то пакеты переименовались и преобразовались в новые.


Перед каждым обновлением изучал, что я могу удалить из устаревших зависимостей, какие модули стали дублировать функциональность.


Требовалось ходить по пакетам, смотреть их Changelog и принимать решение.


Крупные пакеты типа lxml, pandas, numpy приходилось по несколько раз обновлять, а затем снижать версию обратно. Не всегда потребители этих библиотек учитывают изменения в API.

Дальше было несколько итераций обновлений согласно таблицы совместимости Python и Django версий


  1. Python 3.4 + Django 1.9
  2. Python 3.4 + Django 1.11
  3. Python 3.4 + Django 2.0.13
    • Переход на from django.urls import path для описания urls.py
    • Добавление on_delete= для ForeignKey/OneToOneField полей моделей
    • Переименование MIDDLEWARE_CLASSES на MIDDLEWARE
    • Здесь инструкция
  4. Python 3.6 + Django 2.0.13
    • Переход на f-string
  5. Python 3.6 + Django 2.2
  6. Python 3.6 + Django 3.2.16
    • Удаление from django.utils.six import text_type
  7. Python 3.10 + Django 3.2.16
    • Разделение на группы зависимостей в poetry (основная, для разработки, для тестов)
  8. Python 3.10 + Django 4.1
    • Переименование from django.utils.translation import ugettext_lazy -> gettext_lazy
  9. Python 3.11 + Django 4.1

Собирал образ с новым сочетанием зависимостей, прогонял тесты, удалял/добавлял пакеты, модифицировал код под версию Django, учитывая deprecations, обновлял зависимости, экспортировал новые зависимости. И так по кругу.


По итогу был получен образ с Python 3.11 + Django 4.1, который легко было запустить вне контейнера.


Через pyenv было поднято окружение с Python 3.11, прогнаны тесты еще раз.
Теперь можно запускать проект на актуальных операционных системах.




Выводы


Вы прочитали 1 из 4 часть цикла статей про обновление проекта до актуальных технологий. Для себя сделал такие выводы:


  • Для проектов на Python 3.4, 3.5, 3.6 через pyenv не всегда удается развернуть окружение. Для этого можно использовать Docker контейнеры. Собирать образ, поднимать контейнер с синхронизированной папкой исходного кода, из контейнера запускать pip install и так проводить процедуру обновления.
  • С установкой poetry (даже по схеме requirements.txt -> pip-tools -> poetry) заметно упрощается поддержка зависимостей. Poetry в пару команд позволяет выбирать актуальные зависимости для другой версии python, и сами зависимости держать в порядке.
  • Вот до чего может довести «работает — не трогай» :)

P.S. Актуализация существующего проекта весьма затратна по времени. Обращайтесь ко мне, axsapronov, если вам требуется сделать это.




НЛО прилетело и оставило здесь промокод для читателей нашего блога:
15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.