Приветствую, Хабрахабр! Данная статья будет полезна желающим ознакомиться не только с оформлением собственного пакета 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)


В сети существует множество инструкций по инсталляции пакетов в общий каталог, поэтому в данной статье я буду описывать один-единственный метод, который оказался наиболее удобным и продуктивным для меня:

  1. Регистрируем профиль в PyPI;
  2. Устанавливаем утилиту «Twine» для регистрации и загрузки пакетов (подробнее здесь) прямо в корень проекта — «pip install twine»;
  3. Комплектуем дистрибутив — «python setup.py sdist»;
  4. Следующий этап — «колеса», это современный формат распространения (фактически заархивированные данные) пакетов — «python setup.py bdist_wheel» (подробнее здесь и здесь);
  5. Регистрация проекта — необходимо передать указанные метаданные («setup.py»), которые расположены в файле PKG-INFO (находится внутри архивированного проекта);
  6. Финальный этап — загрузить пакет — «twine upload dist/*».

Подробное руководство расположено по ссылке, есть вариант использования также тестового сервера и авторизации из каталога — здесь, в сети доступен материал на русском языке.

Остальные взаимодействия происходят через личный аккаунт в PyPI: удаление, редактирование, журнал историй, релизы, роли и так далее.

Репозиторий пакета


Если проделанная работа в перспективе может стать востребована аудиторией, нужно предпринять следующий ряд мер: собственно создать репозиторий, оформить титульный лист проекта (README.md), подключить тестирование и выбрать лицензию. Если с двумя первыми файлами понятно все, то следующие требуют уточнений.

Лицензия открытого программного обеспечения


Существует разнообразное множество лицензий, но лидирующие позиции занимают MIT License, Apache License и GPL. Я использую первую — она разрешает использование и изменение кода любым образом (при условии наличия копирайта), а также имеет маленький размер. Существуют и минусы — отсутствует патентное право, присущее другим лицензиям, но маленьким проектам данная лицензия подходит хорошо.

Например, вот так выглядит лицензия в моем репозитории — MIT License, Github сам поставит наличие лицензии в верхнюю панельку репозитория.

Тестирование проекта


Travis CI — если описать простыми словами, то каждое изменение в вашем проекте тестируется на указанных вами окружениях. Данная площадка достаточно легкая в использовании — помещаем файл (ссылка на github) с названием «.travis.yml» в корневой каталог, в котором указывают: язык программирования, необходимые версии, интерпретаторы языка, какие зависимости установить при интеграции и разворачивании на сервере, плагин тестирования и еще ряд параметров.

Плюсов очень много, помимо самого тестирования — удобный интерфейс с множеством отображений статуса тестирования, ошибок, истории, быстрое подключение, проверка пользовательских коммитов в ваш проект до слияния. Также «Трэвис» сообщает о результатах по почте.


Первым делом устанавливаем крюк на репозиторий, следом добавляем файл «.travis.yml», затем пушим изменения в проекте и наблюдаем за результатами.


Буду рад, если данная статья поможет кому-нибудь в освоении нового материала или пробудит интерес к этой области. Присылайте свои вопросы и советы, если они у вас есть, в личные сообщения.
Поделиться с друзьями
-->

Комментарии (5)


  1. andreymal
    23.01.2017 21:15

    script: nosetests

    А где собственно тесты-то?)


  1. grossws
    23.01.2017 22:30

    Существуют и минусы — отсутствует патентное право, присущее другим лицензиям, но маленьким проектам данная лицензия подходит хорошо.

    Не отсутствует, а не предоставляет лицензию на использование патентов, которыми может быть защищён код.


    Есть ещё один неочевидный минус: MIT требует таскать её в NOTICES, т. к. сама лицензия для каждой такой зависимости отличается. В случае asl2 можно спокойно перечислить зависимости под этой лицензией и скопом сказать, что они под Apache License. В экосистеме Rust'а вообще пошли по пути двойного лицензирования (Apache OR MIT), т. к. софт под Apache не совместима с GPLv2.


    1. grossws
      23.01.2017 22:31

      Ещё может быть полезно сделать сборку и публикацию релиза на pypi по появлению нового тега определенного вида в репозитории.


  1. Assargin
    23.01.2017 22:30
    +1

    Twine — спасибо за наводку!
    Кстати, можно ещё с помощью TravisCI автоматизировать релизы в PyPi.


  1. homm
    24.01.2017 02:58

    Все же правильно PyPI-пакет. Именно PyPI является репозиторием, в то время как pip — один из клиентов