Рубрики
Без рубрики

Тестовый подход к упаковке Python

Недавно я добрался до последней точки с импортирующим локальными пакетами в Python. Я последовательно нашел мою … Теги с Python, тестированием, кодовым механизмом, учебником.

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

Есть несколько ключевых вопросов, которые я хочу ответить на эту тему, которую я думаю, что сможет прояснить большую часть путаницы.

  1. Это __init__.py Файл, необходимый в каждой папке?
  2. Это пакет, который вы импортируете то же самое, что и пакет, который вы устанавливаете из PYPI?
  3. Встроенный Unittest Module Module Module Patest и Python и импортирует пакеты?

Простые ответы на эти вопросы

  1. да При использовании Python 3.2 или раньше, и нет, если использование Python 3.3 или новее.
  2. Нет, они отличаются, даже если они представляют один и тот же код.
  3. да

Историческая перспектива

Хотя Python 2 достиг конца жизни в этом году, влияние на Python Devs был глубоком. Даже когда я начал писать Python несколько лет назад, каждый проект, который я работал с поддержкой Python 2, а в Python 2 An __init__.py Файл был обязан в каждом каталоге, в противном случае он не был импортируемым. Вы можете проверить это для себя. Создайте структуру каталогов, как показано ниже, откройте Python REPL и попробуйте импортировать приветствие . Это не работает, пока вы не добавите __init__.py к папке Lang.

experiment/
|_greeter
|  |_ __init__.py
|  |_lang
|  |  |_en.py
|  |  |_en.py
|  |
|  |_greeting.py
|
|_tests
|  |_test_greeting.py

Такое поведение присутствовало даже в первые дни Python 3. Pep-420 Наконец удалил это требование в Python 3.3, но тенденция добавления __init__.py Файлы продолжались в течение длительного времени Python Developers, потому что они никогда не знали, чтобы изменить свои привычки, и привычка была передана до новых Devs либо с этих более старых разработчиков, либо из исторических ответов на Q & A, таких как переполнение стека.

Если вы не верите мне, что __init__.py Файлы больше не требуются в каждом каталоге, вы можете прочитать его для себя в документах Python 3 здесь Или на этом скриншоте абзаца, который подтверждает его.

У меня есть несколько примеров общих сценариев, которые вы окажетесь в том, что я хочу поделиться с вами, но до этого я полагаю, что мы должны определить, что на самом деле есть пакет.

Перефразирование из документации в стилю распространения (STYPUTHOOLS – Python lib для строительства и распределения пакетов Pypi),

«Пакет в контексте PYPI – это распределение комплектного> программного обеспечения. Пакет в контексте Python – это контейнер> модулей ».

Просто поставьте модуль – это просто файл исходного кода Python.

Дополнительно существует 2 типа пакетов Python в настоящее время, «обычные пакеты», которые придерживаются старого Python 3.2 и более ранних требований пакета (т. Е.| __init__.py в каждом каталоге) и «пакеты имен», которые следуют требованиям из Pep-420, который приносит совершенно новый смысл быть 420 дружелюбным.

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

Пример 1.

Самый простой случай – это то, где тест находится в том же модуле, что и код, который оно тестирует. Структура каталогов будет выглядеть так.

example-1/
 |_ greeter.py

Оба Pytest Greeter.py и Python -M Unittest Greeter.py проходить. Поскольку нечего импортировать, это следует ожидать. Если вы хотите увидеть исходный код для этого примера, он доступен на Github Отказ

Пример 2.

Чуть более сложный, но более распространенный случай – это когда есть тестовый модуль, отдельный от тестирования модуля. Структура каталогов будет выглядеть так.

example-2/
 |_greeter.py
 |_test_greeter.py

В этом случае pytest test_greeter.py . и Python -M Unittest Test_GreeTer.py Еще раз пройти даже без __init__.py файл. Исходный код для этого примера также доступен на Github Отказ

Более сложные проекты нуждаются в лучшей организации, чем один модуль кода исходного кода и один тестовый модуль. Это где пакеты Python вступают в игру. Примеры 3, 4 и 5 Все сделки с различными способами могут быть структурированы пакет (I.e. вложении в тестовом модуле с пакетом исходного кода или сохранение его отделения от исходного пакета).

Пример 3.

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

example-3/
 |_translator/
 |  |_greeter.py
 |  |_tests/
 |  |  |_test_greeter.py

Это только время Python -M Unittest Translator/Tests/Test_GreeTer.py передает тест. Pytest Translator/Tests/ Не удается с ошибкой ModulEnotFoundError: Нет модуля с именем «Переводчик» Отказ Это потому, что Pтойцы ищет только пакеты Pypi для импорта. Вы можете получить Pтост, чтобы пройти, добавив __init__.py Файл под Переводчик каталог, добавление setup.py Файл в каталоге примерного 3 и работает Установка PIP. . Вы можете увидеть исходный код здесь и PR, который исправляет это здесь Отказ

Пример 4.

Это то же самое, что и пример 3, за исключением пакета тестов находится за пределами пакета исходного кода.

example-4/
 |_ translator/
 |  |_ greeter.py
 |
 |_ tests
 |  |_ test_greeter.py

Еще раз Python -M Unittest Tests/Test_GreeTer.py проходит, но Pтойские испытания/ Не удается с ModulenotfoundError Отказ Исправление точно такое же, как пример 3. Добавить __init__.py Файл под Переводчик каталог и а setup.py Файл под Пример-4. Каталог, затем запустить Установка PIP. . Исходный код можно увидеть здесь и PR исправить его можно увидеть здесь Отказ

Пример 5.

Пример 5 – это немного сложнее, потому что находится подразделение под Переводчик упаковка. Такая структура каталога будет выглядеть так.

example-5/
 |_translator/
 |  |_ greeter.py
 |  |_ lang/
 |  |  |_ en.py
 |  |  |_ es.py
 |
 |_tests
 |  |_test_greeter.py

Как вы, наверное, уже догадались Python -M Unittest Tests/Test_GreeTer.py проходит и Pтойские испытания/ Не удается с ModulenotfoundError Отказ Если вы применяете то же самое исправление из примеров 3 и 4, PteSt все еще не удается, но на этот раз с ошибкой МодуленотФундэрр: Нет модуля по имени «Lang» Отказ Эта ошибка вызвана setup.py файл, который я использовал. В setup.py Я использую функцию Find_packages () Из SetUptools, которые проходят структуру каталогов и пытается найти пакеты, но это только ищет «обычные пакеты». Это правильно те, которые требуют __init__.py файл для присутствия. Так что исправить этот пример __init__.py нужно добавлять под ланг каталог также. Если вы не использовали Флаг в команде установки Yout PIP, вам нужно будет повторно запустить Установка PIP. Снова с момента __init__.py должен существовать до установки пакета PYPI.

Заключение

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

  • __init__.py Файлы должны использоваться только при необходимости, необходимыми SetUpeWools или когда пакет нуждается в некоторой интиственности на Import.
  • Тесты должны оставаться за пределами пакета исходного кода (могут помочь с такими вещами, как Docker, который хочет только код)
  • PIP Установить -e. заберу новые подпакуки, которые имеют __init__.py с нуждающимся в RERUN Установка PIP.

Хороший следующий шаг будет смотреть в ImportLib который подвергает Реализация отчетности импорта.

Спасибо за прочтение. Если вам нравится этот контент, проверьте мои другие статьи на dev.to или настройтесь на Пространство имен Podcast Который я союгую с другим TDD, питона, как я.

Оригинал: “https://dev.to/ezzy1337/a-test-driven-approach-to-python-packaging-105”