Как написать и опубликовать свою библиотеку Python
Основные этапы создания библиотеки Python
Наиболее эффективный и современный подход к созданию библиотеки Python заключается в использовании pyproject.toml совместно с инструментом build и менеджером setuptools. Этот способ рекомендован официальной документацией Python и поддерживает гибкое управление метаданными, зависимостями и версиями.
Как организовать структуру проекта для будущей библиотеки?
Стандартная структура выглядит так:
my_library/
├── pyproject.toml
├── README.md
├── LICENSE
├── src/
│ └── my_library/
│ ├── __init__.py
│ ├── module1.py
│ └── module2.py
└── tests/
├── __init__.py
└── test_module1.py
создание библиотеки python (создание библиотеки python)
Папка src облегчает тестирование и избегает случайного импорта кода из исходников.
Типичная ошибка: размещение кода в корне проекта без папки src. Это может привести к неоднозначности импортов при установке. Решение: всегда использовать явный каталог src или указывать package-dir в конфигурации.
Как настроить pyproject.toml для сборки?
Минимальный файл pyproject.toml:
[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "my-library"
version = "0.1.0"
authors = [{ name = "Ваше Имя", email = "email@example.com" }]
description = "Краткое описание библиотеки"
readme = "README.md"
license = { text = "MIT" }
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
]
dependencies = [
"requests>=2.28",
]
[project.urls]
Homepage = "https://github.com/username/my-library"
[tool.setuptools.packages.find]
where = ["src"]
Python написание библиотеки (создание собственной библиотеки python)
Проблема: несоответствие имени пакета (my-library vs my_library). PyPI требует, чтобы имя не содержало подчёркиваний, поэтому используйте дефисы. В коде же импортируется my_library. Решение: задать name с дефисом, а директорию назвать с подчёркиванием.
Как собрать и установить библиотеку локально?
- Установить сборщик:
pip install build - Собрать пакет:
python -m build - Установить локально:
pip install dist/my_library-0.1.0-py3-none-any.whl
Для разработки удобно использовать pip install -e . (редактируемый режим). Это позволяет изменения в коде сразу видеть в импортах.
Ошибка: при pip install -e . может потребоваться явно указать --no-build-isolation при несовместимости версий setuptools. Решение: обновить инструменты и создать виртуальное окружение.
Как создать простую библиотеку без pyproject.toml (устаревший способ)?
Раньше использовали setup.py и setup.cfg. Пример setup.py:
from setuptools import setup, find_packages
setup(
name='my-library',
version='0.1.0',
packages=find_packages(where='src'),
package_dir={'': 'src'},
install_requires=['requests'],
)
написать библиотеку на python (создание библиотеки на python)
Недостатки: меньше автоматизации, отсутствие единого стандарта, сложности с зависимостями для сборки.
Проблема: использование чистого setup.py может вызвать ошибки при запуске из-за исполнения произвольного кода. Современные инструменты (setuptools+pyproject.toml) этого лишены.
Как использовать Poetry для управления зависимостями и публикацией?
Poetry автоматически создаёт виртуальные окружения и упрощает процесс публикации. Инициализация:
poetry new my-library
cd my-library
poetry add requests
Структура включает pyproject.toml с секцией [tool.poetry]. Для публикации: poetry publish --build.
Ошибка: при конфликте версий Python и зависимостей Poetry может не установить пакет. Решение: точно указать версию Python в pyproject.toml.
Как создать namespace packages (пакеты с общим пространством имён)?
Например, два разных проекта company.a и company.b, каждый находится в своём репозитории. Структура:
company/
├── __init__.py (можно оставить пустым или с комментарием)
├── a/
│ ├── __init__.py
│ └── module_a.py
В pyproject.toml каждого пакета указывается:
[tool.setuptools.packages.find]
where = ["src"]
namespaces = true
Такой подход хорош для крупных экосистем, где модули публикуются отдельно.
Типичная ошибка: забыть добавить файл __init__.py в промежуточные пакеты пространства имён. Без него импорт может не сработать.
Как добавить в библиотеку консольный интерфейс (CLI)?
Создайте внутри пакета файл cli.py с функцией main и укажите точку входа в pyproject.toml:
[project.scripts]
my_tool = "my_library.cli:main"
Теперь после установки будет доступна команда my_tool.
Цели каждого варианта: pyproject.toml – универсальность и соответствие стандартам; setup.py – поддержка старых проектов; Poetry – простота и автоматизация; namespace packages – модульная архитектура; CLI – удобство пользователя.
Расширенные примеры создания и публикации библиотеки
Пример 1. Полный пакет с несколькими модулями и тестами
Создадим библиотеку для работы с числами Фибоначчи.
Файловая структура:
fiblib/
├── pyproject.toml
├── README.md
├── src/
│ └── fiblib/
│ ├── __init__.py
│ ├── core.py
│ └── utils.py
└── tests/
├── __init__.py
├── test_core.py
└── test_utils.py
Содержимое pyproject.toml:
[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "fiblib"
version = "0.2.0"
description = "Библиотека для работы с числами Фибоначчи"
readme = "README.md"
license = { text = "MIT" }
dependencies = []
[tool.setuptools.packages.find]
where = ["src"]
Файл src/fiblib/core.py:
from functools import lru_cache
@lru_cache(maxsize=None)
def fibonacci(n: int) -> int:
"""Возвращает n-е число Фибоначчи."""
if n < 0:
raise ValueError("n must be non-negative")
if n == 0:
return 0
if n == 1:
return 1
return fibonacci(n-1) + fibonacci(n-2)
Файл src/fiblib/utils.py:
def is_fibonacci(x: int) -> bool:
"""Проверяет, является ли число x числом Фибоначчи."""
import math
if x < 0:
return False
a = 5*x*x + 4
b = 5*x*x - 4
s1 = int(math.isqrt(a))
s2 = int(math.isqrt(b))
return s1*s1 == a or s2*s2 == b
Файл src/fiblib/__init__.py:
from .core import fibonacci
from .utils import is_fibonacci
Файл tests/test_core.py:
import pytest
from fiblib import fibonacci
def test_fibonacci_0():
assert fibonacci(0) == 0
def test_fibonacci_1():
assert fibonacci(1) == 1
def test_fibonacci_10():
assert fibonacci(10) == 55
def test_fibonacci_negative():
with pytest.raises(ValueError):
fibonacci(-1)
Файл tests/test_utils.py:
from fiblib import is_fibonacci
def test_is_fibonacci_positive():
assert is_fibonacci(21) == True
assert is_fibonacci(22) == False
def test_is_fibonacci_zero():
assert is_fibonacci(0) == True
def test_is_fibonacci_negative():
assert is_fibonacci(-5) == False
Установка в редактируемом режиме и запуск тестов:
pip install -e .
pytest -v
Результат вывода pytest (усечённый):
============================= test session starts ============================== collected 6 items tests/test_core.py .... [ 66%] tests/test_utils.py .. [100%] ============================== 6 passed in 0.12s ===============================
Пример 2. Публикация на PyPI с помощью twine
После сборки пакета (python -m build) создаются файлы в dist/. Загрузить их на PyPI можно через twine:
pip install twine
twine upload dist/*
При этом потребуется токен из аккаунта PyPI. Для тестирования используют TestPyPI: twine upload --repository testpypi dist/*.
Пример 3. Автоматизация версионирования с setuptools_scm
Чтобы версия бралась из Git-тегов, добавьте в pyproject.toml:
[build-system]
requires = ["setuptools>=68", "wheel", "setuptools_scm>=6"]
[project]
dynamic = ["version"]
[tool.setuptools_scm]
Теперь при каждом коммите с тегом вида v0.1.0 версия обновится автоматически. Без тега будет сформирована версия на основе dev.
Пример 4. Добавление документации с помощью Sphinx
Создайте папку docs/ и выполните:
sphinx-quickstart
Настройте docs/conf.py:
import os
import sys
sys.path.insert(0, os.path.abspath('../src'))
extensions = ['sphinx.ext.autodoc']
Сгенерируйте документацию по докстрингам:
sphinx-apidoc -o docs/source ../src/my_library
make html
Пример 5. Создание пакета с C-расширением через setuptools
Для производительности можно добавить модуль на Cython или C++. Пример с Cython:
# src/my_library/fast_math.pyx
def fast_square(int x):
return x * x
В pyproject.toml укажите:
[build-system]
requires = ["setuptools", "cython", "wheel"]
[tool.setuptools]
package-data = {"my_library": ["*.so"]}
Сборка скомпилирует .so файл автоматически. Пример результата:
>>> from my_library.fast_math import fast_square >>> fast_square(5) 25
Пример 6. Логирование и конфигурация в библиотеке
Добавьте в __init__.py:
import logging
logging.getLogger(__name__).addHandler(logging.NullHandler())
Это позволяет пользователю настраивать логирование без появления лишних сообщений.
Пример 7. Обработка ошибок при несовместимости Python
В pyproject.toml укажите:
[project]
requires-python = ">=3.8"
Если пользователь попытается установить на более старую версию, pip выдаст ошибку совместимости.