Как написать, упаковать и распространять библиотеку на Python
В этом уроке вы узнаете все, что вам нужно знать о написании, упаковке и распространении собственных пакетов.
Как написать библиотеку Python
Библиотека Python представляет собой согласованный набор модулей Python, который организован как пакет Python. В общем, это означает, что все модули живут под одним и тем же каталогом и этот каталог находится на пути поиска Python.
Давайте быстро напишем небольшой пакет Python 3 и проиллюстрируем все эти понятия.
Пакет Pathology
Во многих случаях сценарий может быть установлен в любом месте, поэтому вы не можете использовать абсолютные пути, а рабочий каталог может быть установлен на любое значение, поэтому вы не можете использовать относительный путь. Если вы хотите получить доступ к файлу в подкаталоге или родительском каталоге, вы должны иметь возможность определить текущий каталог сценариев.
Вот как вы это делаете в Python:
Чтобы получить доступ к файлу с именем ‘file.txt’ в подкаталоге данных в каталоге текущего скрипта, вы можете использовать следующий код:print(open(str(script_dir/’data/file.txt’).read())
С пакетом pathology у вас есть встроенный метод script_dir, и вы используете его следующим образом:
Да, это глоток свежего воздуха. Пакет патологии очень прост. Он выводит свой собственный класс Path из Pathlib Path и добавляет статический script_dir(), который всегда возвращает путь вызывающего скрипта.
Из-за кросс-платформенной реализации pathlib.Path вы можете получить непосредственно от него и должны быть получены из определенного подкласса (PosixPath или WindowsPath). Разрешение dir-файла сценария использует модуль проверки, чтобы найти вызывающего, а затем его атрибут имени файла.
Тестирование пакета патологии
Всякий раз, когда вы пишете нечто более сложное, вы должны его протестировать. Модуль патологии не является исключением. Вот тесты с использованием стандартной модульной тестовой платформы:
Путь Python
Обратите внимание, что первая пустая строка вывода представляет текущий каталог, поэтому вы можете импортировать модули из текущего рабочего каталога, что бы это ни было. Вы можете напрямую добавлять или удалять каталоги в / из sys.path.
Вы также можете определить переменную среды PYTHONPATH, и есть несколько других способов ее контролировать. Стандартные site-packages включены по умолчанию, и именно там устанавливаются пакеты, которые вы устанавливаете с помощью pip.
Как упаковать библиотеку Python
Теперь, когда у нас есть наш код и тесты, давайте упакуем все это в нужную библиотеку. Python обеспечивает простой способ через модуль настройки. Вы создаете файл setup.py в корневом каталоге вашего пакета. Затем, чтобы создать исходный дистрибутив, вы запустите: python setup.py sdist
Чтобы создать двоичный дистрибутив, называемый колесом, вы запускаете: python setup.py bdist_wheel
Вот файл setup.py пакета патологии:
Давайте построим дистрибутив источника:
Предупреждение связано с тем, что я использовал нестандартный файл README.md. Это безопасно поэтому игнорируем. Результатом является файл tar-gzipped в каталоге dist:
И вот двоичное распределение:
Пакет патологии содержит только чистые модули Python, поэтому можно создать универсальный пакет. Если ваш пакет включает расширения C, вам нужно будет создать отдельное колесо для каждой платформы:
Для более глубокого погружения в тему упаковки библиотек Python ознакомьтесь, как писать свои собственные пакеты Python.
Как раздавать пакет Python
Python имеет центральный репозиторий пакетов, называемый PyPI (индекс пакетов Python). Когда вы устанавливаете пакет Python с помощью pip, он загружает пакет из PyPI (если вы не укажете другой репозиторий). Чтобы распространять наш пакет патологии, нам нужно загрузить его в PyPI и предоставить некоторые дополнительные метаданные, которые требуется PyPI. Шаги:
Создайте аккаунт
Вы можете создать учетную запись на веб-сайте PyPI. Затем создайте файл .pypirc в своем домашнем каталоге:
В целях тестирования вы можете добавить «pypitest» индексный сервер в ваш .pypirc файл:
Зарегистрируйте свой пакет
Если это первый выпуск вашего пакета, вам необходимо зарегистрировать его с помощью PyPI. Используйте команду register setup.py. Она попросит вас ввести пароль. Обратите внимание, что я указываю его на тестовый репозиторий:
Загрузите свой пакет
Для более глубокого погружения в тему распространения ваших пакетов ознакомьтесь с разделом «Пакеты Python».
Заключение
В этом уроке мы прошли полноценный процесс написания библиотеки Python, ее упаковки и распространения через PyPI. На этом этапе у вас должны быть все инструменты для написания и обмена вашими библиотеками с остальным миром.
Кроме того, не стесняйтесь посмотреть, что у нас есть для продажи и для изучения на рынке, и, пожалуйста, задавайте любые вопросы и предоставляйте свою ценную обратную связь, используя приведенный ниже канал.
Как написать, упаковать и распространить библиотеку в Python
Python — отличный язык программирования, но упаковка — один из его слабых мест. Это общеизвестный факт в обществе. Установка, импорт, использование и создание пакетов значительно улучшились за эти годы, но это все еще не на одном уровне с новыми языками, такими как Go и Rust, которые многому научились из борьбы Python и других зрелых языков.
В этом руководстве вы узнаете все, что вам нужно знать о написании, упаковке и распространении ваших собственных пакетов.
Как написать библиотеку Python
Библиотека Python — это связная коллекция модулей Python, организованная в виде пакета Python. В общем, это означает, что все модули находятся в одном каталоге и этот каталог находится в пути поиска Python.
Давайте быстро напишем небольшой пакет Python 3 и проиллюстрируем все эти концепции.
Пакет патологии
Python 3 имеет отличный объект Path, который является огромным улучшением по сравнению с неуклюжим модулем Python 2 os.path. Но ему не хватает одной важной возможности — найти путь к текущему сценарию. Это очень важно, когда вы хотите найти файлы доступа относительно текущего скрипта.
Во многих случаях сценарий может быть установлен в любом месте, поэтому вы не можете использовать абсолютные пути, а рабочему каталогу может быть присвоено любое значение, поэтому вы не можете использовать относительный путь. Если вы хотите получить доступ к файлу в подкаталоге или родительском каталоге, вы должны быть в состоянии выяснить текущий каталог скриптов.
Объединяя C++ и Python. Тонкости Boost.Python. Часть первая
Оглавление
Введение
Исходим из того, что у вас уже установлен удобный инструментарий для сборки динамически-линкуемой библиотеки на C++, а также установлен интерпретатор Python.
Также понадобится скачать библиотеку Boost, после чего собрать её, следуя инструкции для своей ОС Windows или Linux.
В двух словах в Windows все действия сводятся к двум строкам в командной строке. Распакуйте скачанный архив Boost в любое место на диске, перейдите туда в командной строке и наберите последовательно две команды:
Для сборки x64 нужно добавить аргумент address-model=64
Если у вас уже есть библиотека Boost, но вы не устанавливали Python, либо вы скачали и установили свежий интерпретатор Python и хотите собрать только Boost.Python, это делается дополнительным ключом —with-python
То есть вся строка для сборки только Boost.Python с 64-разрядной адресацией выглядит так:
Стоит заметить, что x64 сборку следует заказывать, если у вас установлен Python x64. Также и модули для него нужно будет собирать с 64-разрядной адресацией.
Ключ —with-python серьёзно сэкономит вам время, если вам из библиотеки Boost кроме функционала Boost.Python ничего не нужно.
Если у вас установлено несколько интерпретаторов, крайне рекомендую прочитать подробную документацию по сборке Boost.Python
После сборки у вас появятся в папке Boost\stage\lib собранные библиотеки Boost.Python, они нам очень скоро понадобятся.
Настраиваем проект на C++
Не забываем также про сборку под x64 если вы собираете для 64-разрядного Python.
Обычный класс с простыми полями
Итак, давайте заведём нашем новом проекте сразу три файла:
some.h
some.cpp
wrap.cpp
В файлах some.h и some.cpp опишем некий замечательный класс Some, который обернём для Python в модуле example в файле wrap.cpp — для этого в файле wrap.cpp следует подключить и использовать макрос BOOST_PYTHON_MODULE( example ) <… >, также для лаконичности будет совсем не лишним использовать using namespace boost::python. В целом наш будущий модуль будет выглядеть вот так:
В файле some.h нам следует наваять объявление нашего чудо-класса. Для объяснения большинства базовых механизмов нам достаточно всего два поля:
Допустим класс содержит описание чего-то, что имеет имя и целочисленный идентификатор. Как ни странно этот несложный класс вызовет кучу сложностей, благодаря в основном стандартному классу string, перегрузкам методов, константной ссылке и статическому свойству NOT_AN_IDENTIFIER, которое мы конечно же тоже введём:
Разумеется эта константа нужна как идентификатор для объекта созданного конструктором по умолчанию, опишем также и другой конструктор, задающий оба поля:
В файле some.cpp опишем реализацию данных конструкторов, в дальнейшем реализацию описывать я не буду, но давайте конструкторы напишем вместе:
Одновременно с появлением класса Some будет появляться обёртка класса для Python в файле wrap.cpp:
Здесь используется бессовестный обман зрения и шаблон boost::python::class_, который создаёт описание класса для Python в указанном модуле с помощью Python C-API, жутко сложного и непонятно при описании методов, а потому полностью скрытого за объявлением простого метода def() на каждой строчке.
Конструктор по умолчанию и конструктор копирования создаются для объекта по умолчанию, если не указано обратное, но мы этого ещё коснёмся чуть ниже.
Уже сейчас можно собрать модуль, импортировать его из интерпретатора Python и даже создать экземпляр класса, но ни прочитать его свойства, ни вызывать методы мы у него пока не можем, пока они физически отсутствуют.
Давайте это исправим, создадим «богатейшее» API нашего чудо класса. Вот полный код нашего заголовочного файла some.h:
Раз реализация методов получилась также довольно короткой, давайте приведу и код some.cpp:
Что ж, самое время описать обёртку в файле wrap.cpp:
Первый метод Some::ID() оборачивается без каких-либо проблем:
Зато второй с результатом в виде константной ссылки на строку уже показывает, что всё не так просто:
Как видите, можно указать, с каким именем в Python будет создан аргумент метода. Как известно имя аргумента в Python куда важнее чем в C++. Рекомендую указывать имена аргументов для каждой обёртки метода, принимающего параметры:
Осталось описать статическим свойством константу NOT_AN_IDENTIFIER:
Здесь используется специальная функция boost::python::make_getter, которая по свойству класса генерирует get-функцию.
Вот так примерно выглядит наша обёртка:
Если написать несложный тестовый скрипт вроде этого (Python 3.x):
Питонизируем обёртку класса
Итак, класс со всеми методами обёрнут, но счастья не наступило. При попытке из командной строки Python выполнив Some(123,’asd’) мы не увидим описания полей и вообще объекта, поскольку мы не обзавелись методом __repr__, так же как и преобразование к строке, тот же print( Some(123,’asd’) ) будет ужасно неинформативен, так как мы не обзавелись методом __str__. Очевидно также, что работать со свойствами через методы на C++ на Python не имеет смысла, это в C++ мы не имеем возможности заводить property, в Python их можно и нужно завести. Однако как же мы навесим методы на готовый класс C++ предназначенные для Python?
Очень просто: вспоминаем, что в Python методы не отличаются от функций, принимающих первым параметром ссылку на self — экземпляр класса. Заводим в C++ такие функции прямо во wrap.cpp и описываем их как методы в обёртке:
Сами функции можно описать например вот так:
Со свойствами идентификатора и имени ещё проще, так как методы set и get для них уже описаны в классе:
Выполняем print( Some(123,’asd’) ) и просто Some(123,’asd’) из командной строки после from example import * и видим что подозрительно похожее на встроенный питоновский dict: < ID: 123, Name: 'asd' >
Почему бы не завести свойство инициализирующее экземпляр Some от стандартного dict и обратно?
Заведём ещё пару питонистических функций и заведём свойство as_dict:
Здесь использован класс boost::python::dict, для доступа на уровне C++ к стандартному dict Python.
Также есть классы для доступа к str, list, tuple, называются они соответственно. Ведут себя классы в C++ так же как и в Python в плане операторов, вот только возвращают по большей части boost::python::object, из которого требуется ещё извлечь значение через функцию boost::python::extract.
Делаем домашнюю библиотеку с Notion и Python
Мне всегда было интересно, как бы получше распределить книги у себя в электронной библиотеке. В итоге пришел к такому варианту с автоматическим подсчетом количества страниц и прочими плюшками. Всех заинтересованных прошу под кат.
Часть 1. Dropbox
Все книжки у меня лежат на дропбоксе. Существуют 4 категории, на которые я все поделил: Учебник, Справочник, Художественное, Нехудожественное. Но справочники я в табличку не добавляю.
Пути до книг у меня примерно такие:
Если книга художественная, то категория (то есть «Дизайн» в случае выше) убирается.
Я решил не заморачиваться с API дропбокса, благо у меня стоит их приложение, которое синхронизирует папку. То есть план такой: берем книги из папки, каждую книгу прогоняем через счетчик слов, добавляем в Notion.
Часть 2. Добавляем строку
Сама таблица должна выглядеть приблизительно следующим образом. ВНИМАНИЕ: названия столбцов лучше делать латиницей.
Использовать будем неофициальное API Notion’а, потому что официальное еще не завезли.
Потом пишем код на подключение к Notion’у.
Далее напишем-ка функцию на добавление строки в табличку.
Что тут происходит. Мы берем и добавляем новую строку к таблице в первой строке. Далее сплитим наш путь по «/» и получаем теги. Теги — в плане «Художественное», «Дизайн», кто автор и так далее. Потом задаем все необходимые поля таблички.
Часть 3. Считаем слова, часы и прочие прелести
Это уже задачка посложнее. Как мы помним, у нас есть два формата: епаб и пдф. Если с епабом все понятно — слова там, вероятно, точно есть, то вот насчет пдф все не так однозначно: оно может состоять просто из склеенных изображений.
Так что функция для подсчета слов в пдф у нас будет выглядеть следующим образом: мы берем количество страниц и домножаем на определенную константу (среднее число слов на странице).
Это самое WORDS_PER_PAGE для страницы A4 примерно равно 300.
Теперь давайте напишем функцию для подсчета страничек. Будем юзать PyPDF2.
Далее напишем штучку для подсчета страниц в епабе. Используем epub_converter. Тут мы берем книжку, конвертируем в строки, и для каждой строки считаем слова.
Теперь сделаем подсчет времени. Берем наше любимое количество слов и делим на вашу скорость чтения.
Часть 4. Соединяем все части
Нам нужно обойти все возможные пути в нашей папке с книгами. Проверить, есть ли уже книга в Notion: если есть — строку создавать нам уже не надо.
Потом нам нужно определить тип файла, в зависимости от этого подсчитать количество слов. В конце добавить книгу.
Вот такой код у нас получается:
А функция для проверки, добавлена ли книга, выглядит так:
Заключение
Спасибо всем, кто прочитал эту статью. Надеюсь, она вам поможет читать больше 🙂
Как опубликовать свою Python библиотеку на PyPI
Leo Matyushkin
Благодаря импортированию, модули Python удобно использовать
1. Создаём элементы библиотеки
Корневой каталог dist_alx содержит следующие файлы и директории:
Подробнее об указанных файлах можно прочитать в документации библиотеки setuptools. Примеры содержания файлов в нашем случае:
Любая публикуемая на PyPI библиотека обязана иметь три вышеуказанных элемента. Помимо этого, пакет должен выполнять следующие условия:
2. Подготавливаем код
3. Создаём аккаунт PyPI
Скриншот окна регистрации в репозитории PyPI
4. Публикуем библиотеку
Публикация осуществляется из командной строки или терминала. Команды идентичны в Windows и Linux.
Для публикации нам потребуется установить две библиотеки – setuptools и twine :
Переходим к родительскому каталогу.
Развёртываем пакет запустив setup.py :
Обратите внимание, что теперь в родительской папке будут созданы две новых директории ( egg-info и dist ).
Теперь с помощью twine развёртываем пакет на PyPI:
Здесь нужно указать своё имя пользователя и пароль PyPI.
Вуаля! Пакет размещён на сервере и готов к использованию!
Переходим в учётную запись на PyPI, идём в раздел Your projects. Если всё в порядке, вы увидите библиотеку.
5. Используем
Заключение
Прежде чем размещать пакет на главном сервере, опубликуйте его сначала на тестовом сервере PyPI.
Итак, последовательность создания библиотеки Python следующая:
Если вы думаете, что пока не готовы публиковать свою библиотеку, вам могут быть полезны такие наши публикации: