6. Приклади Distutils

Примітка

Цей документ зберігається лише до тих пір, поки документація setuptools за адресою https://setuptools.readthedocs.io/en/latest/setuptools.html окремо не охопить всю відповідну інформацію, яка зараз включена тут.

У цьому розділі наведено низку основних прикладів, які допоможуть розпочати роботу з distutils. Додаткову інформацію про використання distutils можна знайти в кулінарній книзі Distutils.

Дивись також

Кулінарна книга Distutils

Збірка рецептів, які показують, як досягти більшого контролю над distutils.

6.1. Чистий дистрибутив Python (за модулями)

Якщо ви просто розповсюджуєте кілька модулів, особливо якщо вони не живуть в окремому пакеті, ви можете вказати їх окремо за допомогою параметра py_modules у сценарії встановлення.

У найпростішому випадку вам доведеться потурбуватися про два файли: сценарій встановлення та єдиний модуль, який ви розповсюджуєте, foo.py у цьому прикладі:

<root>/
        setup.py
        foo.py

(На всіх діаграмах у цьому розділі * <root> * посилатиметься на кореневий каталог розповсюдження.) Мінімальний сценарій налаштування для опису цієї ситуації буде таким:

from distutils.core import setup
setup(name='foo',
      version='1.0',
      py_modules=['foo'],
      )

Зауважте, що ім’я дистрибутива вказується окремо за допомогою параметра name, і немає правила, яке б стверджувало, що воно має бути таким самим, як ім’я єдиного модуля в дистрибутиві (хоча це, ймовірно, гарна угода, якої слід дотримуватися ). Однак ім’я дистрибутива використовується для генерування імен файлів, тому ви повинні дотримуватися літер, цифр, символів підкреслення та дефісів.

Оскільки py_modules є списком, ви, звичайно, можете вказати декілька модулів, наприклад. якщо ви розповсюджуєте модулі foo і bar, ваші налаштування можуть виглядати так:

<root>/
        setup.py
        foo.py
        bar.py

і сценарій налаштування може бути таким:

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      py_modules=['foo', 'bar'],
      )

Ви можете помістити вихідні файли модуля в інший каталог, але якщо у вас достатньо модулів для цього, можливо, простіше вказати модулі за пакетом, а не перераховувати їх окремо.

6.2. Чистий дистрибутив Python (за пакетом)

Якщо у вас є кілька модулів для розповсюдження, особливо якщо вони містяться в кількох пакетах, можливо, простіше вказати цілі пакети, а не окремі модулі. Це працює, навіть якщо ваші модулі не в пакеті; ви можете просто сказати Distutils обробляти модулі з кореневого пакета, і це працює так само, як і будь-який інший пакет (за винятком того, що вам не обов’язково мати файл __init__.py).

Сценарій налаштування з останнього прикладу також можна записати як:

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      packages=[''],
      )

(Порожній рядок означає кореневий пакет.)

Якщо ці два файли переміщуються до підкаталогу, але залишаються в кореневому пакеті, наприклад:

<root>/
        setup.py
        src/      foo.py
                  bar.py

тоді ви все одно вкажете кореневий пакет, але ви повинні повідомити Distutils, де знаходяться вихідні файли в кореневому пакеті:

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      package_dir={'': 'src'},
      packages=[''],
      )

Однак зазвичай ви захочете поширювати кілька модулів в одному пакеті (або в підпакетах). Наприклад, якщо модулі foo і bar належать до пакета foobar, одним зі способів компонування дерева вихідних кодів є:

<root>/
        setup.py
        foobar/
                 __init__.py
                 foo.py
                 bar.py

Насправді це макет за замовчуванням, очікуваний Distutils, і той, який вимагає найменшої роботи для опису у вашому сценарії налаштування:

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      packages=['foobar'],
      )

Якщо ви бажаєте розмістити модулі в каталогах, не названих відповідно до їхнього пакета, вам потрібно знову використати опцію package_dir. Наприклад, якщо каталог src містить модулі в пакеті foobar:

<root>/
        setup.py
        src/
                 __init__.py
                 foo.py
                 bar.py

відповідним сценарієм налаштування буде:

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      package_dir={'foobar': 'src'},
      packages=['foobar'],
      )

Або ви можете розмістити модулі з основного пакета прямо в корені дистрибутива:

<root>/
        setup.py
        __init__.py
        foo.py
        bar.py

у цьому випадку ваш сценарій налаштування буде таким:

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      package_dir={'foobar': ''},
      packages=['foobar'],
      )

(Порожній рядок також означає поточний каталог.)

Якщо у вас є підпакунки, їх потрібно явно вказати в packages, але будь-які записи в package_dir автоматично поширюються на підпакунки. (Іншими словами, Distutils не сканує ваше дерево вихідних кодів, намагаючись з’ясувати, які каталоги відповідають пакетам Python, шукаючи файли __init__.py.) Таким чином, якщо макет за замовчуванням збільшує під- пакет:

<root>/
        setup.py
        foobar/
                 __init__.py
                 foo.py
                 bar.py
                 subfoo/
                           __init__.py
                           blah.py

тоді відповідний сценарій налаштування буде таким:

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      packages=['foobar', 'foobar.subfoo'],
      )

6.3. Один модуль розширення

Модулі розширення вказуються за допомогою параметра ext_modules. package_dir не впливає на те, де знаходяться вихідні файли розширення; це впливає лише на джерело для чистих модулів Python. Найпростіший випадок, один модуль розширення в одному вихідному файлі C, це:

<root>/
        setup.py
        foo.c

Якщо розширення foo належить кореневому пакету, сценарій налаштування для цього може бути таким:

from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
      version='1.0',
      ext_modules=[Extension('foo', ['foo.c'])],
      )

Якщо розширення насправді належить до пакета, скажіть foopkg

З таким же макетом вихідного дерева, це розширення можна помістити в пакет foopkg, просто змінивши назву розширення:

from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
      version='1.0',
      ext_modules=[Extension('foopkg.foo', ['foo.c'])],
      )

6.4. Перевірка посилки

Команда check дозволяє вам перевірити, чи метадані вашого пакета відповідають мінімальним вимогам для створення дистрибутива.

Щоб запустити його, просто викличте його за допомогою свого сценарію setup.py. Якщо чогось не вистачає, check відобразить попередження.

Розглянемо приклад із простим скриптом:

from distutils.core import setup

setup(name='foobar')

Виконання команди check відобразить деякі попередження:

$ python setup.py check
running check
warning: check: missing required meta-data: version, url
warning: check: missing meta-data: either (author and author_email) or
         (maintainer and maintainer_email) should be supplied

Якщо ви використовуєте синтаксис reStructuredText у полі long_description і docutils встановлено, ви можете перевірити, чи синтаксис правильний за допомогою команди check, використовуючи параметр restructuredtext.

Наприклад, якщо сценарій setup.py змінено таким чином:

from distutils.core import setup

desc = """\
My description
==============

This is the description of the ``foobar`` package.
"""

setup(name='foobar', version='1', author='tarek',
    author_email='tarek@ziade.org',
    url='http://example.com', long_description=desc)

Якщо довгий опис пошкоджено, check зможе виявити це за допомогою аналізатора docutils:

$ python setup.py check --restructuredtext
running check
warning: check: Title underline too short. (line 2)
warning: check: Could not finish the parsing.

6.5. Читання метаданих

Функція distutils.core.setup() забезпечує інтерфейс командного рядка, який дозволяє запитувати поля метаданих проекту за допомогою сценарію setup.py даного проекту:

$ python setup.py --name
distribute

Цей виклик читає метадані name за допомогою функції distutils.core.setup(). Хоча, коли вихідний або бінарний дистрибутив створюється за допомогою Distutils, поля метаданих записуються в статичний файл під назвою PKG-INFO. Коли проект на основі Distutils інстальовано в Python, файл PKG-INFO копіюється разом із модулями та пакетами дистрибутива в NAME-VERSION-pyX.X.egg-info, де NAME — це назва проекту, VERSION — його версія, як визначено в метаданих, а pyX.X — основна та допоміжна версії Python, наприклад 2.7 або 3.2.

Ви можете прочитати цей статичний файл за допомогою класу distutils.dist.DistributionMetadata та його методу read_pkg_file():

>>> from distutils.dist import DistributionMetadata
>>> metadata = DistributionMetadata()
>>> metadata.read_pkg_file(open('distribute-0.6.8-py2.7.egg-info'))
>>> metadata.name
'distribute'
>>> metadata.version
'0.6.8'
>>> metadata.description
'Easily download, build, install, upgrade, and uninstall Python packages'

Зауважте, що клас також може бути створений за допомогою шляху до файлу метаданих для завантаження його значень:

>>> pkg_info_path = 'distribute-0.6.8-py2.7.egg-info'
>>> DistributionMetadata(pkg_info_path).name
'distribute'