Приветствую, Хабрахабр! Данная статья будет полезна желающим ознакомиться не только с оформлением собственного пакета Python Package Index (PIP), но и с различными вспомогательными инструментами, помогающими сопровождать разработку на всех стадиях — на примере авторской работы.
Необходимые инструменты:
- Среда разработки — написание объектно-ориентированного кода, тесно работающего с интерфейсом приложения (в нашем случае веб-сайта), другими словами — отправка и обработка запросов к API, и дополнительных вспомогательных файлов;
- Загрузка своих наработок в общий каталог пакетов — PyPI;
- Github — создание репозитория с целью контроля качества, улучшения и перманентного обновления библиотеки, общего взаимодействия с областью открытого исходного кода;
- Одна из лицензий свободного программного обеспечения, в нашем случае — MIT License;
- Travis CI — непрерывная сборка и тестирование разрабатываемого проекта в различных окружениях (например, разные версии языка или интерпретатора).
Данный список можно принимать за содержание статьи в соответствующем порядке.
Хочу добавить, что переходить по предложенным материалам, в которых указано много важной и интересной информации, не обязательно — вы все равно получите необходимые данные для завершения подобного проекта и уже тогда решите, стоит ли вам использовать дополнительные ссылки.
Введение
Я — постоянный посетитель различных интернет-ресурсов, в том числе одного известного украинского сообщества программистов, а по совместительству и постоянно практикующий программист, — поэтому за недостатком идей ринулся конструировать реализацию статистики публикаций (на примере Хабрахабра) (см. рисунок ниже) для пользователей площадки. Не важно, в каком виде проект будет реализован, но промежуточной задачей является предмет обсуждения статьи — работа с API этого сайта, что в значительной мере облегчается написанием собственной обертки (PIP-пакета), включая несомненные дополнительные плюсы от разработки — например, интересная новая область и приобретенный опыт.
Конструкция проекта, необходимая для реализации обертки и загрузки ее в общий каталог, отличается от окончательной, в свою очередь надлежащей для качественного поддержания дальнейшей работы.
Пример окончательного варианта — DouAPI (ссылка на github, держите ее открытой параллельно со статьей и обращайтесь для детализации описываемого, не забудьте прочитать описание репозитория). В нем отсутствуют нужные файлы для тестирования и какие-то вспомогательные решения (например, вынести в отдельное место объемный код, выполняющий форматирование строк или обработку дополнительных запросов), но только из-за отсутствия такого требования на данном этапе разработки.
Непосредственно структура самого пакета должна состоять из главного каталога, подкаталога с основным и вспомогательными файлами и файла дистрибуции «setup.py».
Для большей ясности стоит начать описание реализации с модуля «api.py» (ссылка на github) — импортирование модуля Requests, позволяющего обращаться с HTTP-запросами на улучшеном уровне.
import requests
Создание непосредственно отдельного класса для обращения по заданным к API сайта по заданным URL определенным методом. Если ваша разработка будет ограничиваться какими-то факторами и создание дополнительного класса для запросов излишне — создавайте закрытый метод уже в основном классе, но если вы не знаете, как далеко зайдет проект, какие фичи вы захотите добавить и какая сложность вас ждет — лишним не будет, чтобы не тратить время на переписывание кода.
class DouAPI(object):
def method(self, method, values=None):
if values is not None: values['limit'] = 10000
response = requests.get('https://api.dou.ua/{0}/'.format(method), params=values)
if response.status_code == 200:
return response.json()['results']
else:
message = 'A request to the Dou API was unsuccessful. The server returned HTTP {0} {1}.'
return message.format(response.status_code, response.reason)
Вызов функции «method», принимающий название метода, которое соответствует необходимому URL и набору ключей-значений (HTTP Headers), возвращает результат в виде формата JSON (такой же набор ключей-значений) в успешном случае, и просто строку с кодом ошибки и причиной ответа в противном случае (так старайтесь не делать, лучше создавать отдельно свои пользовательские исключения, как написано здесь или здесь).
Класс, к которому дальше обращается программист через объект (все остальное, что выходит за рамки требуемых инструментов, заворачивайте в приватные переменные и методы), содержит одну функцию «lenta», возвращающую объект класса с методом запросов, передавая название метода «articles» и набор ключей-значений (например, категорию и автора).
class Dou():
__slots__ = ('_dou')
def __init__(self):
self._dou = DouAPI()
def lenta(self, category=None, tag=None, author=None, date_from=None, date_to=None):
values = locals().copy()
values.pop('self')
values = {header:value for header, value in values.items() if value != None}
return self._dou.method('articles', values=values)
Так в итоге выглядит пример использования обертки — импортируем необходимый класс, создаем объект, обращаемся к методу класса, указывая интересующие параметры, получаем в ответ данные в формате JSON.
from dou import Dou
dou = Dou()
news = dou.lenta(category = 'news', date_from='2016-06-01')
print(news)
Если проиллюстрировать процесс, взаимодействие происходит следующим образом:
Теперь рассмотрим момент с файлом «__init__.py», позволяющим проинициализировать класс реализации на уровне обращения к каталогу, записываем в него следующее (ссылка на github).
from .api import Dou
Финальный этап — «setup.py» (ссылка на github) описывает ряд данных, которые необходимы для сборки проекта из исходников (подробнее здесь) — понятнее станет на практике, когда повзаимодействуете с архивацией и распределением пакета. Описывать что-либо излишне, все должно быть понятно интуитивно.
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
setup(
name='DouAPI',
version='1.0.0',
author='DmytryiStriletskyi',
author_email='dmytryi.striletskyi@gmail.com',
url='https://github.com/DmytryiStriletskyi/DouAPI',
description='Dou API wrapper',
download_url='https://github.com/DmytryiStriletskyi/DouAPI/archive/master.zip',
license='MIT',
packages=['dou'],
install_requires=['requests'],
classifiers=[
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
]
)
Python Package Index (PIP)
В сети существует множество инструкций по инсталляции пакетов в общий каталог, поэтому в данной статье я буду описывать один-единственный метод, который оказался наиболее удобным и продуктивным для меня:
- Регистрируем профиль в PyPI;
- Устанавливаем утилиту «Twine» для регистрации и загрузки пакетов (подробнее здесь) прямо в корень проекта — «pip install twine»;
- Комплектуем дистрибутив — «python setup.py sdist»;
- Следующий этап — «колеса», это современный формат распространения (фактически заархивированные данные) пакетов — «python setup.py bdist_wheel» (подробнее здесь и здесь);
- Регистрация проекта — необходимо передать указанные метаданные («setup.py»), которые расположены в файле PKG-INFO (находится внутри архивированного проекта);
- Финальный этап — загрузить пакет — «twine upload dist/*».
Подробное руководство расположено по ссылке, есть вариант использования также тестового сервера и авторизации из каталога — здесь, в сети доступен материал на русском языке.
Остальные взаимодействия происходят через личный аккаунт в PyPI: удаление, редактирование, журнал историй, релизы, роли и так далее.
Репозиторий пакета
Если проделанная работа в перспективе может стать востребована аудиторией, нужно предпринять следующий ряд мер: собственно создать репозиторий, оформить титульный лист проекта (README.md), подключить тестирование и выбрать лицензию. Если с двумя первыми файлами понятно все, то следующие требуют уточнений.
Лицензия открытого программного обеспечения
Существует разнообразное множество лицензий, но лидирующие позиции занимают MIT License, Apache License и GPL. Я использую первую — она разрешает использование и изменение кода любым образом (при условии наличия копирайта), а также имеет маленький размер. Существуют и минусы — отсутствует патентное право, присущее другим лицензиям, но маленьким проектам данная лицензия подходит хорошо.
Например, вот так выглядит лицензия в моем репозитории — MIT License, Github сам поставит наличие лицензии в верхнюю панельку репозитория.
Тестирование проекта
Travis CI — если описать простыми словами, то каждое изменение в вашем проекте тестируется на указанных вами окружениях. Данная площадка достаточно легкая в использовании — помещаем файл (ссылка на github) с названием «.travis.yml» в корневой каталог, в котором указывают: язык программирования, необходимые версии, интерпретаторы языка, какие зависимости установить при интеграции и разворачивании на сервере, плагин тестирования и еще ряд параметров.
Плюсов очень много, помимо самого тестирования — удобный интерфейс с множеством отображений статуса тестирования, ошибок, истории, быстрое подключение, проверка пользовательских коммитов в ваш проект до слияния. Также «Трэвис» сообщает о результатах по почте.
Первым делом устанавливаем крюк на репозиторий, следом добавляем файл «.travis.yml», затем пушим изменения в проекте и наблюдаем за результатами.
Буду рад, если данная статья поможет кому-нибудь в освоении нового материала или пробудит интерес к этой области. Присылайте свои вопросы и советы, если они у вас есть, в личные сообщения.
Комментарии (5)
grossws
23.01.2017 22:30Существуют и минусы — отсутствует патентное право, присущее другим лицензиям, но маленьким проектам данная лицензия подходит хорошо.
Не отсутствует, а не предоставляет лицензию на использование патентов, которыми может быть защищён код.
Есть ещё один неочевидный минус: MIT требует таскать её в
NOTICES
, т. к. сама лицензия для каждой такой зависимости отличается. В случае asl2 можно спокойно перечислить зависимости под этой лицензией и скопом сказать, что они под Apache License. В экосистеме Rust'а вообще пошли по пути двойного лицензирования (Apache OR MIT), т. к. софт под Apache не совместима с GPLv2.grossws
23.01.2017 22:31Ещё может быть полезно сделать сборку и публикацию релиза на pypi по появлению нового тега определенного вида в репозитории.
Assargin
23.01.2017 22:30+1Twine — спасибо за наводку!
Кстати, можно ещё с помощью TravisCI автоматизировать релизы в PyPi.
homm
24.01.2017 02:58Все же правильно PyPI-пакет. Именно PyPI является репозиторием, в то время как pip — один из клиентов
andreymal
А где собственно тесты-то?)