О некоторых нюансах, связанных с setuptools

Эта статья — результат моего более глубокого знакомства с setuptools. В ней описаны основные моменты, на которые стоит обратить внимание, подготавливая дистрибутив с приложением к распространению.

  1. Если ваш проект основан на Django, установите свойство zipsafe=False. Этот параметр не позволит setuptools подготовить egg-файл — сжатый зашифрованный файл с некоторыми дополнительными метаданными.

    Почему это может оказаться полезным:

    • egg-файлы достаточно сильно отличаются от всего остального в стандартной экосистеме Python, они могут запутать ваших пользователей;
    • если вам нужен простой однофайловый и предварительно собранный формат для распространения, то wheel — лучшее решение, которое поддерживают стандартные инструменты для установки, обновления и удаления пакетов в Python;
    • сторонние пакеты могут быть не подготовлены к работе с вашим приложением, если они установлены из egg-файлов.
  2. Команды setup.py register или setup.py sdist upload проверяют, устанавливается ли ваш проект. Но гораздо лучше тестировать установку, используя virtualenv.

  3. Хорошая практика — указывать версию для зависимостей в requirements.py. Вместо «SomePackage» лучше «SomePackage>=1.0.4».

  4. Не стоит в MANIFEST.in перечислять несколько типов файлов шрифтов или шаблонов. Сделайте рекурсивное включение или зацепите весь каталог. Отделяйте исходники от сборок и временных файлов (можно использовать разные каталоги).

  5. Не стоит в package_data перечислять несколько типов файлов шрифтов или шаблонов. Используйте MANIFEST.in и опцию include_package_data=True.

  6. Часто бывает сложно проверить, что все, что должно быть включено в исходный дистрибутив, действительно там присутствует. Замечательный инструмент, который может помочь — check-manifest. Он проверяет, что все, что находится под версионным контролем, включено в дистрибутив, а если чего-то не хватает, check-manifest предложит варианты исправления MANIFEST.in.

  7. Сомнительная идея — жестко указывать список пакетов в setup.py. Лучше вызывать setuptools.find_packages(). Но если есть строгая необходимость перечислять пакеты вручную, покройте код хорошими тестами.

  8. Используйте правильный порядок сортировки правил в MANIFEST.in. Исключения имеют более высокий приоритет, чем, например, включения, указывайте их последними в файле. В противном случае можно столкнуться с проблемой, когда одно правило переопределяет другое.

  9. Жестко указывать список py_modules — сомнительная идея. Лучше поступить, например, так:

    py_modules=[splitext(basename(i))[0] for i in glob.glob("src/*.py")]
    

    Но если есть строгая необходимость перечислять py_modules вручную, покройте код хорошими тестами.

  10. Импорт пакетов в setup.py — рискованное дело. Если у приложения есть неудовлетворенные зависимости, пакет может оказаться неустанавливаемым. Как можно поступить, если вам нужно всего лишь извлечь текущую версию приложения: прочитайте файл и извлеките из него номер версии.

  11. Возможно вы задавались вопросом, почему есть два места, где вы определяете требования к проекту: файл requirements.txt, а также ключевое слово install_requires в setup.py. Разве это не нарушает принцип DRY? Ведь можно написать функцию, которая проанализирует requirements.txt и создаст список зависимостей для install_requires:

    from pip.req import parse_requirements
    from pip.download import PipSession
    import os
    
    def read_requirements():
        '''parses requirements from requirements.txt'''
        reqs_path = os.path.join(__location__, 'requirements.txt')
        install_reqs = parse_requirements(reqs_path, session=PipSession())
        reqs = [str(ir.req) for ir in install_reqs]
        return reqs
    

    Иногда приходится слышать, что это не лучшая практика. Считается, что файл requirements.txt должен быть обычным дополнением. Альтернатива — сгенерировать requirements.txt с помощью pip freeze, а затем вручную скопировать список зависимостей в setup.py и подкорректировать требования к версиям. Но есть риск, связанный с рассинхронизацией списка зависимостей.

  12. В setuptools нельзя использовать список строк, чтобы указать несколько авторов и адресов эл. почты. Список авторов можно указать через запятую, а в качестве author_email может оказаться удобным использование списка рассылки:

    author='Paul Jones, Alex Mitchell',
    author_email='greatproduct@businesslogic.io'
    

Совет: используйте cookiecutter-pylibrary, чтобы избежать многих из описанных выше проблем.