Примечание: исходный код можно найти на GitHub.
Если вы работаете с Python, то, вероятно, знаете о пакетах, которые можно загрузить и импортировать в свои проекты с помощью pip
. Это надежный инструмент, который позволяет разработчикам со всего мира делиться ими.
В статье я покажу вам, как опубликовать пакет Python в сервисе PyPi (Python Package Index). Именно отсюда pip
загружает их для использования в проектах и инструментах командной строки, которые вы можете применить на компьютере.
1. Создание проекта
Первый шаг, который нужно будет сделать — это создать очень простой пакет для публикации. Он может быть настолько сложным, насколько хотите в зависимости от вашего приложения, но для примера мы создадим очень простой пакет.
Создадим новую структуру каталогов для нашего проекта:
sample-pypi-package
└── hello_world
├── __init__.py
└── main.py
sample-pypi-package
— это корневой уровень проекта, и мы создаем модуль hello_world
внутри него. Содержание __init__.py
пусто и main.py
выглядит так:
# main.py
def hello_world():
print("Hello World")
hello_world()
Для первого шага все. Ничего необычного, если вы знакомы с Python. main.py
не нужен для этого урока, но я добавил его просто для того, чтобы у нас был файл внутри модуля.
2. Файл setup.py
Мы создадим новый setup.py
, который действует как скрипт сборки для нашего пакета. Однако нужно будет установить setuptools
через pip
. Вы можете выполнить приведенную ниже команду. Помните, какая версия Python у вас, и используйте соответствующий pip
.
pip install setuptools
ИЛИ
pip3 install setuptools
Теперь, когда мы установили setuptools
, мы можем начать настраивать setup.py
:
# Импорт недавно установленного пакета setuptools.
import setuptools
# Открытие README.md и присвоение его long_description.
with open("README.md", "r") as fh:
long_description = fh.read()
# Определение requests как requirements для того, чтобы этот пакет работал. Зависимости проекта.
# requirements = ["requests<=2.21.0"]
# Функция, которая принимает несколько аргументов. Она присваивает эти значения пакету.
setuptools.setup(
# Имя дистрибутива пакета. Оно должно быть уникальным, поэтому добавление вашего имени пользователя в конце является обычным делом.
name="hello_world_ericjaychi",
# Номер версии вашего пакета. Обычно используется семантическое управление версиями.
version="0.0.1",
# Имя автора.
author="Eric Chi",
# Его почта.
author_email="[email protected]",
# Краткое описание, которое будет показано на странице PyPi.
description="A Hello World package",
# Длинное описание, которое будет отображаться на странице PyPi. Использует README.md репозитория для заполнения.
long_description=long_description,
# Определяет тип контента, используемый в long_description.
long_description_content_type="text/markdown",
# URL-адрес, представляющий домашнюю страницу проекта. Большинство проектов ссылаются на репозиторий.
url="https://github.com/ericjaychi/sample-pypi-package",
# Находит все пакеты внутри проекта и объединяет их в дистрибутив.
packages=setuptools.find_packages(),
# requirements или dependencies, которые будут установлены вместе с пакетом, когда пользователь установит его через pip.
# install_requires=requirements,
# Предоставляет pip некоторые метаданные о пакете. Также отображается на странице PyPi.
classifiers=[
"Programming Language :: Python :: 3.8",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
# Требуемая версия Python.
python_requires='>=3.6',
)
Как упоминалось ранее, этот файл отвечает за создание дистрибутивного файла, который будет загружен в PyPi.
Две строки, которые я хочу выделить:
requirements = ["requests<=2.21.0"]install_requires=requirements,
Я специально прокомментировал их, потому что они не нужны в этом упражнении, однако они важны, если ваш пакет имеет зависимости. Реальный проект будет иметь внешние зависимости, поэтому именно так вы добавляете их в дистрибутив, чтобы пользователь, устанавливающий ваш пакет, получил необходимое для корректной работы. Типичная практика — создание файла requirements.txt
, который setup.py
может читать вместо определения массива.
Если вам интересно узнать обо всех параметрах setuptools.setup()
смотрите официальную документацию.
3. Файлы README.md и LICENSE
Эта часть не так увлекательна, но очень важна. Поскольку вы публикуете в мир, надо убедиться, что у вас есть соответствующая документация, а также надлежащая лицензия с открытым исходным кодом. Мы также нуждаемся в README.md
поскольку используем его внутри setup.py
.
Создадим простой README.md
на корневом уровне проекта:
# Образец пакета PyPi
This is a simple exercise to publish a package onto PyPi.
README.md
сделан.
Выбор лицензии может быть немного сложным. К счастью, https://choosealicense.com/ отлично справляется с обзором различных лицензий с открытым исходным кодом, которые можно использовать. Для этого примера я выбрал лицензию MIT, но если вы публикуете реальный пакет, я советую посмотреть, какую выбрать.
Продолжим и создадим файл LICENSE
на корневом уровне с лицензией MIT:
MIT License
Copyright (c) [year] [fullname]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Обязательно измените [year]
и [fullname]
!
Теперь, когда у нас есть LICENSE
и README.md
, структура каталогов должна выглядеть так:
sample-pypi-package/
├── LICENSE
├── README.md
├── hello_world
│ ├── __init__.py
│ └── main.py
└── setup.py
4. Создание дистрибутивных файлов
Итак, теперь, когда пакет полностью установлен, можно генерировать дистрибутивные файлы. Это файлы, которые пользователь будет загружать и распаковывать, чтобы запустить код. Поэтому, когда вы выполните pip install
с указанием пакета, то загрузите tarball/zip для распаковки на компьютере локально.
Нам понадобится еще один инструмент:
pip install wheel
ИЛИ
pip3 install wheel
После загрузки wheel
мы можем применить его для создания файлов.
Выполните эти команды в том же каталоге, где находится setup.py
(это корневой каталог, если вы следуете руководству):
python setup.py sdist bdist_wheel
ИЛИ
python3 setup.py sdist bdist_wheel
Терминал должен отобразить логи того, что он делает. У вас должно быть создано несколько новых каталогов, которые я выделил жирным шрифтом ниже. Загружать их в репозиторий я не буду.
sample-pypi-package/
├── LICENSE
├── README.md
├── build
│ ├── bdist.macosx-10.15-x86_64
│ └── lib
│ └── hello_world
│ ├── __init__.py
│ └── main.py
├── dist
│ ├── hello_world_ericjaychi-0.0.1-py3-none-any.whl
│ └── hello_world_ericjaychi-0.0.1.tar.gz
├── hello_world
│ ├── __init__.py
│ └── main.py
├── hello_world_ericjaychi.egg-info
│ ├── PKG-INFO
│ ├── SOURCES.txt
│ ├── dependency_links.txt
│ └── top_level.txt
└── setup.p
Оба build
и hello_world_ericjaychi.egg-info
можно проигнорировать. Сосредоточимся на каталоге dist
:
dist/
├── hello_world_ericjaychi-0.0.1-py3-none-any.whl
└── hello_world_ericjaychi-0.0.1.tar.gz
Это дистрибутивные файлы, которые будут выдаваться пользователям, когда они скачают их через pip
.
5. Загрузка дистрибутивных файлов
Пришло время загрузить эти файлы в PyPi. Прежде чем мы это сделаем, нужно сначала создать там учетную запись. Одна деталь: PyPi предлагает тестовую среду для публикации пакетов, так что вы можете поиграть с этим процессом перед публикацией по-настоящему. Я предоставлю обе регистрационные формы. Также я рекомендовал бы поработать с тестовым сайтом, прежде чем публиковать на живой сервер.
Приведу примеры публикации в обеих средах.
После регистрации пришло время установить последний инструмент:
pip install twine
ИЛИ
pip3 install twine
Как только twine
установится, можно использовать его для загрузки пакета в PyPi. Выполните эту команду на корневом уровне проекта:
twine upload --repository testpypi dist/* (TEST)
twine upload --repository pypi dist/* (PROD)
Если они не работают, поместите перед ними python
или python3
.
После ввода соответствующей команды вам будет предложено ввести свои учетные данные для выбранной среды. После он покажет несколько индикаторов выполнения для загружаемых файлов:
Он даже даст вам ссылку в конце, чтобы показать, что пакет был опубликован для конкретной версии. Вот ссылки, которые были сгенерированы во время этого упражнения, представляющие TEST и PROD соответственно:
Далее идет проверка, возможна ли загрузка по ссылке, предоставленной на страницах PyPi. Выполнив ее, вы получите типичное сообщение об установке. Указывать номер версии необязательно.
Также можно опустить -i https//test.pypi.org/simple/
, если хотите загружать из производственной версии PyPi, которая аналогична pip
.
Теперь, когда мы установили пакет, проверим, что он был установлен правильно.
Откройте интерпретатор Python и импортируйте модуль hello_world
, созданный в этой статье.
Наш интерпретатор должен распознать импорт без каких-либо проблем.
Вывод
Поздравляю! Вы успешно опубликовали пакет Python в PyPi.
PyPi — это потрясающая платформа, которая позволяет сообществу Python с легкостью распространять код среди других пользователей по всему миру.
Как видите, в этом процессе для каждой выпускаемой версии может быть много ручной работы.
Читайте также:
- Как создавать и публиковать консольные приложения на Python
- Импорт в Python
- Пять действительно крутых пакетов Python
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Eric Chi: Publishing Your Python Packages to PyPi