open atlas
↑ К треку
Python для JS/TS-разработчиков PY · 02 · 02

HTTP-запросы, venv и упаковка

Полезный Python-инструмент — это два навыка: HTTP-запросы через requests (всегда таймаут, raise_for_status, ключ из env) и изоляция через venv, чтобы зависимости одного проекта не ломали другой.

PY Middle ◷ 18 min
Уровень
ОсновыJuniorMiddleSenior

Ты пишешь 30-строчный Python-скрипт, который зовёт LLM API, чтобы суммировать пачку тикетов. На твоём ноутбуке он работает. Передаёшь коллеге — pip install requests падает: его глобальный Python уже пинит несовместимый urllib3 ради другого проекта. Это чинишь, и скрипт виснет навсегда на одном запросе, потому что у провайдера был сбой, а ты не задал таймаут. Потом ревью безопасности находит API-ключ, захардкоженный в файле, который ты только что запушил. Ни одна из этих проблем не сложная — это ровно три вещи, на которых спотыкается JS/TS-разработчик, приходящий в Python: небрежный HTTP, отсутствие изоляции окружения и секреты в исходниках.

За несколько минут ты увидишь, почему ровно три провала коллеги случаются и какое минимальное изменение предотвращает каждый из них до попадания в прод.

HTTP через requests: четыре вещи, которые нельзя пропускать

requests — де-факто HTTP-клиент Python: для Python он то же, что fetch для браузера, только синхронный и куда удобнее стандартного urllib. GET с query-параметрами и JSON-ответом — это три строки:

import requests

resp = requests.get(
    "https://api.example.com/search",
    params={"q": "python", "limit": 10},
    headers={"Accept": "application/json"},
    timeout=10,
)
resp.raise_for_status()          # превратить 4xx/5xx в исключение
data = resp.json()               # разобранный dict, как await resp.json() у fetch

POST с JSON — той же формы: передаёшь json=, и requests сериализует тело и сам ставит Content-Type: application/json:

resp = requests.post(url, json={"name": "ada"}, timeout=10)

Две привычки отличают скрипт, который выживает в проде, от того, что виснет или врёт:

  • Всегда передавай timeout=. Это сеньорский момент и тот, что упускают новички. По умолчанию requests ждёт вечно. Один медленный или мёртвый апстрим заморозит весь твой скрипт — или каждый воркер в пуле — без ошибки и без восстановления. У fetch тот же пробел (нужен AbortController); в Python ты просто передаёшь число.
  • Вызывай raise_for_status(). В отличие от fetch, который отклоняется только при сетевом сбое и спокойно резолвит 500, requests по умолчанию не бросает исключение на 4xx/5xx — resp выглядит нормально, а resp.json() может вернуть тело с ошибкой, которое ты примешь за данные. raise_for_status() превращает плохой статус в HTTPError, который можно поймать. (fetch — зеркало: там ты сам проверяешь resp.ok.)

Для множества параллельных вызовов или async-приложения бери httpx — та же поверхность API, что у requests, но с async/await и HTTP/2. Retry с backoff стоит завести для любого реального апстрима: на 429 или 503 подожди 0.5с, 1с, 2с… и повтори несколько раз, а не падай на первом же сбое. (Библиотеки вроде tenacity или адаптер Retry из urllib3 делают это за тебя.)

Выигрыш для AI-склейки: безопасный вызов LLM HTTP API

Бо́льшая часть «AI-интеграции» — ровно этот паттерн: POST JSON-тела на эндпоинт провайдера с заголовком авторизации, затем разбор JSON, который вернулся. Сеньорская деталь — где живёт ключ: никогда в исходниках.

import os
import requests

api_key = os.environ.get("LLM_API_KEY")  # читаем из окружения, не из файла
if not api_key:
    raise RuntimeError("LLM_API_KEY не задан")

resp = requests.post(
    "https://api.provider.example/v1/chat/completions",
    headers={"Authorization": f"Bearer {api_key}"},
    json={
        "model": "some-model",
        "messages": [{"role": "user", "content": "Суммируй этот тикет: ..."}],
    },
    timeout=30,                          # LLM медленные; дай им запас, но ограничь
)
resp.raise_for_status()
reply = resp.json()["choices"][0]["message"]["content"]

os.environ["LLM_API_KEY"] — это Python-аналог process.env.LLM_API_KEY. Ты задаёшь его в shell, в .env, загружаемом через python-dotenv, или в хранилище секретов деплой-платформы — и ключ никогда не появляется в репозитории, в git history или при демонстрации экрана. Хардкод ключа — самый частый способ утечки учётных данных. Щедрый timeout=30 тут тоже важен: ответы модели медленные, поэтому тесный таймаут отменяет хорошие запросы, а отсутствие таймаута означает, что одна застрявшая генерация вешает твой инструмент бесконечно.

Node / TypeScriptPythonПримечание
fetch()requests / httpxrequests синхронный; httpx добавляет async + HTTP/2
await resp.json()resp.json()синхронно; без await
сам проверяешь resp.okresp.raise_for_status()ни тот, ни другой не бросают на 4xx/5xx по умолчанию
AbortControllertimeout=без него requests ждёт вечно
process.env.X + dotenvos.environ[“X”] + python-dotenvдержи секреты вне исходников
package.jsonpyproject.toml / requirements.txtманифест проекта + зависимостей
node_modules/.venv/место установки для одного проекта
npm installpip install (или uv)разрешение + установка зависимостей
Викторина

Твой скрипт зовёт LLM API и иногда виснет на минуты без ошибки. Самая вероятная причина?

Викторина

Провайдер возвращает 500 с JSON-телом ошибки. Твой код делает resp.json() без raise_for_status(). Что произойдёт?

Окружения и упаковка: зачем нужен venv

Спроси себя: сколько Python-проектов у тебя или коллег на одной машине? Каждый предъявляет свои требования к версиям библиотек — а по умолчанию Python позволяет им всем делить одну глобальную папку установки. Вот провал коллеги из хука. Python по умолчанию ставит пакеты в один глобальный site-packages, общий для каждого проекта на машине. Проекту A нужен urllib3==1.26; проекту B нужен urllib3==2.x. При глобальной установке второй pip install перезаписывает первый — и теперь тот проект, что загрузит неверную версию, ломается. Нет node_modules на проект, чтобы их развести.

Виртуальное окружение (venv) — и есть эта граница на проект. Это лёгкая изолированная копия интерпретатора со своим site-packages, поэтому зависимости каждого проекта живут в своей папке и никогда не сталкиваются:

python -m venv .venv          # создать изолированное окружение в ./.venv
source .venv/bin/activate     # macOS/Linux  (Windows: .venv\Scripts\activate)
pip install requests httpx    # ставит В .venv, не глобально

Пока окружение активно, python и pip указывают на .venv — аналогично тому, как node_modules ограничивает пакеты одним проектом, только venv изолирует сам интерпретатор, а не одни библиотеки. Папку .venv/ добавляешь в .gitignore (как node_modules/), а вместо неё коммитишь манифест, чтобы кто угодно мог её пересоздать.

Пиннинг: requirements.txt и переход к pyproject.toml

Изоляция — половина истории; вторая половина — воспроизводимость. Если ты pip install requests сегодня, а коллега через месяц, вы можете получить разные версии. Пинни их. Классический файл — requirements.txt:

requests==2.32.3
httpx==0.27.2

Воссоздаёшь точный набор через pip install -r requirements.txt внутри свежего venv. Это package.json + lock-файл, разнесённые по соглашению, а не в один файл. Современный стандартизированный дом для метаданных и зависимостей проекта — pyproject.toml (PEP 621): объявление зависимостей там плюс быстрый резолвер вроде uv или poetry даёт настоящий lock-файл и настройку одной командой, гораздо ближе к опыту npm install. Для быстрого скрипта requirements.txt сойдёт; для всего, что ты отгружаешь или делишь, предпочитай pyproject.toml.

Почему это работает

Почему не просто pip install --user или установить глобально и пропустить venv? Потому что «глобально» — это разделяемое изменяемое состояние на каждый проект и даже на собственный Python-тулинг ОС. Пин одного проекта тихо меняет рантайм другого, апгрейды превращаются в русскую рулетку, а на некоторых системах pip в системный Python заблокирован напрочь (PEP 668, «externally-managed-environment»). venv на проект делает зависимости явными, одноразовыми и воспроизводимыми — удали .venv, пересоздай из манифеста, готово.

Выбери лучший вариант

Ты начинаешь небольшой Python-инструмент, который будут запускать и двое коллег. Как управлять его зависимостями?

Расставь шаги по порядку

Расставь шаги, чтобы настроить и запустить HTTP-инструмент, безопасно зовущий LLM API:

  1. 1 python -m venv .venv — создать изолированное окружение
  2. 2 source .venv/bin/activate — активировать, чтобы pip/python указывали на него
  3. 3 pip install -r requirements.txt — поставить пиннутые зависимости в venv
  4. 4 export LLM_API_KEY=… (или загрузить .env) — ключ в окружении, не в исходниках
  5. 5 python tool.py — POST с заголовком авторизации + таймаутом, затем raise_for_status()
Вспомните перед уходом
  1. 01
    Пройди по безопасному POST к LLM API в Python: что нужно задать и чем каждое отличается от JS fetch?
  2. 02
    pip install коллеги ломает другой проект. Объясни почему и фикс через venv + манифест от начала до конца.
Итог

Полезный Python-инструмент — это два навыка, работающих вместе. Первый — HTTP: requests — это Python-аналог fetch, но синхронный: requests.get/post с params/json и resp.json() без await. Сеньорские привычки не обсуждаются: всегда передавай timeout= (иначе requests ждёт вечно, и один застрявший апстрим вешает весь скрипт), всегда вызывай raise_for_status() (requests не бросает на 4xx/5xx, поэтому тело ошибки проходит как данные), а для множества или async-вызовов бери httpx плюс retry с backoff на 429/503. Выигрыш для AI-склейки той же формы: POST JSON-тела с заголовком Authorization: Bearer провайдеру, читай ключ из os.environ, чтобы он не жил в исходниках, и дай медленным моделям щедрый, но ограниченный таймаут. Второй — окружения: Python по умолчанию ставит глобально, поэтому пины зависимостей двух проектов сталкиваются — venv на проект и есть изолирующая граница (свой интерпретатор и site-packages), в gitignore как node_modules. Пинни версии в requirements.txt или, лучше, в pyproject.toml с uv/poetry, чтобы точное окружение было воспроизводимо на любой машине. Изоляция плюс пиннинг — вот что превращает «работает на моём ноуте» в инструмент, который коллеги реально могут запустить. Теперь, когда встретишь зависший скрипт или сломанный pip install у коллеги, — знаешь, какие две строки добавить первыми: timeout= и venv.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 6 завершено
Связанные уроки

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.