DevGang
Авторизоваться

Как написать, упаковать и распространить библиотеку на Python

Python — отличный язык программирования, но упаковка — одно из его самых слабых мест. Это общеизвестный факт в обществе. Установка, импорт, использование и создание пакетов значительно улучшились за эти годы, но они все еще не соответствуют новым языкам, таким как Go и Rust, которые многому научились в борьбе с Python и другими зрелыми языками.

В этом руководстве вы узнаете все, что вам нужно знать о написании, упаковке и распространении собственных пакетов.

Как написать библиотеку Python

В Python 3 есть отличный объект Path, который является огромным улучшением по сравнению с неудобным модулем os.path в Python 2. Но ему не хватает одной важной возможности — поиска пути к текущему сценарию. Это очень важно, если вы хотите найти файлы доступа относительно текущего скрипта.

Во многих случаях сценарий можно установить в любом месте, поэтому вы не можете использовать абсолютные пути, а в качестве рабочего каталога можно указать любое значение, поэтому вы не можете использовать относительный путь. Если вы хотите получить доступ к файлу в подкаталоге или родительском каталоге, вы должны иметь возможность определить текущий каталог сценария.

Вот как вы это делаете в Python:

import pathlib
 
script_dir = pathlib.Path(__file__).parent.resolve()

Чтобы получить доступ к файлу с именем «file.txt» в подкаталоге «data» каталога текущего скрипта, вы можете использовать следующий код:

print(open(str(script_dir/'data/file.txt').read())

В пакете pathology у вас есть встроенный метод script_dir, и вы используете его следующим образом:

from pathology.Path import script_dir
 
print(open(str(script_dir()/'data/file.txt').read())

Да, это полный рот. Пакет патологии очень прост. Он наследует свой собственный класс Path от Pathlib библиотеки path и добавляет статический script_dir(), который всегда возвращает путь вызывающего скрипта.

Вот реализация:

import pathlib
import inspect
 
class Path(type(pathlib.Path())):
    @staticmethod
    def script_dir():
        print(inspect.stack()[1].filename)
        p = pathlib.Path(inspect.stack()[1].filename)
        return p.parent.resolve()

Из-за кросс-платформенной реализации pathlib.Path вы можете наследовать непосредственно от него и должны наследовать от определенного подкласса (PosixPath или WindowsPath). Разрешение script_dir использует модуль проверки, чтобы найти вызывающую программу, а затем ее атрибут имени файла.

Более 2 миллионов тем и плагинов WordPress, веб-шаблонов и шаблонов электронной почты, наборов пользовательского интерфейса и многого другого

Загрузите тысячи тем и плагинов WordPress, веб-шаблонов, элементов пользовательского интерфейса и многое другое с членством в Envato Elements. Получите неограниченный доступ к растущей библиотеке из миллионов творческих и кодовых ресурсов.

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

import os
import shutil 
from unittest import TestCase
from pathology.path import Path
 
 
class PathTest(TestCase):
    def test_script_dir(self):
        expected = os.path.abspath(os.path.dirname(__file__))
        actual = str(Path.script_dir())
        self.assertEqual(expected, actual)
 
    def test_file_access(self):
        script_dir = os.path.abspath(os.path.dirname(__file__))
        subdir = os.path.join(script_dir, 'test_data')
        if Path(subdir).is_dir():
            shutil.rmtree(subdir)
        os.makedirs(subdir)
        file_path = str(Path(subdir)/'file.txt')
        content = '123'
        open(file_path, 'w').write(content)
        test_path = Path.script_dir()/subdir/'file.txt'
        actual = open(str(test_path)).read()
 
        self.assertEqual(content, actual)

Путь Python

Пакеты Python должны быть установлены где-то на пути поиска Python, чтобы их можно было импортировать модулями Python. Путь поиска Python представляет собой список каталогов и всегда доступен в sys.path. Вот мой текущий sys.path:

>>> print('\n'.join(sys.path))
 
/Users/gigi.sayfan/miniconda3/envs/py3/lib/python36.zip
/Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6
/Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/lib-dynload
/Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/site-packages
/Users/gigi.sayfan/miniconda3/envs/py3/lib/python3.6/site-packages/setuptools-27.2.0-py3.6.egg

Обратите внимание, что первая пустая строка вывода представляет текущий каталог, поэтому вы можете импортировать модули из текущего рабочего каталога, каким бы он ни был. Вы можете напрямую добавлять или удалять каталоги в/из sys.path.

Вы также можете определить переменную среды PYTHONPATH и несколько других способов управления ею. Стандартные site-packages включены по умолчанию, и именно сюда идут пакеты, которые вы устанавливаете с помощью pip.

Как упаковать библиотеку Python

Теперь, когда у нас есть код и тесты, давайте упакуем их в соответствующую библиотеку. Python предоставляет простой способ через модуль установки. Вы создаете файл с именем setup.py в корневом каталоге вашего пакета.

Файлы setup.py содержат множество метаданных, таких как автор, лицензия, сопровождающие и другую информацию о пакете. Это в дополнение к элементу пакетов (packages), который использует функцию find_packages(), импортированную из setuptools, для поиска подпакетов.

Вот файл setup.py пакета патологии:

from setuptools import setup, find_packages
 
setup(name='pathology',
      version='0.1',
      url='https://github.com/the-gigi/pathology',
      license='MIT',
      author='Gigi Sayfan',
      author_email='the.gigi@gmail.com',
      description='Add static script_dir() method to Path',
      packages=find_packages(exclude=['tests']),
      long_description=open('README.md').read(),
      zip_safe=False)

Исходный дистрибутив

Пакет исходного дистрибутива — это архивный файл, содержащий пакеты Python, модули, а также другие файлы, которые используются для выпуска пакета (например, версии 1, 2 и т. д.). После распространения файла конечные пользователи могут загрузить и установить его в своей операционной системе.

Чтобы создать исходный дистрибутив (sdist), запустите: python setup.py sdist

Давайте создадим исходный дистрибутив:

$ python setup.py sdist
running sdist
running egg_info
creating pathology.egg-info
writing pathology.egg-info/PKG-INFO
writing dependency_links to pathology.egg-info/dependency_links.txt
writing top-level names to pathology.egg-info/top_level.txt
writing manifest file 'pathology.egg-info/SOURCES.txt'
reading manifest file 'pathology.egg-info/SOURCES.txt'
writing manifest file 'pathology.egg-info/SOURCES.txt'
warning: sdist: standard file not found: should have one of README, README.rst, README.txt
 
running check
creating pathology-0.1
creating pathology-0.1/pathology
creating pathology-0.1/pathology.egg-info
copying files to pathology-0.1...
copying setup.py -> pathology-0.1
copying pathology/__init__.py -> pathology-0.1/pathology
copying pathology/path.py -> pathology-0.1/pathology
copying pathology.egg-info/PKG-INFO -> pathology-0.1/pathology.egg-info
copying pathology.egg-info/SOURCES.txt -> pathology-0.1/pathology.egg-info
copying pathology.egg-info/dependency_links.txt -> pathology-0.1/pathology.egg-info
copying pathology.egg-info/not-zip-safe -> pathology-0.1/pathology.egg-info
copying pathology.egg-info/top_level.txt -> pathology-0.1/pathology.egg-info
Writing pathology-0.1/setup.cfg
creating dist
Creating tar archive
removing 'pathology-0.1' (and everything under it)

Предупреждение связано с тем, что я использовал нестандартный файл README.md. Безопасно игнорировать. Приведенная выше команда создаст файл архива в формате по умолчанию для текущей операционной системы. Для систем Unix будет сгенерирован сжатый tar-файл в каталоге dist:

$ ls -la dist
total 8
drwxr-xr-x   3 gigi.sayfan  gigi.sayfan   102 Apr 18 21:20 .
drwxr-xr-x  12 gigi.sayfan  gigi.sayfan   408 Apr 18 21:20 ..
-rw-r--r--   1 gigi.sayfan  gigi.sayfan  1223 Apr 18 21:20 pathology-0.1.tar.gz

Если вы используете Windows, создается zip-файл.

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

python setup.py sdist --formats=gztar,zip

Например, приведенная выше команда создаст tar-архив, сжатый gzip, и zip-файл.

Доступны следующие форматы:

  • zip.zip
  • gztar.tar.gz
  • bztar.tar.bz2
  • xztar: .tar.xz
  • ztar.tar.Z
  • tar.tar

Бинарное распространение

Чтобы создать двоичный дистрибутив, называемый колесом, запустите: python setup.py bdist_wheel

$ python setup.py bdist_wheel
running bdist_wheel
running build
running build_py
creating build
creating build/lib
creating build/lib/pathology
copying pathology/__init__.py -> build/lib/pathology
copying pathology/path.py -> build/lib/pathology
installing to build/bdist.macosx-10.7-x86_64/wheel
running install
running install_lib
creating build/bdist.macosx-10.7-x86_64
creating build/bdist.macosx-10.7-x86_64/wheel
creating build/bdist.macosx-10.7-x86_64/wheel/pathology
copying build/lib/pathology/__init__.py -> build/bdist.macosx-10.7-x86_64/wheel/pathology
copying build/lib/pathology/path.py -> build/bdist.macosx-10.7-x86_64/wheel/pathology
running install_egg_info
running egg_info
writing pathology.egg-info/PKG-INFO
writing dependency_links to pathology.egg-info/dependency_links.txt
writing top-level names to pathology.egg-info/top_level.txt
reading manifest file 'pathology.egg-info/SOURCES.txt'
writing manifest file 'pathology.egg-info/SOURCES.txt'
Copying pathology.egg-info to build/bdist.macosx-10.7-x86_64/wheel/pathology-0.1-py3.6.egg-info
running install_scripts
creating build/bdist.macosx-10.7-x86_64/wheel/pathology-0.1.dist-info/WHEEL

Пакет патологии содержит только чистые модули Python, поэтому можно создать универсальный пакет. Если ваш пакет включает расширения C, вам придется создать отдельное колесо для каждой платформы:Пакет патологии содержит только чистые модули Python, поэтому можно создать универсальный пакет. Если ваш пакет включает расширения C, вам придется создать отдельное колесо для каждой платформы:

$ ls -la dist
total 16
drwxr-xr-x   4 gigi.sayfan  gigi.sayfan   136 Apr 18 21:24 .
drwxr-xr-x  13 gigi.sayfan  gigi.sayfan   442 Apr 18 21:24 ..
-rw-r--r--   1 gigi.sayfan  gigi.sayfan  2695 Apr 18 21:24 pathology-0.1-py3-none-any.whl
-rw-r--r--   1 gigi.sayfan  gigi.sayfan  1223 Apr 18 21:20 pathology-0.1.tar.gz

Чтобы глубже погрузиться в тему упаковки библиотек Python, можно ознакомиться информацией как писать собственные пакеты Python.

Как распространять пакет Python

Python имеет центральный репозиторий пакетов под названием PyPI (Python Packages Index). PyPI упрощает управление различными версиями пакетов. Например, если пользователю нужно установить определенную версию пакета, pip знает, где ее искать.

Когда вы устанавливаете пакет Python с помощью pip, он загружает пакет из PyPI (если вы не укажете другой репозиторий). Чтобы распространить наш пакет патологии, нам нужно загрузить его в PyPI и предоставить некоторые дополнительные метаданные, которые требуются PyPI. Шаги:

  • Обновите версию вашего пипса.
  • Создайте учетную запись на PyPI (только один раз).
  • Зарегистрируйте свой пакет.
  • Загрузите свой пакет.

Обновите версию вашего pip

Убедитесь, что в вашей операционной системе установлена последняя версия pip. Чтобы обновить pip, введите следующую команду

python3 -m pip install --upgrade pip

Завести аккаунт

Вы можете создать учетную запись на веб-сайте PyPI. Затем создайте файл .pypirc в своем домашнем каталоге:

[distutils] 
index-servers=pypi
  
[pypi]
repository = https://pypi.python.org/pypi
username = the_gigi

В целях тестирования вы можете добавить индексный сервер pypitest в свой файл .pypirc:

[distutils]
index-servers=
    pypi
    pypitest
 
[pypitest]
repository = https://testpypi.python.org/pypi
username = the_gigi
 
[pypi]
repository = https://pypi.python.org/pypi
username = the_gigi

Зарегистрируйте свой пакет

Если это первый выпуск вашего пакета, вам необходимо зарегистрировать его в PyPI. Используйте команду регистрации файла setup.py. Он попросит вас ввести пароль. Обратите внимание, что я указываю на тестовый репозиторий здесь:

$ python setup.py register -r pypitest
running register
running egg_info
writing pathology.egg-info/PKG-INFO
writing dependency_links to pathology.egg-info/dependency_links.txt
writing top-level names to pathology.egg-info/top_level.txt
reading manifest file 'pathology.egg-info/SOURCES.txt'
writing manifest file 'pathology.egg-info/SOURCES.txt'
running check
Password:
Registering pathology to https://testpypi.python.org/pypi
Server response (200): OK

Загрузите свой пакет

Теперь, когда пакет зарегистрирован, мы можем его загрузить. Я рекомендую использовать twine, который более надежен. Установите его как обычно, используя pip install twine. Затем загрузите свой пакет с помощью twine и укажите свой пароль (отредактировано ниже):

$ twine upload -r pypitest -p <redacted> dist/*
Uploading distributions to https://testpypi.python.org/pypi
Uploading pathology-0.1-py3-none-any.whl
[================================] 5679/5679 - 00:00:02
Uploading pathology-0.1.tar.gz
[================================] 4185/4185 - 00:00:01 

Пакет теперь доступен на официальном сайте Pypi, как показано ниже.

Чтобы установить его с помощью pip, просто введите следующую команду:

pip install pathology

Чтобы глубже погрузиться в тему распространения ваших пакетов, необходимо ознакомьться с информацией как поделиться своими пакетами Python.

Вывод

В этом руководстве мы прошли полноценный процесс написания библиотеки Python, ее упаковки и распространения через PyPI. К этому моменту у вас уже должны быть все инструменты для создания библиотек и обмена ими со всем миром.

Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу