Дата и время публикации:
Проблема и решение
1. Суть проблемы
Заключается в том, что все написанное на Python не относится к компилируемому или собираемому коду, т.к. выполняется в интерпретаторе бинарного кода CPython и находится в уже относительного виде исполняемых сценариев или подключаемых модулей другими сценариями по ходу выполнения.
Поэтому для СPython предусмотрена команда Python Package Index (PIP), с помощью которой мной будет собран и установлен пакет, который имеет shellenv-run.py и подключаемый им файл модуля shellenv.py проекта python-shellenv
2. Решение
2.1 Структура проекта python-shellenv
Пусть имеем два файла shellenv-run.py и подключаемый им модуль shellenv.py, а также README.md, как показано в дампе 2.1.1
Дамп 2.1.1
~/Projects/python-shellenv$ exa --tree . . ├── README.md └── shellenv ├── shellenv-run.py └── shellenv.py
Но, этого мало потому что структура пакета shellenv, должна содержать ряд файлов, которые используются утилитами подобно pip [3.3] и build [3.4] для построения пакета [3.1]
Поэтому, чтобы на выходе сборки получить пакет shellenv, показанную только что структуру нужно привести к удобоваримому виду, как показано в дампе 2.1.2
Дамп 2.1.2
~/Projects/python-shellenv$ exa --tree -L 3. . ├── LICENSE ├── pyproject.toml ├── README.md ├── setup.cfg └── src └── shellenv_module ├── __init__.py ├── shellenv-run.py └── shellenv.py
Поддиректория src/shellenv_module проекта python-shellenv должна содержать __init__.py, если считается, что она содержит файлы пакета. Таким образом, это позволяет избежать директории с общими именами, такими как string, которые могут встретится на пути позднее во время поиска составных частей модуля. При этом, в самом простом случае, __init__.py может быть пустым файлом, но так же может содержать исполняемый код для пакета или устанавливать переменную __all__, как показано в листинге 2.1.3
Листинг 2.1.3
all = [ 'shellenv' ]
Это нужно, чтобы в дальнейшем была возможность импортировать подмодуль shellenv, входящий в пакет shellenv_module, как показано в листинге 2.1.4
Листинг 2.1.4
... from shellenv_module import shellenv ...
Теперь можно наполнить файлы pyproject.toml, setup.cfg и LICENSE
Файл pyproject.toml
Cодержит требования для построения текущего проекта с использованием инструмента setuptools, позволяющего с легкостью создавать дистрибутивы на базе и с учетом зависимостей пакетов окружения python [3.5]. Содержимое файла pyproject.toml приведен в листинге 2.1.5
Листинг 2.1.5
# This is a TOML document. title = "TOML's file for the My Project" [build-system] requires = [ "setuptools>=42", "wheel" ] build-backend = "setuptools.build_meta"
Согласно, PEP 518 для построения пакетов в Python нужен файл проекта с расширением .toml должна содержать таблицу [build-system], которая разрешает зависимости [3.6]. При этом, build-system.requires предоставляет список пакетов, которые необходимы, чтобы построить пакет.
Файл setup.cfg
Теперь, после настройки учета зависимостей, нужно наполнить файл setup.cfg с таблицами метаданных, которые приведены в листинге 2.1.6
Листинг 2.1.6
[metadata] name = shellenv_module-rjaan version = 1.0 author = Andrew Rzhavskov author_email = rjaan@yandex.ru description = print SHELL's enviroment variables on standard output long_description = file: README.md long_description_content_type = text/markdown url = https://localhost/python-shellenv.git project_urls = Bug Tracker = https://localhost/.../issues classifiers = Programming Language :: Python :: 3 License :: OSI Approved :: MIT License Operating System :: OS Independent [options] package_dir = = src packages = find: python_requires = >=3.8 [options.packages.find] where = src
В котором предлагаю особо взглянуть на опции пакета в таблице [options] и [options.packages.find], где корневая директория поиска файлов пакета. Потому что в назначении остальных полей таблицы [metadata] мне не представляется сложным для чего они предназначаются.
Листинг 2.1.7
В таблице [options] указывается одна из распространенных конфигураций пакета, когда все модули (файлы) исходного кода находятся в поддиректории часто называемой src [3.7], в которой находится поддиректория shellenv_module, как показанно в дампе 2.1.7,
Дамп 2.1.7
~/Projects/python-shellenv$ exa --tree --ignore-glob="*~|__pycache__|dist|*.egg-info" . ├── LICENSE ├── pyproject.toml ├── README.md ├── setup.cfg ├── shellenv.py └── src └── shellenv_module ├── __init__.py ├── shellenv-run.py └── shellenv.py
Для устранения избыточности букв в имени пакета, особенно при локальном использовании, рекомендую сократить имя директории с src/shellenv_module на src/shellenv и изменить название пакета в опции name в таблице [metadata] с shellenv_module-rjaan на shellenv также.
На завершающем этапе формирования структуры пакета наполняем файл LICENSE c условиями распространения под лицензией MIT и файл README.md для публикации GitHub или в своем локальном хранилище Git репозитариев. Содержимое указанных файлов приведено в листинге 2.1.8 и 2.1.9 соответственно.
Листинг 2.1.8
Copyright (c) 2018 The Python Packaging Authority 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.
Листинг 2.1.9
# (python-)shellenv is python's module to operate with environment variables **shellenv(_module)** is python's module to display enviroment info and it's consisting of two files written on Python. Both files may use as either an executed script or included module. ... This project is licensed under the Python Software Foundation License Version 2.
2.2 Сборка и установка пакета
Перед началом сборки пакета рекомендую обновить pip до последней версии, как показано в дампе 2.2.1
Дамп 2.2.1
python3 -m pip install --upgrade pip
Затем, нам нужно установить модуль build, если он не был установлен ранее, как показано в дампе 2.2.2
Дамп 2.2.2
~/Projects/python-shellenv$ python3 -m pip install --upgrade build Defaulting to user installation because normal site-packages is not writeable Collecting build Downloading build-0.7.0-py3-none-any.whl (16 kB) Collecting tomli>=1.0.0 Downloading tomli-2.0.1-py3-none-any.whl (12 kB) Collecting pep517>=0.9.1 Downloading pep517-0.12.0-py2.py3-none-any.whl (19 kB) Collecting packaging>=19.0 Downloading packaging-21.3-py3-none-any.whl (40 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 40.8/40.8 KB 483.1 kB/s eta 0:00:00 Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /usr/lib/python3/dist-packages (from packaging>=19.0->build) (2.4.7) Installing collected packages: tomli, packaging, pep517, build WARNING: The script pyproject-build is installed in '/home/user/.local/bin' which is not on PATH. Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. Successfully installed build-0.7.0 packaging-21.3 pep517-0.12.0 tomli-2.0.1
Модуль build используется для генерации дистрибутива пакетов, который будет создан командой, указанной в дампе 2.2.3
Дамп 2.2.3
~/Projects/python-shellenv$ python3 -m build ERROR Source /home/user/Projects/python-shellenv does not appear to be a Python project: no pyproject.toml or setup.py
Выполнение команды python3 -m build, которая закончилось с ошибкой: "no pyproject.toml or setup.py", потому что файлы проекта должны иметь статическую конфигурацию в pyproject.toml или динамическую в setup.py. Поэтому мне пришлось переименовать ранее названный по недразумению файл статической конфмгурации shellenv.toml в pyproject.toml и повторно запустить генерацию подгружаемого RIP архива, как показано в дампе 2.2.4
Дамп 2.2.4
~/Projects/python-shellenv$ python3 -m build * Creating venv isolated environment... * Installing packages in isolated environment... (setuptools>=42, wheel) * Getting dependencies for sdist... ... Successfully built shellenv-package-rjaan-0.1.tar.gz and shellenv_package_rjaan-0.1-py3-none-any.whl
Далее, полученные файлы закладываем в локальный индексный репозитарий пакетов Python, который находится в директории, например, как у меня в /home/git/pypisrv/, как показано в дампе 2.2.5а (неоптимизированное имя) или в дампе 2.2.5б (ноптимизированное имя)
Дамп 2.2.5a
~ /home/git/pypisrv$ exa --tree . ├── shellenv/ └── shellenv-package-rjaan-0.1.tar.gz
Дамп 2.2.5б
~ /home/git/pypisrv$ exa --tree . ├── shellenv/ └── shellenv-1.3.tar.gz
После чего, можно выполнить установку пакета shellenv, как показано в дампе 2.2.6 для версии пакета с оптимизированной версией
Дамп 2.2.6
pip install --upgrade -U shellenv --extra-index-url http://localhost:8080/simple
При этом, если использовать --index-url, то возникает ошибка разршения зависимостей, которую проще обойти с помощью опцией --extra-index-url. При этом, следует учитывать, что рекомендуемая опция --no-deps с --index-url [3.9] не всегда приносит, даже если устанавить все пакеты в локальный индексный репозитарий пакетов Python.
В дампе 2.2.7 приводится пример импорта shellenv и получение значения переменной окружения $HOME
Дамп 2.2.7
>>> import shellenv >>> shellenv.getenv('HOME') '/home/user'
3. Библиография
3.1 Python Packaging User Guide -- packaging projects
3.2 The __init__.py,on the module search path.
3.3 Python Packaging User Guide -- pip
3.4 Python Packaging User Guide -- build
3.5 Python Packaging User Guide - setuptools
3.6 PEP 518 – Specifying Minimum Build System Requirements for Python Projects
3.7 Configuring setup() using setup.cfg files