Дата и время публикации:
Назначение и использование
1. Назначение
Скрипт сценария setup.py содержит сценарий выполнения setuptools, который позволяет не только производить сборку, но и установку пакета в формате weel [3.1]. Поэтому, если файл pyproject.toml отсутствует или не определена фоновая служба сборки (build-backend), таже утилита PIP, не говоря уже об buildout, будет искать сценарий setup.py [3.2].
При этом,
- за частую setup.py на ряду с setup.cfg и, например, src/<package>/__init__.py может иметь одни и теже метаданные, которые можно использовать путем извлечения их значений из указанных файлов с использованием методов и функций, являщиеся встроенными или импортируемые из модулей Python;
- setup.py может содержать формат использования не только setuptools, но так же для distutils и других модулей Python, которые основаны на стиле подачи метаданных в формате setup.py
2. Использование файла Setup.py
Как уже было сказано выше, setup.py является давно устоявшимся форматом представления дерева исходного кода, который часто используется с setuptools, пример которого показана в листинг 2.1
Листинг 2.1
import io import os import re from setuptools import setup, find_packages from setuptools.config import read_configuration def read_config( vkey ): """ Read given configuration file from root directoty and returns options from it as a dict. """ … def read(filename, encoding='utf-8'): """read file contents""" … def get_package_version(): """get version from top-level package init""" … setup( name = "shellenv", version=get_package_version(), url = 'https://github.com/rjaan/python-shellenv.git', license = 'PSF', description = "You able to operate SHELL's enviroment variables", author = 'Andrew Rzhavskov', packages = find_packages('shellenv'), package_dir = {'': 'src'}, install_requires = [ 'setuptools>=42', 'python-dotenv>=0.19' ], python_requires = '>=3.8', long_description=read('README.md'), classifiers=[ "Programming Language :: Python :: 3", "OSI Approved::Python Software Foundation License", "Operating System :: OS Independent" # Get strings from # http://pypi.python.org/pypi?%3Aaction=list_classifiers ], zip_safe=True, options={"bdist_wheel": {"universal": True}}, platforms=["any"], )
В листинге 2.1 , приведен фрагмент файла setup.py, обспечивающий процесс сборки и установки модуля python-shellenv, осуществляемый методом setuptools:setup,который принимает для этого [,достяжения этих целей,] методанные в виде списка атрибутов, перечисляемых ниже [3.5], и базируется на основе модуля distutils [3.4].
Атрибут name
Содержит текстовую строку имени пакета.
Атрибут version
Содержит текстовую строку версии пакета, которая извлекается функций get_package_version() из файла setup.cfg или __init__.py, текст как показано в листинге 2.2
Листинг 2.2
... def get_package_version(): """get version from top-level package init""" version_file = read('src/shellenv/__init__.py') version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M) if version_match: return version_match.group(1) """if you didn't able to read nothing from top-level package init, you should try to get the version from top-level package file setup.cfg """ version=read_config( 'version' ) if version == None : raise RuntimeError('Unable to find version string.') return version ...
Функция get_package_version(), показанная в листинге 2.2, сначала пытается извлеч текстовую строку с версией пакета из файла __init__.py с использованием функции read_config(), текст которой показан в листинге 2.3
Листинг 2.3
... def read(filename, encoding='utf-8'): """read file contents""" full_path = os.path.join(os.path.dirname(__file__), filename) with io.open(full_path, encoding=encoding) as fh: contents = fh.read().strip() return contents ...
Затем, get_package_version() пытается извлечь версию пакета из опции 'version' файла setup.cfg уже c помощью функции read_config(), текст которой показана в листинге 2.4
Листинг 2.4
def read_config( vkey ): """ Read given configuration file from root directoty and returns options from it as a dict. """ conf_dict = read_configuration(os.path.dirname(os.path.abspath(__file__))+'/'+'setup.cfg' ) return conf_dict.get('metadata')[vkey]
Атрибут url
Содержит текстовую строку с адресом URL на домашнюю страницу с исходным кодом, в данном случае на Git репозитрарий python-shellenv.git во внешнем хранилище на GitHub
Атрибут license
Содержит текстовую строку лицензии под условиями которой распространяется данный пакет. В моем случае, такой является лицензия PSF (Python Software Foundation License).
Лицензия Python Software Foundation License
В данном случае, согласно тексту лецензии, данная лицензия не имеет обязательного заголовка [3.6]. Поэтому предлагаю воспользоваться лицензией от удостоверяющего пакетного центра Python (The Python Packaging Authority), текст которой приведен в листинге 2.5
Листинг 2.5
opyright (c) 2022 Andrew Rzhavskov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
При этом не забываем проставить себя или организацию, которой принадлежат права на этот программный продукт.
Атрибут description
Содержит текстовую строку с кратким описанием и/или назначением пакета Python
Атрибут author
Содержит текстовую строку c указанием авторства пакета Python
Атрибут packages
Содержит список строковых значений, возвращаемые методом find_packages модуля setooptools find_packages, в единственном аргументе, которой передано имя пакета. Хотя, это можно было не делать, так как в данном случае поддержка сборки и установки нескольких пакетов не используется.
Атрибут package_dir
Содержит словарь с отражаемых имен к реальным названию директорий, где искать содержимое пакета.
Атрибут install_requires
Содержит список текстовых строк с условиями зависимостей от каких версий других пакетов, которые будут установлены из индекса пакетов Python, если они не были установлены ранее.
Атрибут python_requires
Содержит список текстовых строк с условиями зависимостей c какими версиями Python будет совместим пакет
При этом, условия удовлетворения зависимостей устанавливаются согласно PEP-440 [3.10]
Атрибут long_description
Содержит текстовый контекст файла README.md/README.rst, имеющие форматированный текст, в заданной кодировки. Например, в моем случае, ей является широко используемая utf-8, что видно из применяемой получения данного контекста функции read(), показанной в листинге 2.3
Атрибут classifiers
Содержит список из классификаторов, которые категоризируют проект. Полный список приведен в https://pypi.org/classifiers/, а детальные инструкции как провести категоризацию или вернее классификацию проекта, приведены в PEP-301 [3.12].
В моем случае, как показано в листинге 2.1, мной проект python-shellev был отнесен как платформенно независимый, использующий Python версии 3 и распространяемый под лицензией PSF, о которой рассказывал выше в соответствие с классификаторами: "Programming Language :: Python :: 3", "OSI Approved::Python Software Foundation License", "Operating System :: OS Independent".
Атрибут zip_safe
Для максимальной производительности пакеты Python лучше всего устанавливать в формате zip. Однако, не все пакеты могут быть приведены к сжатому виду, формату zip, потому что они могут потребовать доступ либо к файлам исходного кода или данных операционной системы. Поэтому, какой будет выбран вариант установки проекта Python в формате zip-файла, или директории — зависит от того, как был определен флаг zip_file
Так, если флаг zip_file = True, то пакет будет восприниматься в формате файла ZIP, в обратном случае, он будет рассматриваться в качестве директории [3.13]
Атрибут zip_safe
Содержит словарь с опциями по умолчанию передаваемые скриптом setup. В данном случае, как показано в листинге 2.1, опции передаются расширению bdist_wheel, который обеспечивает сборку пакета в формате wheel
Атрибут Platforms
Содержит текстовую строку c указанием используемой платформы. В моем случае, как показано в листинге 2.1, any – платформа независимая.
Атрибут Entry_points
Содержит словарь, с ключевыми словами которого определяется какие точки входа существуют в данном Python-модуле (проекте) [3.16]. Это позволяет обеспечить более дружественный интерфейс вызова в виде одного или нескольких скриптов-оберток Python-модуля чем через команду pip -m <module>
Применяется entry_points согласно следующему правилу, приведенному в листинге 2.6 для проекта python-shellenv [3.17], на выходе сборке которого получается модуль shellenv и скрипт pypi-shellenv
Листинг 2.6
... from setuptools import setup entry_points={ "console_scripts": ["py-shellenv=shellenv.__main__:main"], }, ...
Из которого вырисовывается общее правило:
"console_scripts": ["<name>=<module>.<submodule>:<exec-function>"],;
- <name> — содержит название скрипта
- <module> — Python пакет, который имеет
- <submodule> — подмодуль и исполняемую функцию.
При этом, в имени <submodule> может быть опущено расширение .py
В заключении
В локальном виртуальном окружении venv-shellenv производим сборку и установку нового модуля, в результате которой получим установленный скрипт-обертку py-shellenv, как показано в дампе 2.7
Дамп 2.7
(venv-shellenv) ~/.../venv-shellenv/src/python-shellenv/$ ../../bin/pip install . Processing /home/user/Projects/python-venv/venv-shellenv/src/python-shellenv Installing build dependencies ... done Getting requirements to build wheel ... done Preparing metadata (pyproject.toml) ... done ... Successfully built shellenv Installing collected packages: shellenv Successfully installed shellenv-1.5 (venv-shellenv) ~/.../venv-shellenv/src/python-shellenv/$ which py-shellenv ~/.../venv-shellenv/bin/py-shellenv Git: add main() in file __main__.py to create wrapper script when package's building and remove unnecessary мodule level "dunders" __author__ and __copyright__ from __main__.py
3. Библиография
3.1 PIP Documention — setup.py (legacy)
3.2 PEP 517 — A build-system independent format for source trees
3.3 Getting Started With setuptools and setup.py
3.4 Setuptool's source code in _init__.py line 73
3.7 Setuptools — Package Discovery and Namespace Package
3.8 PyPA — install_requires vs requirements files
3.9 PyPA — Packaging and distributing projects — attribute python-requires
3.10 PEP-440
3.11 PyPA — Packaging and distributing projects — README.rst / README.md
3.12 PEP-301 — Distutils Trove Classification
3.13 The PEAK Developers' Center — Setting the zip_safe flag
3.15 pip wheel