Skip to content

Python Packages

Python Packages

Що таке PyPi та pip

PyPi (Python Package Index) - це онлайн-репозиторій, де розміщені різні пакети та бібліотеки для мови програмування Python. Ці пакети містять готовий код та ресурси, які можуть бути використані розробниками для вирішення різних завдань.

pip (Pip Installs Packages) - це інструмент командного рядка, який використовується для встановлення та керування пакетами з PyPi. За допомогою pip розробники можуть швидко та легко встановлювати пакети, необхідні для своїх проектів, і також оновлювати або видаляти їх.

Отже, PyPi - це репозиторій пакетів для Python, а pip - інструмент для їх встановлення та керування.

Які є популярні менеджери пакетів ? [💡11/100]

Менеджери пакетів у Python дозволяють легко встановлювати, оновлювати та керувати бібліотеками і залежностями.

Найпопулярніші

  • pip - стандартний менеджер пакетів у Python, який постачається разом із сучасними версіями Python. Використовується для встановлення бібліотек із репозиторію PyPI (Python Package Index). Підтримує базову роботу із залежностями та має широкий набір команд, таких як install, uninstall, freeze.
pip install requests  # Install the requests library
pip freeze > requirements.txt  # Export dependencies to a file
  • poetry - сучасний інструмент для управління залежностями і пакетування проєктів. Включає можливості керування середовищем і генерації файлу pyproject.toml, який стає стандартом для Python-проєктів.
poetry add flask  # Add Flask to the project
poetry install  # Install all dependencies from pyproject.toml
  • pipenv - менеджер для роботи із залежностями та віртуальними середовищами, який інтегрує функції pip і virtualenv. Генерує файли Pipfile і Pipfile.lock для точного відтворення середовища.
pipenv install django  # Install Django and manage virtual environment
pipenv shell  # Activate the virtual environment
  • conda - менеджер пакетів і середовищ, популярний серед науковців та користувачів Anaconda. Підтримує як Python-пакети, так і системні залежності. Добре підходить для управління середовищами з важкими залежностями.
conda install numpy  # Install numpy using conda
conda create -n myenv python=3.9  # Create a new environment
  • uv - надшвидкий менеджер пакетів і проєктів на Rust (від авторів Ruff), що замінює зв'язку pip + pip-tools + virtualenv + pipenv/poetry одним інструментом. На великих залежностях встановлює на порядки швидше. Працює з pyproject.toml, тримає lock-файл uv.lock для детермінованих збірок, підтримує групи залежностей і керує версіями самого Python. У нових проєктах здебільшого витіснив poetry/pipenv.
uv add fastapi   # add a dependency (updates pyproject.toml + uv.lock)
uv sync          # install exactly what is pinned in the lock file
uv run pytest    # run a command inside the project environment

Безпека ланцюга залежностей (SCA)

Summary

Стороння залежність може містити вразливість або навіть зловмисний код (скомпрометований maintainer, typosquatting) - це ризик ланцюга постачання (OWASP A03). Захист будують шарами: фіксувати точні версії + хеші, сканувати залежності на відомі вразливості й оновлюватися контрольовано.

  • Фіксація версій і хешів (pinning). Lock-файл (uv.lock, poetry.lock, requirements.txt із --require-hashes) фіксує точні версії й контрольні суми - підмінений пакет із тим самим іменем і версією, але іншим вмістом не встановиться.
  • Сканери вразливостей (SCA). pip-audit, Safety, Snyk, Trivy, OWASP Dependency-Check звіряють залежності з базами CVE й позначають вразливі. Запускають у CI.
  • Автооновлення зі сповіщеннями. Dependabot / Renovate створюють PR на оновлення залежностей і сповіщають про вразливі версії.
  • Обмеження. Сканери ловлять лише відомі вразливості з баз - свіжого бекдору в новій версії пакета вони не побачать. Тому додатково: мінімізувати кількість залежностей, переглядати оновлення, не оновлюватися наосліп.

Які є популярні Python бібліотеки?

  • NumPy - бібліотека для обчислень з масивами даних, що надає зручні інструменти для роботи з масивами та матрицями, включаючи багато математичних функцій та операцій над масивами.
  • Pandas - бібліотека для аналізу та обробки даних, що надає високопродуктивні та зручні інструменти для роботи з табличними даними, включаючи функціональність для читання, запису, фільтрації, агрегації, об'єднання та маніпуляції даними.
  • Matplotlib - бібліотека для візуалізації даних у Python, що дозволяє створювати графіки, діаграми, діаграми розсіювання та інші типи візуалізації даних.
  • Requests - бібліотека для роботи з HTTP-запитами у Python, що дозволяє виконувати GET, POST та інші типи запитів, управляти сеансами, передавати параметри, обробляти cookies.
  • HTTPX, aiohttp - аналоги requests для асинхронних запитів.
  • Beautiful Soup - бібліотека для витягування даних з HTML та XML файлів.
  • SQLAlchemy - бібліотека для роботи з базами даних, що надає ORM (Object Relational Mapping) та інші інструменти для зручної роботи з різними базами даних, такими як SQLite, MySQL, PostgreSQL та іншими.
  • TensorFlow і PyTorch - бібліотеки машинного навчання та глибокого навчання, що надають інструменти для створення та навчання нейронних мереж, реалізацію різних архітектур моделей та виконання складних завдань машинного навчання.

Що таке веб-фреймворк? Популярні python фреймворки

Веб-фреймворк - це програмний каркас або набір бібліотек та інструментів, які допомагають розробникам створювати веб-додатки та веб-сайти. Вони надають структуру та організовану методологію для розробки веб-додатків, що дозволяє розробникам ефективно працювати над різноманітними аспектами веб-розробки, такими як маршрутизація, обробка запитів, робота з базами даних, безпека і багато інших.

Основні фреймворки

  • Django - це повнофункціональний та високорівневий веб-фреймворк, який надає інструменти для швидкої та зручної розробки веб-додатків. Він включає в себе всі необхідні компоненти, такі як система аутентифікації, ORM (Object-Relational Mapping), система адміністрування і багато інших.
  • Flask - це мінімалістичний веб-фреймворк, який надає базовий набір інструментів для створення веб-додатків. Він дуже легкий і гнучкий, що дає розробникам більший контроль над тим, як організовувати свій код.
  • FastAPI - це сучасний фреймворк для створення веб-додатків API, який відзначається швидкістю розробки та автоматичною генерацією документації за допомогою Swagger.

Існують ще наступні фреймворки

  • Tornado - це асинхронний веб-фреймворк, призначений для створення високоефективних веб-додатків, особливо тих, які вимагають багато взаємодій на основі подій.
  • CherryPy - це легкий і простий веб-фреймворк, який надає основні засоби для створення веб-додатків.
  • Pyramid - це фреймворк, який надає багато гнучкості та дозволяє розробникам обирати компоненти, які вони хочуть використовувати, щоб побудувати свій додаток.
  • Web2py - це фреймворк зі вбудованим середовищем розробки, який полегшує розробку та розгортання веб-додатків.

pytest [💡14/100]

pytest - це популярна Python бібліотека для тестування коду.

Основні переваги pytest

  • Дозволяє створювати тести у вигляді звичайних функцій, які починаються з test_, без необхідності використання класів.
  • Забезпечує зручний механізм для асертів: assert працює інтуїтивно і надає зрозумілий звіт у разі помилок.
  • Підтримує автоматичне виявлення тестових файлів і функцій, що спрощує організацію тестів.
  • Надає фікстури (fixtures) для налаштування та повторного використання коду, що виконується до або після тестів.
  • Підтримує розширення функціоналу через вбудовані або сторонні плагіни, наприклад, для тестування API, продуктивності, інтеграції з CI/CD.
  • Пропонує докладні та зрозумілі звіти про результати виконання тестів, зручні для аналізу.

Фікстури у pytest - це спеціальні функції або методи, які допомагають підготувати середовище для виконання тестів. Вони дозволяють створювати початкові умови для тестів та об'єкти, які тести будуть використовувати. Фікстури можуть викликати одна одну, бути динамічними. Можна налаштувати scope - як довго буде існувати фікстура під час виконання тестів. Це дозволяє керувати тим, коли фікстура буде створюватися та знищуватися - перед кожною функцією-тестом (function), один раз для кожного класу тестів (class) чи один раз на весь період виконання pytest-сесії, тобто під час виконання всіх тестів (session).

import pytest

def add(a, b):  # Function to be tested
    return a + b

def test_add():  # Test function
    assert add(2, 3) == 5  # Assert the result
    assert add(-1, 1) == 0
    assert add(0, 0) == 0

@pytest.fixture  # Using a fixture for setup and teardown
def sample_data():
    return {"key": "value"}

def test_with_fixture(sample_data):
    assert sample_data["key"] == "value"

Links

Які ви знаєте інструменти для перевірки кодового стилю? [💡11/100]

Flake8 - обгортка для python утиліт - pyflakes, pycodestyle, mccabe. Flake8 має схожий на pylint основний функціонал. Проте він має кілька відмінностей та особливостей

  • Можливості статистичних звітів обмежені підрахунком кількості кожної з помилок (—statistics) та їх загальною кількістю (—count).
  • Для запуску у кілька потоків (—jobs=<num>) використовується модуль multiprocessing, через що багатопоточність не працюватиме на системах Windows.
  • Відсутня можливість генерації звітів у форматі json, при виклику з ключем —bug-report створюється лише заголовок для звіту з вказанням платформи та версій вхідних утиліт.
  • Коментарі у коді, які блокують вивід. Додавання коментаря # noqa у рядку з помилкою вилучить його зі звіту.
  • Під час редагування можна вказати виключені помилки "на льоту" у ключі —extend-ignore=<errors>
  • Перевірка синтаксису у рядках doctest (-doctests).
  • Наявність Version Control Hooks. Інтеграція з системами контролю версій відбувається буквально за допомогою двох команд (підтримуються git та mercurial).
  • Розширюваність. Flake8 для аналізу коду Python дозволяє створювати та використовувати плагіни. За допомогою плагінів у Flake8 можна: додавати додаткові перевірки, використовувати інші формати звітів або автоматично виправляти знайдені помилки. На PyPi можна знайти велику кількість відкритих плагінів.

black - популярний інструмент для форматування коду. Підтримує гнучке налаштування.

Pylint поєднує в собі як пошук логічних, так і стилістичних помилок. Цей потужний інструмент для аналізу коду Python відрізняється великою кількістю перевірок і різноманітністю звітів.

autopep8 модифікує код, який не відповідає PEP8. Перевірка відповідності конвенціям здійснюється за допомогою утиліти pycodestyle. У autopep8 є підтримка багатопоточності, рекурсивного обходу каталогів, можливість збереження налаштувань у файлі, вказання діапазону рядків для виправлення, фільтрація помилок та безпосереднє форматування файлу.

Vulture - невеликий утилітарний інструмент для пошуку "мертвого" коду в програмах Python. Він використовує модуль ast стандартної бібліотеки і створює абстрактні синтаксичні дерева для всіх файлів початкового коду в проекті. Далі він шукає всі об'єкти, які були визначені, але не використовуються. Vulture корисно використовувати для очищення та виявлення помилок у великих базових кодах.

Ruff - сучасний лінтер і форматер на Rust, що замінює зв'язку Flake8 + Black + isort + частину pylint/pyupgrade одним інструментом. На великих кодових базах працює на порядки швидше за Python-аналоги, тому й витіснив їх у нових проєктах. Підтримує сотні правил (зокрема ті, що раніше були плагінами Flake8), автовиправлення (ruff check --fix) і форматування (ruff format). Перевірку типів Ruff не робить - для цього лишаються окремі type-checker'и (mypy, pyright, а також новіші швидкі - ty від авторів Ruff і pyrefly від Meta).

Links

Профілювання Python-коду

Summary

Спершу - вимір, потім профілювання. Не оптимізувати "за відчуттям": дочекатися сигналу від моніторингу (latency-перцентилі за межами SLA, алерт у трасуванні на кшталт Sentry) і лише тоді запускати профайлер на гарячій ділянці. Інструменти діляться на детерміновані (інструментують кожен виклик - точно, але з великими накладними витратами) і семплінгові (періодично знімають стек - дешево, придатно для продакшну).

CPU

  • cProfile (stdlib, детермінований) - кількість викликів і сумарний/кумулятивний час на кожну функцію: python -m cProfile -s cumtime script.py. Результат зручно дивитися через pstats чи snakeviz. Оверхед спотворює абсолютні числа - порівнювати відносно.
  • line_profiler (kernprof -l -v, сторонній) - час по рядках усередині функції, коли вузьке місце треба локалізувати точніше за рівень функції.
  • py-spy (семплінговий, сторонній) - підключається до вже запущеного процесу без перезапуску й змін у коді, малі накладні витрати, будує flame graph. Підходить для продакшну.

Пам'ять

  • tracemalloc (stdlib) - відстежує алокації, робить знімки й показує топ місць за спожитою пам'яттю; основний інструмент пошуку витоків/росту пам'яті.
  • memory_profiler (сторонній) - споживання пам'яті по рядках.

Безперервне профілювання (continuous profiling)

Семплінгові профайлери (py-spy, Pyroscope/Grafana, Datadog, Sentry Profiling) працюють постійно у продакшні з низькими накладними витратами і агрегуються у flame graph - так видно гарячі шляхи під реальним навантаженням. Для проблем, що відтворюються лише на проді, профайлер запускають проти shadow-трафіку (дзеркало бойового), щоб не зачепити основний.

Оптимізація гарячих циклів (CPython)

Summary

Коли профайлер показав гарячу ділянку, спершу шукають алгоритмічну ваду (зайвий вкладений цикл, повторні обчислення, лінійний пошук замість set/dict). Лише потім - мікрооптимізації рівня CPython. А якщо й цього мало - компіляція (PyPy/Cython) чи C-розширення.

  • Винесення інваріантів циклу. Обчислення, що не залежить від ітерації, рахують один раз поза циклом, а не на кожному кроці. Часто це найбільший виграш.
  • Лінива оцінка для агрегатів. sum(x*x for x in data) замість sum([x*x for x in data]) - не матеріалізує проміжний список (менше пам'яті й виділень).
  • set/dict замість лінійного пошуку. x in seen_set - O(1) проти x in seen_list - O(n).
  • Прив'язування глобалів і методів до локальних імен у тісних циклах. Доступ до локальної змінної (LOAD_FAST) швидший за пошук глобального імені (LOAD_GLOBAL) і за розіменування атрибута. У гарячому циклі rnd = random.random один раз, а далі rnd() економить пошук на кожній ітерації. Виграш помітний лише на дуже гарячих циклах.
  • Перевіряти гіпотези заміром. Деякі "оптимізації" шкодять: наприклад, SystemRandom повільніший за random, а зайве передвиділення списку може не дати виграшу. Кожну зміну звіряють із заміром, а не за відчуттям.

Якщо рівень CPython вичерпано - PyPy (трасуючий JIT компілює гарячі цикли) або Cython (типізація + компіляція у C-розширення).

SQLAlchemy

SQLAlchemy винесений у окремий файл - див. python_framework/sqlalchemy.md: Core vs ORM, Lazy/Eager loading, Session vs sessionmaker, connection pool параметри, прогрів пула.

Celery [💡20/100]

Celery — це зручний інструмент для асинхронного виконання завдань у Python. Він дозволяє виконувати довготривалі або ресурсозатратні операції у фоновому режимі, що підвищує продуктивність та швидкодію веб-застосунків.

Основні аспекти Celery

  • Це система управління чергами завдань, яка використовує брокери повідомлень (наприклад, RabbitMQ, Redis) для обробки черг.
  • Підтримує асинхронні, відкладені або періодичні завдання через інтеграцію з планувальниками, такими як Celery Beat.
  • Забезпечує високу масштабованість завдяки можливості запуску кількох воркерів, які паралельно обробляють завдання.
  • Підтримує відстеження статусу завдань та отримання результатів через систему бекендів, таких як Redis, SQL, чи AMQP.
  • Інтегрується з популярними веб-фреймворками, такими як Django чи Flask, що дозволяє легко налаштувати асинхронну обробку в проєктах.
  • Забезпечує автоматичну повторну спробу завдань у разі помилок.

Celery підтримує

  • Асинхронні завдання: Завдання виконуються у фоновому режимі без блокування основного процесу.
  • Періодичні завдання: Використання Celery Beat дозволяє запускати завдання за розкладом.
  • Моніторинг: Підтримує інтеграцію з такими інструментами, як Flower, для моніторингу черг і стану завдань.

  • Налаштування Celery у проєкті

from celery import Celery


app = Celery("tasks", broker="redis://localhost:6379/0", backend="redis://localhost:6379/0")  # Initialize Celery


@app.task  
def add(x, y):  # Define a task
    return x + y
  • Запуск завдання
from tasks import add

result = add.delay(4, 6)  # Call the task asynchronously

print("Task Status:", result.status)  # Check the status or get the result
print("Task Result:", result.get(timeout=10))
  • Запуск воркера
celery -A tasks worker --loglevel=info

Taskiq - async-native альтернатива. Celery історично синхронний (воркер виконує задачі в потоках/процесах). Taskiq - сучасніша черга задач, побудована навколо async/await і з повноцінними type hints: задачі-корутини виконуються в event loop воркера, що природніше для async-застосунків (FastAPI). Підтримує різні брокери (NATS, RabbitMQ, Redis, Kafka) і result-backends, має планувальник на кшталт Celery Beat. Вибір: Celery - зрілий стандарт із величезною екосистемою; Taskiq - коли застосунок повністю async і потрібна типізація.

  • Періодичні завдання з Celery Beat:
from celery import Celery
from celery.schedules import crontab

app = Celery("tasks", broker="redis://localhost:6379/0")

@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):  # Define periodic tasks

    sender.add_periodic_task(60.0, sample_task.s("Hello"), name="Print every minute")  # Execute every minute

@app.task
def sample_task(message):
    print(message)

Celery надає набір примітивів для створення складних робочих процесів і управління асинхронними завданнями. Ці примітиви дозволяють будувати ланцюжки, групи завдань і виконувати їх паралельно чи послідовно.

Основні примітиви Celery

  • Task - основний будівельний блок у Celery. Завдання (task) — це окрема функція, яка може виконуватись асинхронно. Завдання можна запускати незалежно, передавати аргументи, отримувати статус і результат.
@app.task
def add(x, y):
    return x + y

result = add.delay(4, 5)  # Asynchronous call
print(result.get())  # Get result
  • Chain - використовується для послідовного виконання завдань, де результат одного завдання передається як вхідний аргумент наступному.
from celery import chain

@app.task
def multiply(x, y):
    return x * y

chain_result = chain(add.s(4, 5), multiply.s(10))()
print(chain_result.get())  # Output: (4 + 5) * 10 = 90
  • Group - дозволяє виконувати кілька завдань паралельно, після чого результати збираються разом. Корисно для незалежних обчислень.
from celery import group

group_result = group(add.s(2, 2), add.s(4, 4), add.s(6, 6))()
print(group_result.get())  # Output: [4, 8, 12]
  • Chord - комбінація групи та завершального завдання. Спочатку виконується група завдань паралельно, а після їх завершення викликається фінальне завдання, яке отримує результати всієї групи.
from celery import chord

def final_task(results):
    return sum(results)

chord_result = chord(group(add.s(2, 2), add.s(4, 4), add.s(6, 6)))(final_task.s())
print(chord_result.get())  # Output: 4 + 8 + 12 = 24
  • Map і Starmap - використовуються для виконання функцій з ітеративними аргументами, подібно до функції map.
from celery import group

mapped_result = group(add.starmap([(2, 2), (4, 4), (6, 6)]))()
print(mapped_result.get())  # Output: [4, 8, 12]
  • Chunks - використовується для розбиття великого набору даних на менші частини та паралельної обробки кожної частини.
from celery import chunks

chunked_result = add.chunks([(i, i) for i in range(10)], 3)()
print(chunked_result.get())
  • Retry - завдання можуть автоматично повторюватись у разі помилки, що корисно для роботи з ненадійними зовнішніми сервісами.
@app.task(bind=True, max_retries=3)
def fragile_task(self):
    try:
        # Perform some operation
        pass
    except Exception as e:
        self.retry(countdown=5, exc=e)

Що використовувати для логування помилок у Celery?

Summary

Стандартний модуль logging через celery.utils.log.get_task_logger(__name__) для базового логування, плюс інтеграція з зовнішнім сервісом (Sentry, ELK, Loki, Datadog) для агрегації, трасування і алертингу.

1. Стандартний логер Python + Celery

Celery використовує модуль logging зі стандартної бібліотеки. Бажано брати логер через спеціальний хелпер - він додає префікс імені таски, що полегшує фільтрацію в логах.

from celery.utils.log import get_task_logger

logger = get_task_logger(__name__)

@app.task
def process_order(order_id):
    logger.info("Processing order %s", order_id)
    try:
        ...
    except Exception:
        logger.exception("Failed to process order %s", order_id)
        raise

2. Інтеграція з зовнішніми сервісами

  • Sentry - найпопулярніший варіант. Через sentry-sdk з інтеграцією CeleryIntegration автоматично збираються винятки, traceback, теги, аргументи таски.
  • ELK Stack (Elasticsearch + Logstash + Kibana) - структуроване логування у JSON через python-json-logger або відправка в Logstash через UDP/HTTP.
  • Loki + Grafana, Graylog, Datadog - альтернативи з готовими дашбордами.

Базова інтеграція з Sentry:

import sentry_sdk
from sentry_sdk.integrations.celery import CeleryIntegration

sentry_sdk.init(
    dsn="https://...",
    integrations=[CeleryIntegration()],
    traces_sample_rate=0.1,
)

Як Celery вміє працювати з пріоритетними повідомленнями?

Summary

Через параметр priority у apply_async() + черга, створена з x-max-priority. Працює тільки з RabbitMQ-брокером, лише в межах однієї черги, і має ряд обмежень.

my_task.apply_async(args=[...], priority=9)

Налаштування

  1. Створити чергу з аргументом x-max-priority:
from kombu import Queue

app.conf.task_queues = [
    Queue("default", queue_arguments={"x-max-priority": 10}),
]
  1. Запустити воркер: celery -A proj worker -Q default.

Принцип роботи

  • Celery ставить priority в AMQP message property.
  • RabbitMQ сортує повідомлення в черзі за пріоритетом.
  • Воркер бере повідомлення в порядку пріоритету в межах однієї черги.

Обмеження:

  • Пріоритет працює тільки в межах однієї черги - глобального пріоритету між high/low чергами немає.
  • AMQP priority range - 0..255, але рекомендують 0–9 (більше = повільніше через переоцінку порядку).
  • Нативний пріоритет на рівні брокера має лише RabbitMQ (x-max-priority). Redis сам пріоритетів не має, але Celery їх емулює через окремі черги на кожен рівень (priority_steps / queue_order_strategy) - працює, хоч і гірше за нативний (у Redis-режимі 0 - найвищий пріоритет).
  • prefetch_count впливає: якщо воркер заздалегідь "захопив" 10 низькопріоритетних задач - високопріоритетна чекатиме на наступний раунд.

Як зрозуміти, що таска виконається?

Summary

Жорсткої гарантії виконання немає - є гарантія доставки до брокера і механізми відстеження результату: result_backend, acks_late, моніторинг (Flower, Sentry), retry на помилках.

Чек-лист для "хочу бути впевнений, що таска точно відпрацює":

  • result_backend - увімкнути бекенд для збереження результату (Redis, Postgres, RPC). Без нього AsyncResult.get() не поверне нічого.
  • acks_late=True - підтвердження від воркера надсилається після завершення таски, а не на старті. Якщо воркер впав посередині - повідомлення повернеться в чергу.
  • task_reject_on_worker_lost=True - якщо воркер впав, повернути повідомлення в чергу замість підтвердження.
  • max_retries + retry_backoff=True - автоматичні повторні спроби з експоненційним нарощуванням затримки.
  • Моніторинг: Flower (real-time monitoring UI), Sentry (помилки), Prometheus + Grafana (метрики).
  • Idempotency на стороні таски - обов'язково, бо acks_late + retry гарантують at-least-once доставку, а не exactly-once.
@app.task(
    bind=True,
    acks_late=True,
    autoretry_for=(Exception,),
    max_retries=5,
    retry_backoff=True,
    retry_jitter=True,
)
def reliable_task(self, payload):
    process(payload)